2 * Copyright (c) 2023 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;
75 * @brief Macro to skip useless line fast.
77 #define SKIP_GLYPH_SCANLINE(skipLine) \
78 if(useLocalScanline) \
80 for(int32_t lineIndex = 0; lineIndex < skipLine; ++lineIndex) \
82 TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
87 glyphScanline += skipLine * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize); \
91 * @brief Prepare scanline of glyph bitmap data per each lines. It must be call END_GLYPH_SCANLINE_DECODE end of same scope.
93 #define BEGIN_GLYPH_SCANLINE_DECODE(data) \
95 if(useLocalScanline) \
97 TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
101 * @brief Finalize scanline of glyph bitmap data per each lines.
103 #define END_GLYPH_SCANLINE_DECODE(data) \
104 if(!useLocalScanline) \
106 glyphScanline += data.glyphBitmap.width * glyphPixelSize; \
108 } // For ensure that we call BEGIN_GLYPH_SCANLINE_DECODE before
111 * @brief Finalize decode glyph bitmap data.
113 #define END_GLYPH_BITMAP() \
114 if(useLocalScanline) \
116 free(glyphScanline); \
118 } // For ensure that we call BEGIN_GLYPH_BITMAP before
121 /// Helper macro define end.
124 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
128 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
129 Vector2* position; ///< The position of the glyph.
130 TextAbstraction::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
131 uint32_t width; ///< The bitmap's width.
132 uint32_t height; ///< The bitmap's height.
133 int32_t horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
134 int32_t verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
138 * @brief Sets the glyph's buffer into the bitmap's buffer.
140 * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
141 * @param[in] position The position of the glyph.
142 * @param[in] color The color of the glyph.
143 * @param[in] style The style of the text.
144 * @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).
146 void TypesetGlyph(GlyphData& __restrict__ data,
147 const Vector2* const __restrict__ position,
148 const Vector4* const __restrict__ color,
149 const Typesetter::Style style,
150 const Pixel::Format pixelFormat)
152 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
154 // Nothing to do if the width or height of the buffer is zero.
158 // Initial vertical / horizontal offset.
159 const int32_t yOffset = data.verticalOffset + position->y;
160 const int32_t xOffset = data.horizontalOffset + position->x;
162 // Whether the given glyph is a color one.
163 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
164 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
165 const uint32_t glyphAlphaIndex = glyphPixelSize - 1u;
167 // Determinate iterator range.
168 const int32_t lineIndexRangeMin = std::max(0, -yOffset);
169 const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
170 const int32_t indexRangeMin = std::max(0, -xOffset);
171 const int32_t indexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
173 // If current glyph don't need to be rendered, just ignore.
174 if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
179 if(Pixel::RGBA8888 == pixelFormat)
181 uint32_t* __restrict__ bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
183 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
185 // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
186 // Just overwrite transparent color and return.
187 if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
189 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
191 // We can use memset here.
192 memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
193 bitmapBuffer += data.width;
198 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
200 // Precalculate input color's packed result.
201 uint32_t packedInputColor = 0u;
202 uint8_t* __restrict__ packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
204 *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
205 *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
206 *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
207 *(packedInputColorBuffer) = static_cast<uint8_t>(color->r * 255);
209 // Prepare glyph bitmap
210 BEGIN_GLYPH_BITMAP(data);
212 // Skip basic line of glyph.
213 SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
215 // Traverse the pixels of the glyph line per line.
218 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
220 BEGIN_GLYPH_SCANLINE_DECODE(data);
222 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
224 const int32_t xOffsetIndex = xOffset + index;
226 // Retrieves the color from the color glyph.
227 uint32_t packedColorGlyph = *(reinterpret_cast<const uint32_t*>(glyphScanline + (index << 2)));
228 uint8_t* __restrict__ packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
230 // Update the alpha channel.
231 const uint8_t colorAlpha = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
232 *(packedColorGlyphBuffer + 3u) = colorAlpha;
234 if(Typesetter::STYLE_SHADOW == style)
236 // The shadow of color glyph needs to have the shadow color.
237 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
238 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
239 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
245 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
248 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
249 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
250 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
252 if(data.glyphBitmap.isColorBitmap)
254 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
255 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
256 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
260 // Set the color into the final pixel buffer.
261 *(bitmapBuffer + xOffsetIndex) = packedColorGlyph;
264 bitmapBuffer += data.width;
266 END_GLYPH_SCANLINE_DECODE(data);
271 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
273 BEGIN_GLYPH_SCANLINE_DECODE(data);
275 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
277 // Update the alpha channel.
278 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
280 // Copy non-transparent pixels only
283 const int32_t xOffsetIndex = xOffset + index;
285 // Check alpha of overlapped pixels
286 uint32_t& currentColor = *(bitmapBuffer + xOffsetIndex);
287 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
289 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
290 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
291 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
292 // happen, for example, in the RTL text when we copy glyphs from right to left).
293 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
294 currentAlpha = std::max(currentAlpha, alpha);
295 if(currentAlpha == 255)
297 // Fast-cut to avoid float type operation.
298 currentColor = packedInputColor;
302 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
303 // The format is RGBA8888.
304 uint32_t packedColor = 0u;
305 uint8_t* __restrict__ packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
307 // Color is pre-muliplied with its alpha.
308 *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
309 *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
310 *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
311 *(packedColorBuffer) = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
313 // Set the color into the final pixel buffer.
314 currentColor = packedColor;
319 bitmapBuffer += data.width;
321 END_GLYPH_SCANLINE_DECODE(data);
329 // Below codes required only if not color glyph.
332 uint8_t* __restrict__ bitmapBuffer = data.bitmapBuffer.GetBuffer();
334 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
336 // Prepare glyph bitmap
337 BEGIN_GLYPH_BITMAP(data);
339 // Skip basic line of glyph.
340 SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
342 // Traverse the pixels of the glyph line per line.
343 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
345 BEGIN_GLYPH_SCANLINE_DECODE(data);
347 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
349 const int32_t xOffsetIndex = xOffset + index;
351 // Update the alpha channel.
352 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
354 // Copy non-transparent pixels only
357 // Check alpha of overlapped pixels
358 uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
360 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
361 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
362 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
363 // happen, for example, in the RTL text when we copy glyphs from right to left).
364 currentAlpha = std::max(currentAlpha, alpha);
368 bitmapBuffer += data.width;
370 END_GLYPH_SCANLINE_DECODE(data);
378 /// Draws the specified underline color to the buffer
380 const uint32_t bufferWidth,
381 const uint32_t bufferHeight,
382 GlyphData& glyphData,
383 const float baseline,
384 const float currentUnderlinePosition,
385 const float maxUnderlineHeight,
386 const float lineExtentLeft,
387 const float lineExtentRight,
388 const UnderlineStyleProperties& commonUnderlineProperties,
389 const UnderlineStyleProperties& currentUnderlineProperties,
392 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
393 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
394 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
395 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
397 int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
399 const uint32_t yRangeMin = underlineYOffset;
400 const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
401 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
402 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
404 // If current glyph don't need to be rendered, just ignore.
405 if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
410 // We can optimize by memset when underlineColor.a is near zero
411 uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
413 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
415 // Skip yRangeMin line.
416 bitmapBuffer += yRangeMin * glyphData.width;
418 // Note if underlineType is DASHED, we cannot setup color by memset.
419 if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
421 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
423 // We can use memset.
424 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
425 bitmapBuffer += glyphData.width;
427 if(underlineType == Text::Underline::DOUBLE)
429 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
430 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
431 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
433 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
434 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
436 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
438 // We can use memset.
439 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
440 bitmapBuffer += glyphData.width;
446 uint32_t packedUnderlineColor = 0u;
447 uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
449 // Write the color to the pixel buffer
450 *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
451 *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
452 *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
453 *(packedUnderlineColorBuffer) = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
455 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
457 if(underlineType == Text::Underline::DASHED)
459 float dashWidth = dashedUnderlineWidth;
462 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
464 if(Dali::EqualsZero(dashGap) && dashWidth > 0)
466 // Note : this is same logic as bitmap[y][x] = underlineColor;
467 *(bitmapBuffer + x) = packedUnderlineColor;
470 else if(dashGap < dashedUnderlineGap)
477 dashWidth = dashedUnderlineWidth;
484 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
486 // Note : this is same logic as bitmap[y][x] = underlineColor;
487 *(bitmapBuffer + x) = packedUnderlineColor;
490 bitmapBuffer += glyphData.width;
492 if(underlineType == Text::Underline::DOUBLE)
494 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
495 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
496 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
498 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
499 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
501 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
503 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
505 // Note : this is same logic as bitmap[y][x] = underlineColor;
506 *(bitmapBuffer + x) = packedUnderlineColor;
508 bitmapBuffer += glyphData.width;
514 /// Draws the background color to the buffer
515 void DrawBackgroundColor(
516 Vector4 backgroundColor,
517 const uint32_t bufferWidth,
518 const uint32_t bufferHeight,
519 GlyphData& glyphData,
520 const float baseline,
522 const float lineExtentLeft,
523 const float lineExtentRight)
525 const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
526 const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
527 const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
528 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
530 // If current glyph don't need to be rendered, just ignore.
531 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
536 // We can optimize by memset when backgroundColor.a is near zero
537 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
539 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
541 // Skip yRangeMin line.
542 bitmapBuffer += yRangeMin * glyphData.width;
544 if(backgroundColorAlpha == 0)
546 for(int32_t y = yRangeMin; y < yRangeMax; y++)
548 // We can use memset.
549 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
550 bitmapBuffer += glyphData.width;
555 uint32_t packedBackgroundColor = 0u;
556 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
558 // Write the color to the pixel buffer
559 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
560 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
561 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
562 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
564 for(int32_t y = yRangeMin; y < yRangeMax; y++)
566 for(int32_t x = xRangeMin; x < xRangeMax; x++)
568 // Note : this is same logic as bitmap[y][x] = backgroundColor;
569 *(bitmapBuffer + x) = packedBackgroundColor;
571 bitmapBuffer += glyphData.width;
576 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)
578 // Retrieve lines, glyphs, positions and colors from the view model.
579 const Length modelNumberOfLines = model->GetNumberOfLines();
580 const LineRun* const modelLinesBuffer = model->GetLines();
581 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
582 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
583 const Vector2* const positionBuffer = model->GetLayout();
584 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
585 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
587 const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
589 // Create and initialize the pixel buffer.
591 glyphData.verticalOffset = verticalOffset;
592 glyphData.width = bufferWidth;
593 glyphData.height = bufferHeight;
594 glyphData.bitmapBuffer = buffer;
595 glyphData.horizontalOffset = 0;
597 ColorIndex prevBackgroundColorIndex = 0;
598 ColorIndex backgroundColorIndex = 0;
600 // Traverses the lines of the text.
601 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
603 const LineRun& line = *(modelLinesBuffer + lineIndex);
605 // Sets the horizontal offset of the line.
606 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
607 glyphData.horizontalOffset += horizontalOffset;
609 // Increases the vertical offset with the line's ascender.
610 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
612 float left = bufferWidth;
614 float baseline = 0.0f;
616 // Traverses the glyphs of the line.
617 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
618 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
620 // Retrieve the glyph's info.
621 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
623 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
624 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
626 // Nothing to do if default background color, the glyph's width or height is zero.
630 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
632 if((backgroundColorIndex != prevBackgroundColorIndex) &&
633 (prevBackgroundColorIndex != 0u))
635 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
636 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
639 if(backgroundColorIndex == 0u)
641 prevBackgroundColorIndex = backgroundColorIndex;
642 //if background color is the default do nothing
646 // Retrieves the glyph's position.
647 const Vector2* const position = positionBuffer + glyphIndex;
649 if(baseline < position->y + glyphInfo->yBearing)
651 baseline = position->y + glyphInfo->yBearing;
654 // Calculate the positions of leftmost and rightmost glyphs in the current line
655 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
657 left = position->x - glyphInfo->xBearing;
660 if(position->x + glyphInfo->width > right)
662 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
665 prevBackgroundColorIndex = backgroundColorIndex;
668 //draw last background at line end if not default
669 if(backgroundColorIndex != 0u)
671 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
672 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
675 // Increases the vertical offset with the line's descender.
676 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
679 return glyphData.bitmapBuffer;
682 /// Draws the specified strikethrough color to the buffer
683 void DrawStrikethrough(const uint32_t bufferWidth,
684 const uint32_t bufferHeight,
685 GlyphData& glyphData,
686 const float baseline,
687 const float strikethroughStartingYPosition,
688 const float maxStrikethroughHeight,
689 const float lineExtentLeft,
690 const float lineExtentRight,
691 const StrikethroughStyleProperties& commonStrikethroughProperties,
692 const StrikethroughStyleProperties& currentStrikethroughProperties,
695 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
697 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
698 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
699 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
700 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
702 // If current glyph don't need to be rendered, just ignore.
703 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
708 // We can optimize by memset when strikethroughColor.a is near zero
709 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
711 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
713 // Skip yRangeMin line.
714 bitmapBuffer += yRangeMin * glyphData.width;
716 if(strikethroughColorAlpha == 0)
718 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
720 // We can use memset.
721 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
722 bitmapBuffer += glyphData.width;
727 uint32_t packedStrikethroughColor = 0u;
728 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
730 // Write the color to the pixel buffer
731 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
732 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
733 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
734 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
736 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
738 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
740 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
741 *(bitmapBuffer + x) = packedStrikethroughColor;
743 bitmapBuffer += glyphData.width;
749 * @brief Create an initialized image buffer filled with transparent color.
751 * Creates the pixel data used to generate the final image with the given size.
753 * @param[in] bufferWidth The width of the image buffer.
754 * @param[in] bufferHeight The height of the image buffer.
755 * @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).
757 * @return An image buffer.
759 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat)
761 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
763 if(Pixel::RGBA8888 == pixelFormat)
765 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
766 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
767 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
771 memset(imageBuffer.GetBuffer(), 0, static_cast<std::size_t>(bufferWidth * bufferHeight));
778 * @brief Combine the two RGBA image buffers together.
780 * The top layer buffer will blend over the bottom layer buffer:
781 * - If the pixel is not fully opaque from either buffer, it will be blended with
782 * the pixel from the other buffer and copied to the combined buffer.
783 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
784 * buffer will be copied to the combined buffer.
786 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
787 * We can determine which pixelBuffer's memory is destination
789 * @param[in, out] topPixelBuffer The top layer buffer.
790 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
791 * @param[in] bufferWidth The width of the image buffer.
792 * @param[in] bufferHeight The height of the image buffer.
793 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
794 * False if we store the combined image buffer result into bottomPixelBuffer.
797 void CombineImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, bool storeResultIntoTop)
799 // Assume that we always combine two RGBA images
800 // Jump with 4bytes for optimize runtime.
801 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
802 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
804 if(topBuffer == NULL && bottomBuffer == NULL)
806 // Nothing to do if both buffers are empty.
810 if(topBuffer == NULL)
812 // Nothing to do if topBuffer is empty.
813 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
814 if(storeResultIntoTop)
816 topPixelBuffer = bottomPixelBuffer;
821 if(bottomBuffer == NULL)
823 // Nothing to do if bottomBuffer is empty.
824 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
825 if(!storeResultIntoTop)
827 bottomPixelBuffer = topPixelBuffer;
832 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
834 uint32_t* __restrict__ combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
835 uint8_t* __restrict__ topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
837 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
839 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
840 // Otherwise, copy pixel from topBuffer to combinedBuffer.
841 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
843 uint8_t topAlpha = *topAlphaBufferPointer;
847 // Copy the pixel from bottomBuffer to combinedBuffer
848 if(storeResultIntoTop)
850 *(combinedBuffer) = *(bottomBuffer);
853 else if(topAlpha == 255)
855 // Copy the pixel from topBuffer to combinedBuffer
856 if(!storeResultIntoTop)
858 *(combinedBuffer) = *(topBuffer);
863 // At least one pixel is not fully opaque
864 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
865 uint32_t blendedBottomBufferColor = *(bottomBuffer);
866 uint8_t* __restrict__ blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
868 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
869 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
870 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
871 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
873 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
876 // Increase each buffer's pointer.
880 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
886 TypesetterPtr Typesetter::New(const ModelInterface* const model)
888 return TypesetterPtr(new Typesetter(model));
891 ViewModel* Typesetter::GetViewModel()
896 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
898 DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_RENDERING_TYPESETTER");
899 // @todo. This initial implementation for a TextLabel has only one visible page.
901 // Elides the text if needed.
902 mModel->ElideGlyphs();
904 // Retrieves the layout size.
905 const Size& layoutSize = mModel->GetLayoutSize();
907 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
909 // Set the offset for the horizontal alignment according to the text direction and outline width.
912 switch(mModel->GetHorizontalAlignment())
914 case HorizontalAlignment::BEGIN:
919 case HorizontalAlignment::CENTER:
921 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
924 case HorizontalAlignment::END:
926 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
931 // Set the offset for the vertical alignment.
934 switch(mModel->GetVerticalAlignment())
936 case VerticalAlignment::TOP:
941 case VerticalAlignment::CENTER:
943 penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
944 penY = penY < 0.f ? 0.f : penY;
947 case VerticalAlignment::BOTTOM:
949 penY = static_cast<int32_t>(size.height - layoutSize.height);
954 // Generate the image buffers of the text for each different style first,
955 // then combine all of them together as one final image buffer. We try to
956 // do all of these in CPU only, so that once the final texture is generated,
957 // no calculation is needed in GPU during each frame.
959 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
960 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
962 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
963 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
965 //Elided text in ellipsis at START could start on index greater than 0
966 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
967 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
969 Devel::PixelBuffer imageBuffer;
971 if(RENDER_MASK == behaviour)
973 // Generate the image buffer as an alpha mask for color glyphs.
974 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
976 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
978 // Generate an empty image buffer so that it can been combined with the image buffers for styles
979 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
980 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
984 // Generate the image buffer for the text with no style.
985 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
988 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
990 // Generate the outline if enabled
991 const uint16_t outlineWidth = mModel->GetOutlineWidth();
992 const float outlineAlpha = mModel->GetOutlineColor().a;
993 if(outlineWidth != 0u && fabsf(outlineAlpha) > Math::MACHINE_EPSILON_1 && RENDER_OVERLAY_STYLE != behaviour)
995 // Create the image buffer for outline
996 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
998 // Combine the two buffers
999 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
1002 // @todo. Support shadow for partial text later on.
1004 // Generate the shadow if enabled
1005 const Vector2& shadowOffset = mModel->GetShadowOffset();
1006 const float shadowAlpha = mModel->GetShadowColor().a;
1007 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))
1009 // Create the image buffer for shadow
1010 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1012 // Check whether it will be a soft shadow
1013 const float& blurRadius = mModel->GetShadowBlurRadius();
1015 if(blurRadius > Math::MACHINE_EPSILON_1)
1017 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
1020 // Combine the two buffers
1021 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
1024 // Generate the background if enabled
1025 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
1026 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
1027 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
1029 Devel::PixelBuffer backgroundImageBuffer;
1031 if(backgroundEnabled)
1033 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1037 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1040 if(backgroundMarkupSet)
1042 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
1045 // Combine the two buffers
1046 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1049 if(RENDER_OVERLAY_STYLE == behaviour)
1051 if(mModel->IsUnderlineEnabled())
1053 // Create the image buffer for underline
1054 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1056 // Combine the two buffers
1057 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
1060 if(mModel->IsStrikethroughEnabled())
1062 // Create the image buffer for strikethrough
1063 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
1065 // Combine the two buffers
1066 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
1069 // Markup-Processor for overlay styles
1070 if(mModel->IsMarkupProcessorEnabled() || mModel->IsSpannedTextPlaced())
1072 if(mModel->IsMarkupUnderlineSet())
1074 imageBuffer = ApplyUnderlineMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1077 if(mModel->IsMarkupStrikethroughSet())
1079 imageBuffer = ApplyStrikethroughMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1085 // Create the final PixelData for the combined image buffer
1086 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
1091 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)
1093 // Retrieve lines, glyphs, positions and colors from the view model.
1094 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1095 const LineRun* const __restrict__ modelLinesBuffer = mModel->GetLines();
1096 const GlyphInfo* const __restrict__ glyphsBuffer = mModel->GetGlyphs();
1097 const Vector2* const __restrict__ positionBuffer = mModel->GetLayout();
1098 const Vector4* const __restrict__ colorsBuffer = mModel->GetColors();
1099 const ColorIndex* const __restrict__ colorIndexBuffer = mModel->GetColorIndices();
1100 const GlyphInfo* __restrict__ hyphens = mModel->GetHyphens();
1101 const Length* __restrict__ hyphenIndices = mModel->GetHyphenIndices();
1102 const Length hyphensCount = mModel->GetHyphensCount();
1104 // Elided text info. Indices according to elided text and Ellipsis position.
1105 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1106 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1107 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1108 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1109 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1111 // Whether to use the default color.
1112 const bool useDefaultColor = (NULL == colorsBuffer);
1113 const Vector4& defaultColor = mModel->GetDefaultColor();
1115 // Create and initialize the pixel buffer.
1116 GlyphData glyphData;
1117 glyphData.verticalOffset = verticalOffset;
1118 glyphData.width = bufferWidth;
1119 glyphData.height = bufferHeight;
1120 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1121 glyphData.horizontalOffset = 0;
1123 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1124 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1125 Length hyphenIndex = 0;
1127 const Character* __restrict__ textBuffer = mModel->GetTextBuffer();
1128 float calculatedAdvance = 0.f;
1129 const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1130 const CharacterIndex* __restrict__ glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1132 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1134 // Traverses the lines of the text.
1135 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1137 const LineRun& line = *(modelLinesBuffer + lineIndex);
1139 // Sets the horizontal offset of the line.
1140 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1141 glyphData.horizontalOffset += horizontalOffset;
1143 // Increases the vertical offset with the line's ascender.
1144 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1146 // Retrieves the glyph's outline width
1147 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1149 if(style == Typesetter::STYLE_OUTLINE)
1151 glyphData.horizontalOffset -= outlineWidth;
1154 // Only need to add the vertical outline offset for the first line
1155 glyphData.verticalOffset -= outlineWidth;
1158 else if(style == Typesetter::STYLE_SHADOW)
1160 const Vector2& shadowOffset = mModel->GetShadowOffset();
1161 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1165 // Only need to add the vertical shadow offset for first line
1166 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1170 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1171 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1172 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1174 // Get the character-spacing runs.
1175 const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1177 // Aggregate underline-style-properties from mModel
1178 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1179 mModel->GetUnderlineColor(),
1180 mModel->GetUnderlineHeight(),
1181 mModel->GetDashedUnderlineGap(),
1182 mModel->GetDashedUnderlineWidth(),
1189 // Aggregate strikethrough-style-properties from mModel
1190 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1191 mModel->GetStrikethroughHeight(),
1195 // Get the underline runs.
1196 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1197 Vector<UnderlinedGlyphRun> underlineRuns;
1198 underlineRuns.Resize(numberOfUnderlineRuns);
1199 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1201 // Get the strikethrough runs.
1202 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1203 Vector<StrikethroughGlyphRun> strikethroughRuns;
1204 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1205 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1207 bool thereAreUnderlinedGlyphs = false;
1208 bool thereAreStrikethroughGlyphs = false;
1210 float currentUnderlinePosition = 0.0f;
1211 float currentUnderlineHeight = modelUnderlineProperties.height;
1212 float maxUnderlineHeight = currentUnderlineHeight;
1213 auto currentUnderlineProperties = modelUnderlineProperties;
1215 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1216 float maxStrikethroughHeight = currentStrikethroughHeight;
1217 auto currentStrikethroughProperties = modelStrikethroughProperties;
1218 float strikethroughStartingYPosition = 0.0f;
1220 FontId lastFontId = 0;
1222 float lineExtentLeft = bufferWidth;
1223 float lineExtentRight = 0.0f;
1224 float baseline = 0.0f;
1225 bool addHyphen = false;
1227 // Traverses the glyphs of the line.
1228 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1229 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1230 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1232 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1234 //To handle START case of ellipsis, the first glyph has been shifted
1235 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1236 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1238 //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.
1239 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1241 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1242 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1244 // Ignore any glyph that removed for MIDDLE ellipsis
1247 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1249 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1253 // Retrieve the glyph's info.
1254 const GlyphInfo* glyphInfo;
1256 if(addHyphen && hyphens)
1258 glyphInfo = hyphens + hyphenIndex;
1263 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1266 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1267 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1269 // Nothing to do if the glyph's width or height is zero.
1273 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1274 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1275 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1276 currentUnderlineHeight = currentUnderlineProperties.height;
1277 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1279 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1280 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1281 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1282 currentStrikethroughHeight = currentStrikethroughProperties.height;
1283 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1285 // Are we still using the same fontId as previous
1286 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1288 // We need to fetch fresh font underline metrics
1289 FontMetrics fontMetrics;
1290 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1292 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1293 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1297 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1300 if(strikethroughGlyph)
1302 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1305 // Update lastFontId because fontId is changed
1306 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1309 // Retrieves the glyph's position.
1310 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1314 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1315 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1316 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1317 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1318 position.y = -glyphInfo->yBearing;
1321 if(baseline < position.y + glyphInfo->yBearing)
1323 baseline = position.y + glyphInfo->yBearing;
1326 // Calculate the positions of leftmost and rightmost glyphs in the current line
1327 if(position.x < lineExtentLeft)
1329 lineExtentLeft = position.x;
1332 if(position.x + glyphInfo->width > lineExtentRight)
1334 lineExtentRight = position.x + glyphInfo->width;
1337 // Retrieves the glyph's color.
1338 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1341 if(style == Typesetter::STYLE_SHADOW)
1343 color = mModel->GetShadowColor();
1345 else if(style == Typesetter::STYLE_OUTLINE)
1347 color = mModel->GetOutlineColor();
1351 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1354 // Premultiply alpha
1359 // Retrieves the glyph's bitmap.
1360 glyphData.glyphBitmap.buffer = NULL;
1361 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1362 glyphData.glyphBitmap.height = glyphInfo->height;
1364 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1366 // Don't render outline for other styles
1367 outlineWidth = 0.0f;
1370 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1372 fontClient.CreateBitmap(glyphInfo->fontId,
1374 glyphInfo->isItalicRequired,
1375 glyphInfo->isBoldRequired,
1376 glyphData.glyphBitmap,
1377 static_cast<int32_t>(outlineWidth));
1380 // Sets the glyph's bitmap into the bitmap of the whole text.
1381 if(NULL != glyphData.glyphBitmap.buffer)
1383 if(style == Typesetter::STYLE_OUTLINE)
1385 // Set the position offset for the current glyph
1386 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1387 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1390 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1391 TypesetGlyph(glyphData,
1397 if(style == Typesetter::STYLE_OUTLINE)
1399 // Reset the position offset for the next glyph
1400 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1401 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1404 // free the glyphBitmap.buffer if it is owner of buffer
1405 if(glyphData.glyphBitmap.isBufferOwned)
1407 free(glyphData.glyphBitmap.buffer);
1408 glyphData.glyphBitmap.isBufferOwned = false;
1410 glyphData.glyphBitmap.buffer = NULL;
1415 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1420 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1428 // Draw the underline from the leftmost glyph to the rightmost glyph
1429 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1431 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1434 // Draw the background color from the leftmost glyph to the rightmost glyph
1435 if(style == Typesetter::STYLE_BACKGROUND)
1437 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1440 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1441 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1443 //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.
1444 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.
1445 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1448 // Increases the vertical offset with the line's descender & line spacing.
1449 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1452 return glyphData.bitmapBuffer;
1455 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)
1457 // Underline-tags (this is for Markup case)
1458 // Get the underline runs.
1459 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1460 Vector<UnderlinedGlyphRun> underlineRuns;
1461 underlineRuns.Resize(numberOfUnderlineRuns);
1462 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1464 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1465 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1466 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1467 GlyphIndex startGlyphIndex, endGlyphIndex;
1469 //The outer loop to iterate on the separated chunks of underlined glyph runs
1470 while(itGlyphRun != endItGlyphRun)
1472 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1473 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1475 // Create the image buffer for underline
1476 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1477 // Combine the two buffers
1478 // Result pixel buffer will be stored into topPixelBuffer.
1479 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1484 return topPixelBuffer;
1487 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)
1489 // strikethrough-tags (this is for Markup case)
1490 // Get the strikethrough runs.
1491 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1492 Vector<StrikethroughGlyphRun> strikethroughRuns;
1493 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1494 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1496 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1497 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1498 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1499 GlyphIndex startGlyphIndex, endGlyphIndex;
1501 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1502 while(itGlyphRun != endItGlyphRun)
1504 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1505 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1507 // Create the image buffer for strikethrough
1508 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1509 // Combine the two buffers
1510 // Result pixel buffer will be stored into topPixelBuffer.
1511 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1516 return topPixelBuffer;
1519 Typesetter::Typesetter(const ModelInterface* const model)
1520 : mModel(new ViewModel(model))
1524 Typesetter::~Typesetter()
1531 } // namespace Toolkit