2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/rendering/text-typesetter.h>
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/integration-api/trace.h>
24 #include <dali/public-api/common/constants.h>
25 #include <dali/public-api/math/math-utils.h>
29 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
30 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
31 #include <dali-toolkit/internal/text/line-helper-functions.h>
32 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
33 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
34 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
35 #include <dali-toolkit/internal/text/rendering/view-model.h>
45 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
47 const float HALF(0.5f);
48 const float ONE_AND_A_HALF(1.5f);
51 * @brief Fast multiply & divide by 255. It wiil be useful when we applying alpha value in color
53 * @param x The value between [0..255]
54 * @param y The value between [0..255]
57 inline uint8_t MultiplyAndNormalizeColor(const uint8_t x, const uint8_t y) noexcept
59 const uint32_t xy = static_cast<const uint32_t>(x) * y;
60 return ((xy << 15) + (xy << 7) + xy) >> 23;
63 /// Helper macro define for glyph typesetter. It will reduce some duplicated code line.
66 * @brief Prepare decode glyph bitmap data. It must be call END_GLYPH_BITMAP end of same scope.
68 #define BEGIN_GLYPH_BITMAP(data) \
70 uint32_t glyphOffet = 0u; \
71 const bool useLocalScanline = data.glyphBitmap.compressionType != TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION; \
72 uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer; \
73 DALI_ASSERT_ALWAYS(glyphScanline && "Glyph scanline for buffer is null!");
76 * @brief Macro to skip useless line fast.
78 #define SKIP_GLYPH_SCANLINE(skipLine) \
79 if(useLocalScanline) \
81 for(int32_t lineIndex = 0; lineIndex < skipLine; ++lineIndex) \
83 TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
88 glyphScanline += skipLine * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize); \
92 * @brief Prepare scanline of glyph bitmap data per each lines. It must be call END_GLYPH_SCANLINE_DECODE end of same scope.
94 #define BEGIN_GLYPH_SCANLINE_DECODE(data) \
96 if(useLocalScanline) \
98 TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
102 * @brief Finalize scanline of glyph bitmap data per each lines.
104 #define END_GLYPH_SCANLINE_DECODE(data) \
105 if(!useLocalScanline) \
107 glyphScanline += data.glyphBitmap.width * glyphPixelSize; \
109 } // For ensure that we call BEGIN_GLYPH_SCANLINE_DECODE before
112 * @brief Finalize decode glyph bitmap data.
114 #define END_GLYPH_BITMAP() \
115 if(useLocalScanline) \
117 free(glyphScanline); \
119 } // For ensure that we call BEGIN_GLYPH_BITMAP before
122 /// Helper macro define end.
125 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
129 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
130 Vector2* position; ///< The position of the glyph.
131 TextAbstraction::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
132 uint32_t width; ///< The bitmap's width.
133 uint32_t height; ///< The bitmap's height.
134 int32_t horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
135 int32_t verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
139 * @brief Sets the glyph's buffer into the bitmap's buffer.
141 * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
142 * @param[in] position The position of the glyph.
143 * @param[in] color The color of the glyph.
144 * @param[in] style The style of the text.
145 * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
147 void TypesetGlyph(GlyphData& __restrict__ data,
148 const Vector2* const __restrict__ position,
149 const Vector4* const __restrict__ color,
150 const Typesetter::Style style,
151 const Pixel::Format pixelFormat)
153 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
155 // Nothing to do if the width or height of the buffer is zero.
159 // Initial vertical / horizontal offset.
160 const int32_t yOffset = data.verticalOffset + position->y;
161 const int32_t xOffset = data.horizontalOffset + position->x;
163 // Whether the given glyph is a color one.
164 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
165 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
166 const uint32_t glyphAlphaIndex = (glyphPixelSize > 0u) ? glyphPixelSize - 1u : 0u;
168 // Determinate iterator range.
169 const int32_t lineIndexRangeMin = std::max(0, -yOffset);
170 const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
171 const int32_t indexRangeMin = std::max(0, -xOffset);
172 const int32_t indexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
174 // If current glyph don't need to be rendered, just ignore.
175 if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
180 if(Pixel::RGBA8888 == pixelFormat)
182 uint32_t* __restrict__ bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
184 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
186 // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
187 // Just overwrite transparent color and return.
188 if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
190 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
192 // We can use memset here.
193 memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
194 bitmapBuffer += data.width;
199 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
201 // Precalculate input color's packed result.
202 uint32_t packedInputColor = 0u;
203 uint8_t* __restrict__ packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
205 *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
206 *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
207 *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
208 *(packedInputColorBuffer) = static_cast<uint8_t>(color->r * 255);
210 // Prepare glyph bitmap
211 BEGIN_GLYPH_BITMAP(data);
213 // Skip basic line of glyph.
214 SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
216 // Traverse the pixels of the glyph line per line.
219 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
221 BEGIN_GLYPH_SCANLINE_DECODE(data);
223 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
225 const int32_t xOffsetIndex = xOffset + index;
227 // Retrieves the color from the color glyph.
228 uint32_t packedColorGlyph = *(reinterpret_cast<const uint32_t*>(glyphScanline + (index << 2)));
229 uint8_t* __restrict__ packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
231 // Update the alpha channel.
232 const uint8_t colorAlpha = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
233 *(packedColorGlyphBuffer + 3u) = colorAlpha;
235 if(Typesetter::STYLE_SHADOW == style)
237 // The shadow of color glyph needs to have the shadow color.
238 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
239 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
240 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
246 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
249 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
250 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
251 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
253 if(data.glyphBitmap.isColorBitmap)
255 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
256 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
257 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
261 // Set the color into the final pixel buffer.
262 *(bitmapBuffer + xOffsetIndex) = packedColorGlyph;
265 bitmapBuffer += data.width;
267 END_GLYPH_SCANLINE_DECODE(data);
272 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
274 BEGIN_GLYPH_SCANLINE_DECODE(data);
276 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
278 // Update the alpha channel.
279 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
281 // Copy non-transparent pixels only
284 const int32_t xOffsetIndex = xOffset + index;
286 // Check alpha of overlapped pixels
287 uint32_t& currentColor = *(bitmapBuffer + xOffsetIndex);
288 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
290 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
291 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
292 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
293 // happen, for example, in the RTL text when we copy glyphs from right to left).
294 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
295 currentAlpha = std::max(currentAlpha, alpha);
296 if(currentAlpha == 255)
298 // Fast-cut to avoid float type operation.
299 currentColor = packedInputColor;
303 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
304 // The format is RGBA8888.
305 uint32_t packedColor = 0u;
306 uint8_t* __restrict__ packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
308 // Color is pre-muliplied with its alpha.
309 *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
310 *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
311 *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
312 *(packedColorBuffer) = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
314 // Set the color into the final pixel buffer.
315 currentColor = packedColor;
320 bitmapBuffer += data.width;
322 END_GLYPH_SCANLINE_DECODE(data);
330 // Below codes required only if not color glyph.
333 uint8_t* __restrict__ bitmapBuffer = data.bitmapBuffer.GetBuffer();
335 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
337 // Prepare glyph bitmap
338 BEGIN_GLYPH_BITMAP(data);
340 // Skip basic line of glyph.
341 SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
343 // Traverse the pixels of the glyph line per line.
344 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
346 BEGIN_GLYPH_SCANLINE_DECODE(data);
348 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
350 const int32_t xOffsetIndex = xOffset + index;
352 // Update the alpha channel.
353 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
355 // Copy non-transparent pixels only
358 // Check alpha of overlapped pixels
359 uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
361 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
362 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
363 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
364 // happen, for example, in the RTL text when we copy glyphs from right to left).
365 currentAlpha = std::max(currentAlpha, alpha);
369 bitmapBuffer += data.width;
371 END_GLYPH_SCANLINE_DECODE(data);
379 /// Draws the specified underline color to the buffer
381 const uint32_t bufferWidth,
382 const uint32_t bufferHeight,
383 GlyphData& glyphData,
384 const float baseline,
385 const float currentUnderlinePosition,
386 const float maxUnderlineHeight,
387 const float lineExtentLeft,
388 const float lineExtentRight,
389 const UnderlineStyleProperties& commonUnderlineProperties,
390 const UnderlineStyleProperties& currentUnderlineProperties,
393 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
394 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
395 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
396 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
398 int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
400 const uint32_t yRangeMin = underlineYOffset;
401 const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
402 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
403 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
405 // If current glyph don't need to be rendered, just ignore.
406 if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
411 // We can optimize by memset when underlineColor.a is near zero
412 uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
414 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
416 // Skip yRangeMin line.
417 bitmapBuffer += yRangeMin * glyphData.width;
419 // Note if underlineType is DASHED, we cannot setup color by memset.
420 if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
422 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
424 // We can use memset.
425 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
426 bitmapBuffer += glyphData.width;
428 if(underlineType == Text::Underline::DOUBLE)
430 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
431 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
432 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
434 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
435 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
437 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
439 // We can use memset.
440 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
441 bitmapBuffer += glyphData.width;
447 uint32_t packedUnderlineColor = 0u;
448 uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
450 // Write the color to the pixel buffer
451 *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
452 *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
453 *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
454 *(packedUnderlineColorBuffer) = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
456 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
458 if(underlineType == Text::Underline::DASHED)
460 float dashWidth = dashedUnderlineWidth;
463 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
465 if(Dali::EqualsZero(dashGap) && dashWidth > 0)
467 // Note : this is same logic as bitmap[y][x] = underlineColor;
468 *(bitmapBuffer + x) = packedUnderlineColor;
471 else if(dashGap < dashedUnderlineGap)
478 dashWidth = dashedUnderlineWidth;
485 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
487 // Note : this is same logic as bitmap[y][x] = underlineColor;
488 *(bitmapBuffer + x) = packedUnderlineColor;
491 bitmapBuffer += glyphData.width;
493 if(underlineType == Text::Underline::DOUBLE)
495 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
496 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
497 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
499 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
500 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
502 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
504 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
506 // Note : this is same logic as bitmap[y][x] = underlineColor;
507 *(bitmapBuffer + x) = packedUnderlineColor;
509 bitmapBuffer += glyphData.width;
515 /// Draws the background color to the buffer
516 void DrawBackgroundColor(
517 Vector4 backgroundColor,
518 const uint32_t bufferWidth,
519 const uint32_t bufferHeight,
520 GlyphData& glyphData,
521 const float baseline,
523 const float lineExtentLeft,
524 const float lineExtentRight)
526 const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
527 const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
528 const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
529 const int32_t xRangeMax = std::min(static_cast<int32_t>(bufferWidth), static_cast<int32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
531 // If current glyph don't need to be rendered, just ignore.
532 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
537 // We can optimize by memset when backgroundColor.a is near zero
538 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
540 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
542 // Skip yRangeMin line.
543 bitmapBuffer += yRangeMin * glyphData.width;
545 if(backgroundColorAlpha == 0)
547 for(int32_t y = yRangeMin; y < yRangeMax; y++)
549 // We can use memset.
550 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
551 bitmapBuffer += glyphData.width;
556 uint32_t packedBackgroundColor = 0u;
557 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
559 // Write the color to the pixel buffer
560 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
561 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
562 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
563 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
565 for(int32_t y = yRangeMin; y < yRangeMax; y++)
567 for(int32_t x = xRangeMin; x < xRangeMax; x++)
569 // Note : this is same logic as bitmap[y][x] = backgroundColor;
570 *(bitmapBuffer + x) = packedBackgroundColor;
572 bitmapBuffer += glyphData.width;
577 Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuffer& buffer, const uint32_t bufferWidth, const uint32_t bufferHeight, const bool ignoreHorizontalAlignment, const int32_t horizontalOffset, const int32_t verticalOffset)
579 // Retrieve lines, glyphs, positions and colors from the view model.
580 const Length modelNumberOfLines = model->GetNumberOfLines();
581 const LineRun* const modelLinesBuffer = model->GetLines();
582 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
583 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
584 const Vector2* const positionBuffer = model->GetLayout();
585 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
586 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
588 const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
590 // Create and initialize the pixel buffer.
592 glyphData.verticalOffset = verticalOffset;
593 glyphData.width = bufferWidth;
594 glyphData.height = bufferHeight;
595 glyphData.bitmapBuffer = buffer;
596 glyphData.horizontalOffset = 0;
598 ColorIndex prevBackgroundColorIndex = 0;
599 ColorIndex backgroundColorIndex = 0;
601 // Traverses the lines of the text.
602 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
604 const LineRun& line = *(modelLinesBuffer + lineIndex);
606 // Sets the horizontal offset of the line.
607 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
608 glyphData.horizontalOffset += horizontalOffset;
610 // Increases the vertical offset with the line's ascender.
611 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
613 float left = bufferWidth;
615 float baseline = 0.0f;
617 // Traverses the glyphs of the line.
618 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
619 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
621 // Retrieve the glyph's info.
622 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
624 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
625 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
627 // Nothing to do if default background color, the glyph's width or height is zero.
631 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
633 if((backgroundColorIndex != prevBackgroundColorIndex) &&
634 (prevBackgroundColorIndex != 0u))
636 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
637 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
640 if(backgroundColorIndex == 0u)
642 prevBackgroundColorIndex = backgroundColorIndex;
643 //if background color is the default do nothing
647 // Retrieves the glyph's position.
648 const Vector2* const position = positionBuffer + glyphIndex;
650 if(baseline < position->y + glyphInfo->yBearing)
652 baseline = position->y + glyphInfo->yBearing;
655 // Calculate the positions of leftmost and rightmost glyphs in the current line
656 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
658 left = position->x - glyphInfo->xBearing;
661 if(position->x + glyphInfo->width > right)
663 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
666 prevBackgroundColorIndex = backgroundColorIndex;
669 //draw last background at line end if not default
670 if(backgroundColorIndex != 0u)
672 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
673 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
676 // Increases the vertical offset with the line's descender.
677 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
680 return glyphData.bitmapBuffer;
683 /// Draws the specified strikethrough color to the buffer
684 void DrawStrikethrough(const uint32_t bufferWidth,
685 const uint32_t bufferHeight,
686 GlyphData& glyphData,
687 const float baseline,
688 const float strikethroughStartingYPosition,
689 const float maxStrikethroughHeight,
690 const float lineExtentLeft,
691 const float lineExtentRight,
692 const StrikethroughStyleProperties& commonStrikethroughProperties,
693 const StrikethroughStyleProperties& currentStrikethroughProperties,
696 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
698 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
699 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
700 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
701 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
703 // If current glyph don't need to be rendered, just ignore.
704 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
709 // We can optimize by memset when strikethroughColor.a is near zero
710 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
712 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
714 // Skip yRangeMin line.
715 bitmapBuffer += yRangeMin * glyphData.width;
717 if(strikethroughColorAlpha == 0)
719 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
721 // We can use memset.
722 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
723 bitmapBuffer += glyphData.width;
728 uint32_t packedStrikethroughColor = 0u;
729 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
731 // Write the color to the pixel buffer
732 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
733 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
734 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
735 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
737 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
739 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
741 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
742 *(bitmapBuffer + x) = packedStrikethroughColor;
744 bitmapBuffer += glyphData.width;
750 * @brief Create an initialized image buffer filled with transparent color.
752 * Creates the pixel data used to generate the final image with the given size.
754 * @param[in] bufferWidth The width of the image buffer.
755 * @param[in] bufferHeight The height of the image buffer.
756 * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
758 * @return An image buffer.
760 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat)
762 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
764 if(Pixel::RGBA8888 == pixelFormat)
766 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
767 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
768 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
772 memset(imageBuffer.GetBuffer(), 0, static_cast<std::size_t>(bufferWidth * bufferHeight));
779 * @brief Combine the two RGBA image buffers together.
781 * The top layer buffer will blend over the bottom layer buffer:
782 * - If the pixel is not fully opaque from either buffer, it will be blended with
783 * the pixel from the other buffer and copied to the combined buffer.
784 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
785 * buffer will be copied to the combined buffer.
787 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
788 * We can determine which pixelBuffer's memory is destination
790 * @param[in, out] topPixelBuffer The top layer buffer.
791 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
792 * @param[in] bufferWidth The width of the image buffer.
793 * @param[in] bufferHeight The height of the image buffer.
794 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
795 * False if we store the combined image buffer result into bottomPixelBuffer.
798 void CombineImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, bool storeResultIntoTop)
800 // Assume that we always combine two RGBA images
801 // Jump with 4bytes for optimize runtime.
802 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
803 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
805 if(topBuffer == NULL && bottomBuffer == NULL)
807 // Nothing to do if both buffers are empty.
811 if(topBuffer == NULL)
813 // Nothing to do if topBuffer is empty.
814 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
815 if(storeResultIntoTop)
817 topPixelBuffer = bottomPixelBuffer;
822 if(bottomBuffer == NULL)
824 // Nothing to do if bottomBuffer is empty.
825 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
826 if(!storeResultIntoTop)
828 bottomPixelBuffer = topPixelBuffer;
833 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
835 uint32_t* __restrict__ combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
836 uint8_t* __restrict__ topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
838 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
840 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
841 // Otherwise, copy pixel from topBuffer to combinedBuffer.
842 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
844 uint8_t topAlpha = *topAlphaBufferPointer;
848 // Copy the pixel from bottomBuffer to combinedBuffer
849 if(storeResultIntoTop)
851 *(combinedBuffer) = *(bottomBuffer);
854 else if(topAlpha == 255)
856 // Copy the pixel from topBuffer to combinedBuffer
857 if(!storeResultIntoTop)
859 *(combinedBuffer) = *(topBuffer);
864 // At least one pixel is not fully opaque
865 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
866 uint32_t blendedBottomBufferColor = *(bottomBuffer);
867 uint8_t* __restrict__ blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
869 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
870 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
871 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
872 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
874 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
877 // Increase each buffer's pointer.
881 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
887 TypesetterPtr Typesetter::New(const ModelInterface* const model)
889 return TypesetterPtr(new Typesetter(model));
892 ViewModel* Typesetter::GetViewModel()
897 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
899 DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_RENDERING_TYPESETTER");
900 // @todo. This initial implementation for a TextLabel has only one visible page.
902 // Elides the text if needed.
903 mModel->ElideGlyphs();
905 // Retrieves the layout size.
906 const Size& layoutSize = mModel->GetLayoutSize();
908 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
910 // Set the offset for the horizontal alignment according to the text direction and outline width.
913 switch(mModel->GetHorizontalAlignment())
915 case HorizontalAlignment::BEGIN:
920 case HorizontalAlignment::CENTER:
922 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
925 case HorizontalAlignment::END:
927 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
932 // Set the offset for the vertical alignment.
935 switch(mModel->GetVerticalAlignment())
937 case VerticalAlignment::TOP:
942 case VerticalAlignment::CENTER:
944 penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
945 penY = penY < 0.f ? 0.f : penY;
948 case VerticalAlignment::BOTTOM:
950 penY = static_cast<int32_t>(size.height - layoutSize.height);
955 // Generate the image buffers of the text for each different style first,
956 // then combine all of them together as one final image buffer. We try to
957 // do all of these in CPU only, so that once the final texture is generated,
958 // no calculation is needed in GPU during each frame.
960 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
961 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
963 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
964 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
966 //Elided text in ellipsis at START could start on index greater than 0
967 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
968 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
970 Devel::PixelBuffer imageBuffer;
972 if(RENDER_MASK == behaviour)
974 // Generate the image buffer as an alpha mask for color glyphs.
975 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
977 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
979 // Generate an empty image buffer so that it can been combined with the image buffers for styles
980 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
981 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
985 // Generate the image buffer for the text with no style.
986 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
989 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
991 // Generate the outline if enabled
992 const uint16_t outlineWidth = mModel->GetOutlineWidth();
993 const float outlineAlpha = mModel->GetOutlineColor().a;
994 if(outlineWidth != 0u && fabsf(outlineAlpha) > Math::MACHINE_EPSILON_1 && RENDER_OVERLAY_STYLE != behaviour)
996 // Create the image buffer for outline
997 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
999 // Combine the two buffers
1000 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
1003 // @todo. Support shadow for partial text later on.
1005 // Generate the shadow if enabled
1006 const Vector2& shadowOffset = mModel->GetShadowOffset();
1007 const float shadowAlpha = mModel->GetShadowColor().a;
1008 if(RENDER_OVERLAY_STYLE != behaviour && fabsf(shadowAlpha) > Math::MACHINE_EPSILON_1 && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
1010 // Create the image buffer for shadow
1011 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1013 // Check whether it will be a soft shadow
1014 const float& blurRadius = mModel->GetShadowBlurRadius();
1016 if(blurRadius > Math::MACHINE_EPSILON_1)
1018 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
1021 // Combine the two buffers
1022 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
1025 // Generate the background if enabled
1026 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
1027 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
1028 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
1030 Devel::PixelBuffer backgroundImageBuffer;
1032 if(backgroundEnabled)
1034 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1038 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1041 if(backgroundMarkupSet)
1043 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
1046 // Combine the two buffers
1047 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1050 if(RENDER_OVERLAY_STYLE == behaviour)
1052 if(mModel->IsUnderlineEnabled())
1054 // Create the image buffer for underline
1055 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1057 // Combine the two buffers
1058 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
1061 if(mModel->IsStrikethroughEnabled())
1063 // Create the image buffer for strikethrough
1064 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
1066 // Combine the two buffers
1067 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
1070 // Markup-Processor for overlay styles
1071 if(mModel->IsMarkupProcessorEnabled() || mModel->IsSpannedTextPlaced())
1073 if(mModel->IsMarkupUnderlineSet())
1075 imageBuffer = ApplyUnderlineMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1078 if(mModel->IsMarkupStrikethroughSet())
1080 imageBuffer = ApplyStrikethroughMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1086 // Create the final PixelData for the combined image buffer
1087 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
1092 Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Typesetter::Style style, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset, const GlyphIndex fromGlyphIndex, const GlyphIndex toGlyphIndex)
1094 // Retrieve lines, glyphs, positions and colors from the view model.
1095 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1096 const LineRun* const __restrict__ modelLinesBuffer = mModel->GetLines();
1097 const GlyphInfo* const __restrict__ glyphsBuffer = mModel->GetGlyphs();
1098 const Vector2* const __restrict__ positionBuffer = mModel->GetLayout();
1099 const Vector4* const __restrict__ colorsBuffer = mModel->GetColors();
1100 const ColorIndex* const __restrict__ colorIndexBuffer = mModel->GetColorIndices();
1101 const GlyphInfo* __restrict__ hyphens = mModel->GetHyphens();
1102 const Length* __restrict__ hyphenIndices = mModel->GetHyphenIndices();
1103 const Length hyphensCount = mModel->GetHyphensCount();
1105 // Elided text info. Indices according to elided text and Ellipsis position.
1106 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1107 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1108 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1109 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1110 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1112 // Whether to use the default color.
1113 const bool useDefaultColor = (NULL == colorsBuffer);
1114 const Vector4& defaultColor = mModel->GetDefaultColor();
1116 // Create and initialize the pixel buffer.
1117 GlyphData glyphData;
1118 glyphData.verticalOffset = verticalOffset;
1119 glyphData.width = bufferWidth;
1120 glyphData.height = bufferHeight;
1121 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1122 glyphData.horizontalOffset = 0;
1124 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1125 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1126 Length hyphenIndex = 0;
1128 const Character* __restrict__ textBuffer = mModel->GetTextBuffer();
1129 float calculatedAdvance = 0.f;
1130 const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1131 const CharacterIndex* __restrict__ glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1133 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1135 // Traverses the lines of the text.
1136 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1138 const LineRun& line = *(modelLinesBuffer + lineIndex);
1140 // Sets the horizontal offset of the line.
1141 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1142 glyphData.horizontalOffset += horizontalOffset;
1144 // Increases the vertical offset with the line's ascender.
1145 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1147 // Retrieves the glyph's outline width
1148 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1150 if(style == Typesetter::STYLE_OUTLINE)
1152 glyphData.horizontalOffset -= outlineWidth;
1155 // Only need to add the vertical outline offset for the first line
1156 glyphData.verticalOffset -= outlineWidth;
1159 else if(style == Typesetter::STYLE_SHADOW)
1161 const Vector2& shadowOffset = mModel->GetShadowOffset();
1162 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1166 // Only need to add the vertical shadow offset for first line
1167 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1171 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1172 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1173 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1175 // Get the character-spacing runs.
1176 const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1178 // Aggregate underline-style-properties from mModel
1179 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1180 mModel->GetUnderlineColor(),
1181 mModel->GetUnderlineHeight(),
1182 mModel->GetDashedUnderlineGap(),
1183 mModel->GetDashedUnderlineWidth(),
1190 // Aggregate strikethrough-style-properties from mModel
1191 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1192 mModel->GetStrikethroughHeight(),
1196 // Get the underline runs.
1197 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1198 Vector<UnderlinedGlyphRun> underlineRuns;
1199 underlineRuns.Resize(numberOfUnderlineRuns);
1200 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1202 // Get the strikethrough runs.
1203 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1204 Vector<StrikethroughGlyphRun> strikethroughRuns;
1205 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1206 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1208 bool thereAreUnderlinedGlyphs = false;
1209 bool thereAreStrikethroughGlyphs = false;
1211 float currentUnderlinePosition = 0.0f;
1212 float currentUnderlineHeight = modelUnderlineProperties.height;
1213 float maxUnderlineHeight = currentUnderlineHeight;
1214 auto currentUnderlineProperties = modelUnderlineProperties;
1216 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1217 float maxStrikethroughHeight = currentStrikethroughHeight;
1218 auto currentStrikethroughProperties = modelStrikethroughProperties;
1219 float strikethroughStartingYPosition = 0.0f;
1221 FontId lastFontId = 0;
1223 float lineExtentLeft = bufferWidth;
1224 float lineExtentRight = 0.0f;
1225 float baseline = 0.0f;
1226 bool addHyphen = false;
1228 // Traverses the glyphs of the line.
1229 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1230 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1231 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1233 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1235 //To handle START case of ellipsis, the first glyph has been shifted
1236 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1237 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1239 //To handle MIDDLE case of ellipsis, the first glyph in the second half of line has been shifted and skip the removed glyph from middle.
1240 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1242 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1243 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1245 // Ignore any glyph that removed for MIDDLE ellipsis
1248 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1250 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1254 // Retrieve the glyph's info.
1255 const GlyphInfo* glyphInfo;
1257 if(addHyphen && hyphens)
1259 glyphInfo = hyphens + hyphenIndex;
1264 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1267 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1268 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1270 // Nothing to do if the glyph's width or height is zero.
1274 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1275 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1276 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1277 currentUnderlineHeight = currentUnderlineProperties.height;
1278 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1280 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1281 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1282 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1283 currentStrikethroughHeight = currentStrikethroughProperties.height;
1284 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1286 // Are we still using the same fontId as previous
1287 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1289 // We need to fetch fresh font underline metrics
1290 FontMetrics fontMetrics;
1291 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1293 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1294 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1298 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1301 if(strikethroughGlyph)
1303 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1306 // Update lastFontId because fontId is changed
1307 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1310 // Retrieves the glyph's position.
1311 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1315 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1316 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1317 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1318 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1319 position.y = -glyphInfo->yBearing;
1322 if(baseline < position.y + glyphInfo->yBearing)
1324 baseline = position.y + glyphInfo->yBearing;
1327 // Calculate the positions of leftmost and rightmost glyphs in the current line
1328 if(position.x < lineExtentLeft)
1330 lineExtentLeft = position.x;
1333 if(position.x + glyphInfo->width > lineExtentRight)
1335 lineExtentRight = position.x + glyphInfo->width;
1338 // Retrieves the glyph's color.
1339 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1342 if(style == Typesetter::STYLE_SHADOW)
1344 color = mModel->GetShadowColor();
1346 else if(style == Typesetter::STYLE_OUTLINE)
1348 color = mModel->GetOutlineColor();
1352 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1355 // Premultiply alpha
1360 // Retrieves the glyph's bitmap.
1361 glyphData.glyphBitmap.buffer = NULL;
1362 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1363 glyphData.glyphBitmap.height = glyphInfo->height;
1365 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1367 // Don't render outline for other styles
1368 outlineWidth = 0.0f;
1371 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1373 fontClient.CreateBitmap(glyphInfo->fontId,
1375 glyphInfo->isItalicRequired,
1376 glyphInfo->isBoldRequired,
1377 glyphData.glyphBitmap,
1378 static_cast<int32_t>(outlineWidth));
1381 // Sets the glyph's bitmap into the bitmap of the whole text.
1382 if(NULL != glyphData.glyphBitmap.buffer)
1384 if(style == Typesetter::STYLE_OUTLINE)
1386 // Set the position offset for the current glyph
1387 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1388 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1391 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1392 TypesetGlyph(glyphData,
1398 if(style == Typesetter::STYLE_OUTLINE)
1400 // Reset the position offset for the next glyph
1401 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1402 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1405 // free the glyphBitmap.buffer if it is owner of buffer
1406 if(glyphData.glyphBitmap.isBufferOwned)
1408 free(glyphData.glyphBitmap.buffer);
1409 glyphData.glyphBitmap.isBufferOwned = false;
1411 glyphData.glyphBitmap.buffer = NULL;
1416 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1421 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1429 // Draw the underline from the leftmost glyph to the rightmost glyph
1430 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1432 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1435 // Draw the background color from the leftmost glyph to the rightmost glyph
1436 if(style == Typesetter::STYLE_BACKGROUND)
1438 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1441 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1442 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1444 //TODO : The currently implemented strikethrough creates a strikethrough on the line level. We need to create different strikethroughs the case of glyphs with different sizes.
1445 strikethroughStartingYPosition = (glyphData.verticalOffset + baseline + currentUnderlinePosition) - ((line.ascender) * HALF); // Since Free Type font doesn't contain the strikethrough-position property, strikethrough position will be calculated by moving the underline position upwards by half the value of the line height.
1446 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1449 // Increases the vertical offset with the line's descender & line spacing.
1450 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1453 return glyphData.bitmapBuffer;
1456 Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset)
1458 // Underline-tags (this is for Markup case)
1459 // Get the underline runs.
1460 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1461 Vector<UnderlinedGlyphRun> underlineRuns;
1462 underlineRuns.Resize(numberOfUnderlineRuns);
1463 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1465 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1466 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1467 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1468 GlyphIndex startGlyphIndex, endGlyphIndex;
1470 //The outer loop to iterate on the separated chunks of underlined glyph runs
1471 while(itGlyphRun != endItGlyphRun)
1473 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1474 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1476 // Create the image buffer for underline
1477 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1478 // Combine the two buffers
1479 // Result pixel buffer will be stored into topPixelBuffer.
1480 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1485 return topPixelBuffer;
1488 Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset)
1490 // strikethrough-tags (this is for Markup case)
1491 // Get the strikethrough runs.
1492 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1493 Vector<StrikethroughGlyphRun> strikethroughRuns;
1494 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1495 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1497 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1498 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1499 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1500 GlyphIndex startGlyphIndex, endGlyphIndex;
1502 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1503 while(itGlyphRun != endItGlyphRun)
1505 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1506 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1508 // Create the image buffer for strikethrough
1509 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1510 // Combine the two buffers
1511 // Result pixel buffer will be stored into topPixelBuffer.
1512 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1517 return topPixelBuffer;
1520 Typesetter::Typesetter(const ModelInterface* const model)
1521 : mModel(new ViewModel(model))
1525 Typesetter::~Typesetter()
1532 } // namespace Toolkit