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/integration-api/trace.h>
24 #include <dali/public-api/common/constants.h>
25 #include <dali/public-api/math/math-utils.h>
29 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
30 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
31 #include <dali-toolkit/internal/text/line-helper-functions.h>
32 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
33 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
34 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
35 #include <dali-toolkit/internal/text/rendering/view-model.h>
45 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
47 const float HALF(0.5f);
48 const float ONE_AND_A_HALF(1.5f);
51 * @brief Fast multiply & divide by 255. It wiil be useful when we applying alpha value in color
53 * @param x The value between [0..255]
54 * @param y The value between [0..255]
57 inline uint8_t MultiplyAndNormalizeColor(const uint8_t x, const uint8_t y) noexcept
59 const uint32_t xy = static_cast<const uint32_t>(x) * y;
60 return ((xy << 15) + (xy << 7) + xy) >> 23;
64 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
68 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
69 Vector2* position; ///< The position of the glyph.
70 TextAbstraction::FontClient::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
71 uint32_t width; ///< The bitmap's width.
72 uint32_t height; ///< The bitmap's height.
73 int32_t horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
74 int32_t verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
78 * @brief Sets the glyph's buffer into the bitmap's buffer.
80 * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
81 * @param[in] position The position of the glyph.
82 * @param[in] color The color of the glyph.
83 * @param[in] style The style of the text.
84 * @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).
86 void TypesetGlyph(GlyphData& __restrict__ data,
87 const Vector2* const __restrict__ position,
88 const Vector4* const __restrict__ color,
89 const Typesetter::Style style,
90 const Pixel::Format pixelFormat)
92 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
94 // Nothing to do if the width or height of the buffer is zero.
98 // Initial vertical / horizontal offset.
99 const int32_t yOffset = data.verticalOffset + position->y;
100 const int32_t xOffset = data.horizontalOffset + position->x;
102 // Whether the given glyph is a color one.
103 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
104 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
105 const uint32_t glyphAlphaIndex = glyphPixelSize - 1u;
107 // Determinate iterator range.
108 const int32_t lineIndexRangeMin = std::max(0, -yOffset);
109 const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
110 const int32_t indexRangeMin = std::max(0, -xOffset);
111 const int32_t indexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
113 // If current glyph don't need to be rendered, just ignore.
114 if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
119 if(Pixel::RGBA8888 == pixelFormat)
121 uint32_t* __restrict__ bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
123 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
125 // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
126 // Just overwrite transparent color and return.
127 if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
129 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
131 // We can use memset here.
132 memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
133 bitmapBuffer += data.width;
138 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
140 // Offset byte value of glyph bitmap.
141 uint32_t glyphOffet = 0u;
143 // Allocate scanline memory for glyph bitmap if we need.
144 const bool useLocalScanline = data.glyphBitmap.compressionType != TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
145 uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer;
147 // Precalculate input color's packed result.
148 uint32_t packedInputColor = 0u;
149 uint8_t* __restrict__ packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
151 *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
152 *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
153 *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
154 *(packedInputColorBuffer) = static_cast<uint8_t>(color->r * 255);
156 // Skip basic line of glyph.
159 for(int32_t lineIndex = 0; lineIndex < lineIndexRangeMin; ++lineIndex)
161 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
166 glyphScanline += lineIndexRangeMin * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize);
169 // Traverse the pixels of the glyph line per line.
172 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
176 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
179 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
181 const int32_t xOffsetIndex = xOffset + index;
183 // Retrieves the color from the color glyph.
184 uint32_t packedColorGlyph = *(reinterpret_cast<const uint32_t*>(glyphScanline + (index << 2)));
185 uint8_t* __restrict__ packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
187 // Update the alpha channel.
188 const uint8_t colorAlpha = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
189 *(packedColorGlyphBuffer + 3u) = colorAlpha;
191 if(Typesetter::STYLE_SHADOW == style)
193 // The shadow of color glyph needs to have the shadow color.
194 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
195 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
196 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
202 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
205 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
206 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
207 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
209 if(data.glyphBitmap.isColorBitmap)
211 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
212 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
213 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
217 // Set the color into the final pixel buffer.
218 *(bitmapBuffer + xOffsetIndex) = packedColorGlyph;
221 bitmapBuffer += data.width;
222 if(!useLocalScanline)
224 glyphScanline += data.glyphBitmap.width * glyphPixelSize;
230 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
234 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
237 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
239 // Update the alpha channel.
240 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
242 // Copy non-transparent pixels only
245 const int32_t xOffsetIndex = xOffset + index;
247 // Check alpha of overlapped pixels
248 uint32_t& currentColor = *(bitmapBuffer + xOffsetIndex);
249 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
251 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
252 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
253 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
254 // happen, for example, in the RTL text when we copy glyphs from right to left).
255 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
256 currentAlpha = std::max(currentAlpha, alpha);
257 if(currentAlpha == 255)
259 // Fast-cut to avoid float type operation.
260 currentColor = packedInputColor;
264 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
265 // The format is RGBA8888.
266 uint32_t packedColor = 0u;
267 uint8_t* __restrict__ packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
269 // Color is pre-muliplied with its alpha.
270 *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
271 *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
272 *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
273 *(packedColorBuffer) = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
275 // Set the color into the final pixel buffer.
276 currentColor = packedColor;
281 bitmapBuffer += data.width;
282 if(!useLocalScanline)
284 glyphScanline += data.glyphBitmap.width * glyphPixelSize;
296 // Below codes required only if not color glyph.
299 uint8_t* __restrict__ bitmapBuffer = data.bitmapBuffer.GetBuffer();
301 // Offset byte value of glyph bitmap.
302 uint32_t glyphOffet = 0u;
304 // Allocate scanline memory for glyph bitmap if we need.
305 const bool useLocalScanline = data.glyphBitmap.compressionType != TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
306 uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer;
309 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
311 // Skip basic line of glyph.
314 for(int32_t lineIndex = 0; lineIndex < lineIndexRangeMin; ++lineIndex)
316 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
321 glyphScanline += lineIndexRangeMin * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize);
324 // Traverse the pixels of the glyph line per line.
325 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
329 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
332 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
334 const int32_t xOffsetIndex = xOffset + index;
336 // Update the alpha channel.
337 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
339 // Copy non-transparent pixels only
342 // Check alpha of overlapped pixels
343 uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
345 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
346 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
347 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
348 // happen, for example, in the RTL text when we copy glyphs from right to left).
349 currentAlpha = std::max(currentAlpha, alpha);
353 bitmapBuffer += data.width;
354 if(!useLocalScanline)
356 glyphScanline += data.glyphBitmap.width * glyphPixelSize;
368 /// Draws the specified underline color to the buffer
370 const uint32_t bufferWidth,
371 const uint32_t bufferHeight,
372 GlyphData& glyphData,
373 const float baseline,
374 const float currentUnderlinePosition,
375 const float maxUnderlineHeight,
376 const float lineExtentLeft,
377 const float lineExtentRight,
378 const UnderlineStyleProperties& commonUnderlineProperties,
379 const UnderlineStyleProperties& currentUnderlineProperties,
382 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
383 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
384 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
385 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
387 int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
389 const uint32_t yRangeMin = underlineYOffset;
390 const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
391 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
392 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
394 // If current glyph don't need to be rendered, just ignore.
395 if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
400 // We can optimize by memset when underlineColor.a is near zero
401 uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
403 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
405 // Skip yRangeMin line.
406 bitmapBuffer += yRangeMin * glyphData.width;
408 // Note if underlineType is DASHED, we cannot setup color by memset.
409 if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
411 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
413 // We can use memset.
414 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
415 bitmapBuffer += glyphData.width;
417 if(underlineType == Text::Underline::DOUBLE)
419 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
420 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
421 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
423 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
424 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
426 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
428 // We can use memset.
429 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
430 bitmapBuffer += glyphData.width;
436 uint32_t packedUnderlineColor = 0u;
437 uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
439 // Write the color to the pixel buffer
440 *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
441 *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
442 *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
443 *(packedUnderlineColorBuffer) = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
445 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
447 if(underlineType == Text::Underline::DASHED)
449 float dashWidth = dashedUnderlineWidth;
452 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
454 if(Dali::EqualsZero(dashGap) && dashWidth > 0)
456 // Note : this is same logic as bitmap[y][x] = underlineColor;
457 *(bitmapBuffer + x) = packedUnderlineColor;
460 else if(dashGap < dashedUnderlineGap)
467 dashWidth = dashedUnderlineWidth;
474 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
476 // Note : this is same logic as bitmap[y][x] = underlineColor;
477 *(bitmapBuffer + x) = packedUnderlineColor;
480 bitmapBuffer += glyphData.width;
482 if(underlineType == Text::Underline::DOUBLE)
484 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
485 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
486 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
488 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
489 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
491 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
493 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
495 // Note : this is same logic as bitmap[y][x] = underlineColor;
496 *(bitmapBuffer + x) = packedUnderlineColor;
498 bitmapBuffer += glyphData.width;
504 /// Draws the background color to the buffer
505 void DrawBackgroundColor(
506 Vector4 backgroundColor,
507 const uint32_t bufferWidth,
508 const uint32_t bufferHeight,
509 GlyphData& glyphData,
510 const float baseline,
512 const float lineExtentLeft,
513 const float lineExtentRight)
515 const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
516 const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
517 const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
518 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
520 // If current glyph don't need to be rendered, just ignore.
521 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
526 // We can optimize by memset when backgroundColor.a is near zero
527 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
529 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
531 // Skip yRangeMin line.
532 bitmapBuffer += yRangeMin * glyphData.width;
534 if(backgroundColorAlpha == 0)
536 for(int32_t y = yRangeMin; y < yRangeMax; y++)
538 // We can use memset.
539 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
540 bitmapBuffer += glyphData.width;
545 uint32_t packedBackgroundColor = 0u;
546 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
548 // Write the color to the pixel buffer
549 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
550 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
551 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
552 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
554 for(int32_t y = yRangeMin; y < yRangeMax; y++)
556 for(int32_t x = xRangeMin; x < xRangeMax; x++)
558 // Note : this is same logic as bitmap[y][x] = backgroundColor;
559 *(bitmapBuffer + x) = packedBackgroundColor;
561 bitmapBuffer += glyphData.width;
566 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)
568 // Retrieve lines, glyphs, positions and colors from the view model.
569 const Length modelNumberOfLines = model->GetNumberOfLines();
570 const LineRun* const modelLinesBuffer = model->GetLines();
571 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
572 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
573 const Vector2* const positionBuffer = model->GetLayout();
574 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
575 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
577 const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
579 // Create and initialize the pixel buffer.
581 glyphData.verticalOffset = verticalOffset;
582 glyphData.width = bufferWidth;
583 glyphData.height = bufferHeight;
584 glyphData.bitmapBuffer = buffer;
585 glyphData.horizontalOffset = 0;
587 ColorIndex prevBackgroundColorIndex = 0;
588 ColorIndex backgroundColorIndex = 0;
590 // Traverses the lines of the text.
591 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
593 const LineRun& line = *(modelLinesBuffer + lineIndex);
595 // Sets the horizontal offset of the line.
596 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
597 glyphData.horizontalOffset += horizontalOffset;
599 // Increases the vertical offset with the line's ascender.
600 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
602 float left = bufferWidth;
604 float baseline = 0.0f;
606 // Traverses the glyphs of the line.
607 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
608 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
610 // Retrieve the glyph's info.
611 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
613 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
614 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
616 // Nothing to do if default background color, the glyph's width or height is zero.
620 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
622 if((backgroundColorIndex != prevBackgroundColorIndex) &&
623 (prevBackgroundColorIndex != 0u))
625 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
626 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
629 if(backgroundColorIndex == 0u)
631 prevBackgroundColorIndex = backgroundColorIndex;
632 //if background color is the default do nothing
636 // Retrieves the glyph's position.
637 const Vector2* const position = positionBuffer + glyphIndex;
639 if(baseline < position->y + glyphInfo->yBearing)
641 baseline = position->y + glyphInfo->yBearing;
644 // Calculate the positions of leftmost and rightmost glyphs in the current line
645 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
647 left = position->x - glyphInfo->xBearing;
650 if(position->x + glyphInfo->width > right)
652 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
655 prevBackgroundColorIndex = backgroundColorIndex;
658 //draw last background at line end if not default
659 if(backgroundColorIndex != 0u)
661 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
662 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
665 // Increases the vertical offset with the line's descender.
666 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
669 return glyphData.bitmapBuffer;
672 /// Draws the specified strikethrough color to the buffer
673 void DrawStrikethrough(const uint32_t bufferWidth,
674 const uint32_t bufferHeight,
675 GlyphData& glyphData,
676 const float baseline,
677 const float strikethroughStartingYPosition,
678 const float maxStrikethroughHeight,
679 const float lineExtentLeft,
680 const float lineExtentRight,
681 const StrikethroughStyleProperties& commonStrikethroughProperties,
682 const StrikethroughStyleProperties& currentStrikethroughProperties,
685 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
687 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
688 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
689 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
690 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
692 // If current glyph don't need to be rendered, just ignore.
693 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
698 // We can optimize by memset when strikethroughColor.a is near zero
699 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
701 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
703 // Skip yRangeMin line.
704 bitmapBuffer += yRangeMin * glyphData.width;
706 if(strikethroughColorAlpha == 0)
708 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
710 // We can use memset.
711 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
712 bitmapBuffer += glyphData.width;
717 uint32_t packedStrikethroughColor = 0u;
718 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
720 // Write the color to the pixel buffer
721 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
722 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
723 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
724 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
726 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
728 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
730 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
731 *(bitmapBuffer + x) = packedStrikethroughColor;
733 bitmapBuffer += glyphData.width;
739 * @brief Create an initialized image buffer filled with transparent color.
741 * Creates the pixel data used to generate the final image with the given size.
743 * @param[in] bufferWidth The width of the image buffer.
744 * @param[in] bufferHeight The height of the image buffer.
745 * @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).
747 * @return An image buffer.
749 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat)
751 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
753 if(Pixel::RGBA8888 == pixelFormat)
755 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
756 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
757 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
761 memset(imageBuffer.GetBuffer(), 0, static_cast<std::size_t>(bufferWidth * bufferHeight));
768 * @brief Combine the two RGBA image buffers together.
770 * The top layer buffer will blend over the bottom layer buffer:
771 * - If the pixel is not fully opaque from either buffer, it will be blended with
772 * the pixel from the other buffer and copied to the combined buffer.
773 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
774 * buffer will be copied to the combined buffer.
776 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
777 * We can determine which pixelBuffer's memory is destination
779 * @param[in, out] topPixelBuffer The top layer buffer.
780 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
781 * @param[in] bufferWidth The width of the image buffer.
782 * @param[in] bufferHeight The height of the image buffer.
783 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
784 * False if we store the combined image buffer result into bottomPixelBuffer.
787 void CombineImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, bool storeResultIntoTop)
789 // Assume that we always combine two RGBA images
790 // Jump with 4bytes for optimize runtime.
791 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
792 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
794 if(topBuffer == NULL && bottomBuffer == NULL)
796 // Nothing to do if both buffers are empty.
800 if(topBuffer == NULL)
802 // Nothing to do if topBuffer is empty.
803 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
804 if(storeResultIntoTop)
806 topPixelBuffer = bottomPixelBuffer;
811 if(bottomBuffer == NULL)
813 // Nothing to do if bottomBuffer is empty.
814 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
815 if(!storeResultIntoTop)
817 bottomPixelBuffer = topPixelBuffer;
822 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
824 uint32_t* __restrict__ combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
825 uint8_t* __restrict__ topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
827 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
829 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
830 // Otherwise, copy pixel from topBuffer to combinedBuffer.
831 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
833 uint8_t topAlpha = *topAlphaBufferPointer;
837 // Copy the pixel from bottomBuffer to combinedBuffer
838 if(storeResultIntoTop)
840 *(combinedBuffer) = *(bottomBuffer);
843 else if(topAlpha == 255)
845 // Copy the pixel from topBuffer to combinedBuffer
846 if(!storeResultIntoTop)
848 *(combinedBuffer) = *(topBuffer);
853 // At least one pixel is not fully opaque
854 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
855 uint32_t blendedBottomBufferColor = *(bottomBuffer);
856 uint8_t* __restrict__ blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
858 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
859 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
860 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
861 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
863 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
866 // Increase each buffer's pointer.
870 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
876 TypesetterPtr Typesetter::New(const ModelInterface* const model)
878 return TypesetterPtr(new Typesetter(model));
881 ViewModel* Typesetter::GetViewModel()
886 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
888 DALI_TRACE_BEGIN(gTraceFilter, "DALI_TEXT_RENDERING_TYPESETTER");
889 // @todo. This initial implementation for a TextLabel has only one visible page.
891 // Elides the text if needed.
892 mModel->ElideGlyphs();
894 // Retrieves the layout size.
895 const Size& layoutSize = mModel->GetLayoutSize();
897 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
899 // Set the offset for the horizontal alignment according to the text direction and outline width.
902 switch(mModel->GetHorizontalAlignment())
904 case HorizontalAlignment::BEGIN:
909 case HorizontalAlignment::CENTER:
911 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
914 case HorizontalAlignment::END:
916 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
921 // Set the offset for the vertical alignment.
924 switch(mModel->GetVerticalAlignment())
926 case VerticalAlignment::TOP:
931 case VerticalAlignment::CENTER:
933 penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
934 penY = penY < 0.f ? 0.f : penY;
937 case VerticalAlignment::BOTTOM:
939 penY = static_cast<int32_t>(size.height - layoutSize.height);
944 // Generate the image buffers of the text for each different style first,
945 // then combine all of them together as one final image buffer. We try to
946 // do all of these in CPU only, so that once the final texture is generated,
947 // no calculation is needed in GPU during each frame.
949 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
950 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
952 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
953 const size_t bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
955 //Elided text in ellipsis at START could start on index greater than 0
956 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
957 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
959 Devel::PixelBuffer imageBuffer;
961 if(RENDER_MASK == behaviour)
963 // Generate the image buffer as an alpha mask for color glyphs.
964 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
966 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
968 // Generate an empty image buffer so that it can been combined with the image buffers for styles
969 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
970 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
974 // Generate the image buffer for the text with no style.
975 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
978 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
980 // Generate the outline if enabled
981 const uint16_t outlineWidth = mModel->GetOutlineWidth();
982 const float outlineAlpha = mModel->GetOutlineColor().a;
983 if(outlineWidth != 0u && fabsf(outlineAlpha) > Math::MACHINE_EPSILON_1 && RENDER_OVERLAY_STYLE != behaviour)
985 // Create the image buffer for outline
986 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
988 // Combine the two buffers
989 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
992 // @todo. Support shadow for partial text later on.
994 // Generate the shadow if enabled
995 const Vector2& shadowOffset = mModel->GetShadowOffset();
996 const float shadowAlpha = mModel->GetShadowColor().a;
997 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))
999 // Create the image buffer for shadow
1000 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1002 // Check whether it will be a soft shadow
1003 const float& blurRadius = mModel->GetShadowBlurRadius();
1005 if(blurRadius > Math::MACHINE_EPSILON_1)
1007 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
1010 // Combine the two buffers
1011 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
1014 // Generate the background if enabled
1015 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
1016 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
1017 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
1019 Devel::PixelBuffer backgroundImageBuffer;
1021 if(backgroundEnabled)
1023 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1027 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1030 if(backgroundMarkupSet)
1032 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
1035 // Combine the two buffers
1036 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1039 if(RENDER_OVERLAY_STYLE == behaviour)
1041 if(mModel->IsUnderlineEnabled())
1043 // Create the image buffer for underline
1044 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1046 // Combine the two buffers
1047 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
1050 if(mModel->IsStrikethroughEnabled())
1052 // Create the image buffer for strikethrough
1053 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
1055 // Combine the two buffers
1056 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
1059 // Markup-Processor for overlay styles
1060 if(mModel->IsMarkupProcessorEnabled() || mModel->IsSpannedTextPlaced())
1062 if(mModel->IsMarkupUnderlineSet())
1064 imageBuffer = ApplyUnderlineMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1067 if(mModel->IsMarkupStrikethroughSet())
1069 imageBuffer = ApplyStrikethroughMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1075 // Create the final PixelData for the combined image buffer
1076 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
1078 DALI_TRACE_END(gTraceFilter, "DALI_TEXT_RENDERING_TYPESETTER");
1083 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)
1085 // Retrieve lines, glyphs, positions and colors from the view model.
1086 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1087 const LineRun* const __restrict__ modelLinesBuffer = mModel->GetLines();
1088 const GlyphInfo* const __restrict__ glyphsBuffer = mModel->GetGlyphs();
1089 const Vector2* const __restrict__ positionBuffer = mModel->GetLayout();
1090 const Vector4* const __restrict__ colorsBuffer = mModel->GetColors();
1091 const ColorIndex* const __restrict__ colorIndexBuffer = mModel->GetColorIndices();
1092 const GlyphInfo* __restrict__ hyphens = mModel->GetHyphens();
1093 const Length* __restrict__ hyphenIndices = mModel->GetHyphenIndices();
1094 const Length hyphensCount = mModel->GetHyphensCount();
1096 // Elided text info. Indices according to elided text and Ellipsis position.
1097 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1098 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1099 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1100 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1101 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1103 // Whether to use the default color.
1104 const bool useDefaultColor = (NULL == colorsBuffer);
1105 const Vector4& defaultColor = mModel->GetDefaultColor();
1107 // Create and initialize the pixel buffer.
1108 GlyphData glyphData;
1109 glyphData.verticalOffset = verticalOffset;
1110 glyphData.width = bufferWidth;
1111 glyphData.height = bufferHeight;
1112 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1113 glyphData.horizontalOffset = 0;
1115 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1116 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1117 Length hyphenIndex = 0;
1119 const Character* __restrict__ textBuffer = mModel->GetTextBuffer();
1120 float calculatedAdvance = 0.f;
1121 const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1122 const CharacterIndex* __restrict__ glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1124 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1126 // Traverses the lines of the text.
1127 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1129 const LineRun& line = *(modelLinesBuffer + lineIndex);
1131 // Sets the horizontal offset of the line.
1132 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1133 glyphData.horizontalOffset += horizontalOffset;
1135 // Increases the vertical offset with the line's ascender.
1136 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1138 // Retrieves the glyph's outline width
1139 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1141 if(style == Typesetter::STYLE_OUTLINE)
1143 glyphData.horizontalOffset -= outlineWidth;
1146 // Only need to add the vertical outline offset for the first line
1147 glyphData.verticalOffset -= outlineWidth;
1150 else if(style == Typesetter::STYLE_SHADOW)
1152 const Vector2& shadowOffset = mModel->GetShadowOffset();
1153 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1157 // Only need to add the vertical shadow offset for first line
1158 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1162 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1163 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1164 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1166 // Get the character-spacing runs.
1167 const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1169 // Aggregate underline-style-properties from mModel
1170 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1171 mModel->GetUnderlineColor(),
1172 mModel->GetUnderlineHeight(),
1173 mModel->GetDashedUnderlineGap(),
1174 mModel->GetDashedUnderlineWidth(),
1181 // Aggregate strikethrough-style-properties from mModel
1182 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1183 mModel->GetStrikethroughHeight(),
1187 // Get the underline runs.
1188 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1189 Vector<UnderlinedGlyphRun> underlineRuns;
1190 underlineRuns.Resize(numberOfUnderlineRuns);
1191 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1193 // Get the strikethrough runs.
1194 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1195 Vector<StrikethroughGlyphRun> strikethroughRuns;
1196 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1197 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1199 bool thereAreUnderlinedGlyphs = false;
1200 bool thereAreStrikethroughGlyphs = false;
1202 float currentUnderlinePosition = 0.0f;
1203 float currentUnderlineHeight = modelUnderlineProperties.height;
1204 float maxUnderlineHeight = currentUnderlineHeight;
1205 auto currentUnderlineProperties = modelUnderlineProperties;
1207 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1208 float maxStrikethroughHeight = currentStrikethroughHeight;
1209 auto currentStrikethroughProperties = modelStrikethroughProperties;
1210 float strikethroughStartingYPosition = 0.0f;
1212 FontId lastFontId = 0;
1214 float lineExtentLeft = bufferWidth;
1215 float lineExtentRight = 0.0f;
1216 float baseline = 0.0f;
1217 bool addHyphen = false;
1219 // Traverses the glyphs of the line.
1220 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1221 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1222 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1224 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1226 //To handle START case of ellipsis, the first glyph has been shifted
1227 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1228 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1230 //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.
1231 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1233 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1234 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1236 // Ignore any glyph that removed for MIDDLE ellipsis
1239 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1241 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1245 // Retrieve the glyph's info.
1246 const GlyphInfo* glyphInfo;
1248 if(addHyphen && hyphens)
1250 glyphInfo = hyphens + hyphenIndex;
1255 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1258 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1259 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1261 // Nothing to do if the glyph's width or height is zero.
1265 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1266 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1267 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1268 currentUnderlineHeight = currentUnderlineProperties.height;
1269 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1271 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1272 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1273 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1274 currentStrikethroughHeight = currentStrikethroughProperties.height;
1275 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1277 // Are we still using the same fontId as previous
1278 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1280 // We need to fetch fresh font underline metrics
1281 FontMetrics fontMetrics;
1282 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1284 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1285 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1289 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1292 if(strikethroughGlyph)
1294 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1297 // Update lastFontId because fontId is changed
1298 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1301 // Retrieves the glyph's position.
1302 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1306 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1307 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1308 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1309 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1310 position.y = -glyphInfo->yBearing;
1313 if(baseline < position.y + glyphInfo->yBearing)
1315 baseline = position.y + glyphInfo->yBearing;
1318 // Calculate the positions of leftmost and rightmost glyphs in the current line
1319 if(position.x < lineExtentLeft)
1321 lineExtentLeft = position.x;
1324 if(position.x + glyphInfo->width > lineExtentRight)
1326 lineExtentRight = position.x + glyphInfo->width;
1329 // Retrieves the glyph's color.
1330 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1333 if(style == Typesetter::STYLE_SHADOW)
1335 color = mModel->GetShadowColor();
1337 else if(style == Typesetter::STYLE_OUTLINE)
1339 color = mModel->GetOutlineColor();
1343 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1346 // Premultiply alpha
1351 // Retrieves the glyph's bitmap.
1352 glyphData.glyphBitmap.buffer = NULL;
1353 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1354 glyphData.glyphBitmap.height = glyphInfo->height;
1356 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1358 // Don't render outline for other styles
1359 outlineWidth = 0.0f;
1362 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1364 fontClient.CreateBitmap(glyphInfo->fontId,
1366 glyphInfo->isItalicRequired,
1367 glyphInfo->isBoldRequired,
1368 glyphData.glyphBitmap,
1369 static_cast<int32_t>(outlineWidth));
1372 // Sets the glyph's bitmap into the bitmap of the whole text.
1373 if(NULL != glyphData.glyphBitmap.buffer)
1375 if(style == Typesetter::STYLE_OUTLINE)
1377 // Set the position offset for the current glyph
1378 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1379 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1382 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1383 TypesetGlyph(glyphData,
1389 if(style == Typesetter::STYLE_OUTLINE)
1391 // Reset the position offset for the next glyph
1392 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1393 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1396 // free the glyphBitmap.buffer if it is owner of buffer
1397 if(glyphData.glyphBitmap.isBufferOwned)
1399 free(glyphData.glyphBitmap.buffer);
1400 glyphData.glyphBitmap.isBufferOwned = false;
1402 glyphData.glyphBitmap.buffer = NULL;
1407 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1412 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1420 // Draw the underline from the leftmost glyph to the rightmost glyph
1421 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1423 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1426 // Draw the background color from the leftmost glyph to the rightmost glyph
1427 if(style == Typesetter::STYLE_BACKGROUND)
1429 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1432 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1433 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1435 //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.
1436 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.
1437 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1440 // Increases the vertical offset with the line's descender & line spacing.
1441 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1444 return glyphData.bitmapBuffer;
1447 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)
1449 // Underline-tags (this is for Markup case)
1450 // Get the underline runs.
1451 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1452 Vector<UnderlinedGlyphRun> underlineRuns;
1453 underlineRuns.Resize(numberOfUnderlineRuns);
1454 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1456 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1457 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1458 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1459 GlyphIndex startGlyphIndex, endGlyphIndex;
1461 //The outer loop to iterate on the separated chunks of underlined glyph runs
1462 while(itGlyphRun != endItGlyphRun)
1464 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1465 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1467 // Create the image buffer for underline
1468 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1469 // Combine the two buffers
1470 // Result pixel buffer will be stored into topPixelBuffer.
1471 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1476 return topPixelBuffer;
1479 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)
1481 // strikethrough-tags (this is for Markup case)
1482 // Get the strikethrough runs.
1483 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1484 Vector<StrikethroughGlyphRun> strikethroughRuns;
1485 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1486 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1488 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1489 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1490 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1491 GlyphIndex startGlyphIndex, endGlyphIndex;
1493 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1494 while(itGlyphRun != endItGlyphRun)
1496 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1497 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1499 // Create the image buffer for strikethrough
1500 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1501 // Combine the two buffers
1502 // Result pixel buffer will be stored into topPixelBuffer.
1503 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1508 return topPixelBuffer;
1511 Typesetter::Typesetter(const ModelInterface* const model)
1512 : mModel(new ViewModel(model))
1516 Typesetter::~Typesetter()
1523 } // namespace Toolkit