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 const Vector2& outlineOffset = mModel->GetOutlineOffset();
1154 glyphData.horizontalOffset -= outlineWidth;
1155 glyphData.horizontalOffset += outlineOffset.x;
1158 // Only need to add the vertical outline offset for the first line
1159 glyphData.verticalOffset -= outlineWidth;
1160 glyphData.verticalOffset += outlineOffset.y;
1163 else if(style == Typesetter::STYLE_SHADOW)
1165 const Vector2& shadowOffset = mModel->GetShadowOffset();
1166 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1170 // Only need to add the vertical shadow offset for first line
1171 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1175 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1176 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1177 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1179 // Get the character-spacing runs.
1180 const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1182 // Aggregate underline-style-properties from mModel
1183 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1184 mModel->GetUnderlineColor(),
1185 mModel->GetUnderlineHeight(),
1186 mModel->GetDashedUnderlineGap(),
1187 mModel->GetDashedUnderlineWidth(),
1194 // Aggregate strikethrough-style-properties from mModel
1195 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1196 mModel->GetStrikethroughHeight(),
1200 // Get the underline runs.
1201 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1202 Vector<UnderlinedGlyphRun> underlineRuns;
1203 underlineRuns.Resize(numberOfUnderlineRuns);
1204 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1206 // Get the strikethrough runs.
1207 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1208 Vector<StrikethroughGlyphRun> strikethroughRuns;
1209 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1210 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1212 bool thereAreUnderlinedGlyphs = false;
1213 bool thereAreStrikethroughGlyphs = false;
1215 float currentUnderlinePosition = 0.0f;
1216 float currentUnderlineHeight = modelUnderlineProperties.height;
1217 float maxUnderlineHeight = currentUnderlineHeight;
1218 auto currentUnderlineProperties = modelUnderlineProperties;
1220 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1221 float maxStrikethroughHeight = currentStrikethroughHeight;
1222 auto currentStrikethroughProperties = modelStrikethroughProperties;
1223 float strikethroughStartingYPosition = 0.0f;
1225 FontId lastFontId = 0;
1227 float lineExtentLeft = bufferWidth;
1228 float lineExtentRight = 0.0f;
1229 float baseline = 0.0f;
1230 bool addHyphen = false;
1232 // Traverses the glyphs of the line.
1233 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1234 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1235 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1237 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1239 //To handle START case of ellipsis, the first glyph has been shifted
1240 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1241 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1243 //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.
1244 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1246 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1247 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1249 // Ignore any glyph that removed for MIDDLE ellipsis
1252 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1254 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1258 // Retrieve the glyph's info.
1259 const GlyphInfo* glyphInfo;
1261 if(addHyphen && hyphens)
1263 glyphInfo = hyphens + hyphenIndex;
1268 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1271 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1272 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1274 // Nothing to do if the glyph's width or height is zero.
1278 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1279 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1280 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1281 currentUnderlineHeight = currentUnderlineProperties.height;
1282 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1284 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1285 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1286 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1287 currentStrikethroughHeight = currentStrikethroughProperties.height;
1288 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1290 // Are we still using the same fontId as previous
1291 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1293 // We need to fetch fresh font underline metrics
1294 FontMetrics fontMetrics;
1295 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1297 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1298 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1302 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1305 if(strikethroughGlyph)
1307 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1310 // Update lastFontId because fontId is changed
1311 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1314 // Retrieves the glyph's position.
1315 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1319 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1320 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1321 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1322 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1323 position.y = -glyphInfo->yBearing;
1326 if(baseline < position.y + glyphInfo->yBearing)
1328 baseline = position.y + glyphInfo->yBearing;
1331 // Calculate the positions of leftmost and rightmost glyphs in the current line
1332 if(position.x < lineExtentLeft)
1334 lineExtentLeft = position.x;
1337 if(position.x + glyphInfo->width > lineExtentRight)
1339 lineExtentRight = position.x + glyphInfo->width;
1342 // Retrieves the glyph's color.
1343 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1346 if(style == Typesetter::STYLE_SHADOW)
1348 color = mModel->GetShadowColor();
1350 else if(style == Typesetter::STYLE_OUTLINE)
1352 color = mModel->GetOutlineColor();
1356 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1359 // Premultiply alpha
1364 // Retrieves the glyph's bitmap.
1365 glyphData.glyphBitmap.buffer = NULL;
1366 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1367 glyphData.glyphBitmap.height = glyphInfo->height;
1369 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1371 // Don't render outline for other styles
1372 outlineWidth = 0.0f;
1375 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1377 fontClient.CreateBitmap(glyphInfo->fontId,
1379 glyphInfo->isItalicRequired,
1380 glyphInfo->isBoldRequired,
1381 glyphData.glyphBitmap,
1382 static_cast<int32_t>(outlineWidth));
1385 // Sets the glyph's bitmap into the bitmap of the whole text.
1386 if(NULL != glyphData.glyphBitmap.buffer)
1388 if(style == Typesetter::STYLE_OUTLINE)
1390 // Set the position offset for the current glyph
1391 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1392 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1395 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1396 TypesetGlyph(glyphData,
1402 if(style == Typesetter::STYLE_OUTLINE)
1404 // Reset the position offset for the next glyph
1405 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1406 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1409 // free the glyphBitmap.buffer if it is owner of buffer
1410 if(glyphData.glyphBitmap.isBufferOwned)
1412 free(glyphData.glyphBitmap.buffer);
1413 glyphData.glyphBitmap.isBufferOwned = false;
1415 glyphData.glyphBitmap.buffer = NULL;
1420 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1425 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1433 // Draw the underline from the leftmost glyph to the rightmost glyph
1434 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1436 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1439 // Draw the background color from the leftmost glyph to the rightmost glyph
1440 if(style == Typesetter::STYLE_BACKGROUND)
1442 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1445 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1446 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1448 //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.
1449 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.
1450 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1453 // Increases the vertical offset with the line's descender & line spacing.
1454 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1457 return glyphData.bitmapBuffer;
1460 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)
1462 // Underline-tags (this is for Markup case)
1463 // Get the underline runs.
1464 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1465 Vector<UnderlinedGlyphRun> underlineRuns;
1466 underlineRuns.Resize(numberOfUnderlineRuns);
1467 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1469 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1470 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1471 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1472 GlyphIndex startGlyphIndex, endGlyphIndex;
1474 //The outer loop to iterate on the separated chunks of underlined glyph runs
1475 while(itGlyphRun != endItGlyphRun)
1477 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1478 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1480 // Create the image buffer for underline
1481 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1482 // Combine the two buffers
1483 // Result pixel buffer will be stored into topPixelBuffer.
1484 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1489 return topPixelBuffer;
1492 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)
1494 // strikethrough-tags (this is for Markup case)
1495 // Get the strikethrough runs.
1496 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1497 Vector<StrikethroughGlyphRun> strikethroughRuns;
1498 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1499 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1501 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1502 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1503 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1504 GlyphIndex startGlyphIndex, endGlyphIndex;
1506 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1507 while(itGlyphRun != endItGlyphRun)
1509 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1510 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1512 // Create the image buffer for strikethrough
1513 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1514 // Combine the two buffers
1515 // Result pixel buffer will be stored into topPixelBuffer.
1516 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1521 return topPixelBuffer;
1524 Typesetter::Typesetter(const ModelInterface* const model)
1525 : mModel(new ViewModel(model))
1529 Typesetter::~Typesetter()
1536 } // namespace Toolkit