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;
66 * @brief Fast multiply & Summation & divide by 255.
68 * @param x1 The value between [0..255]
69 * @param y1 The value between [0..255]
70 * @param x2 The value between [0..255]
71 * @param y2 The value between [0..255]
72 * @return min(255, (x1*y1)/255 + (x2*y2)/255)
74 inline uint8_t MultiplyAndSummationAndNormalizeColor(const uint8_t x1, const uint8_t y1, const uint8_t x2, const uint8_t y2) noexcept
76 const uint32_t xy1 = static_cast<const uint32_t>(x1) * y1;
77 const uint32_t xy2 = static_cast<const uint32_t>(x2) * y2;
78 const uint32_t res = std::min(65025u, xy1 + xy2); // 65025 is 255 * 255.
79 return ((res + ((res + 257) >> 8)) >> 8); // fast divide by 255.
82 /// Helper macro define for glyph typesetter. It will reduce some duplicated code line.
85 * @brief Prepare decode glyph bitmap data. It must be call END_GLYPH_BITMAP end of same scope.
87 #define BEGIN_GLYPH_BITMAP(data) \
89 uint32_t glyphOffet = 0u; \
90 const bool useLocalScanline = data.glyphBitmap.compressionType != TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION; \
91 uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer; \
92 DALI_ASSERT_ALWAYS(glyphScanline && "Glyph scanline for buffer is null!");
95 * @brief Macro to skip useless line fast.
97 #define SKIP_GLYPH_SCANLINE(skipLine) \
98 if(useLocalScanline) \
100 for(int32_t lineIndex = 0; lineIndex < skipLine; ++lineIndex) \
102 TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
107 glyphScanline += skipLine * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize); \
111 * @brief Prepare scanline of glyph bitmap data per each lines. It must be call END_GLYPH_SCANLINE_DECODE end of same scope.
113 #define BEGIN_GLYPH_SCANLINE_DECODE(data) \
115 if(useLocalScanline) \
117 TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
121 * @brief Finalize scanline of glyph bitmap data per each lines.
123 #define END_GLYPH_SCANLINE_DECODE(data) \
124 if(!useLocalScanline) \
126 glyphScanline += data.glyphBitmap.width * glyphPixelSize; \
128 } // For ensure that we call BEGIN_GLYPH_SCANLINE_DECODE before
131 * @brief Finalize decode glyph bitmap data.
133 #define END_GLYPH_BITMAP() \
134 if(useLocalScanline) \
136 free(glyphScanline); \
138 } // For ensure that we call BEGIN_GLYPH_BITMAP before
141 /// Helper macro define end.
144 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
148 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
149 Vector2* position; ///< The position of the glyph.
150 TextAbstraction::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
151 uint32_t width; ///< The bitmap's width.
152 uint32_t height; ///< The bitmap's height.
153 int32_t horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
154 int32_t verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
158 * @brief Sets the glyph's buffer into the bitmap's buffer.
160 * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
161 * @param[in] position The position of the glyph.
162 * @param[in] color The color of the glyph.
163 * @param[in] style The style of the text.
164 * @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).
166 void TypesetGlyph(GlyphData& __restrict__ data,
167 const Vector2* const __restrict__ position,
168 const Vector4* const __restrict__ color,
169 const Typesetter::Style style,
170 const Pixel::Format pixelFormat)
172 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
174 // Nothing to do if the width or height of the buffer is zero.
178 // Initial vertical / horizontal offset.
179 const int32_t yOffset = data.verticalOffset + position->y;
180 const int32_t xOffset = data.horizontalOffset + position->x;
182 // Whether the given glyph is a color one.
183 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
184 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
185 const uint32_t glyphAlphaIndex = (glyphPixelSize > 0u) ? glyphPixelSize - 1u : 0u;
187 // Determinate iterator range.
188 const int32_t lineIndexRangeMin = std::max(0, -yOffset);
189 const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
190 const int32_t indexRangeMin = std::max(0, -xOffset);
191 const int32_t indexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
193 // If current glyph don't need to be rendered, just ignore.
194 if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
199 if(Pixel::RGBA8888 == pixelFormat)
201 uint32_t* __restrict__ bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
203 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
205 // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
206 // Just overwrite transparent color and return.
207 if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
209 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
211 // We can use memset here.
212 memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
213 bitmapBuffer += data.width;
218 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
220 // Precalculate input color's packed result.
221 uint32_t packedInputColor = 0u;
222 uint8_t* __restrict__ packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
224 *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
225 *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
226 *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
227 *(packedInputColorBuffer) = static_cast<uint8_t>(color->r * 255);
229 // Prepare glyph bitmap
230 BEGIN_GLYPH_BITMAP(data);
232 // Skip basic line of glyph.
233 SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
235 // Traverse the pixels of the glyph line per line.
238 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
240 BEGIN_GLYPH_SCANLINE_DECODE(data);
242 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
244 const int32_t xOffsetIndex = xOffset + index;
246 // Retrieves the color from the color glyph.
247 uint32_t packedColorGlyph = *(reinterpret_cast<const uint32_t*>(glyphScanline + (index << 2)));
248 uint8_t* __restrict__ packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
250 // Update the alpha channel.
251 const uint8_t colorAlpha = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
252 *(packedColorGlyphBuffer + 3u) = colorAlpha;
254 if(Typesetter::STYLE_SHADOW == style)
256 // The shadow of color glyph needs to have the shadow color.
257 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
258 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
259 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
265 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
268 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
269 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
270 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
272 if(data.glyphBitmap.isColorBitmap)
274 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
275 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
276 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
280 // Set the color into the final pixel buffer.
281 *(bitmapBuffer + xOffsetIndex) = packedColorGlyph;
284 bitmapBuffer += data.width;
286 END_GLYPH_SCANLINE_DECODE(data);
291 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
293 BEGIN_GLYPH_SCANLINE_DECODE(data);
295 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
297 // Update the alpha channel.
298 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
300 // Copy non-transparent pixels only
303 const int32_t xOffsetIndex = xOffset + index;
305 // Check alpha of overlapped pixels
306 uint32_t& currentColor = *(bitmapBuffer + xOffsetIndex);
307 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
309 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
310 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
311 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
312 // happen, for example, in the RTL text when we copy glyphs from right to left).
313 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
314 currentAlpha = std::max(currentAlpha, alpha);
315 if(currentAlpha == 255)
317 // Fast-cut to avoid float type operation.
318 currentColor = packedInputColor;
322 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
323 // The format is RGBA8888.
324 uint32_t packedColor = 0u;
325 uint8_t* __restrict__ packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
327 // Color is pre-muliplied with its alpha.
328 *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
329 *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
330 *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
331 *(packedColorBuffer) = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
333 // Set the color into the final pixel buffer.
334 currentColor = packedColor;
339 bitmapBuffer += data.width;
341 END_GLYPH_SCANLINE_DECODE(data);
349 // Below codes required only if not color glyph.
352 uint8_t* __restrict__ bitmapBuffer = data.bitmapBuffer.GetBuffer();
354 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
356 // Prepare glyph bitmap
357 BEGIN_GLYPH_BITMAP(data);
359 // Skip basic line of glyph.
360 SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
362 // Traverse the pixels of the glyph line per line.
363 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
365 BEGIN_GLYPH_SCANLINE_DECODE(data);
367 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
369 const int32_t xOffsetIndex = xOffset + index;
371 // Update the alpha channel.
372 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
374 // Copy non-transparent pixels only
377 // Check alpha of overlapped pixels
378 uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
380 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
381 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
382 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
383 // happen, for example, in the RTL text when we copy glyphs from right to left).
384 currentAlpha = std::max(currentAlpha, alpha);
388 bitmapBuffer += data.width;
390 END_GLYPH_SCANLINE_DECODE(data);
398 /// Draws the specified underline color to the buffer
400 const uint32_t bufferWidth,
401 const uint32_t bufferHeight,
402 GlyphData& glyphData,
403 const float baseline,
404 const float currentUnderlinePosition,
405 const float maxUnderlineHeight,
406 const float lineExtentLeft,
407 const float lineExtentRight,
408 const UnderlineStyleProperties& commonUnderlineProperties,
409 const UnderlineStyleProperties& currentUnderlineProperties,
412 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
413 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
414 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
415 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
417 int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
419 const uint32_t yRangeMin = underlineYOffset;
420 const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
421 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
422 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
424 // If current glyph don't need to be rendered, just ignore.
425 if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
430 // We can optimize by memset when underlineColor.a is near zero
431 uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
433 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
435 // Skip yRangeMin line.
436 bitmapBuffer += yRangeMin * glyphData.width;
438 // Note if underlineType is DASHED, we cannot setup color by memset.
439 if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
441 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
443 // We can use memset.
444 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
445 bitmapBuffer += glyphData.width;
447 if(underlineType == Text::Underline::DOUBLE)
449 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
450 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
451 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
453 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
454 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
456 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
458 // We can use memset.
459 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
460 bitmapBuffer += glyphData.width;
466 uint32_t packedUnderlineColor = 0u;
467 uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
469 // Write the color to the pixel buffer
470 *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
471 *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
472 *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
473 *(packedUnderlineColorBuffer) = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
475 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
477 if(underlineType == Text::Underline::DASHED)
479 float dashWidth = dashedUnderlineWidth;
482 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
484 if(Dali::EqualsZero(dashGap) && dashWidth > 0)
486 // Note : this is same logic as bitmap[y][x] = underlineColor;
487 *(bitmapBuffer + x) = packedUnderlineColor;
490 else if(dashGap < dashedUnderlineGap)
497 dashWidth = dashedUnderlineWidth;
504 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
506 // Note : this is same logic as bitmap[y][x] = underlineColor;
507 *(bitmapBuffer + x) = packedUnderlineColor;
510 bitmapBuffer += glyphData.width;
512 if(underlineType == Text::Underline::DOUBLE)
514 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
515 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
516 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
518 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
519 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
521 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
523 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
525 // Note : this is same logic as bitmap[y][x] = underlineColor;
526 *(bitmapBuffer + x) = packedUnderlineColor;
528 bitmapBuffer += glyphData.width;
534 /// Draws the background color to the buffer
535 void DrawBackgroundColor(
536 Vector4 backgroundColor,
537 const uint32_t bufferWidth,
538 const uint32_t bufferHeight,
539 GlyphData& glyphData,
540 const float baseline,
542 const float lineExtentLeft,
543 const float lineExtentRight)
545 const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
546 const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
547 const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
548 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
550 // If current glyph don't need to be rendered, just ignore.
551 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
556 // We can optimize by memset when backgroundColor.a is near zero
557 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
559 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
561 // Skip yRangeMin line.
562 bitmapBuffer += yRangeMin * glyphData.width;
564 if(backgroundColorAlpha == 0)
566 for(int32_t y = yRangeMin; y < yRangeMax; y++)
568 // We can use memset.
569 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
570 bitmapBuffer += glyphData.width;
575 uint32_t packedBackgroundColor = 0u;
576 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
578 // Write the color to the pixel buffer
579 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
580 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
581 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
582 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
584 for(int32_t y = yRangeMin; y < yRangeMax; y++)
586 for(int32_t x = xRangeMin; x < xRangeMax; x++)
588 // Note : this is same logic as bitmap[y][x] = backgroundColor;
589 *(bitmapBuffer + x) = packedBackgroundColor;
591 bitmapBuffer += glyphData.width;
596 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)
598 // Retrieve lines, glyphs, positions and colors from the view model.
599 const Length modelNumberOfLines = model->GetNumberOfLines();
600 const LineRun* const modelLinesBuffer = model->GetLines();
601 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
602 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
603 const Vector2* const positionBuffer = model->GetLayout();
604 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
605 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
606 const bool removeFrontInset = model->IsRemoveFrontInset();
607 const bool removeBackInset = model->IsRemoveBackInset();
609 const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
611 // Create and initialize the pixel buffer.
613 glyphData.verticalOffset = verticalOffset;
614 glyphData.width = bufferWidth;
615 glyphData.height = bufferHeight;
616 glyphData.bitmapBuffer = buffer;
617 glyphData.horizontalOffset = 0;
619 ColorIndex prevBackgroundColorIndex = 0;
620 ColorIndex backgroundColorIndex = 0;
622 // Traverses the lines of the text.
623 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
625 const LineRun& line = *(modelLinesBuffer + lineIndex);
627 // Sets the horizontal offset of the line.
628 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
629 glyphData.horizontalOffset += horizontalOffset;
631 // Increases the vertical offset with the line's ascender.
632 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
634 float left = bufferWidth;
636 float baseline = 0.0f;
638 // Traverses the glyphs of the line.
639 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
640 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
642 // Retrieve the glyph's info.
643 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
645 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
646 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
648 // Nothing to do if default background color, the glyph's width or height is zero.
652 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
654 if((backgroundColorIndex != prevBackgroundColorIndex) &&
655 (prevBackgroundColorIndex != 0u))
657 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
658 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
661 if(backgroundColorIndex == 0u)
663 prevBackgroundColorIndex = backgroundColorIndex;
664 //if background color is the default do nothing
668 // Retrieves the glyph's position.
669 const Vector2* const position = positionBuffer + glyphIndex;
671 if(baseline < position->y + glyphInfo->yBearing)
673 baseline = position->y + glyphInfo->yBearing;
676 // Calculate the positions of leftmost and rightmost glyphs in the current line
679 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
686 const float originPositionLeft = position->x - glyphInfo->xBearing;
687 if((originPositionLeft < left) || (backgroundColorIndex != prevBackgroundColorIndex))
689 left = originPositionLeft;
695 if(position->x + glyphInfo->width > right)
697 right = position->x - position->x + glyphInfo->width;
702 const float originPositionRight = position->x - glyphInfo->xBearing + glyphInfo->advance;
703 if(originPositionRight > right)
705 right = originPositionRight;
709 prevBackgroundColorIndex = backgroundColorIndex;
712 //draw last background at line end if not default
713 if(backgroundColorIndex != 0u)
715 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
716 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
719 // Increases the vertical offset with the line's descender.
720 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
723 return glyphData.bitmapBuffer;
726 /// Draws the specified strikethrough color to the buffer
727 void DrawStrikethrough(const uint32_t bufferWidth,
728 const uint32_t bufferHeight,
729 GlyphData& glyphData,
730 const float baseline,
731 const float strikethroughStartingYPosition,
732 const float maxStrikethroughHeight,
733 const float lineExtentLeft,
734 const float lineExtentRight,
735 const StrikethroughStyleProperties& commonStrikethroughProperties,
736 const StrikethroughStyleProperties& currentStrikethroughProperties,
739 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
741 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
742 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
743 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
744 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
746 // If current glyph don't need to be rendered, just ignore.
747 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
752 // We can optimize by memset when strikethroughColor.a is near zero
753 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
755 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
757 // Skip yRangeMin line.
758 bitmapBuffer += yRangeMin * glyphData.width;
760 if(strikethroughColorAlpha == 0)
762 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
764 // We can use memset.
765 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
766 bitmapBuffer += glyphData.width;
771 uint32_t packedStrikethroughColor = 0u;
772 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
774 // Write the color to the pixel buffer
775 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
776 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
777 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
778 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
780 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
782 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
784 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
785 *(bitmapBuffer + x) = packedStrikethroughColor;
787 bitmapBuffer += glyphData.width;
793 * @brief Create an initialized image buffer filled with transparent color.
795 * Creates the pixel data used to generate the final image with the given size.
797 * @param[in] bufferWidth The width of the image buffer.
798 * @param[in] bufferHeight The height of the image buffer.
799 * @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).
801 * @return An image buffer.
803 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat)
805 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
807 if(Pixel::RGBA8888 == pixelFormat)
809 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
810 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
811 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
815 memset(imageBuffer.GetBuffer(), 0, static_cast<std::size_t>(bufferWidth * bufferHeight));
822 * @brief Combine the two RGBA image buffers together.
824 * The top layer buffer will blend over the bottom layer buffer:
825 * - If the pixel is not fully opaque from either buffer, it will be blended with
826 * the pixel from the other buffer and copied to the combined buffer.
827 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
828 * buffer will be copied to the combined buffer.
830 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
831 * We can determine which pixelBuffer's memory is destination
833 * @param[in, out] topPixelBuffer The top layer buffer.
834 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
835 * @param[in] bufferWidth The width of the image buffer.
836 * @param[in] bufferHeight The height of the image buffer.
837 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
838 * False if we store the combined image buffer result into bottomPixelBuffer.
841 void CombineImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, bool storeResultIntoTop)
843 // Assume that we always combine two RGBA images
844 // Jump with 4bytes for optimize runtime.
845 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
846 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
848 if(topBuffer == NULL && bottomBuffer == NULL)
850 // Nothing to do if both buffers are empty.
854 if(topBuffer == NULL)
856 // Nothing to do if topBuffer is empty.
857 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
858 if(storeResultIntoTop)
860 topPixelBuffer = bottomPixelBuffer;
865 if(bottomBuffer == NULL)
867 // Nothing to do if bottomBuffer is empty.
868 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
869 if(!storeResultIntoTop)
871 bottomPixelBuffer = topPixelBuffer;
876 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
878 uint32_t* __restrict__ combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
879 uint8_t* __restrict__ topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
881 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
883 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
884 // Otherwise, copy pixel from topBuffer to combinedBuffer.
885 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
887 uint8_t topAlpha = *topAlphaBufferPointer;
891 // Copy the pixel from bottomBuffer to combinedBuffer
892 if(storeResultIntoTop)
894 *(combinedBuffer) = *(bottomBuffer);
897 else if(topAlpha == 255)
899 // Copy the pixel from topBuffer to combinedBuffer
900 if(!storeResultIntoTop)
902 *(combinedBuffer) = *(topBuffer);
907 // At least one pixel is not fully opaque
908 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
909 uint32_t blendedBottomBufferColor = *(bottomBuffer);
910 uint8_t* __restrict__ blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
912 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
913 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
914 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
915 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
917 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
920 // Increase each buffer's pointer.
924 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
930 TypesetterPtr Typesetter::New(const ModelInterface* const model)
932 return TypesetterPtr(new Typesetter(model));
935 ViewModel* Typesetter::GetViewModel()
940 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
942 Devel::PixelBuffer result = RenderWithPixelBuffer(size, textDirection, behaviour, ignoreHorizontalAlignment, pixelFormat);
943 PixelData pixelData = Devel::PixelBuffer::Convert(result);
948 PixelData Typesetter::RenderWithCutout(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, Devel::PixelBuffer mask, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, float originAlpha)
950 Devel::PixelBuffer result = RenderWithPixelBuffer(size, textDirection, behaviour, ignoreHorizontalAlignment, pixelFormat);
951 SetMaskForImageBuffer(mask, result, size.width, size.height, originAlpha);
953 PixelData pixelData = Devel::PixelBuffer::Convert(result);
958 Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
960 DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_RENDERING_TYPESETTER");
961 // @todo. This initial implementation for a TextLabel has only one visible page.
963 // Elides the text if needed.
964 mModel->ElideGlyphs();
966 // Retrieves the layout size.
967 const Size& layoutSize = mModel->GetLayoutSize();
968 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
970 // Set the offset for the horizontal alignment according to the text direction and outline width.
972 switch(mModel->GetHorizontalAlignment())
974 case HorizontalAlignment::BEGIN:
979 case HorizontalAlignment::CENTER:
981 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
984 case HorizontalAlignment::END:
986 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
991 // Set the offset for the vertical alignment.
993 switch(mModel->GetVerticalAlignment())
995 case VerticalAlignment::TOP:
1000 case VerticalAlignment::CENTER:
1002 penY = static_cast<int32_t>(std::round(0.5f * (size.height - layoutSize.height)));
1003 penY = penY < 0.f ? 0.f : penY;
1007 case VerticalAlignment::BOTTOM:
1009 penY = static_cast<int32_t>(size.height - layoutSize.height);
1014 // Generate the image buffers of the text for each different style first,
1015 // then combine all of them together as one final image buffer. We try to
1016 // do all of these in CPU only, so that once the final texture is generated,
1017 // no calculation is needed in GPU during each frame.
1019 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
1020 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
1022 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
1023 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
1025 //Elided text in ellipsis at START could start on index greater than 0
1026 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1027 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1029 Devel::PixelBuffer imageBuffer;
1031 if(RENDER_MASK == behaviour)
1033 // Generate the image buffer as an alpha mask for color glyphs.
1034 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1036 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
1038 // Generate an empty image buffer so that it can been combined with the image buffers for styles
1039 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1040 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
1044 // Generate the image buffer for the text with no style.
1045 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1048 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
1050 // Generate the outline if enabled
1051 const uint16_t outlineWidth = mModel->GetOutlineWidth();
1052 const float outlineAlpha = mModel->GetOutlineColor().a;
1053 if(outlineWidth != 0u && fabsf(outlineAlpha) > Math::MACHINE_EPSILON_1 && RENDER_OVERLAY_STYLE != behaviour)
1055 // Create the image buffer for outline
1056 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1058 const float& blurRadius = mModel->GetOutlineBlurRadius();
1060 if(blurRadius > Math::MACHINE_EPSILON_1)
1062 outlineImageBuffer.ApplyGaussianBlur(blurRadius);
1065 // Combine the two buffers
1066 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
1069 // @todo. Support shadow for partial text later on.
1071 // Generate the shadow if enabled
1072 const Vector2& shadowOffset = mModel->GetShadowOffset();
1073 const float shadowAlpha = mModel->GetShadowColor().a;
1074 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))
1076 // Create the image buffer for shadow
1077 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1079 // Check whether it will be a soft shadow
1080 const float& blurRadius = mModel->GetShadowBlurRadius();
1082 if(blurRadius > Math::MACHINE_EPSILON_1)
1084 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
1087 // Combine the two buffers
1088 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
1091 // Generate the background if enabled
1092 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
1093 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
1094 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
1096 Devel::PixelBuffer backgroundImageBuffer;
1098 if(backgroundEnabled)
1100 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1104 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1107 if(backgroundMarkupSet)
1109 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
1112 // Combine the two buffers
1113 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1116 // Generate the background_with_mask if enabled
1117 const bool backgroundWithCutoutEnabled = mModel->IsBackgroundWithCutoutEnabled();
1118 if((backgroundWithCutoutEnabled) && RENDER_OVERLAY_STYLE != behaviour)
1120 Devel::PixelBuffer backgroundImageBuffer;
1122 backgroundImageBuffer = CreateFullBackgroundBuffer(bufferWidth, bufferHeight, mModel->GetBackgroundColorWithCutout());
1124 // Combine the two buffers
1125 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1128 if(RENDER_OVERLAY_STYLE == behaviour)
1130 if(mModel->IsUnderlineEnabled())
1132 // Create the image buffer for underline
1133 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1135 // Combine the two buffers
1136 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
1139 if(mModel->IsStrikethroughEnabled())
1141 // Create the image buffer for strikethrough
1142 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
1144 // Combine the two buffers
1145 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
1148 // Markup-Processor for overlay styles
1149 if(mModel->IsMarkupProcessorEnabled() || mModel->IsSpannedTextPlaced())
1151 if(mModel->IsMarkupUnderlineSet())
1153 imageBuffer = ApplyUnderlineMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1156 if(mModel->IsMarkupStrikethroughSet())
1158 imageBuffer = ApplyStrikethroughMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1167 Devel::PixelBuffer Typesetter::CreateFullBackgroundBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Vector4& backgroundColor)
1169 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
1170 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
1172 Devel::PixelBuffer buffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1174 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(buffer.GetBuffer());
1176 uint32_t packedBackgroundColor = 0u;
1177 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
1179 // Write the color to the pixel buffer
1180 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
1181 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
1182 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
1183 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
1185 std::fill(bitmapBuffer, bitmapBuffer + bufferSizeInt, packedBackgroundColor);
1190 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)
1192 // Retrieve lines, glyphs, positions and colors from the view model.
1193 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1194 const LineRun* const __restrict__ modelLinesBuffer = mModel->GetLines();
1195 const GlyphInfo* const __restrict__ glyphsBuffer = mModel->GetGlyphs();
1196 const Vector2* const __restrict__ positionBuffer = mModel->GetLayout();
1197 const Vector4* const __restrict__ colorsBuffer = mModel->GetColors();
1198 const ColorIndex* const __restrict__ colorIndexBuffer = mModel->GetColorIndices();
1199 const GlyphInfo* __restrict__ hyphens = mModel->GetHyphens();
1200 const Length* __restrict__ hyphenIndices = mModel->GetHyphenIndices();
1201 const Length hyphensCount = mModel->GetHyphensCount();
1202 const bool removeFrontInset = mModel->IsRemoveFrontInset();
1203 const bool removeBackInset = mModel->IsRemoveBackInset();
1204 const bool cutoutEnabled = mModel->IsCutoutEnabled();
1206 // Elided text info. Indices according to elided text and Ellipsis position.
1207 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1208 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1209 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1210 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1211 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1213 // Whether to use the default color.
1214 const bool useDefaultColor = (NULL == colorsBuffer);
1215 const Vector4& defaultColor = mModel->GetDefaultColor();
1217 // Create and initialize the pixel buffer.
1218 GlyphData glyphData;
1219 glyphData.verticalOffset = verticalOffset;
1220 glyphData.width = bufferWidth;
1221 glyphData.height = bufferHeight;
1222 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1223 glyphData.horizontalOffset = 0;
1225 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1226 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1227 Length hyphenIndex = 0;
1229 const Character* __restrict__ textBuffer = mModel->GetTextBuffer();
1230 float calculatedAdvance = 0.f;
1231 const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1232 const CharacterIndex* __restrict__ glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1234 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1236 // Traverses the lines of the text.
1237 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1239 const LineRun& line = *(modelLinesBuffer + lineIndex);
1241 // Sets the horizontal offset of the line.
1242 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1243 glyphData.horizontalOffset += horizontalOffset;
1245 // Increases the vertical offset with the line's ascender.
1246 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1248 // Retrieves the glyph's outline width
1249 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1251 if(style == Typesetter::STYLE_OUTLINE)
1253 const Vector2& outlineOffset = mModel->GetOutlineOffset();
1255 glyphData.horizontalOffset -= outlineWidth;
1256 glyphData.horizontalOffset += outlineOffset.x;
1259 // Only need to add the vertical outline offset for the first line
1260 glyphData.verticalOffset -= outlineWidth;
1261 glyphData.verticalOffset += outlineOffset.y;
1264 else if(style == Typesetter::STYLE_SHADOW)
1266 const Vector2& shadowOffset = mModel->GetShadowOffset();
1267 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1271 // Only need to add the vertical shadow offset for first line
1272 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1276 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1277 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1278 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1280 // Get the character-spacing runs.
1281 const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1283 // Aggregate underline-style-properties from mModel
1284 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1285 mModel->GetUnderlineColor(),
1286 mModel->GetUnderlineHeight(),
1287 mModel->GetDashedUnderlineGap(),
1288 mModel->GetDashedUnderlineWidth(),
1295 // Aggregate strikethrough-style-properties from mModel
1296 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1297 mModel->GetStrikethroughHeight(),
1301 // Get the underline runs.
1302 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1303 Vector<UnderlinedGlyphRun> underlineRuns;
1304 underlineRuns.Resize(numberOfUnderlineRuns);
1305 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1307 // Get the strikethrough runs.
1308 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1309 Vector<StrikethroughGlyphRun> strikethroughRuns;
1310 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1311 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1313 bool thereAreUnderlinedGlyphs = false;
1314 bool thereAreStrikethroughGlyphs = false;
1316 float currentUnderlinePosition = 0.0f;
1317 float currentUnderlineHeight = modelUnderlineProperties.height;
1318 float maxUnderlineHeight = currentUnderlineHeight;
1319 auto currentUnderlineProperties = modelUnderlineProperties;
1321 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1322 float maxStrikethroughHeight = currentStrikethroughHeight;
1323 auto currentStrikethroughProperties = modelStrikethroughProperties;
1324 float strikethroughStartingYPosition = 0.0f;
1326 FontId lastFontId = 0;
1328 float lineExtentLeft = bufferWidth;
1329 float lineExtentRight = 0.0f;
1330 float baseline = 0.0f;
1331 bool addHyphen = false;
1333 // Traverses the glyphs of the line.
1334 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1335 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1336 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1338 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1340 //To handle START case of ellipsis, the first glyph has been shifted
1341 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1342 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1344 //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.
1345 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1347 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1348 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1350 // Ignore any glyph that removed for MIDDLE ellipsis
1353 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1355 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1359 // Retrieve the glyph's info.
1360 const GlyphInfo* glyphInfo;
1362 if(addHyphen && hyphens)
1364 glyphInfo = hyphens + hyphenIndex;
1369 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1372 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1373 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1375 // Nothing to do if the glyph's width or height is zero.
1379 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1380 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1381 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1382 currentUnderlineHeight = currentUnderlineProperties.height;
1383 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1385 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1386 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1387 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1388 currentStrikethroughHeight = currentStrikethroughProperties.height;
1389 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1391 // Are we still using the same fontId as previous
1392 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1394 // We need to fetch fresh font underline metrics
1395 FontMetrics fontMetrics;
1396 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1398 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1399 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1403 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1406 if(strikethroughGlyph)
1408 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1411 // Update lastFontId because fontId is changed
1412 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1415 // Retrieves the glyph's position.
1416 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1420 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1421 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1422 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1423 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1424 position.y = -glyphInfo->yBearing;
1427 if(baseline < position.y + glyphInfo->yBearing)
1429 baseline = position.y + glyphInfo->yBearing;
1432 // Calculate the positions of leftmost and rightmost glyphs in the current line
1433 if(removeFrontInset)
1435 if(position.x < lineExtentLeft)
1437 lineExtentLeft = position.x;
1442 const float originPositionLeft = position.x - glyphInfo->xBearing;
1443 if(originPositionLeft < lineExtentLeft)
1445 lineExtentLeft = originPositionLeft;
1451 if(position.x + glyphInfo->width > lineExtentRight)
1453 lineExtentRight = position.x + glyphInfo->width;
1458 const float originPositionRight = position.x - glyphInfo->xBearing + glyphInfo->advance;
1459 if(originPositionRight > lineExtentRight)
1461 lineExtentRight = originPositionRight;
1465 // Retrieves the glyph's color.
1466 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1469 if(style == Typesetter::STYLE_SHADOW)
1471 color = mModel->GetShadowColor();
1473 else if(style == Typesetter::STYLE_OUTLINE)
1475 color = mModel->GetOutlineColor();
1479 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1482 if(style == Typesetter::STYLE_NONE && cutoutEnabled)
1484 // Temporarily adjust the transparency to 1.f
1488 // Premultiply alpha
1493 // Retrieves the glyph's bitmap.
1494 glyphData.glyphBitmap.buffer = NULL;
1495 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1496 glyphData.glyphBitmap.height = glyphInfo->height;
1498 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1500 // Don't render outline for other styles
1501 outlineWidth = 0.0f;
1504 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1506 fontClient.CreateBitmap(glyphInfo->fontId,
1508 glyphInfo->isItalicRequired,
1509 glyphInfo->isBoldRequired,
1510 glyphData.glyphBitmap,
1511 static_cast<int32_t>(outlineWidth));
1514 // Sets the glyph's bitmap into the bitmap of the whole text.
1515 if(NULL != glyphData.glyphBitmap.buffer)
1517 if(style == Typesetter::STYLE_OUTLINE)
1519 // Set the position offset for the current glyph
1520 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1521 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1524 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1525 TypesetGlyph(glyphData,
1531 if(style == Typesetter::STYLE_OUTLINE)
1533 // Reset the position offset for the next glyph
1534 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1535 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1538 // free the glyphBitmap.buffer if it is owner of buffer
1539 if(glyphData.glyphBitmap.isBufferOwned)
1541 free(glyphData.glyphBitmap.buffer);
1542 glyphData.glyphBitmap.isBufferOwned = false;
1544 glyphData.glyphBitmap.buffer = NULL;
1549 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1554 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1562 // Draw the underline from the leftmost glyph to the rightmost glyph
1563 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1565 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1568 // Draw the background color from the leftmost glyph to the rightmost glyph
1569 if(style == Typesetter::STYLE_BACKGROUND)
1571 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1574 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1575 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1577 //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.
1578 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.
1579 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1582 // Increases the vertical offset with the line's descender & line spacing.
1583 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1586 return glyphData.bitmapBuffer;
1589 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)
1591 // Underline-tags (this is for Markup case)
1592 // Get the underline runs.
1593 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1594 Vector<UnderlinedGlyphRun> underlineRuns;
1595 underlineRuns.Resize(numberOfUnderlineRuns);
1596 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1598 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1599 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1600 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1601 GlyphIndex startGlyphIndex, endGlyphIndex;
1603 //The outer loop to iterate on the separated chunks of underlined glyph runs
1604 while(itGlyphRun != endItGlyphRun)
1606 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1607 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1609 // Create the image buffer for underline
1610 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1611 // Combine the two buffers
1612 // Result pixel buffer will be stored into topPixelBuffer.
1613 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1618 return topPixelBuffer;
1621 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)
1623 // strikethrough-tags (this is for Markup case)
1624 // Get the strikethrough runs.
1625 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1626 Vector<StrikethroughGlyphRun> strikethroughRuns;
1627 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1628 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1630 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1631 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1632 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1633 GlyphIndex startGlyphIndex, endGlyphIndex;
1635 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1636 while(itGlyphRun != endItGlyphRun)
1638 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1639 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1641 // Create the image buffer for strikethrough
1642 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1643 // Combine the two buffers
1644 // Result pixel buffer will be stored into topPixelBuffer.
1645 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1650 return topPixelBuffer;
1653 void Typesetter::SetMaskForImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, float originAlpha)
1655 // Assume that we always combine two RGBA images
1656 // Jump with 4bytes for optimize runtime.
1657 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
1658 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
1660 if(topBuffer == NULL || bottomBuffer == NULL)
1662 // Nothing to do if one of both buffers are empty.
1666 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
1668 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
1670 uint32_t topBufferColor = *(topBuffer);
1671 uint32_t bottomBufferColor = *(bottomBuffer);
1672 uint8_t* __restrict__ topBufferColorBuffer = reinterpret_cast<uint8_t*>(&topBufferColor);
1673 uint8_t* __restrict__ bottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&bottomBufferColor);
1675 // Return the transparency of the text to original.
1676 uint8_t originAlphaInt = originAlpha * 255;
1678 uint8_t topAlpha = topBufferColorBuffer[3];
1679 uint8_t bottomAlpha = 255 - topAlpha;
1682 bottomBufferColorBuffer[0] = MultiplyAndSummationAndNormalizeColor(topBufferColorBuffer[0], originAlphaInt, bottomBufferColorBuffer[0], bottomAlpha);
1683 bottomBufferColorBuffer[1] = MultiplyAndSummationAndNormalizeColor(topBufferColorBuffer[1], originAlphaInt, bottomBufferColorBuffer[1], bottomAlpha);
1684 bottomBufferColorBuffer[2] = MultiplyAndSummationAndNormalizeColor(topBufferColorBuffer[2], originAlphaInt, bottomBufferColorBuffer[2], bottomAlpha);
1685 bottomBufferColorBuffer[3] = MultiplyAndSummationAndNormalizeColor(topBufferColorBuffer[3], originAlphaInt, bottomBufferColorBuffer[3], bottomAlpha);
1687 *(bottomBuffer) = bottomBufferColor;
1689 // Increase each buffer's pointer.
1695 Typesetter::Typesetter(const ModelInterface* const model)
1696 : mModel(new ViewModel(model))
1700 Typesetter::~Typesetter()
1707 } // namespace Toolkit