2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/rendering/text-typesetter.h>
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/public-api/common/constants.h>
24 #include <dali/public-api/math/math-utils.h>
28 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
29 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
30 #include <dali-toolkit/internal/text/line-helper-functions.h>
31 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
32 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
33 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
34 #include <dali-toolkit/internal/text/rendering/view-model.h>
44 const float HALF(0.5f);
45 const float ONE_AND_A_HALF(1.5f);
48 * @brief Fast multiply & divide by 255. It wiil be useful when we applying alpha value in color
50 * @param x The value between [0..255]
51 * @param y The value between [0..255]
54 inline uint8_t MultiplyAndNormalizeColor(const uint8_t x, const uint8_t y) noexcept
56 const uint32_t xy = static_cast<const uint32_t>(x) * y;
57 return ((xy << 15) + (xy << 7) + xy) >> 23;
61 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
65 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
66 Vector2* position; ///< The position of the glyph.
67 TextAbstraction::FontClient::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
68 uint32_t width; ///< The bitmap's width.
69 uint32_t height; ///< The bitmap's height.
70 int32_t horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
71 int32_t verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
75 * @brief Sets the glyph's buffer into the bitmap's buffer.
77 * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
78 * @param[in] position The position of the glyph.
79 * @param[in] color The color of the glyph.
80 * @param[in] style The style of the text.
81 * @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).
83 void TypesetGlyph(GlyphData& __restrict__ data,
84 const Vector2* const __restrict__ position,
85 const Vector4* const __restrict__ color,
86 const Typesetter::Style style,
87 const Pixel::Format pixelFormat)
89 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
91 // Nothing to do if the width or height of the buffer is zero.
95 // Initial vertical / horizontal offset.
96 const int32_t yOffset = data.verticalOffset + position->y;
97 const int32_t xOffset = data.horizontalOffset + position->x;
99 // Whether the given glyph is a color one.
100 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
101 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
102 const uint32_t glyphAlphaIndex = glyphPixelSize - 1u;
104 // Determinate iterator range.
105 const int32_t lineIndexRangeMin = std::max(0, -yOffset);
106 const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
107 const int32_t indexRangeMin = std::max(0, -xOffset);
108 const int32_t indexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
110 // If current glyph don't need to be rendered, just ignore.
111 if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
116 if(Pixel::RGBA8888 == pixelFormat)
118 uint32_t* __restrict__ bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
120 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
122 // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
123 // Just overwrite transparent color and return.
124 if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
126 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
128 // We can use memset here.
129 memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
130 bitmapBuffer += data.width;
135 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
137 // Offset byte value of glyph bitmap.
138 uint32_t glyphOffet = 0u;
140 // Allocate scanline memory for glyph bitmap if we need.
141 const bool useLocalScanline = data.glyphBitmap.compressionType != TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
142 uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer;
144 // Precalculate input color's packed result.
145 uint32_t packedInputColor = 0u;
146 uint8_t* __restrict__ packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
148 *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
149 *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
150 *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
151 *(packedInputColorBuffer) = static_cast<uint8_t>(color->r * 255);
153 // Skip basic line of glyph.
156 for(int32_t lineIndex = 0; lineIndex < lineIndexRangeMin; ++lineIndex)
158 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
163 glyphScanline += lineIndexRangeMin * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize);
166 // Traverse the pixels of the glyph line per line.
169 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
173 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
176 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
178 const int32_t xOffsetIndex = xOffset + index;
180 // Retrieves the color from the color glyph.
181 uint32_t packedColorGlyph = *(reinterpret_cast<const uint32_t*>(glyphScanline + (index << 2)));
182 uint8_t* __restrict__ packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
184 // Update the alpha channel.
185 const uint8_t colorAlpha = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
186 *(packedColorGlyphBuffer + 3u) = colorAlpha;
188 if(Typesetter::STYLE_SHADOW == style)
190 // The shadow of color glyph needs to have the shadow color.
191 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
192 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
193 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
199 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
202 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
203 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
204 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
206 if(data.glyphBitmap.isColorBitmap)
208 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
209 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
210 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
214 // Set the color into the final pixel buffer.
215 *(bitmapBuffer + xOffsetIndex) = packedColorGlyph;
218 bitmapBuffer += data.width;
219 if(!useLocalScanline)
221 glyphScanline += data.glyphBitmap.width * glyphPixelSize;
227 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
231 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
234 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
236 // Update the alpha channel.
237 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
239 // Copy non-transparent pixels only
242 const int32_t xOffsetIndex = xOffset + index;
244 // Check alpha of overlapped pixels
245 uint32_t& currentColor = *(bitmapBuffer + xOffsetIndex);
246 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
248 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
249 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
250 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
251 // happen, for example, in the RTL text when we copy glyphs from right to left).
252 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
253 currentAlpha = std::max(currentAlpha, alpha);
254 if(currentAlpha == 255)
256 // Fast-cut to avoid float type operation.
257 currentColor = packedInputColor;
261 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
262 // The format is RGBA8888.
263 uint32_t packedColor = 0u;
264 uint8_t* __restrict__ packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
266 // Color is pre-muliplied with its alpha.
267 *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
268 *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
269 *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
270 *(packedColorBuffer) = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
272 // Set the color into the final pixel buffer.
273 currentColor = packedColor;
278 bitmapBuffer += data.width;
279 if(!useLocalScanline)
281 glyphScanline += data.glyphBitmap.width * glyphPixelSize;
293 // Below codes required only if not color glyph.
296 uint8_t* __restrict__ bitmapBuffer = data.bitmapBuffer.GetBuffer();
298 // Offset byte value of glyph bitmap.
299 uint32_t glyphOffet = 0u;
301 // Allocate scanline memory for glyph bitmap if we need.
302 const bool useLocalScanline = data.glyphBitmap.compressionType != TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
303 uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer;
306 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
308 // Skip basic line of glyph.
311 for(int32_t lineIndex = 0; lineIndex < lineIndexRangeMin; ++lineIndex)
313 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
318 glyphScanline += lineIndexRangeMin * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize);
321 // Traverse the pixels of the glyph line per line.
322 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
326 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
329 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
331 const int32_t xOffsetIndex = xOffset + index;
333 // Update the alpha channel.
334 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
336 // Copy non-transparent pixels only
339 // Check alpha of overlapped pixels
340 uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
342 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
343 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
344 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
345 // happen, for example, in the RTL text when we copy glyphs from right to left).
346 currentAlpha = std::max(currentAlpha, alpha);
350 bitmapBuffer += data.width;
351 if(!useLocalScanline)
353 glyphScanline += data.glyphBitmap.width * glyphPixelSize;
365 /// Draws the specified underline color to the buffer
367 const uint32_t bufferWidth,
368 const uint32_t bufferHeight,
369 GlyphData& glyphData,
370 const float baseline,
371 const float currentUnderlinePosition,
372 const float maxUnderlineHeight,
373 const float lineExtentLeft,
374 const float lineExtentRight,
375 const UnderlineStyleProperties& commonUnderlineProperties,
376 const UnderlineStyleProperties& currentUnderlineProperties,
379 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
380 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
381 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
382 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
384 int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
386 const uint32_t yRangeMin = underlineYOffset;
387 const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
388 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
389 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
391 // If current glyph don't need to be rendered, just ignore.
392 if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
397 // We can optimize by memset when underlineColor.a is near zero
398 uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
400 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
402 // Skip yRangeMin line.
403 bitmapBuffer += yRangeMin * glyphData.width;
405 // Note if underlineType is DASHED, we cannot setup color by memset.
406 if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
408 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
410 // We can use memset.
411 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
412 bitmapBuffer += glyphData.width;
414 if(underlineType == Text::Underline::DOUBLE)
416 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
417 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
418 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
420 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
421 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
423 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
425 // We can use memset.
426 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
427 bitmapBuffer += glyphData.width;
433 uint32_t packedUnderlineColor = 0u;
434 uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
436 // Write the color to the pixel buffer
437 *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
438 *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
439 *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
440 *(packedUnderlineColorBuffer) = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
442 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
444 if(underlineType == Text::Underline::DASHED)
446 float dashWidth = dashedUnderlineWidth;
449 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
451 if(Dali::EqualsZero(dashGap) && dashWidth > 0)
453 // Note : this is same logic as bitmap[y][x] = underlineColor;
454 *(bitmapBuffer + x) = packedUnderlineColor;
457 else if(dashGap < dashedUnderlineGap)
464 dashWidth = dashedUnderlineWidth;
471 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
473 // Note : this is same logic as bitmap[y][x] = underlineColor;
474 *(bitmapBuffer + x) = packedUnderlineColor;
477 bitmapBuffer += glyphData.width;
479 if(underlineType == Text::Underline::DOUBLE)
481 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
482 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
483 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
485 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
486 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
488 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
490 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
492 // Note : this is same logic as bitmap[y][x] = underlineColor;
493 *(bitmapBuffer + x) = packedUnderlineColor;
495 bitmapBuffer += glyphData.width;
501 /// Draws the background color to the buffer
502 void DrawBackgroundColor(
503 Vector4 backgroundColor,
504 const uint32_t bufferWidth,
505 const uint32_t bufferHeight,
506 GlyphData& glyphData,
507 const float baseline,
509 const float lineExtentLeft,
510 const float lineExtentRight)
512 const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
513 const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
514 const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
515 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
517 // If current glyph don't need to be rendered, just ignore.
518 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
523 // We can optimize by memset when backgroundColor.a is near zero
524 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
526 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
528 // Skip yRangeMin line.
529 bitmapBuffer += yRangeMin * glyphData.width;
531 if(backgroundColorAlpha == 0)
533 for(int32_t y = yRangeMin; y < yRangeMax; y++)
535 // We can use memset.
536 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
537 bitmapBuffer += glyphData.width;
542 uint32_t packedBackgroundColor = 0u;
543 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
545 // Write the color to the pixel buffer
546 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
547 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
548 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
549 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
551 for(int32_t y = yRangeMin; y < yRangeMax; y++)
553 for(int32_t x = xRangeMin; x < xRangeMax; x++)
555 // Note : this is same logic as bitmap[y][x] = backgroundColor;
556 *(bitmapBuffer + x) = packedBackgroundColor;
558 bitmapBuffer += glyphData.width;
563 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)
565 // Retrieve lines, glyphs, positions and colors from the view model.
566 const Length modelNumberOfLines = model->GetNumberOfLines();
567 const LineRun* const modelLinesBuffer = model->GetLines();
568 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
569 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
570 const Vector2* const positionBuffer = model->GetLayout();
571 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
572 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
574 const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
576 // Create and initialize the pixel buffer.
578 glyphData.verticalOffset = verticalOffset;
579 glyphData.width = bufferWidth;
580 glyphData.height = bufferHeight;
581 glyphData.bitmapBuffer = buffer;
582 glyphData.horizontalOffset = 0;
584 ColorIndex prevBackgroundColorIndex = 0;
585 ColorIndex backgroundColorIndex = 0;
587 // Traverses the lines of the text.
588 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
590 const LineRun& line = *(modelLinesBuffer + lineIndex);
592 // Sets the horizontal offset of the line.
593 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
594 glyphData.horizontalOffset += horizontalOffset;
596 // Increases the vertical offset with the line's ascender.
597 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
599 float left = bufferWidth;
601 float baseline = 0.0f;
603 // Traverses the glyphs of the line.
604 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
605 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
607 // Retrieve the glyph's info.
608 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
610 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
611 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
613 // Nothing to do if default background color, the glyph's width or height is zero.
617 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
619 if((backgroundColorIndex != prevBackgroundColorIndex) &&
620 (prevBackgroundColorIndex != 0u))
622 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
623 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
626 if(backgroundColorIndex == 0u)
628 prevBackgroundColorIndex = backgroundColorIndex;
629 //if background color is the default do nothing
633 // Retrieves the glyph's position.
634 const Vector2* const position = positionBuffer + glyphIndex;
636 if(baseline < position->y + glyphInfo->yBearing)
638 baseline = position->y + glyphInfo->yBearing;
641 // Calculate the positions of leftmost and rightmost glyphs in the current line
642 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
644 left = position->x - glyphInfo->xBearing;
647 if(position->x + glyphInfo->width > right)
649 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
652 prevBackgroundColorIndex = backgroundColorIndex;
655 //draw last background at line end if not default
656 if(backgroundColorIndex != 0u)
658 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
659 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
662 // Increases the vertical offset with the line's descender.
663 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
666 return glyphData.bitmapBuffer;
669 /// Draws the specified strikethrough color to the buffer
670 void DrawStrikethrough(const uint32_t bufferWidth,
671 const uint32_t bufferHeight,
672 GlyphData& glyphData,
673 const float baseline,
674 const float strikethroughStartingYPosition,
675 const float maxStrikethroughHeight,
676 const float lineExtentLeft,
677 const float lineExtentRight,
678 const StrikethroughStyleProperties& commonStrikethroughProperties,
679 const StrikethroughStyleProperties& currentStrikethroughProperties,
682 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
684 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
685 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
686 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
687 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
689 // If current glyph don't need to be rendered, just ignore.
690 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
695 // We can optimize by memset when strikethroughColor.a is near zero
696 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
698 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
700 // Skip yRangeMin line.
701 bitmapBuffer += yRangeMin * glyphData.width;
703 if(strikethroughColorAlpha == 0)
705 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
707 // We can use memset.
708 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
709 bitmapBuffer += glyphData.width;
714 uint32_t packedStrikethroughColor = 0u;
715 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
717 // Write the color to the pixel buffer
718 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
719 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
720 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
721 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
723 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
725 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
727 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
728 *(bitmapBuffer + x) = packedStrikethroughColor;
730 bitmapBuffer += glyphData.width;
736 * @brief Create an initialized image buffer filled with transparent color.
738 * Creates the pixel data used to generate the final image with the given size.
740 * @param[in] bufferWidth The width of the image buffer.
741 * @param[in] bufferHeight The height of the image buffer.
742 * @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).
744 * @return An image buffer.
746 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat)
748 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
750 if(Pixel::RGBA8888 == pixelFormat)
752 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
753 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
754 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
758 memset(imageBuffer.GetBuffer(), 0, static_cast<std::size_t>(bufferWidth * bufferHeight));
765 * @brief Combine the two RGBA image buffers together.
767 * The top layer buffer will blend over the bottom layer buffer:
768 * - If the pixel is not fully opaque from either buffer, it will be blended with
769 * the pixel from the other buffer and copied to the combined buffer.
770 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
771 * buffer will be copied to the combined buffer.
773 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
774 * We can determine which pixelBuffer's memory is destination
776 * @param[in, out] topPixelBuffer The top layer buffer.
777 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
778 * @param[in] bufferWidth The width of the image buffer.
779 * @param[in] bufferHeight The height of the image buffer.
780 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
781 * False if we store the combined image buffer result into bottomPixelBuffer.
784 void CombineImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, bool storeResultIntoTop)
786 // Assume that we always combine two RGBA images
787 // Jump with 4bytes for optimize runtime.
788 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
789 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
791 if(topBuffer == NULL && bottomBuffer == NULL)
793 // Nothing to do if both buffers are empty.
797 if(topBuffer == NULL)
799 // Nothing to do if topBuffer is empty.
800 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
801 if(storeResultIntoTop)
803 topPixelBuffer = bottomPixelBuffer;
808 if(bottomBuffer == NULL)
810 // Nothing to do if bottomBuffer is empty.
811 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
812 if(!storeResultIntoTop)
814 bottomPixelBuffer = topPixelBuffer;
819 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
821 uint32_t* __restrict__ combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
822 uint8_t* __restrict__ topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
824 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
826 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
827 // Otherwise, copy pixel from topBuffer to combinedBuffer.
828 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
830 uint8_t topAlpha = *topAlphaBufferPointer;
834 // Copy the pixel from bottomBuffer to combinedBuffer
835 if(storeResultIntoTop)
837 *(combinedBuffer) = *(bottomBuffer);
840 else if(topAlpha == 255)
842 // Copy the pixel from topBuffer to combinedBuffer
843 if(!storeResultIntoTop)
845 *(combinedBuffer) = *(topBuffer);
850 // At least one pixel is not fully opaque
851 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
852 uint32_t blendedBottomBufferColor = *(bottomBuffer);
853 uint8_t* __restrict__ blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
855 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
856 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
857 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
858 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
860 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
863 // Increase each buffer's pointer.
867 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
873 TypesetterPtr Typesetter::New(const ModelInterface* const model)
875 return TypesetterPtr(new Typesetter(model));
878 ViewModel* Typesetter::GetViewModel()
883 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
885 // @todo. This initial implementation for a TextLabel has only one visible page.
887 // Elides the text if needed.
888 mModel->ElideGlyphs();
890 // Retrieves the layout size.
891 const Size& layoutSize = mModel->GetLayoutSize();
893 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
895 // Set the offset for the horizontal alignment according to the text direction and outline width.
898 switch(mModel->GetHorizontalAlignment())
900 case HorizontalAlignment::BEGIN:
905 case HorizontalAlignment::CENTER:
907 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
910 case HorizontalAlignment::END:
912 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
917 // Set the offset for the vertical alignment.
920 switch(mModel->GetVerticalAlignment())
922 case VerticalAlignment::TOP:
927 case VerticalAlignment::CENTER:
929 penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
930 penY = penY < 0.f ? 0.f : penY;
933 case VerticalAlignment::BOTTOM:
935 penY = static_cast<int32_t>(size.height - layoutSize.height);
940 // Generate the image buffers of the text for each different style first,
941 // then combine all of them together as one final image buffer. We try to
942 // do all of these in CPU only, so that once the final texture is generated,
943 // no calculation is needed in GPU during each frame.
945 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
946 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
948 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
949 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
951 //Elided text in ellipsis at START could start on index greater than 0
952 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
953 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
955 Devel::PixelBuffer imageBuffer;
957 if(RENDER_MASK == behaviour)
959 // Generate the image buffer as an alpha mask for color glyphs.
960 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
962 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
964 // Generate an empty image buffer so that it can been combined with the image buffers for styles
965 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
966 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
970 // Generate the image buffer for the text with no style.
971 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
974 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
976 // Generate the outline if enabled
977 const uint16_t outlineWidth = mModel->GetOutlineWidth();
978 const float outlineAlpha = mModel->GetOutlineColor().a;
979 if(outlineWidth != 0u && fabsf(outlineAlpha) > Math::MACHINE_EPSILON_1 && RENDER_OVERLAY_STYLE != behaviour)
981 // Create the image buffer for outline
982 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
984 // Combine the two buffers
985 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
988 // @todo. Support shadow for partial text later on.
990 // Generate the shadow if enabled
991 const Vector2& shadowOffset = mModel->GetShadowOffset();
992 const float shadowAlpha = mModel->GetShadowColor().a;
993 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))
995 // Create the image buffer for shadow
996 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
998 // Check whether it will be a soft shadow
999 const float& blurRadius = mModel->GetShadowBlurRadius();
1001 if(blurRadius > Math::MACHINE_EPSILON_1)
1003 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
1006 // Combine the two buffers
1007 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
1010 // Generate the background if enabled
1011 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
1012 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
1013 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
1015 Devel::PixelBuffer backgroundImageBuffer;
1017 if(backgroundEnabled)
1019 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1023 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1026 if(backgroundMarkupSet)
1028 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
1031 // Combine the two buffers
1032 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1035 if(RENDER_OVERLAY_STYLE == behaviour)
1037 if(mModel->IsUnderlineEnabled())
1039 // Create the image buffer for underline
1040 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1042 // Combine the two buffers
1043 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
1046 if(mModel->IsStrikethroughEnabled())
1048 // Create the image buffer for strikethrough
1049 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
1051 // Combine the two buffers
1052 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
1055 // Markup-Processor for overlay styles
1056 if(mModel->IsMarkupProcessorEnabled() || mModel->IsSpannedTextPlaced())
1058 if(mModel->IsMarkupUnderlineSet())
1060 imageBuffer = ApplyUnderlineMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1063 if(mModel->IsMarkupStrikethroughSet())
1065 imageBuffer = ApplyStrikethroughMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1071 // Create the final PixelData for the combined image buffer
1072 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
1077 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)
1079 // Retrieve lines, glyphs, positions and colors from the view model.
1080 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1081 const LineRun* const __restrict__ modelLinesBuffer = mModel->GetLines();
1082 const GlyphInfo* const __restrict__ glyphsBuffer = mModel->GetGlyphs();
1083 const Vector2* const __restrict__ positionBuffer = mModel->GetLayout();
1084 const Vector4* const __restrict__ colorsBuffer = mModel->GetColors();
1085 const ColorIndex* const __restrict__ colorIndexBuffer = mModel->GetColorIndices();
1086 const GlyphInfo* __restrict__ hyphens = mModel->GetHyphens();
1087 const Length* __restrict__ hyphenIndices = mModel->GetHyphenIndices();
1088 const Length hyphensCount = mModel->GetHyphensCount();
1090 // Elided text info. Indices according to elided text and Ellipsis position.
1091 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1092 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1093 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1094 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1095 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1097 // Whether to use the default color.
1098 const bool useDefaultColor = (NULL == colorsBuffer);
1099 const Vector4& defaultColor = mModel->GetDefaultColor();
1101 // Create and initialize the pixel buffer.
1102 GlyphData glyphData;
1103 glyphData.verticalOffset = verticalOffset;
1104 glyphData.width = bufferWidth;
1105 glyphData.height = bufferHeight;
1106 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1107 glyphData.horizontalOffset = 0;
1109 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1110 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1111 Length hyphenIndex = 0;
1113 const Character* __restrict__ textBuffer = mModel->GetTextBuffer();
1114 float calculatedAdvance = 0.f;
1115 const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1116 const CharacterIndex* __restrict__ glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1118 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1120 // Traverses the lines of the text.
1121 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1123 const LineRun& line = *(modelLinesBuffer + lineIndex);
1125 // Sets the horizontal offset of the line.
1126 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1127 glyphData.horizontalOffset += horizontalOffset;
1129 // Increases the vertical offset with the line's ascender.
1130 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1132 // Retrieves the glyph's outline width
1133 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1135 if(style == Typesetter::STYLE_OUTLINE)
1137 glyphData.horizontalOffset -= outlineWidth;
1140 // Only need to add the vertical outline offset for the first line
1141 glyphData.verticalOffset -= outlineWidth;
1144 else if(style == Typesetter::STYLE_SHADOW)
1146 const Vector2& shadowOffset = mModel->GetShadowOffset();
1147 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1151 // Only need to add the vertical shadow offset for first line
1152 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1156 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1157 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1158 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1160 // Get the character-spacing runs.
1161 const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1163 // Aggregate underline-style-properties from mModel
1164 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1165 mModel->GetUnderlineColor(),
1166 mModel->GetUnderlineHeight(),
1167 mModel->GetDashedUnderlineGap(),
1168 mModel->GetDashedUnderlineWidth(),
1175 // Aggregate strikethrough-style-properties from mModel
1176 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1177 mModel->GetStrikethroughHeight(),
1181 // Get the underline runs.
1182 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1183 Vector<UnderlinedGlyphRun> underlineRuns;
1184 underlineRuns.Resize(numberOfUnderlineRuns);
1185 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1187 // Get the strikethrough runs.
1188 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1189 Vector<StrikethroughGlyphRun> strikethroughRuns;
1190 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1191 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1193 bool thereAreUnderlinedGlyphs = false;
1194 bool thereAreStrikethroughGlyphs = false;
1196 float currentUnderlinePosition = 0.0f;
1197 float currentUnderlineHeight = modelUnderlineProperties.height;
1198 float maxUnderlineHeight = currentUnderlineHeight;
1199 auto currentUnderlineProperties = modelUnderlineProperties;
1201 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1202 float maxStrikethroughHeight = currentStrikethroughHeight;
1203 auto currentStrikethroughProperties = modelStrikethroughProperties;
1204 float strikethroughStartingYPosition = 0.0f;
1206 FontId lastFontId = 0;
1208 float lineExtentLeft = bufferWidth;
1209 float lineExtentRight = 0.0f;
1210 float baseline = 0.0f;
1211 bool addHyphen = false;
1213 // Traverses the glyphs of the line.
1214 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1215 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1216 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1218 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1220 //To handle START case of ellipsis, the first glyph has been shifted
1221 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1222 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1224 //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.
1225 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1227 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1228 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1230 // Ignore any glyph that removed for MIDDLE ellipsis
1233 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1235 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1239 // Retrieve the glyph's info.
1240 const GlyphInfo* glyphInfo;
1242 if(addHyphen && hyphens)
1244 glyphInfo = hyphens + hyphenIndex;
1249 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1252 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1253 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1255 // Nothing to do if the glyph's width or height is zero.
1259 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1260 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1261 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1262 currentUnderlineHeight = currentUnderlineProperties.height;
1263 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1265 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1266 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1267 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1268 currentStrikethroughHeight = currentStrikethroughProperties.height;
1269 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1271 // Are we still using the same fontId as previous
1272 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1274 // We need to fetch fresh font underline metrics
1275 FontMetrics fontMetrics;
1276 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1278 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1279 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1283 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1286 if(strikethroughGlyph)
1288 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1291 // Update lastFontId because fontId is changed
1292 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1295 // Retrieves the glyph's position.
1296 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1300 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1301 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1302 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1303 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1304 position.y = -glyphInfo->yBearing;
1307 if(baseline < position.y + glyphInfo->yBearing)
1309 baseline = position.y + glyphInfo->yBearing;
1312 // Calculate the positions of leftmost and rightmost glyphs in the current line
1313 if(position.x < lineExtentLeft)
1315 lineExtentLeft = position.x;
1318 if(position.x + glyphInfo->width > lineExtentRight)
1320 lineExtentRight = position.x + glyphInfo->width;
1323 // Retrieves the glyph's color.
1324 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1327 if(style == Typesetter::STYLE_SHADOW)
1329 color = mModel->GetShadowColor();
1331 else if(style == Typesetter::STYLE_OUTLINE)
1333 color = mModel->GetOutlineColor();
1337 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1340 // Premultiply alpha
1345 // Retrieves the glyph's bitmap.
1346 glyphData.glyphBitmap.buffer = NULL;
1347 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1348 glyphData.glyphBitmap.height = glyphInfo->height;
1350 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1352 // Don't render outline for other styles
1353 outlineWidth = 0.0f;
1356 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1358 fontClient.CreateBitmap(glyphInfo->fontId,
1360 glyphInfo->isItalicRequired,
1361 glyphInfo->isBoldRequired,
1362 glyphData.glyphBitmap,
1363 static_cast<int32_t>(outlineWidth));
1366 // Sets the glyph's bitmap into the bitmap of the whole text.
1367 if(NULL != glyphData.glyphBitmap.buffer)
1369 if(style == Typesetter::STYLE_OUTLINE)
1371 // Set the position offset for the current glyph
1372 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1373 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1376 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1377 TypesetGlyph(glyphData,
1383 if(style == Typesetter::STYLE_OUTLINE)
1385 // Reset the position offset for the next glyph
1386 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1387 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1390 // free the glyphBitmap.buffer if it is owner of buffer
1391 if(glyphData.glyphBitmap.isBufferOwned)
1393 free(glyphData.glyphBitmap.buffer);
1394 glyphData.glyphBitmap.isBufferOwned = false;
1396 glyphData.glyphBitmap.buffer = NULL;
1401 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1406 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1414 // Draw the underline from the leftmost glyph to the rightmost glyph
1415 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1417 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1420 // Draw the background color from the leftmost glyph to the rightmost glyph
1421 if(style == Typesetter::STYLE_BACKGROUND)
1423 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1426 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1427 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1429 //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.
1430 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.
1431 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1434 // Increases the vertical offset with the line's descender & line spacing.
1435 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1438 return glyphData.bitmapBuffer;
1441 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)
1443 // Underline-tags (this is for Markup case)
1444 // Get the underline runs.
1445 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1446 Vector<UnderlinedGlyphRun> underlineRuns;
1447 underlineRuns.Resize(numberOfUnderlineRuns);
1448 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1450 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1451 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1452 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1453 GlyphIndex startGlyphIndex, endGlyphIndex;
1455 //The outer loop to iterate on the separated chunks of underlined glyph runs
1456 while(itGlyphRun != endItGlyphRun)
1458 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1459 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1461 // Create the image buffer for underline
1462 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1463 // Combine the two buffers
1464 // Result pixel buffer will be stored into topPixelBuffer.
1465 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1470 return topPixelBuffer;
1473 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)
1475 // strikethrough-tags (this is for Markup case)
1476 // Get the strikethrough runs.
1477 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1478 Vector<StrikethroughGlyphRun> strikethroughRuns;
1479 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1480 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1482 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1483 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1484 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1485 GlyphIndex startGlyphIndex, endGlyphIndex;
1487 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1488 while(itGlyphRun != endItGlyphRun)
1490 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1491 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1493 // Create the image buffer for strikethrough
1494 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1495 // Combine the two buffers
1496 // Result pixel buffer will be stored into topPixelBuffer.
1497 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1502 return topPixelBuffer;
1505 Typesetter::Typesetter(const ModelInterface* const model)
1506 : mModel(new ViewModel(model))
1510 Typesetter::~Typesetter()
1517 } // namespace Toolkit