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>
23 #include <dali/devel-api/text-abstraction/font-client.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/integration-api/trace.h>
26 #include <dali/public-api/common/constants.h>
27 #include <dali/public-api/math/math-utils.h>
31 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
32 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
33 #include <dali-toolkit/internal/text/line-helper-functions.h>
34 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
35 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
36 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
37 #include <dali-toolkit/internal/text/rendering/view-model.h>
47 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
49 const float HALF(0.5f);
50 const float ONE_AND_A_HALF(1.5f);
53 * @brief Fast multiply & divide by 255. It wiil be useful when we applying alpha value in color
55 * @param x The value between [0..255]
56 * @param y The value between [0..255]
59 inline uint8_t MultiplyAndNormalizeColor(const uint8_t x, const uint8_t y) noexcept
61 const uint32_t xy = static_cast<const uint32_t>(x) * y;
62 return ((xy << 15) + (xy << 7) + xy) >> 23;
65 /// Helper macro define for glyph typesetter. It will reduce some duplicated code line.
68 * @brief Prepare decode glyph bitmap data. It must be call END_GLYPH_BITMAP end of same scope.
70 #define BEGIN_GLYPH_BITMAP(data) \
72 uint32_t glyphOffet = 0u; \
73 const bool useLocalScanline = data.glyphBitmap.compressionType != TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION; \
74 uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer; \
75 DALI_ASSERT_ALWAYS(glyphScanline && "Glyph scanline for buffer is null!");
78 * @brief Macro to skip useless line fast.
80 #define SKIP_GLYPH_SCANLINE(skipLine) \
81 if(useLocalScanline) \
83 for(int32_t lineIndex = 0; lineIndex < skipLine; ++lineIndex) \
85 TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
90 glyphScanline += skipLine * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize); \
94 * @brief Prepare scanline of glyph bitmap data per each lines. It must be call END_GLYPH_SCANLINE_DECODE end of same scope.
96 #define BEGIN_GLYPH_SCANLINE_DECODE(data) \
98 if(useLocalScanline) \
100 TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
104 * @brief Finalize scanline of glyph bitmap data per each lines.
106 #define END_GLYPH_SCANLINE_DECODE(data) \
107 if(!useLocalScanline) \
109 glyphScanline += data.glyphBitmap.width * glyphPixelSize; \
111 } // For ensure that we call BEGIN_GLYPH_SCANLINE_DECODE before
114 * @brief Finalize decode glyph bitmap data.
116 #define END_GLYPH_BITMAP() \
117 if(useLocalScanline) \
119 free(glyphScanline); \
121 } // For ensure that we call BEGIN_GLYPH_BITMAP before
124 /// Helper macro define end.
127 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
131 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
132 Vector2* position; ///< The position of the glyph.
133 TextAbstraction::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
134 uint32_t width; ///< The bitmap's width.
135 uint32_t height; ///< The bitmap's height.
136 int32_t horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
137 int32_t verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
141 * @brief Sets the glyph's buffer into the bitmap's buffer.
143 * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
144 * @param[in] position The position of the glyph.
145 * @param[in] color The color of the glyph.
146 * @param[in] style The style of the text.
147 * @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).
149 void TypesetGlyph(GlyphData& __restrict__ data,
150 const Vector2* const __restrict__ position,
151 const Vector4* const __restrict__ color,
152 const Typesetter::Style style,
153 const Pixel::Format pixelFormat)
155 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
157 // Nothing to do if the width or height of the buffer is zero.
161 // Initial vertical / horizontal offset.
162 const int32_t yOffset = data.verticalOffset + position->y;
163 const int32_t xOffset = data.horizontalOffset + position->x;
165 // Whether the given glyph is a color one.
166 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
167 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
168 const uint32_t glyphAlphaIndex = (glyphPixelSize > 0u) ? glyphPixelSize - 1u : 0u;
170 // Determinate iterator range.
171 const int32_t lineIndexRangeMin = std::max(0, -yOffset);
172 const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
173 const int32_t indexRangeMin = std::max(0, -xOffset);
174 const int32_t indexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
176 // If current glyph don't need to be rendered, just ignore.
177 if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
182 if(Pixel::RGBA8888 == pixelFormat)
184 uint32_t* __restrict__ bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
186 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
188 // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
189 // Just overwrite transparent color and return.
190 if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
192 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
194 // We can use memset here.
195 memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
196 bitmapBuffer += data.width;
201 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
203 // Precalculate input color's packed result.
204 uint32_t packedInputColor = 0u;
205 uint8_t* __restrict__ packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
207 *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
208 *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
209 *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
210 *(packedInputColorBuffer) = static_cast<uint8_t>(color->r * 255);
212 // Prepare glyph bitmap
213 BEGIN_GLYPH_BITMAP(data);
215 // Skip basic line of glyph.
216 SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
218 // Traverse the pixels of the glyph line per line.
221 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
223 BEGIN_GLYPH_SCANLINE_DECODE(data);
225 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
227 const int32_t xOffsetIndex = xOffset + index;
229 // Retrieves the color from the color glyph.
230 uint32_t packedColorGlyph = *(reinterpret_cast<const uint32_t*>(glyphScanline + (index << 2)));
231 uint8_t* __restrict__ packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
233 // Update the alpha channel.
234 const uint8_t colorAlpha = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
235 *(packedColorGlyphBuffer + 3u) = colorAlpha;
237 if(Typesetter::STYLE_SHADOW == style)
239 // The shadow of color glyph needs to have the shadow color.
240 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
241 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
242 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
248 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
251 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
252 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
253 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
255 if(data.glyphBitmap.isColorBitmap)
257 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
258 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
259 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
263 // Set the color into the final pixel buffer.
264 *(bitmapBuffer + xOffsetIndex) = packedColorGlyph;
267 bitmapBuffer += data.width;
269 END_GLYPH_SCANLINE_DECODE(data);
274 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
276 BEGIN_GLYPH_SCANLINE_DECODE(data);
278 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
280 // Update the alpha channel.
281 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
283 // Copy non-transparent pixels only
286 const int32_t xOffsetIndex = xOffset + index;
288 // Check alpha of overlapped pixels
289 uint32_t& currentColor = *(bitmapBuffer + xOffsetIndex);
290 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
292 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
293 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
294 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
295 // happen, for example, in the RTL text when we copy glyphs from right to left).
296 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
297 currentAlpha = std::max(currentAlpha, alpha);
298 if(currentAlpha == 255)
300 // Fast-cut to avoid float type operation.
301 currentColor = packedInputColor;
305 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
306 // The format is RGBA8888.
307 uint32_t packedColor = 0u;
308 uint8_t* __restrict__ packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
310 // Color is pre-muliplied with its alpha.
311 *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
312 *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
313 *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
314 *(packedColorBuffer) = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
316 // Set the color into the final pixel buffer.
317 currentColor = packedColor;
322 bitmapBuffer += data.width;
324 END_GLYPH_SCANLINE_DECODE(data);
332 // Below codes required only if not color glyph.
335 uint8_t* __restrict__ bitmapBuffer = data.bitmapBuffer.GetBuffer();
337 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
339 // Prepare glyph bitmap
340 BEGIN_GLYPH_BITMAP(data);
342 // Skip basic line of glyph.
343 SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
345 // Traverse the pixels of the glyph line per line.
346 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
348 BEGIN_GLYPH_SCANLINE_DECODE(data);
350 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
352 const int32_t xOffsetIndex = xOffset + index;
354 // Update the alpha channel.
355 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
357 // Copy non-transparent pixels only
360 // Check alpha of overlapped pixels
361 uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
363 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
364 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
365 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
366 // happen, for example, in the RTL text when we copy glyphs from right to left).
367 currentAlpha = std::max(currentAlpha, alpha);
371 bitmapBuffer += data.width;
373 END_GLYPH_SCANLINE_DECODE(data);
381 /// Draws the specified underline color to the buffer
383 const uint32_t bufferWidth,
384 const uint32_t bufferHeight,
385 GlyphData& glyphData,
386 const float baseline,
387 const float currentUnderlinePosition,
388 const float maxUnderlineHeight,
389 const float lineExtentLeft,
390 const float lineExtentRight,
391 const UnderlineStyleProperties& commonUnderlineProperties,
392 const UnderlineStyleProperties& currentUnderlineProperties,
395 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
396 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
397 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
398 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
400 int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
402 const uint32_t yRangeMin = underlineYOffset;
403 const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
404 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
405 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
407 // If current glyph don't need to be rendered, just ignore.
408 if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
413 // We can optimize by memset when underlineColor.a is near zero
414 uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
416 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
418 // Skip yRangeMin line.
419 bitmapBuffer += yRangeMin * glyphData.width;
421 // Note if underlineType is DASHED, we cannot setup color by memset.
422 if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
424 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
426 // We can use memset.
427 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
428 bitmapBuffer += glyphData.width;
430 if(underlineType == Text::Underline::DOUBLE)
432 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
433 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
434 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
436 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
437 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
439 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
441 // We can use memset.
442 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
443 bitmapBuffer += glyphData.width;
449 uint32_t packedUnderlineColor = 0u;
450 uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
452 // Write the color to the pixel buffer
453 *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
454 *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
455 *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
456 *(packedUnderlineColorBuffer) = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
458 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
460 if(underlineType == Text::Underline::DASHED)
462 float dashWidth = dashedUnderlineWidth;
465 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
467 if(Dali::EqualsZero(dashGap) && dashWidth > 0)
469 // Note : this is same logic as bitmap[y][x] = underlineColor;
470 *(bitmapBuffer + x) = packedUnderlineColor;
473 else if(dashGap < dashedUnderlineGap)
480 dashWidth = dashedUnderlineWidth;
487 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
489 // Note : this is same logic as bitmap[y][x] = underlineColor;
490 *(bitmapBuffer + x) = packedUnderlineColor;
493 bitmapBuffer += glyphData.width;
495 if(underlineType == Text::Underline::DOUBLE)
497 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
498 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
499 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
501 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
502 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
504 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
506 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
508 // Note : this is same logic as bitmap[y][x] = underlineColor;
509 *(bitmapBuffer + x) = packedUnderlineColor;
511 bitmapBuffer += glyphData.width;
517 /// Draws the background color to the buffer
518 void DrawBackgroundColor(
519 Vector4 backgroundColor,
520 const uint32_t bufferWidth,
521 const uint32_t bufferHeight,
522 GlyphData& glyphData,
523 const float baseline,
525 const float lineExtentLeft,
526 const float lineExtentRight)
528 const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
529 const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
530 const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
531 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
533 // If current glyph don't need to be rendered, just ignore.
534 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
539 // We can optimize by memset when backgroundColor.a is near zero
540 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
542 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
544 // Skip yRangeMin line.
545 bitmapBuffer += yRangeMin * glyphData.width;
547 if(backgroundColorAlpha == 0)
549 for(int32_t y = yRangeMin; y < yRangeMax; y++)
551 // We can use memset.
552 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
553 bitmapBuffer += glyphData.width;
558 uint32_t packedBackgroundColor = 0u;
559 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
561 // Write the color to the pixel buffer
562 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
563 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
564 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
565 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
567 for(int32_t y = yRangeMin; y < yRangeMax; y++)
569 for(int32_t x = xRangeMin; x < xRangeMax; x++)
571 // Note : this is same logic as bitmap[y][x] = backgroundColor;
572 *(bitmapBuffer + x) = packedBackgroundColor;
574 bitmapBuffer += glyphData.width;
579 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)
581 // Retrieve lines, glyphs, positions and colors from the view model.
582 const Length modelNumberOfLines = model->GetNumberOfLines();
583 const LineRun* const modelLinesBuffer = model->GetLines();
584 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
585 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
586 const Vector2* const positionBuffer = model->GetLayout();
587 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
588 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
589 const bool removeFrontInset = model->IsRemoveFrontInset();
590 const bool removeBackInset = model->IsRemoveBackInset();
592 const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
594 // Create and initialize the pixel buffer.
596 glyphData.verticalOffset = verticalOffset;
597 glyphData.width = bufferWidth;
598 glyphData.height = bufferHeight;
599 glyphData.bitmapBuffer = buffer;
600 glyphData.horizontalOffset = 0;
602 ColorIndex prevBackgroundColorIndex = 0;
603 ColorIndex backgroundColorIndex = 0;
605 // Traverses the lines of the text.
606 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
608 const LineRun& line = *(modelLinesBuffer + lineIndex);
610 // Sets the horizontal offset of the line.
611 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
612 glyphData.horizontalOffset += horizontalOffset;
614 // Increases the vertical offset with the line's ascender.
615 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
617 float left = bufferWidth;
619 float baseline = 0.0f;
621 // Traverses the glyphs of the line.
622 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
623 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
625 // Retrieve the glyph's info.
626 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
628 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
629 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
631 // Nothing to do if default background color, the glyph's width or height is zero.
635 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
637 if((backgroundColorIndex != prevBackgroundColorIndex) &&
638 (prevBackgroundColorIndex != 0u))
640 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
641 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
644 if(backgroundColorIndex == 0u)
646 prevBackgroundColorIndex = backgroundColorIndex;
647 //if background color is the default do nothing
651 // Retrieves the glyph's position.
652 const Vector2* const position = positionBuffer + glyphIndex;
654 if(baseline < position->y + glyphInfo->yBearing)
656 baseline = position->y + glyphInfo->yBearing;
659 // Calculate the positions of leftmost and rightmost glyphs in the current line
662 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
669 const float originPositionLeft = position->x - glyphInfo->xBearing;
670 if((originPositionLeft < left) || (backgroundColorIndex != prevBackgroundColorIndex))
672 left = originPositionLeft;
678 if(position->x + glyphInfo->width > right)
680 right = position->x - position->x + glyphInfo->width;
685 const float originPositionRight = position->x - glyphInfo->xBearing + glyphInfo->advance;
686 if(originPositionRight > right)
688 right = originPositionRight;
692 prevBackgroundColorIndex = backgroundColorIndex;
695 //draw last background at line end if not default
696 if(backgroundColorIndex != 0u)
698 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
699 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
702 // Increases the vertical offset with the line's descender.
703 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
706 return glyphData.bitmapBuffer;
709 /// Draws the specified strikethrough color to the buffer
710 void DrawStrikethrough(const uint32_t bufferWidth,
711 const uint32_t bufferHeight,
712 GlyphData& glyphData,
713 const float baseline,
714 const float strikethroughStartingYPosition,
715 const float maxStrikethroughHeight,
716 const float lineExtentLeft,
717 const float lineExtentRight,
718 const StrikethroughStyleProperties& commonStrikethroughProperties,
719 const StrikethroughStyleProperties& currentStrikethroughProperties,
722 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
724 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
725 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
726 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
727 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
729 // If current glyph don't need to be rendered, just ignore.
730 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
735 // We can optimize by memset when strikethroughColor.a is near zero
736 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
738 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
740 // Skip yRangeMin line.
741 bitmapBuffer += yRangeMin * glyphData.width;
743 if(strikethroughColorAlpha == 0)
745 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
747 // We can use memset.
748 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
749 bitmapBuffer += glyphData.width;
754 uint32_t packedStrikethroughColor = 0u;
755 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
757 // Write the color to the pixel buffer
758 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
759 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
760 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
761 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
763 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
765 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
767 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
768 *(bitmapBuffer + x) = packedStrikethroughColor;
770 bitmapBuffer += glyphData.width;
776 * @brief Create an initialized image buffer filled with transparent color.
778 * Creates the pixel data used to generate the final image with the given size.
780 * @param[in] bufferWidth The width of the image buffer.
781 * @param[in] bufferHeight The height of the image buffer.
782 * @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).
784 * @return An image buffer.
786 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat)
788 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
790 if(Pixel::RGBA8888 == pixelFormat)
792 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
793 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
794 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
798 memset(imageBuffer.GetBuffer(), 0, static_cast<std::size_t>(bufferWidth * bufferHeight));
805 * @brief Combine the two RGBA image buffers together.
807 * The top layer buffer will blend over the bottom layer buffer:
808 * - If the pixel is not fully opaque from either buffer, it will be blended with
809 * the pixel from the other buffer and copied to the combined buffer.
810 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
811 * buffer will be copied to the combined buffer.
813 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
814 * We can determine which pixelBuffer's memory is destination
816 * @param[in, out] topPixelBuffer The top layer buffer.
817 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
818 * @param[in] bufferWidth The width of the image buffer.
819 * @param[in] bufferHeight The height of the image buffer.
820 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
821 * False if we store the combined image buffer result into bottomPixelBuffer.
824 void CombineImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, bool storeResultIntoTop)
826 // Assume that we always combine two RGBA images
827 // Jump with 4bytes for optimize runtime.
828 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
829 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
831 if(topBuffer == NULL && bottomBuffer == NULL)
833 // Nothing to do if both buffers are empty.
837 if(topBuffer == NULL)
839 // Nothing to do if topBuffer is empty.
840 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
841 if(storeResultIntoTop)
843 topPixelBuffer = bottomPixelBuffer;
848 if(bottomBuffer == NULL)
850 // Nothing to do if bottomBuffer is empty.
851 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
852 if(!storeResultIntoTop)
854 bottomPixelBuffer = topPixelBuffer;
859 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
861 uint32_t* __restrict__ combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
862 uint8_t* __restrict__ topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
864 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
866 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
867 // Otherwise, copy pixel from topBuffer to combinedBuffer.
868 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
870 uint8_t topAlpha = *topAlphaBufferPointer;
874 // Copy the pixel from bottomBuffer to combinedBuffer
875 if(storeResultIntoTop)
877 *(combinedBuffer) = *(bottomBuffer);
880 else if(topAlpha == 255)
882 // Copy the pixel from topBuffer to combinedBuffer
883 if(!storeResultIntoTop)
885 *(combinedBuffer) = *(topBuffer);
890 // At least one pixel is not fully opaque
891 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
892 uint32_t blendedBottomBufferColor = *(bottomBuffer);
893 uint8_t* __restrict__ blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
895 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
896 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
897 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
898 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
900 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
903 // Increase each buffer's pointer.
907 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
913 TypesetterPtr Typesetter::New(const ModelInterface* const model)
915 return TypesetterPtr(new Typesetter(model));
918 ViewModel* Typesetter::GetViewModel()
923 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
925 Devel::PixelBuffer result = RenderWithPixelBuffer(size, textDirection, behaviour, ignoreHorizontalAlignment, pixelFormat);
926 PixelData pixelData = Devel::PixelBuffer::Convert(result);
931 PixelData Typesetter::RenderWithCutout(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, Devel::PixelBuffer mask, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, float originAlpha)
933 Devel::PixelBuffer result = RenderWithPixelBuffer(size, textDirection, behaviour, ignoreHorizontalAlignment, pixelFormat);
934 SetMaskForImageBuffer(mask, result, size.width, size.height, originAlpha);
936 PixelData pixelData = Devel::PixelBuffer::Convert(result);
941 Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
943 DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_RENDERING_TYPESETTER");
944 // @todo. This initial implementation for a TextLabel has only one visible page.
946 // Elides the text if needed.
947 mModel->ElideGlyphs();
949 // Retrieves the layout size.
950 const Size& layoutSize = mModel->GetLayoutSize();
951 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
953 // Set the offset for the horizontal alignment according to the text direction and outline width.
955 switch(mModel->GetHorizontalAlignment())
957 case HorizontalAlignment::BEGIN:
962 case HorizontalAlignment::CENTER:
964 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
967 case HorizontalAlignment::END:
969 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
974 // Set the offset for the vertical alignment.
976 switch(mModel->GetVerticalAlignment())
978 case VerticalAlignment::TOP:
983 case VerticalAlignment::CENTER:
985 penY = static_cast<int32_t>(std::round(0.5f * (size.height - layoutSize.height)));
986 penY = penY < 0.f ? 0.f : penY;
990 case VerticalAlignment::BOTTOM:
992 penY = static_cast<int32_t>(size.height - layoutSize.height);
997 // Generate the image buffers of the text for each different style first,
998 // then combine all of them together as one final image buffer. We try to
999 // do all of these in CPU only, so that once the final texture is generated,
1000 // no calculation is needed in GPU during each frame.
1002 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
1003 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
1005 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
1006 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
1008 //Elided text in ellipsis at START could start on index greater than 0
1009 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1010 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1012 Devel::PixelBuffer imageBuffer;
1014 if(RENDER_MASK == behaviour)
1016 // Generate the image buffer as an alpha mask for color glyphs.
1017 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1019 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
1021 // Generate an empty image buffer so that it can been combined with the image buffers for styles
1022 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1023 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
1027 // Generate the image buffer for the text with no style.
1028 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1031 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
1033 // Generate the outline if enabled
1034 const uint16_t outlineWidth = mModel->GetOutlineWidth();
1035 const float outlineAlpha = mModel->GetOutlineColor().a;
1036 if(outlineWidth != 0u && fabsf(outlineAlpha) > Math::MACHINE_EPSILON_1 && RENDER_OVERLAY_STYLE != behaviour)
1038 // Create the image buffer for outline
1039 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1041 const float& blurRadius = mModel->GetOutlineBlurRadius();
1043 if(blurRadius > Math::MACHINE_EPSILON_1)
1045 outlineImageBuffer.ApplyGaussianBlur(blurRadius);
1048 // Combine the two buffers
1049 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
1052 // @todo. Support shadow for partial text later on.
1054 // Generate the shadow if enabled
1055 const Vector2& shadowOffset = mModel->GetShadowOffset();
1056 const float shadowAlpha = mModel->GetShadowColor().a;
1057 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))
1059 // Create the image buffer for shadow
1060 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1062 // Check whether it will be a soft shadow
1063 const float& blurRadius = mModel->GetShadowBlurRadius();
1065 if(blurRadius > Math::MACHINE_EPSILON_1)
1067 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
1070 // Combine the two buffers
1071 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
1074 // Generate the background if enabled
1075 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
1076 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
1077 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
1079 Devel::PixelBuffer backgroundImageBuffer;
1081 if(backgroundEnabled)
1083 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1087 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1090 if(backgroundMarkupSet)
1092 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
1095 // Combine the two buffers
1096 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1099 // Generate the background_with_mask if enabled
1100 const bool backgroundWithCutoutEnabled = mModel->IsBackgroundWithCutoutEnabled();
1101 if((backgroundWithCutoutEnabled) && RENDER_OVERLAY_STYLE != behaviour)
1103 Devel::PixelBuffer backgroundImageBuffer;
1105 backgroundImageBuffer = CreateFullBackgroundBuffer(bufferWidth, bufferHeight, mModel->GetBackgroundColorWithCutout());
1107 // Combine the two buffers
1108 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1111 if(RENDER_OVERLAY_STYLE == behaviour)
1113 if(mModel->IsUnderlineEnabled())
1115 // Create the image buffer for underline
1116 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1118 // Combine the two buffers
1119 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
1122 if(mModel->IsStrikethroughEnabled())
1124 // Create the image buffer for strikethrough
1125 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
1127 // Combine the two buffers
1128 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
1131 // Markup-Processor for overlay styles
1132 if(mModel->IsMarkupProcessorEnabled() || mModel->IsSpannedTextPlaced())
1134 if(mModel->IsMarkupUnderlineSet())
1136 imageBuffer = ApplyUnderlineMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1139 if(mModel->IsMarkupStrikethroughSet())
1141 imageBuffer = ApplyStrikethroughMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1150 Devel::PixelBuffer Typesetter::CreateFullBackgroundBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Vector4& backgroundColor)
1152 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
1153 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
1155 Devel::PixelBuffer buffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1157 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(buffer.GetBuffer());
1159 uint32_t packedBackgroundColor = 0u;
1160 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
1162 // Write the color to the pixel buffer
1163 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
1164 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
1165 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
1166 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
1168 std::fill(bitmapBuffer, bitmapBuffer + bufferSizeInt, packedBackgroundColor);
1173 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)
1175 // Retrieve lines, glyphs, positions and colors from the view model.
1176 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1177 const LineRun* const __restrict__ modelLinesBuffer = mModel->GetLines();
1178 const GlyphInfo* const __restrict__ glyphsBuffer = mModel->GetGlyphs();
1179 const Vector2* const __restrict__ positionBuffer = mModel->GetLayout();
1180 const Vector4* const __restrict__ colorsBuffer = mModel->GetColors();
1181 const ColorIndex* const __restrict__ colorIndexBuffer = mModel->GetColorIndices();
1182 const GlyphInfo* __restrict__ hyphens = mModel->GetHyphens();
1183 const Length* __restrict__ hyphenIndices = mModel->GetHyphenIndices();
1184 const Length hyphensCount = mModel->GetHyphensCount();
1185 const bool removeFrontInset = mModel->IsRemoveFrontInset();
1186 const bool removeBackInset = mModel->IsRemoveBackInset();
1187 const bool cutoutEnabled = mModel->IsCutoutEnabled();
1189 // Elided text info. Indices according to elided text and Ellipsis position.
1190 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1191 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1192 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1193 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1194 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1196 // Whether to use the default color.
1197 const bool useDefaultColor = (NULL == colorsBuffer);
1198 const Vector4& defaultColor = mModel->GetDefaultColor();
1200 // Create and initialize the pixel buffer.
1201 GlyphData glyphData;
1202 glyphData.verticalOffset = verticalOffset;
1203 glyphData.width = bufferWidth;
1204 glyphData.height = bufferHeight;
1205 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1206 glyphData.horizontalOffset = 0;
1208 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1209 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1210 Length hyphenIndex = 0;
1212 const Character* __restrict__ textBuffer = mModel->GetTextBuffer();
1213 float calculatedAdvance = 0.f;
1214 const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1215 const CharacterIndex* __restrict__ glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1217 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1219 // Traverses the lines of the text.
1220 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1222 const LineRun& line = *(modelLinesBuffer + lineIndex);
1224 // Sets the horizontal offset of the line.
1225 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1226 glyphData.horizontalOffset += horizontalOffset;
1228 // Increases the vertical offset with the line's ascender.
1229 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1231 // Retrieves the glyph's outline width
1232 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1234 if(style == Typesetter::STYLE_OUTLINE)
1236 const Vector2& outlineOffset = mModel->GetOutlineOffset();
1238 glyphData.horizontalOffset -= outlineWidth;
1239 glyphData.horizontalOffset += outlineOffset.x;
1242 // Only need to add the vertical outline offset for the first line
1243 glyphData.verticalOffset -= outlineWidth;
1244 glyphData.verticalOffset += outlineOffset.y;
1247 else if(style == Typesetter::STYLE_SHADOW)
1249 const Vector2& shadowOffset = mModel->GetShadowOffset();
1250 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1254 // Only need to add the vertical shadow offset for first line
1255 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1259 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1260 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1261 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1263 // Get the character-spacing runs.
1264 const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1266 // Aggregate underline-style-properties from mModel
1267 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1268 mModel->GetUnderlineColor(),
1269 mModel->GetUnderlineHeight(),
1270 mModel->GetDashedUnderlineGap(),
1271 mModel->GetDashedUnderlineWidth(),
1278 // Aggregate strikethrough-style-properties from mModel
1279 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1280 mModel->GetStrikethroughHeight(),
1284 // Get the underline runs.
1285 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1286 Vector<UnderlinedGlyphRun> underlineRuns;
1287 underlineRuns.Resize(numberOfUnderlineRuns);
1288 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1290 // Get the strikethrough runs.
1291 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1292 Vector<StrikethroughGlyphRun> strikethroughRuns;
1293 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1294 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1296 bool thereAreUnderlinedGlyphs = false;
1297 bool thereAreStrikethroughGlyphs = false;
1299 float currentUnderlinePosition = 0.0f;
1300 float currentUnderlineHeight = modelUnderlineProperties.height;
1301 float maxUnderlineHeight = currentUnderlineHeight;
1302 auto currentUnderlineProperties = modelUnderlineProperties;
1304 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1305 float maxStrikethroughHeight = currentStrikethroughHeight;
1306 auto currentStrikethroughProperties = modelStrikethroughProperties;
1307 float strikethroughStartingYPosition = 0.0f;
1309 FontId lastFontId = 0;
1311 float lineExtentLeft = bufferWidth;
1312 float lineExtentRight = 0.0f;
1313 float baseline = 0.0f;
1314 bool addHyphen = false;
1316 // Traverses the glyphs of the line.
1317 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1318 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1319 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1321 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1323 //To handle START case of ellipsis, the first glyph has been shifted
1324 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1325 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1327 //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.
1328 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1330 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1331 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1333 // Ignore any glyph that removed for MIDDLE ellipsis
1336 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1338 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1342 // Retrieve the glyph's info.
1343 const GlyphInfo* glyphInfo;
1345 if(addHyphen && hyphens)
1347 glyphInfo = hyphens + hyphenIndex;
1352 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1355 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1356 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1358 // Nothing to do if the glyph's width or height is zero.
1362 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1363 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1364 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1365 currentUnderlineHeight = currentUnderlineProperties.height;
1366 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1368 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1369 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1370 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1371 currentStrikethroughHeight = currentStrikethroughProperties.height;
1372 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1374 // Are we still using the same fontId as previous
1375 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1377 // We need to fetch fresh font underline metrics
1378 FontMetrics fontMetrics;
1379 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1381 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1382 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1386 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1389 if(strikethroughGlyph)
1391 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1394 // Update lastFontId because fontId is changed
1395 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1398 // Retrieves the glyph's position.
1399 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1403 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1404 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1405 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1406 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1407 position.y = -glyphInfo->yBearing;
1410 if(baseline < position.y + glyphInfo->yBearing)
1412 baseline = position.y + glyphInfo->yBearing;
1415 // Calculate the positions of leftmost and rightmost glyphs in the current line
1416 if(removeFrontInset)
1418 if(position.x < lineExtentLeft)
1420 lineExtentLeft = position.x;
1425 const float originPositionLeft = position.x - glyphInfo->xBearing;
1426 if(originPositionLeft < lineExtentLeft)
1428 lineExtentLeft = originPositionLeft;
1434 if(position.x + glyphInfo->width > lineExtentRight)
1436 lineExtentRight = position.x + glyphInfo->width;
1441 const float originPositionRight = position.x - glyphInfo->xBearing + glyphInfo->advance;
1442 if(originPositionRight > lineExtentRight)
1444 lineExtentRight = originPositionRight;
1448 // Retrieves the glyph's color.
1449 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1452 if(style == Typesetter::STYLE_SHADOW)
1454 color = mModel->GetShadowColor();
1456 else if(style == Typesetter::STYLE_OUTLINE)
1458 color = mModel->GetOutlineColor();
1462 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1465 if(style == Typesetter::STYLE_NONE && cutoutEnabled)
1467 // Temporarily adjust the transparency to 1.f
1471 // Premultiply alpha
1476 // Retrieves the glyph's bitmap.
1477 glyphData.glyphBitmap.buffer = NULL;
1478 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1479 glyphData.glyphBitmap.height = glyphInfo->height;
1481 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1483 // Don't render outline for other styles
1484 outlineWidth = 0.0f;
1487 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1489 fontClient.CreateBitmap(glyphInfo->fontId,
1491 glyphInfo->isItalicRequired,
1492 glyphInfo->isBoldRequired,
1493 glyphData.glyphBitmap,
1494 static_cast<int32_t>(outlineWidth));
1497 // Sets the glyph's bitmap into the bitmap of the whole text.
1498 if(NULL != glyphData.glyphBitmap.buffer)
1500 if(style == Typesetter::STYLE_OUTLINE)
1502 // Set the position offset for the current glyph
1503 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1504 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1507 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1508 TypesetGlyph(glyphData,
1514 if(style == Typesetter::STYLE_OUTLINE)
1516 // Reset the position offset for the next glyph
1517 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1518 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1521 // free the glyphBitmap.buffer if it is owner of buffer
1522 if(glyphData.glyphBitmap.isBufferOwned)
1524 free(glyphData.glyphBitmap.buffer);
1525 glyphData.glyphBitmap.isBufferOwned = false;
1527 glyphData.glyphBitmap.buffer = NULL;
1532 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1537 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1545 // Draw the underline from the leftmost glyph to the rightmost glyph
1546 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1548 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1551 // Draw the background color from the leftmost glyph to the rightmost glyph
1552 if(style == Typesetter::STYLE_BACKGROUND)
1554 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1557 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1558 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1560 //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.
1561 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.
1562 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1565 // Increases the vertical offset with the line's descender & line spacing.
1566 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1569 return glyphData.bitmapBuffer;
1572 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)
1574 // Underline-tags (this is for Markup case)
1575 // Get the underline runs.
1576 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1577 Vector<UnderlinedGlyphRun> underlineRuns;
1578 underlineRuns.Resize(numberOfUnderlineRuns);
1579 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1581 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1582 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1583 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1584 GlyphIndex startGlyphIndex, endGlyphIndex;
1586 //The outer loop to iterate on the separated chunks of underlined glyph runs
1587 while(itGlyphRun != endItGlyphRun)
1589 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1590 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1592 // Create the image buffer for underline
1593 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1594 // Combine the two buffers
1595 // Result pixel buffer will be stored into topPixelBuffer.
1596 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1601 return topPixelBuffer;
1604 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)
1606 // strikethrough-tags (this is for Markup case)
1607 // Get the strikethrough runs.
1608 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1609 Vector<StrikethroughGlyphRun> strikethroughRuns;
1610 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1611 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1613 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1614 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1615 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1616 GlyphIndex startGlyphIndex, endGlyphIndex;
1618 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1619 while(itGlyphRun != endItGlyphRun)
1621 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1622 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1624 // Create the image buffer for strikethrough
1625 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1626 // Combine the two buffers
1627 // Result pixel buffer will be stored into topPixelBuffer.
1628 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1633 return topPixelBuffer;
1636 void Typesetter::SetMaskForImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, float originAlpha)
1638 // Assume that we always combine two RGBA images
1639 // Jump with 4bytes for optimize runtime.
1640 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
1641 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
1643 if(topBuffer == NULL || bottomBuffer == NULL)
1645 // Nothing to do if one of both buffers are empty.
1649 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
1651 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
1653 uint32_t topBufferColor = *(topBuffer);
1654 uint32_t bottomBufferColor = *(bottomBuffer);
1655 uint8_t* __restrict__ topBufferColorBuffer = reinterpret_cast<uint8_t*>(&topBufferColor);
1656 uint8_t* __restrict__ bottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&bottomBufferColor);
1658 uint8_t topAlpha = topBufferColorBuffer[3];
1659 uint8_t bottomAlpha = 255 - topAlpha;
1661 float tempTop[4], tempBottom[4];
1663 // Return the transparency of the text to original.
1664 tempTop[0] = static_cast<float>(topBufferColorBuffer[0]) * originAlpha;
1665 tempTop[1] = static_cast<float>(topBufferColorBuffer[1]) * originAlpha;
1666 tempTop[2] = static_cast<float>(topBufferColorBuffer[2]) * originAlpha;
1667 tempTop[3] = static_cast<float>(topBufferColorBuffer[3]) * originAlpha;
1670 tempBottom[0] = static_cast<float>(bottomBufferColorBuffer[0]) * static_cast<float>(bottomAlpha) / 255.f;
1671 tempBottom[1] = static_cast<float>(bottomBufferColorBuffer[1]) * static_cast<float>(bottomAlpha) / 255.f;
1672 tempBottom[2] = static_cast<float>(bottomBufferColorBuffer[2]) * static_cast<float>(bottomAlpha) / 255.f;
1673 tempBottom[3] = static_cast<float>(bottomBufferColorBuffer[3]) * static_cast<float>(bottomAlpha) / 255.f;
1675 bottomBufferColorBuffer[0] = static_cast<uint8_t>(std::min(255u, static_cast<uint32_t>(tempBottom[0] + tempTop[0])));
1676 bottomBufferColorBuffer[1] = static_cast<uint8_t>(std::min(255u, static_cast<uint32_t>(tempBottom[1] + tempTop[1])));
1677 bottomBufferColorBuffer[2] = static_cast<uint8_t>(std::min(255u, static_cast<uint32_t>(tempBottom[2] + tempTop[2])));
1678 bottomBufferColorBuffer[3] = static_cast<uint8_t>(std::min(255u, static_cast<uint32_t>(tempBottom[3] + tempTop[3])));
1680 *(bottomBuffer) = bottomBufferColor;
1682 // Increase each buffer's pointer.
1688 Typesetter::Typesetter(const ModelInterface* const model)
1689 : mModel(new ViewModel(model))
1693 Typesetter::~Typesetter()
1700 } // namespace Toolkit