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/debug.h>
24 #include <dali/integration-api/trace.h>
25 #include <dali/public-api/common/constants.h>
26 #include <dali/public-api/math/math-utils.h>
30 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
31 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
32 #include <dali-toolkit/internal/text/line-helper-functions.h>
33 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
34 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
35 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
36 #include <dali-toolkit/internal/text/rendering/view-model.h>
46 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
48 const float HALF(0.5f);
49 const float ONE_AND_A_HALF(1.5f);
52 * @brief Fast multiply & divide by 255. It wiil be useful when we applying alpha value in color
54 * @param x The value between [0..255]
55 * @param y The value between [0..255]
58 inline uint8_t MultiplyAndNormalizeColor(const uint8_t x, const uint8_t y) noexcept
60 const uint32_t xy = static_cast<const uint32_t>(x) * y;
61 return ((xy << 15) + (xy << 7) + xy) >> 23;
64 /// Helper macro define for glyph typesetter. It will reduce some duplicated code line.
67 * @brief Prepare decode glyph bitmap data. It must be call END_GLYPH_BITMAP end of same scope.
69 #define BEGIN_GLYPH_BITMAP(data) \
71 uint32_t glyphOffet = 0u; \
72 const bool useLocalScanline = data.glyphBitmap.compressionType != TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION; \
73 uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer; \
74 DALI_ASSERT_ALWAYS(glyphScanline && "Glyph scanline for buffer is null!");
77 * @brief Macro to skip useless line fast.
79 #define SKIP_GLYPH_SCANLINE(skipLine) \
80 if(useLocalScanline) \
82 for(int32_t lineIndex = 0; lineIndex < skipLine; ++lineIndex) \
84 TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
89 glyphScanline += skipLine * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize); \
93 * @brief Prepare scanline of glyph bitmap data per each lines. It must be call END_GLYPH_SCANLINE_DECODE end of same scope.
95 #define BEGIN_GLYPH_SCANLINE_DECODE(data) \
97 if(useLocalScanline) \
99 TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
103 * @brief Finalize scanline of glyph bitmap data per each lines.
105 #define END_GLYPH_SCANLINE_DECODE(data) \
106 if(!useLocalScanline) \
108 glyphScanline += data.glyphBitmap.width * glyphPixelSize; \
110 } // For ensure that we call BEGIN_GLYPH_SCANLINE_DECODE before
113 * @brief Finalize decode glyph bitmap data.
115 #define END_GLYPH_BITMAP() \
116 if(useLocalScanline) \
118 free(glyphScanline); \
120 } // For ensure that we call BEGIN_GLYPH_BITMAP before
123 /// Helper macro define end.
126 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
130 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
131 Vector2* position; ///< The position of the glyph.
132 TextAbstraction::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
133 uint32_t width; ///< The bitmap's width.
134 uint32_t height; ///< The bitmap's height.
135 int32_t horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
136 int32_t verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
140 * @brief Sets the glyph's buffer into the bitmap's buffer.
142 * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
143 * @param[in] position The position of the glyph.
144 * @param[in] color The color of the glyph.
145 * @param[in] style The style of the text.
146 * @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).
148 void TypesetGlyph(GlyphData& __restrict__ data,
149 const Vector2* const __restrict__ position,
150 const Vector4* const __restrict__ color,
151 const Typesetter::Style style,
152 const Pixel::Format pixelFormat)
154 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
156 // Nothing to do if the width or height of the buffer is zero.
160 // Initial vertical / horizontal offset.
161 const int32_t yOffset = data.verticalOffset + position->y;
162 const int32_t xOffset = data.horizontalOffset + position->x;
164 // Whether the given glyph is a color one.
165 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
166 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
167 const uint32_t glyphAlphaIndex = (glyphPixelSize > 0u) ? glyphPixelSize - 1u : 0u;
169 // Determinate iterator range.
170 const int32_t lineIndexRangeMin = std::max(0, -yOffset);
171 const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
172 const int32_t indexRangeMin = std::max(0, -xOffset);
173 const int32_t indexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
175 // If current glyph don't need to be rendered, just ignore.
176 if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
181 if(Pixel::RGBA8888 == pixelFormat)
183 uint32_t* __restrict__ bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
185 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
187 // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
188 // Just overwrite transparent color and return.
189 if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
191 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
193 // We can use memset here.
194 memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
195 bitmapBuffer += data.width;
200 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
202 // Precalculate input color's packed result.
203 uint32_t packedInputColor = 0u;
204 uint8_t* __restrict__ packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
206 *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
207 *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
208 *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
209 *(packedInputColorBuffer) = static_cast<uint8_t>(color->r * 255);
211 // Prepare glyph bitmap
212 BEGIN_GLYPH_BITMAP(data);
214 // Skip basic line of glyph.
215 SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
217 // Traverse the pixels of the glyph line per line.
220 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
222 BEGIN_GLYPH_SCANLINE_DECODE(data);
224 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
226 const int32_t xOffsetIndex = xOffset + index;
228 // Retrieves the color from the color glyph.
229 uint32_t packedColorGlyph = *(reinterpret_cast<const uint32_t*>(glyphScanline + (index << 2)));
230 uint8_t* __restrict__ packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
232 // Update the alpha channel.
233 const uint8_t colorAlpha = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
234 *(packedColorGlyphBuffer + 3u) = colorAlpha;
236 if(Typesetter::STYLE_SHADOW == style)
238 // The shadow of color glyph needs to have the shadow color.
239 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
240 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
241 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
247 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
250 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
251 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
252 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
254 if(data.glyphBitmap.isColorBitmap)
256 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
257 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
258 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
262 // Set the color into the final pixel buffer.
263 *(bitmapBuffer + xOffsetIndex) = packedColorGlyph;
266 bitmapBuffer += data.width;
268 END_GLYPH_SCANLINE_DECODE(data);
273 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
275 BEGIN_GLYPH_SCANLINE_DECODE(data);
277 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
279 // Update the alpha channel.
280 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
282 // Copy non-transparent pixels only
285 const int32_t xOffsetIndex = xOffset + index;
287 // Check alpha of overlapped pixels
288 uint32_t& currentColor = *(bitmapBuffer + xOffsetIndex);
289 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
291 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
292 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
293 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
294 // happen, for example, in the RTL text when we copy glyphs from right to left).
295 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
296 currentAlpha = std::max(currentAlpha, alpha);
297 if(currentAlpha == 255)
299 // Fast-cut to avoid float type operation.
300 currentColor = packedInputColor;
304 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
305 // The format is RGBA8888.
306 uint32_t packedColor = 0u;
307 uint8_t* __restrict__ packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
309 // Color is pre-muliplied with its alpha.
310 *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
311 *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
312 *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
313 *(packedColorBuffer) = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
315 // Set the color into the final pixel buffer.
316 currentColor = packedColor;
321 bitmapBuffer += data.width;
323 END_GLYPH_SCANLINE_DECODE(data);
331 // Below codes required only if not color glyph.
334 uint8_t* __restrict__ bitmapBuffer = data.bitmapBuffer.GetBuffer();
336 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
338 // Prepare glyph bitmap
339 BEGIN_GLYPH_BITMAP(data);
341 // Skip basic line of glyph.
342 SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
344 // Traverse the pixels of the glyph line per line.
345 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
347 BEGIN_GLYPH_SCANLINE_DECODE(data);
349 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
351 const int32_t xOffsetIndex = xOffset + index;
353 // Update the alpha channel.
354 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
356 // Copy non-transparent pixels only
359 // Check alpha of overlapped pixels
360 uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
362 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
363 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
364 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
365 // happen, for example, in the RTL text when we copy glyphs from right to left).
366 currentAlpha = std::max(currentAlpha, alpha);
370 bitmapBuffer += data.width;
372 END_GLYPH_SCANLINE_DECODE(data);
380 /// Draws the specified underline color to the buffer
382 const uint32_t bufferWidth,
383 const uint32_t bufferHeight,
384 GlyphData& glyphData,
385 const float baseline,
386 const float currentUnderlinePosition,
387 const float maxUnderlineHeight,
388 const float lineExtentLeft,
389 const float lineExtentRight,
390 const UnderlineStyleProperties& commonUnderlineProperties,
391 const UnderlineStyleProperties& currentUnderlineProperties,
394 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
395 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
396 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
397 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
399 int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
401 const uint32_t yRangeMin = underlineYOffset;
402 const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
403 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
404 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
406 // If current glyph don't need to be rendered, just ignore.
407 if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
412 // We can optimize by memset when underlineColor.a is near zero
413 uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
415 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
417 // Skip yRangeMin line.
418 bitmapBuffer += yRangeMin * glyphData.width;
420 // Note if underlineType is DASHED, we cannot setup color by memset.
421 if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
423 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
425 // We can use memset.
426 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
427 bitmapBuffer += glyphData.width;
429 if(underlineType == Text::Underline::DOUBLE)
431 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
432 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
433 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
435 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
436 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
438 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
440 // We can use memset.
441 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
442 bitmapBuffer += glyphData.width;
448 uint32_t packedUnderlineColor = 0u;
449 uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
451 // Write the color to the pixel buffer
452 *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
453 *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
454 *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
455 *(packedUnderlineColorBuffer) = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
457 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
459 if(underlineType == Text::Underline::DASHED)
461 float dashWidth = dashedUnderlineWidth;
464 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
466 if(Dali::EqualsZero(dashGap) && dashWidth > 0)
468 // Note : this is same logic as bitmap[y][x] = underlineColor;
469 *(bitmapBuffer + x) = packedUnderlineColor;
472 else if(dashGap < dashedUnderlineGap)
479 dashWidth = dashedUnderlineWidth;
486 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
488 // Note : this is same logic as bitmap[y][x] = underlineColor;
489 *(bitmapBuffer + x) = packedUnderlineColor;
492 bitmapBuffer += glyphData.width;
494 if(underlineType == Text::Underline::DOUBLE)
496 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
497 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
498 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
500 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
501 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
503 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
505 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
507 // Note : this is same logic as bitmap[y][x] = underlineColor;
508 *(bitmapBuffer + x) = packedUnderlineColor;
510 bitmapBuffer += glyphData.width;
516 /// Draws the background color to the buffer
517 void DrawBackgroundColor(
518 Vector4 backgroundColor,
519 const uint32_t bufferWidth,
520 const uint32_t bufferHeight,
521 GlyphData& glyphData,
522 const float baseline,
524 const float lineExtentLeft,
525 const float lineExtentRight)
527 const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
528 const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
529 const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
530 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
532 // If current glyph don't need to be rendered, just ignore.
533 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
538 // We can optimize by memset when backgroundColor.a is near zero
539 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
541 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
543 // Skip yRangeMin line.
544 bitmapBuffer += yRangeMin * glyphData.width;
546 if(backgroundColorAlpha == 0)
548 for(int32_t y = yRangeMin; y < yRangeMax; y++)
550 // We can use memset.
551 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
552 bitmapBuffer += glyphData.width;
557 uint32_t packedBackgroundColor = 0u;
558 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
560 // Write the color to the pixel buffer
561 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
562 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
563 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
564 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
566 for(int32_t y = yRangeMin; y < yRangeMax; y++)
568 for(int32_t x = xRangeMin; x < xRangeMax; x++)
570 // Note : this is same logic as bitmap[y][x] = backgroundColor;
571 *(bitmapBuffer + x) = packedBackgroundColor;
573 bitmapBuffer += glyphData.width;
578 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)
580 // Retrieve lines, glyphs, positions and colors from the view model.
581 const Length modelNumberOfLines = model->GetNumberOfLines();
582 const LineRun* const modelLinesBuffer = model->GetLines();
583 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
584 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
585 const Vector2* const positionBuffer = model->GetLayout();
586 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
587 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
588 const bool removeFrontInset = model->IsRemoveFrontInset();
589 const bool removeBackInset = model->IsRemoveBackInset();
591 const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
593 // Create and initialize the pixel buffer.
595 glyphData.verticalOffset = verticalOffset;
596 glyphData.width = bufferWidth;
597 glyphData.height = bufferHeight;
598 glyphData.bitmapBuffer = buffer;
599 glyphData.horizontalOffset = 0;
601 ColorIndex prevBackgroundColorIndex = 0;
602 ColorIndex backgroundColorIndex = 0;
604 // Traverses the lines of the text.
605 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
607 const LineRun& line = *(modelLinesBuffer + lineIndex);
609 // Sets the horizontal offset of the line.
610 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
611 glyphData.horizontalOffset += horizontalOffset;
613 // Increases the vertical offset with the line's ascender.
614 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
616 float left = bufferWidth;
618 float baseline = 0.0f;
620 // Traverses the glyphs of the line.
621 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
622 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
624 // Retrieve the glyph's info.
625 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
627 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
628 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
630 // Nothing to do if default background color, the glyph's width or height is zero.
634 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
636 if((backgroundColorIndex != prevBackgroundColorIndex) &&
637 (prevBackgroundColorIndex != 0u))
639 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
640 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
643 if(backgroundColorIndex == 0u)
645 prevBackgroundColorIndex = backgroundColorIndex;
646 //if background color is the default do nothing
650 // Retrieves the glyph's position.
651 const Vector2* const position = positionBuffer + glyphIndex;
653 if(baseline < position->y + glyphInfo->yBearing)
655 baseline = position->y + glyphInfo->yBearing;
658 // Calculate the positions of leftmost and rightmost glyphs in the current line
661 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
668 const float originPositionLeft = position->x - glyphInfo->xBearing;
669 if((originPositionLeft < left) || (backgroundColorIndex != prevBackgroundColorIndex))
671 left = originPositionLeft;
677 if(position->x + glyphInfo->width > right)
679 right = position->x - position->x + glyphInfo->width;
684 const float originPositionRight = position->x - glyphInfo->xBearing + glyphInfo->advance;
685 if(originPositionRight > right)
687 right = originPositionRight;
691 prevBackgroundColorIndex = backgroundColorIndex;
694 //draw last background at line end if not default
695 if(backgroundColorIndex != 0u)
697 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
698 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
701 // Increases the vertical offset with the line's descender.
702 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
705 return glyphData.bitmapBuffer;
708 /// Draws the specified strikethrough color to the buffer
709 void DrawStrikethrough(const uint32_t bufferWidth,
710 const uint32_t bufferHeight,
711 GlyphData& glyphData,
712 const float baseline,
713 const float strikethroughStartingYPosition,
714 const float maxStrikethroughHeight,
715 const float lineExtentLeft,
716 const float lineExtentRight,
717 const StrikethroughStyleProperties& commonStrikethroughProperties,
718 const StrikethroughStyleProperties& currentStrikethroughProperties,
721 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
723 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
724 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
725 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
726 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
728 // If current glyph don't need to be rendered, just ignore.
729 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
734 // We can optimize by memset when strikethroughColor.a is near zero
735 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
737 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
739 // Skip yRangeMin line.
740 bitmapBuffer += yRangeMin * glyphData.width;
742 if(strikethroughColorAlpha == 0)
744 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
746 // We can use memset.
747 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
748 bitmapBuffer += glyphData.width;
753 uint32_t packedStrikethroughColor = 0u;
754 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
756 // Write the color to the pixel buffer
757 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
758 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
759 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
760 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
762 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
764 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
766 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
767 *(bitmapBuffer + x) = packedStrikethroughColor;
769 bitmapBuffer += glyphData.width;
775 * @brief Create an initialized image buffer filled with transparent color.
777 * Creates the pixel data used to generate the final image with the given size.
779 * @param[in] bufferWidth The width of the image buffer.
780 * @param[in] bufferHeight The height of the image buffer.
781 * @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).
783 * @return An image buffer.
785 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat)
787 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
789 if(Pixel::RGBA8888 == pixelFormat)
791 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
792 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
793 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
797 memset(imageBuffer.GetBuffer(), 0, static_cast<std::size_t>(bufferWidth * bufferHeight));
804 * @brief Combine the two RGBA image buffers together.
806 * The top layer buffer will blend over the bottom layer buffer:
807 * - If the pixel is not fully opaque from either buffer, it will be blended with
808 * the pixel from the other buffer and copied to the combined buffer.
809 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
810 * buffer will be copied to the combined buffer.
812 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
813 * We can determine which pixelBuffer's memory is destination
815 * @param[in, out] topPixelBuffer The top layer buffer.
816 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
817 * @param[in] bufferWidth The width of the image buffer.
818 * @param[in] bufferHeight The height of the image buffer.
819 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
820 * False if we store the combined image buffer result into bottomPixelBuffer.
823 void CombineImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, bool storeResultIntoTop)
825 // Assume that we always combine two RGBA images
826 // Jump with 4bytes for optimize runtime.
827 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
828 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
830 if(topBuffer == NULL && bottomBuffer == NULL)
832 // Nothing to do if both buffers are empty.
836 if(topBuffer == NULL)
838 // Nothing to do if topBuffer is empty.
839 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
840 if(storeResultIntoTop)
842 topPixelBuffer = bottomPixelBuffer;
847 if(bottomBuffer == NULL)
849 // Nothing to do if bottomBuffer is empty.
850 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
851 if(!storeResultIntoTop)
853 bottomPixelBuffer = topPixelBuffer;
858 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
860 uint32_t* __restrict__ combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
861 uint8_t* __restrict__ topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
863 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
865 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
866 // Otherwise, copy pixel from topBuffer to combinedBuffer.
867 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
869 uint8_t topAlpha = *topAlphaBufferPointer;
873 // Copy the pixel from bottomBuffer to combinedBuffer
874 if(storeResultIntoTop)
876 *(combinedBuffer) = *(bottomBuffer);
879 else if(topAlpha == 255)
881 // Copy the pixel from topBuffer to combinedBuffer
882 if(!storeResultIntoTop)
884 *(combinedBuffer) = *(topBuffer);
889 // At least one pixel is not fully opaque
890 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
891 uint32_t blendedBottomBufferColor = *(bottomBuffer);
892 uint8_t* __restrict__ blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
894 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
895 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
896 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
897 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
899 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
902 // Increase each buffer's pointer.
906 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
912 TypesetterPtr Typesetter::New(const ModelInterface* const model)
914 return TypesetterPtr(new Typesetter(model));
917 ViewModel* Typesetter::GetViewModel()
922 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
924 Devel::PixelBuffer result = RenderWithPixelBuffer(size, textDirection, behaviour, ignoreHorizontalAlignment, pixelFormat);
925 PixelData pixelData = Devel::PixelBuffer::Convert(result);
930 PixelData Typesetter::RenderWithCutout(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, Devel::PixelBuffer mask, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, float originAlpha)
932 Devel::PixelBuffer result = RenderWithPixelBuffer(size, textDirection, behaviour, ignoreHorizontalAlignment, pixelFormat);
933 SetMaskForImageBuffer(mask, result, size.width, size.height, originAlpha);
935 PixelData pixelData = Devel::PixelBuffer::Convert(result);
940 Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
942 DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_RENDERING_TYPESETTER");
943 // @todo. This initial implementation for a TextLabel has only one visible page.
945 // Elides the text if needed.
946 mModel->ElideGlyphs();
948 // Retrieves the layout size.
949 const Size& layoutSize = mModel->GetLayoutSize();
950 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
952 // Set the offset for the horizontal alignment according to the text direction and outline width.
954 switch(mModel->GetHorizontalAlignment())
956 case HorizontalAlignment::BEGIN:
961 case HorizontalAlignment::CENTER:
963 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
966 case HorizontalAlignment::END:
968 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
973 // Set the offset for the vertical alignment.
975 switch(mModel->GetVerticalAlignment())
977 case VerticalAlignment::TOP:
982 case VerticalAlignment::CENTER:
984 penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
985 penY = penY < 0.f ? 0.f : penY;
988 case VerticalAlignment::BOTTOM:
990 penY = static_cast<int32_t>(size.height - layoutSize.height);
995 // Generate the image buffers of the text for each different style first,
996 // then combine all of them together as one final image buffer. We try to
997 // do all of these in CPU only, so that once the final texture is generated,
998 // no calculation is needed in GPU during each frame.
1000 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
1001 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
1003 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
1004 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
1006 //Elided text in ellipsis at START could start on index greater than 0
1007 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1008 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1010 Devel::PixelBuffer imageBuffer;
1012 if(RENDER_MASK == behaviour)
1014 // Generate the image buffer as an alpha mask for color glyphs.
1015 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1017 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
1019 // Generate an empty image buffer so that it can been combined with the image buffers for styles
1020 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1021 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
1025 // Generate the image buffer for the text with no style.
1026 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1029 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
1031 // Generate the outline if enabled
1032 const uint16_t outlineWidth = mModel->GetOutlineWidth();
1033 const float outlineAlpha = mModel->GetOutlineColor().a;
1034 if(outlineWidth != 0u && fabsf(outlineAlpha) > Math::MACHINE_EPSILON_1 && RENDER_OVERLAY_STYLE != behaviour)
1036 // Create the image buffer for outline
1037 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1039 const float& blurRadius = mModel->GetOutlineBlurRadius();
1041 if(blurRadius > Math::MACHINE_EPSILON_1)
1043 outlineImageBuffer.ApplyGaussianBlur(blurRadius);
1046 // Combine the two buffers
1047 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
1050 // @todo. Support shadow for partial text later on.
1052 // Generate the shadow if enabled
1053 const Vector2& shadowOffset = mModel->GetShadowOffset();
1054 const float shadowAlpha = mModel->GetShadowColor().a;
1055 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))
1057 // Create the image buffer for shadow
1058 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1060 // Check whether it will be a soft shadow
1061 const float& blurRadius = mModel->GetShadowBlurRadius();
1063 if(blurRadius > Math::MACHINE_EPSILON_1)
1065 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
1068 // Combine the two buffers
1069 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
1072 // Generate the background if enabled
1073 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
1074 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
1075 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
1077 Devel::PixelBuffer backgroundImageBuffer;
1079 if(backgroundEnabled)
1081 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1085 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1088 if(backgroundMarkupSet)
1090 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
1093 // Combine the two buffers
1094 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1097 // Generate the background_with_mask if enabled
1098 const bool backgroundWithCutoutEnabled = mModel->IsBackgroundWithCutoutEnabled();
1099 if((backgroundWithCutoutEnabled) && RENDER_OVERLAY_STYLE != behaviour)
1101 Devel::PixelBuffer backgroundImageBuffer;
1103 backgroundImageBuffer = CreateFullBackgroundBuffer(bufferWidth, bufferHeight, mModel->GetBackgroundColorWithCutout());
1105 // Combine the two buffers
1106 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1109 if(RENDER_OVERLAY_STYLE == behaviour)
1111 if(mModel->IsUnderlineEnabled())
1113 // Create the image buffer for underline
1114 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1116 // Combine the two buffers
1117 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
1120 if(mModel->IsStrikethroughEnabled())
1122 // Create the image buffer for strikethrough
1123 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
1125 // Combine the two buffers
1126 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
1129 // Markup-Processor for overlay styles
1130 if(mModel->IsMarkupProcessorEnabled() || mModel->IsSpannedTextPlaced())
1132 if(mModel->IsMarkupUnderlineSet())
1134 imageBuffer = ApplyUnderlineMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1137 if(mModel->IsMarkupStrikethroughSet())
1139 imageBuffer = ApplyStrikethroughMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1148 Devel::PixelBuffer Typesetter::CreateFullBackgroundBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Vector4& backgroundColor)
1150 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
1151 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
1153 Devel::PixelBuffer buffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1155 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(buffer.GetBuffer());
1157 uint32_t packedBackgroundColor = 0u;
1158 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
1160 // Write the color to the pixel buffer
1161 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
1162 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
1163 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
1164 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
1166 std::fill(bitmapBuffer, bitmapBuffer + bufferSizeInt, packedBackgroundColor);
1171 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)
1173 // Retrieve lines, glyphs, positions and colors from the view model.
1174 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1175 const LineRun* const __restrict__ modelLinesBuffer = mModel->GetLines();
1176 const GlyphInfo* const __restrict__ glyphsBuffer = mModel->GetGlyphs();
1177 const Vector2* const __restrict__ positionBuffer = mModel->GetLayout();
1178 const Vector4* const __restrict__ colorsBuffer = mModel->GetColors();
1179 const ColorIndex* const __restrict__ colorIndexBuffer = mModel->GetColorIndices();
1180 const GlyphInfo* __restrict__ hyphens = mModel->GetHyphens();
1181 const Length* __restrict__ hyphenIndices = mModel->GetHyphenIndices();
1182 const Length hyphensCount = mModel->GetHyphensCount();
1183 const bool removeFrontInset = mModel->IsRemoveFrontInset();
1184 const bool removeBackInset = mModel->IsRemoveBackInset();
1185 const bool cutoutEnabled = mModel->IsCutoutEnabled();
1187 // Elided text info. Indices according to elided text and Ellipsis position.
1188 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1189 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1190 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1191 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1192 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1194 // Whether to use the default color.
1195 const bool useDefaultColor = (NULL == colorsBuffer);
1196 const Vector4& defaultColor = mModel->GetDefaultColor();
1198 // Create and initialize the pixel buffer.
1199 GlyphData glyphData;
1200 glyphData.verticalOffset = verticalOffset;
1201 glyphData.width = bufferWidth;
1202 glyphData.height = bufferHeight;
1203 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1204 glyphData.horizontalOffset = 0;
1206 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1207 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1208 Length hyphenIndex = 0;
1210 const Character* __restrict__ textBuffer = mModel->GetTextBuffer();
1211 float calculatedAdvance = 0.f;
1212 const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1213 const CharacterIndex* __restrict__ glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1215 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1217 // Traverses the lines of the text.
1218 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1220 const LineRun& line = *(modelLinesBuffer + lineIndex);
1222 // Sets the horizontal offset of the line.
1223 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1224 glyphData.horizontalOffset += horizontalOffset;
1226 // Increases the vertical offset with the line's ascender.
1227 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1229 // Retrieves the glyph's outline width
1230 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1232 if(style == Typesetter::STYLE_OUTLINE)
1234 const Vector2& outlineOffset = mModel->GetOutlineOffset();
1236 glyphData.horizontalOffset -= outlineWidth;
1237 glyphData.horizontalOffset += outlineOffset.x;
1240 // Only need to add the vertical outline offset for the first line
1241 glyphData.verticalOffset -= outlineWidth;
1242 glyphData.verticalOffset += outlineOffset.y;
1245 else if(style == Typesetter::STYLE_SHADOW)
1247 const Vector2& shadowOffset = mModel->GetShadowOffset();
1248 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1252 // Only need to add the vertical shadow offset for first line
1253 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1257 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1258 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1259 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1261 // Get the character-spacing runs.
1262 const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1264 // Aggregate underline-style-properties from mModel
1265 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1266 mModel->GetUnderlineColor(),
1267 mModel->GetUnderlineHeight(),
1268 mModel->GetDashedUnderlineGap(),
1269 mModel->GetDashedUnderlineWidth(),
1276 // Aggregate strikethrough-style-properties from mModel
1277 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1278 mModel->GetStrikethroughHeight(),
1282 // Get the underline runs.
1283 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1284 Vector<UnderlinedGlyphRun> underlineRuns;
1285 underlineRuns.Resize(numberOfUnderlineRuns);
1286 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1288 // Get the strikethrough runs.
1289 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1290 Vector<StrikethroughGlyphRun> strikethroughRuns;
1291 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1292 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1294 bool thereAreUnderlinedGlyphs = false;
1295 bool thereAreStrikethroughGlyphs = false;
1297 float currentUnderlinePosition = 0.0f;
1298 float currentUnderlineHeight = modelUnderlineProperties.height;
1299 float maxUnderlineHeight = currentUnderlineHeight;
1300 auto currentUnderlineProperties = modelUnderlineProperties;
1302 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1303 float maxStrikethroughHeight = currentStrikethroughHeight;
1304 auto currentStrikethroughProperties = modelStrikethroughProperties;
1305 float strikethroughStartingYPosition = 0.0f;
1307 FontId lastFontId = 0;
1309 float lineExtentLeft = bufferWidth;
1310 float lineExtentRight = 0.0f;
1311 float baseline = 0.0f;
1312 bool addHyphen = false;
1314 // Traverses the glyphs of the line.
1315 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1316 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1317 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1319 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1321 //To handle START case of ellipsis, the first glyph has been shifted
1322 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1323 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1325 //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.
1326 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1328 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1329 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1331 // Ignore any glyph that removed for MIDDLE ellipsis
1334 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1336 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1340 // Retrieve the glyph's info.
1341 const GlyphInfo* glyphInfo;
1343 if(addHyphen && hyphens)
1345 glyphInfo = hyphens + hyphenIndex;
1350 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1353 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1354 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1356 // Nothing to do if the glyph's width or height is zero.
1360 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1361 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1362 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1363 currentUnderlineHeight = currentUnderlineProperties.height;
1364 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1366 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1367 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1368 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1369 currentStrikethroughHeight = currentStrikethroughProperties.height;
1370 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1372 // Are we still using the same fontId as previous
1373 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1375 // We need to fetch fresh font underline metrics
1376 FontMetrics fontMetrics;
1377 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1379 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1380 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1384 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1387 if(strikethroughGlyph)
1389 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1392 // Update lastFontId because fontId is changed
1393 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1396 // Retrieves the glyph's position.
1397 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1401 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1402 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1403 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1404 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1405 position.y = -glyphInfo->yBearing;
1408 if(baseline < position.y + glyphInfo->yBearing)
1410 baseline = position.y + glyphInfo->yBearing;
1413 // Calculate the positions of leftmost and rightmost glyphs in the current line
1414 if(removeFrontInset)
1416 if(position.x < lineExtentLeft)
1418 lineExtentLeft = position.x;
1423 const float originPositionLeft = position.x - glyphInfo->xBearing;
1424 if(originPositionLeft < lineExtentLeft)
1426 lineExtentLeft = originPositionLeft;
1432 if(position.x + glyphInfo->width > lineExtentRight)
1434 lineExtentRight = position.x + glyphInfo->width;
1439 const float originPositionRight = position.x - glyphInfo->xBearing + glyphInfo->advance;
1440 if(originPositionRight > lineExtentRight)
1442 lineExtentRight = originPositionRight;
1446 // Retrieves the glyph's color.
1447 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1450 if(style == Typesetter::STYLE_SHADOW)
1452 color = mModel->GetShadowColor();
1454 else if(style == Typesetter::STYLE_OUTLINE)
1456 color = mModel->GetOutlineColor();
1460 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1463 if(style == Typesetter::STYLE_NONE && cutoutEnabled)
1465 // Temporarily adjust the transparency to 1.f
1469 // Premultiply alpha
1474 // Retrieves the glyph's bitmap.
1475 glyphData.glyphBitmap.buffer = NULL;
1476 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1477 glyphData.glyphBitmap.height = glyphInfo->height;
1479 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1481 // Don't render outline for other styles
1482 outlineWidth = 0.0f;
1485 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1487 fontClient.CreateBitmap(glyphInfo->fontId,
1489 glyphInfo->isItalicRequired,
1490 glyphInfo->isBoldRequired,
1491 glyphData.glyphBitmap,
1492 static_cast<int32_t>(outlineWidth));
1495 // Sets the glyph's bitmap into the bitmap of the whole text.
1496 if(NULL != glyphData.glyphBitmap.buffer)
1498 if(style == Typesetter::STYLE_OUTLINE)
1500 // Set the position offset for the current glyph
1501 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1502 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1505 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1506 TypesetGlyph(glyphData,
1512 if(style == Typesetter::STYLE_OUTLINE)
1514 // Reset the position offset for the next glyph
1515 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1516 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1519 // free the glyphBitmap.buffer if it is owner of buffer
1520 if(glyphData.glyphBitmap.isBufferOwned)
1522 free(glyphData.glyphBitmap.buffer);
1523 glyphData.glyphBitmap.isBufferOwned = false;
1525 glyphData.glyphBitmap.buffer = NULL;
1530 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1535 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1543 // Draw the underline from the leftmost glyph to the rightmost glyph
1544 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1546 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1549 // Draw the background color from the leftmost glyph to the rightmost glyph
1550 if(style == Typesetter::STYLE_BACKGROUND)
1552 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1555 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1556 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1558 //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.
1559 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.
1560 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1563 // Increases the vertical offset with the line's descender & line spacing.
1564 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1567 return glyphData.bitmapBuffer;
1570 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)
1572 // Underline-tags (this is for Markup case)
1573 // Get the underline runs.
1574 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1575 Vector<UnderlinedGlyphRun> underlineRuns;
1576 underlineRuns.Resize(numberOfUnderlineRuns);
1577 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1579 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1580 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1581 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1582 GlyphIndex startGlyphIndex, endGlyphIndex;
1584 //The outer loop to iterate on the separated chunks of underlined glyph runs
1585 while(itGlyphRun != endItGlyphRun)
1587 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1588 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1590 // Create the image buffer for underline
1591 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1592 // Combine the two buffers
1593 // Result pixel buffer will be stored into topPixelBuffer.
1594 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1599 return topPixelBuffer;
1602 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)
1604 // strikethrough-tags (this is for Markup case)
1605 // Get the strikethrough runs.
1606 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1607 Vector<StrikethroughGlyphRun> strikethroughRuns;
1608 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1609 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1611 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1612 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1613 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1614 GlyphIndex startGlyphIndex, endGlyphIndex;
1616 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1617 while(itGlyphRun != endItGlyphRun)
1619 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1620 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1622 // Create the image buffer for strikethrough
1623 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1624 // Combine the two buffers
1625 // Result pixel buffer will be stored into topPixelBuffer.
1626 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1631 return topPixelBuffer;
1634 void Typesetter::SetMaskForImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, float originAlpha)
1636 // Assume that we always combine two RGBA images
1637 // Jump with 4bytes for optimize runtime.
1638 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
1639 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
1641 if(topBuffer == NULL || bottomBuffer == NULL)
1643 // Nothing to do if one of both buffers are empty.
1647 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
1649 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
1651 uint32_t topBufferColor = *(topBuffer);
1652 uint32_t bottomBufferColor = *(bottomBuffer);
1653 uint8_t* __restrict__ topBufferColorBuffer = reinterpret_cast<uint8_t*>(&topBufferColor);
1654 uint8_t* __restrict__ bottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&bottomBufferColor);
1656 uint8_t topAlpha = topBufferColorBuffer[3];
1657 uint8_t bottomAlpha = 255 - topAlpha;
1659 float tempTop[4], tempBottom[4];
1661 // Return the transparency of the text to original.
1662 tempTop[0] = static_cast<float>(topBufferColorBuffer[0]) * originAlpha;
1663 tempTop[1] = static_cast<float>(topBufferColorBuffer[1]) * originAlpha;
1664 tempTop[2] = static_cast<float>(topBufferColorBuffer[2]) * originAlpha;
1665 tempTop[3] = static_cast<float>(topBufferColorBuffer[3]) * originAlpha;
1668 tempBottom[0] = static_cast<float>(bottomBufferColorBuffer[0]) * static_cast<float>(bottomAlpha) / 255.f;
1669 tempBottom[1] = static_cast<float>(bottomBufferColorBuffer[1]) * static_cast<float>(bottomAlpha) / 255.f;
1670 tempBottom[2] = static_cast<float>(bottomBufferColorBuffer[2]) * static_cast<float>(bottomAlpha) / 255.f;
1671 tempBottom[3] = static_cast<float>(bottomBufferColorBuffer[3]) * static_cast<float>(bottomAlpha) / 255.f;
1673 bottomBufferColorBuffer[0] = static_cast<uint8_t>(std::min(255u, static_cast<uint32_t>(tempBottom[0] + tempTop[0])));
1674 bottomBufferColorBuffer[1] = static_cast<uint8_t>(std::min(255u, static_cast<uint32_t>(tempBottom[1] + tempTop[1])));
1675 bottomBufferColorBuffer[2] = static_cast<uint8_t>(std::min(255u, static_cast<uint32_t>(tempBottom[2] + tempTop[2])));
1676 bottomBufferColorBuffer[3] = static_cast<uint8_t>(std::min(255u, static_cast<uint32_t>(tempBottom[3] + tempTop[3])));
1678 *(bottomBuffer) = bottomBufferColor;
1680 // Increase each buffer's pointer.
1686 Typesetter::Typesetter(const ModelInterface* const model)
1687 : mModel(new ViewModel(model))
1691 Typesetter::~Typesetter()
1698 } // namespace Toolkit