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_TEXT_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_SCOPE(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);
1081 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)
1083 // Retrieve lines, glyphs, positions and colors from the view model.
1084 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1085 const LineRun* const __restrict__ modelLinesBuffer = mModel->GetLines();
1086 const GlyphInfo* const __restrict__ glyphsBuffer = mModel->GetGlyphs();
1087 const Vector2* const __restrict__ positionBuffer = mModel->GetLayout();
1088 const Vector4* const __restrict__ colorsBuffer = mModel->GetColors();
1089 const ColorIndex* const __restrict__ colorIndexBuffer = mModel->GetColorIndices();
1090 const GlyphInfo* __restrict__ hyphens = mModel->GetHyphens();
1091 const Length* __restrict__ hyphenIndices = mModel->GetHyphenIndices();
1092 const Length hyphensCount = mModel->GetHyphensCount();
1094 // Elided text info. Indices according to elided text and Ellipsis position.
1095 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1096 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1097 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1098 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1099 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1101 // Whether to use the default color.
1102 const bool useDefaultColor = (NULL == colorsBuffer);
1103 const Vector4& defaultColor = mModel->GetDefaultColor();
1105 // Create and initialize the pixel buffer.
1106 GlyphData glyphData;
1107 glyphData.verticalOffset = verticalOffset;
1108 glyphData.width = bufferWidth;
1109 glyphData.height = bufferHeight;
1110 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1111 glyphData.horizontalOffset = 0;
1113 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1114 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1115 Length hyphenIndex = 0;
1117 const Character* __restrict__ textBuffer = mModel->GetTextBuffer();
1118 float calculatedAdvance = 0.f;
1119 const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1120 const CharacterIndex* __restrict__ glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1122 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1124 // Traverses the lines of the text.
1125 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1127 const LineRun& line = *(modelLinesBuffer + lineIndex);
1129 // Sets the horizontal offset of the line.
1130 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1131 glyphData.horizontalOffset += horizontalOffset;
1133 // Increases the vertical offset with the line's ascender.
1134 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1136 // Retrieves the glyph's outline width
1137 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1139 if(style == Typesetter::STYLE_OUTLINE)
1141 glyphData.horizontalOffset -= outlineWidth;
1144 // Only need to add the vertical outline offset for the first line
1145 glyphData.verticalOffset -= outlineWidth;
1148 else if(style == Typesetter::STYLE_SHADOW)
1150 const Vector2& shadowOffset = mModel->GetShadowOffset();
1151 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1155 // Only need to add the vertical shadow offset for first line
1156 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1160 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1161 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1162 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1164 // Get the character-spacing runs.
1165 const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1167 // Aggregate underline-style-properties from mModel
1168 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1169 mModel->GetUnderlineColor(),
1170 mModel->GetUnderlineHeight(),
1171 mModel->GetDashedUnderlineGap(),
1172 mModel->GetDashedUnderlineWidth(),
1179 // Aggregate strikethrough-style-properties from mModel
1180 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1181 mModel->GetStrikethroughHeight(),
1185 // Get the underline runs.
1186 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1187 Vector<UnderlinedGlyphRun> underlineRuns;
1188 underlineRuns.Resize(numberOfUnderlineRuns);
1189 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1191 // Get the strikethrough runs.
1192 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1193 Vector<StrikethroughGlyphRun> strikethroughRuns;
1194 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1195 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1197 bool thereAreUnderlinedGlyphs = false;
1198 bool thereAreStrikethroughGlyphs = false;
1200 float currentUnderlinePosition = 0.0f;
1201 float currentUnderlineHeight = modelUnderlineProperties.height;
1202 float maxUnderlineHeight = currentUnderlineHeight;
1203 auto currentUnderlineProperties = modelUnderlineProperties;
1205 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1206 float maxStrikethroughHeight = currentStrikethroughHeight;
1207 auto currentStrikethroughProperties = modelStrikethroughProperties;
1208 float strikethroughStartingYPosition = 0.0f;
1210 FontId lastFontId = 0;
1212 float lineExtentLeft = bufferWidth;
1213 float lineExtentRight = 0.0f;
1214 float baseline = 0.0f;
1215 bool addHyphen = false;
1217 // Traverses the glyphs of the line.
1218 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1219 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1220 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1222 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1224 //To handle START case of ellipsis, the first glyph has been shifted
1225 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1226 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1228 //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.
1229 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1231 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1232 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1234 // Ignore any glyph that removed for MIDDLE ellipsis
1237 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1239 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1243 // Retrieve the glyph's info.
1244 const GlyphInfo* glyphInfo;
1246 if(addHyphen && hyphens)
1248 glyphInfo = hyphens + hyphenIndex;
1253 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1256 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1257 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1259 // Nothing to do if the glyph's width or height is zero.
1263 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1264 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1265 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1266 currentUnderlineHeight = currentUnderlineProperties.height;
1267 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1269 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1270 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1271 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1272 currentStrikethroughHeight = currentStrikethroughProperties.height;
1273 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1275 // Are we still using the same fontId as previous
1276 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1278 // We need to fetch fresh font underline metrics
1279 FontMetrics fontMetrics;
1280 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1282 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1283 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1287 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1290 if(strikethroughGlyph)
1292 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1295 // Update lastFontId because fontId is changed
1296 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1299 // Retrieves the glyph's position.
1300 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1304 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1305 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1306 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1307 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1308 position.y = -glyphInfo->yBearing;
1311 if(baseline < position.y + glyphInfo->yBearing)
1313 baseline = position.y + glyphInfo->yBearing;
1316 // Calculate the positions of leftmost and rightmost glyphs in the current line
1317 if(position.x < lineExtentLeft)
1319 lineExtentLeft = position.x;
1322 if(position.x + glyphInfo->width > lineExtentRight)
1324 lineExtentRight = position.x + glyphInfo->width;
1327 // Retrieves the glyph's color.
1328 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1331 if(style == Typesetter::STYLE_SHADOW)
1333 color = mModel->GetShadowColor();
1335 else if(style == Typesetter::STYLE_OUTLINE)
1337 color = mModel->GetOutlineColor();
1341 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1344 // Premultiply alpha
1349 // Retrieves the glyph's bitmap.
1350 glyphData.glyphBitmap.buffer = NULL;
1351 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1352 glyphData.glyphBitmap.height = glyphInfo->height;
1354 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1356 // Don't render outline for other styles
1357 outlineWidth = 0.0f;
1360 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1362 fontClient.CreateBitmap(glyphInfo->fontId,
1364 glyphInfo->isItalicRequired,
1365 glyphInfo->isBoldRequired,
1366 glyphData.glyphBitmap,
1367 static_cast<int32_t>(outlineWidth));
1370 // Sets the glyph's bitmap into the bitmap of the whole text.
1371 if(NULL != glyphData.glyphBitmap.buffer)
1373 if(style == Typesetter::STYLE_OUTLINE)
1375 // Set the position offset for the current glyph
1376 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1377 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1380 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1381 TypesetGlyph(glyphData,
1387 if(style == Typesetter::STYLE_OUTLINE)
1389 // Reset the position offset for the next glyph
1390 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1391 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1394 // free the glyphBitmap.buffer if it is owner of buffer
1395 if(glyphData.glyphBitmap.isBufferOwned)
1397 free(glyphData.glyphBitmap.buffer);
1398 glyphData.glyphBitmap.isBufferOwned = false;
1400 glyphData.glyphBitmap.buffer = NULL;
1405 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1410 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1418 // Draw the underline from the leftmost glyph to the rightmost glyph
1419 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1421 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1424 // Draw the background color from the leftmost glyph to the rightmost glyph
1425 if(style == Typesetter::STYLE_BACKGROUND)
1427 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1430 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1431 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1433 //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.
1434 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.
1435 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1438 // Increases the vertical offset with the line's descender & line spacing.
1439 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1442 return glyphData.bitmapBuffer;
1445 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)
1447 // Underline-tags (this is for Markup case)
1448 // Get the underline runs.
1449 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1450 Vector<UnderlinedGlyphRun> underlineRuns;
1451 underlineRuns.Resize(numberOfUnderlineRuns);
1452 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1454 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1455 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1456 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1457 GlyphIndex startGlyphIndex, endGlyphIndex;
1459 //The outer loop to iterate on the separated chunks of underlined glyph runs
1460 while(itGlyphRun != endItGlyphRun)
1462 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1463 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1465 // Create the image buffer for underline
1466 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1467 // Combine the two buffers
1468 // Result pixel buffer will be stored into topPixelBuffer.
1469 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1474 return topPixelBuffer;
1477 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)
1479 // strikethrough-tags (this is for Markup case)
1480 // Get the strikethrough runs.
1481 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1482 Vector<StrikethroughGlyphRun> strikethroughRuns;
1483 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1484 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1486 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1487 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1488 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1489 GlyphIndex startGlyphIndex, endGlyphIndex;
1491 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1492 while(itGlyphRun != endItGlyphRun)
1494 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1495 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1497 // Create the image buffer for strikethrough
1498 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1499 // Combine the two buffers
1500 // Result pixel buffer will be stored into topPixelBuffer.
1501 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1506 return topPixelBuffer;
1509 Typesetter::Typesetter(const ModelInterface* const model)
1510 : mModel(new ViewModel(model))
1514 Typesetter::~Typesetter()
1521 } // namespace Toolkit