2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/rendering/text-typesetter.h>
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/public-api/common/constants.h>
27 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
28 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
29 #include <dali-toolkit/internal/text/line-helper-functions.h>
30 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
31 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
32 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
33 #include <dali-toolkit/internal/text/rendering/view-model.h>
43 const float HALF(0.5f);
44 const float ONE_AND_A_HALF(1.5f);
47 * @brief Fast multiply & divide by 255. It wiil be useful when we applying alpha value in color
49 * @param x The value between [0..255]
50 * @param y The value between [0..255]
53 inline uint8_t MultiplyAndNormalizeColor(const uint8_t x, const uint8_t y) noexcept
55 const uint32_t xy = static_cast<const uint32_t>(x) * y;
56 return ((xy << 15) + (xy << 7) + xy) >> 23;
60 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
64 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
65 Vector2* position; ///< The position of the glyph.
66 TextAbstraction::FontClient::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
67 uint32_t width; ///< The bitmap's width.
68 uint32_t height; ///< The bitmap's height.
69 int32_t horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
70 int32_t verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
74 * @brief Sets the glyph's buffer into the bitmap's buffer.
76 * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
77 * @param[in] position The position of the glyph.
78 * @param[in] color The color of the glyph.
79 * @param[in] style The style of the text.
80 * @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).
82 void TypesetGlyph(GlyphData& __restrict__ data,
83 const Vector2* const __restrict__ position,
84 const Vector4* const __restrict__ color,
85 const Typesetter::Style style,
86 const Pixel::Format pixelFormat)
88 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
90 // Nothing to do if the width or height of the buffer is zero.
94 // Initial vertical / horizontal offset.
95 const int32_t yOffset = data.verticalOffset + position->y;
96 const int32_t xOffset = data.horizontalOffset + position->x;
98 // Whether the given glyph is a color one.
99 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
100 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
101 const uint32_t glyphAlphaIndex = glyphPixelSize - 1u;
103 // Determinate iterator range.
104 const int32_t lineIndexRangeMin = std::max(0, -yOffset);
105 const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
106 const int32_t indexRangeMin = std::max(0, -xOffset);
107 const int32_t indexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
109 // If current glyph don't need to be rendered, just ignore.
110 if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
115 if(Pixel::RGBA8888 == pixelFormat)
117 uint32_t* __restrict__ bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
119 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
121 // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
122 // Just overwrite transparent color and return.
123 if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
125 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
127 // We can use memset here.
128 memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
129 bitmapBuffer += data.width;
134 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
136 // Offset byte value of glyph bitmap.
137 uint32_t glyphOffet = 0u;
139 // Allocate scanline memory for glyph bitmap if we need.
140 const bool useLocalScanline = data.glyphBitmap.compressType != TextAbstraction::FontClient::GlyphBufferData::CompressType::NO_COMPRESS;
141 uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer;
143 // Precalculate input color's packed result.
144 uint32_t packedInputColor = 0u;
145 uint8_t* __restrict__ packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
147 *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
148 *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
149 *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
150 *(packedInputColorBuffer) = static_cast<uint8_t>(color->r * 255);
152 // Skip basic line of glyph.
155 for(int32_t lineIndex = 0; lineIndex < lineIndexRangeMin; ++lineIndex)
157 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
162 glyphScanline += lineIndexRangeMin * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize);
165 // Traverse the pixels of the glyph line per line.
168 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
172 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
175 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
177 const int32_t xOffsetIndex = xOffset + index;
179 // Retrieves the color from the color glyph.
180 uint32_t packedColorGlyph = *(reinterpret_cast<const uint32_t*>(glyphScanline + (index << 2)));
181 uint8_t* __restrict__ packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
183 // Update the alpha channel.
184 const uint8_t colorAlpha = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
185 *(packedColorGlyphBuffer + 3u) = colorAlpha;
187 if(Typesetter::STYLE_SHADOW == style)
189 // The shadow of color glyph needs to have the shadow color.
190 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
191 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
192 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
198 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
201 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
202 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
203 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
205 if(data.glyphBitmap.isColorBitmap)
207 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
208 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
209 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
213 // Set the color into the final pixel buffer.
214 *(bitmapBuffer + xOffsetIndex) = packedColorGlyph;
217 bitmapBuffer += data.width;
218 if(!useLocalScanline)
220 glyphScanline += data.glyphBitmap.width * glyphPixelSize;
226 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
230 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
233 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
235 // Update the alpha channel.
236 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
238 // Copy non-transparent pixels only
241 const int32_t xOffsetIndex = xOffset + index;
243 // Check alpha of overlapped pixels
244 uint32_t& currentColor = *(bitmapBuffer + xOffsetIndex);
245 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
247 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
248 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
249 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
250 // happen, for example, in the RTL text when we copy glyphs from right to left).
251 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
252 currentAlpha = std::max(currentAlpha, alpha);
253 if(currentAlpha == 255)
255 // Fast-cut to avoid float type operation.
256 currentColor = packedInputColor;
260 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
261 // The format is RGBA8888.
262 uint32_t packedColor = 0u;
263 uint8_t* __restrict__ packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
265 // Color is pre-muliplied with its alpha.
266 *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
267 *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
268 *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
269 *(packedColorBuffer) = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
271 // Set the color into the final pixel buffer.
272 currentColor = packedColor;
277 bitmapBuffer += data.width;
278 if(!useLocalScanline)
280 glyphScanline += data.glyphBitmap.width * glyphPixelSize;
292 // Below codes required only if not color glyph.
295 uint8_t* __restrict__ bitmapBuffer = data.bitmapBuffer.GetBuffer();
297 // Offset byte value of glyph bitmap.
298 uint32_t glyphOffet = 0u;
300 // Allocate scanline memory for glyph bitmap if we need.
301 const bool useLocalScanline = data.glyphBitmap.compressType != TextAbstraction::FontClient::GlyphBufferData::CompressType::NO_COMPRESS;
302 uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer;
305 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
307 // Skip basic line of glyph.
310 for(int32_t lineIndex = 0; lineIndex < lineIndexRangeMin; ++lineIndex)
312 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
317 glyphScanline += lineIndexRangeMin * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize);
320 // Traverse the pixels of the glyph line per line.
321 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
325 TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
328 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
330 const int32_t xOffsetIndex = xOffset + index;
332 // Update the alpha channel.
333 const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
335 // Copy non-transparent pixels only
338 // Check alpha of overlapped pixels
339 uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
341 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
342 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
343 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
344 // happen, for example, in the RTL text when we copy glyphs from right to left).
345 currentAlpha = std::max(currentAlpha, alpha);
349 bitmapBuffer += data.width;
350 if(!useLocalScanline)
352 glyphScanline += data.glyphBitmap.width * glyphPixelSize;
364 /// Draws the specified underline color to the buffer
366 const uint32_t bufferWidth,
367 const uint32_t bufferHeight,
368 GlyphData& glyphData,
369 const float baseline,
370 const float currentUnderlinePosition,
371 const float maxUnderlineHeight,
372 const float lineExtentLeft,
373 const float lineExtentRight,
374 const UnderlineStyleProperties& commonUnderlineProperties,
375 const UnderlineStyleProperties& currentUnderlineProperties,
378 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
379 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
380 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
381 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
383 int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
385 const uint32_t yRangeMin = underlineYOffset;
386 const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
387 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
388 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
390 // If current glyph don't need to be rendered, just ignore.
391 if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
396 // We can optimize by memset when underlineColor.a is near zero
397 uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
399 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
401 // Skip yRangeMin line.
402 bitmapBuffer += yRangeMin * glyphData.width;
404 // Note if underlineType is DASHED, we cannot setup color by memset.
405 if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
407 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
409 // We can use memset.
410 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
411 bitmapBuffer += glyphData.width;
413 if(underlineType == Text::Underline::DOUBLE)
415 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
416 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
417 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
419 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
420 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
422 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
424 // We can use memset.
425 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
426 bitmapBuffer += glyphData.width;
432 uint32_t packedUnderlineColor = 0u;
433 uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
435 // Write the color to the pixel buffer
436 *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
437 *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
438 *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
439 *(packedUnderlineColorBuffer) = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
441 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
443 if(underlineType == Text::Underline::DASHED)
445 float dashWidth = dashedUnderlineWidth;
448 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
450 if(dashGap == 0 && dashWidth > 0)
452 // Note : this is same logic as bitmap[y][x] = underlineColor;
453 *(bitmapBuffer + x) = packedUnderlineColor;
456 else if(dashGap < dashedUnderlineGap)
463 dashWidth = dashedUnderlineWidth;
470 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
472 // Note : this is same logic as bitmap[y][x] = underlineColor;
473 *(bitmapBuffer + x) = packedUnderlineColor;
476 bitmapBuffer += glyphData.width;
478 if(underlineType == Text::Underline::DOUBLE)
480 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
481 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
482 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
484 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
485 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
487 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
489 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
491 // Note : this is same logic as bitmap[y][x] = underlineColor;
492 *(bitmapBuffer + x) = packedUnderlineColor;
494 bitmapBuffer += glyphData.width;
500 /// Draws the background color to the buffer
501 void DrawBackgroundColor(
502 Vector4 backgroundColor,
503 const uint32_t bufferWidth,
504 const uint32_t bufferHeight,
505 GlyphData& glyphData,
506 const float baseline,
508 const float lineExtentLeft,
509 const float lineExtentRight)
511 const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
512 const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
513 const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
514 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
516 // If current glyph don't need to be rendered, just ignore.
517 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
522 // We can optimize by memset when backgroundColor.a is near zero
523 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
525 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
527 // Skip yRangeMin line.
528 bitmapBuffer += yRangeMin * glyphData.width;
530 if(backgroundColorAlpha == 0)
532 for(int32_t y = yRangeMin; y < yRangeMax; y++)
534 // We can use memset.
535 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
536 bitmapBuffer += glyphData.width;
541 uint32_t packedBackgroundColor = 0u;
542 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
544 // Write the color to the pixel buffer
545 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
546 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
547 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
548 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
550 for(int32_t y = yRangeMin; y < yRangeMax; y++)
552 for(int32_t x = xRangeMin; x < xRangeMax; x++)
554 // Note : this is same logic as bitmap[y][x] = backgroundColor;
555 *(bitmapBuffer + x) = packedBackgroundColor;
557 bitmapBuffer += glyphData.width;
562 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)
564 // Retrieve lines, glyphs, positions and colors from the view model.
565 const Length modelNumberOfLines = model->GetNumberOfLines();
566 const LineRun* const modelLinesBuffer = model->GetLines();
567 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
568 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
569 const Vector2* const positionBuffer = model->GetLayout();
570 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
571 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
573 const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
575 // Create and initialize the pixel buffer.
577 glyphData.verticalOffset = verticalOffset;
578 glyphData.width = bufferWidth;
579 glyphData.height = bufferHeight;
580 glyphData.bitmapBuffer = buffer;
581 glyphData.horizontalOffset = 0;
583 ColorIndex prevBackgroundColorIndex = 0;
584 ColorIndex backgroundColorIndex = 0;
586 // Traverses the lines of the text.
587 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
589 const LineRun& line = *(modelLinesBuffer + lineIndex);
591 // Sets the horizontal offset of the line.
592 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
593 glyphData.horizontalOffset += horizontalOffset;
595 // Increases the vertical offset with the line's ascender.
596 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
598 float left = bufferWidth;
600 float baseline = 0.0f;
602 // Traverses the glyphs of the line.
603 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
604 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
606 // Retrieve the glyph's info.
607 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
609 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
610 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
612 // Nothing to do if default background color, the glyph's width or height is zero.
616 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
618 if((backgroundColorIndex != prevBackgroundColorIndex) &&
619 (prevBackgroundColorIndex != 0u))
621 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
622 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
625 if(backgroundColorIndex == 0u)
627 prevBackgroundColorIndex = backgroundColorIndex;
628 //if background color is the default do nothing
632 // Retrieves the glyph's position.
633 const Vector2* const position = positionBuffer + glyphIndex;
635 if(baseline < position->y + glyphInfo->yBearing)
637 baseline = position->y + glyphInfo->yBearing;
640 // Calculate the positions of leftmost and rightmost glyphs in the current line
641 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
643 left = position->x - glyphInfo->xBearing;
646 if(position->x + glyphInfo->width > right)
648 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
651 prevBackgroundColorIndex = backgroundColorIndex;
654 //draw last background at line end if not default
655 if(backgroundColorIndex != 0u)
657 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
658 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
661 // Increases the vertical offset with the line's descender.
662 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
665 return glyphData.bitmapBuffer;
668 /// Draws the specified strikethrough color to the buffer
669 void DrawStrikethrough(const uint32_t bufferWidth,
670 const uint32_t bufferHeight,
671 GlyphData& glyphData,
672 const float baseline,
673 const float strikethroughStartingYPosition,
674 const float maxStrikethroughHeight,
675 const float lineExtentLeft,
676 const float lineExtentRight,
677 const StrikethroughStyleProperties& commonStrikethroughProperties,
678 const StrikethroughStyleProperties& currentStrikethroughProperties,
681 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
683 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
684 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
685 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
686 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
688 // If current glyph don't need to be rendered, just ignore.
689 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
694 // We can optimize by memset when strikethroughColor.a is near zero
695 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
697 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
699 // Skip yRangeMin line.
700 bitmapBuffer += yRangeMin * glyphData.width;
702 if(strikethroughColorAlpha == 0)
704 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
706 // We can use memset.
707 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
708 bitmapBuffer += glyphData.width;
713 uint32_t packedStrikethroughColor = 0u;
714 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
716 // Write the color to the pixel buffer
717 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
718 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
719 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
720 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
722 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
724 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
726 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
727 *(bitmapBuffer + x) = packedStrikethroughColor;
729 bitmapBuffer += glyphData.width;
735 * @brief Create an initialized image buffer filled with transparent color.
737 * Creates the pixel data used to generate the final image with the given size.
739 * @param[in] bufferWidth The width of the image buffer.
740 * @param[in] bufferHeight The height of the image buffer.
741 * @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).
743 * @return An image buffer.
745 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat)
747 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
749 if(Pixel::RGBA8888 == pixelFormat)
751 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
752 const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
753 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
757 memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
764 * @brief Combine the two RGBA image buffers together.
766 * The top layer buffer will blend over the bottom layer buffer:
767 * - If the pixel is not fully opaque from either buffer, it will be blended with
768 * the pixel from the other buffer and copied to the combined buffer.
769 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
770 * buffer will be copied to the combined buffer.
772 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
773 * We can determine which pixelBuffer's memory is destination
775 * @param[in, out] topPixelBuffer The top layer buffer.
776 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
777 * @param[in] bufferWidth The width of the image buffer.
778 * @param[in] bufferHeight The height of the image buffer.
779 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
780 * False if we store the combined image buffer result into bottomPixelBuffer.
783 void CombineImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, bool storeResultIntoTop)
785 // Assume that we always combine two RGBA images
786 // Jump with 4bytes for optimize runtime.
787 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
788 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
790 if(topBuffer == NULL && bottomBuffer == NULL)
792 // Nothing to do if both buffers are empty.
796 if(topBuffer == NULL)
798 // Nothing to do if topBuffer is empty.
799 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
800 if(storeResultIntoTop)
802 topPixelBuffer = bottomPixelBuffer;
807 if(bottomBuffer == NULL)
809 // Nothing to do if bottomBuffer is empty.
810 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
811 if(!storeResultIntoTop)
813 bottomPixelBuffer = topPixelBuffer;
818 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
820 uint32_t* __restrict__ combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
821 uint8_t* __restrict__ topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
823 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
825 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
826 // Otherwise, copy pixel from topBuffer to combinedBuffer.
827 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
829 uint8_t topAlpha = *topAlphaBufferPointer;
833 // Copy the pixel from bottomBuffer to combinedBuffer
834 if(storeResultIntoTop)
836 *(combinedBuffer) = *(bottomBuffer);
839 else if(topAlpha == 255)
841 // Copy the pixel from topBuffer to combinedBuffer
842 if(!storeResultIntoTop)
844 *(combinedBuffer) = *(topBuffer);
849 // At least one pixel is not fully opaque
850 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
851 uint32_t blendedBottomBufferColor = *(bottomBuffer);
852 uint8_t* __restrict__ blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
854 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
855 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
856 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
857 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
859 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
862 // Increase each buffer's pointer.
866 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
872 TypesetterPtr Typesetter::New(const ModelInterface* const model)
874 return TypesetterPtr(new Typesetter(model));
877 ViewModel* Typesetter::GetViewModel()
882 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
884 // @todo. This initial implementation for a TextLabel has only one visible page.
886 // Elides the text if needed.
887 mModel->ElideGlyphs();
889 // Retrieves the layout size.
890 const Size& layoutSize = mModel->GetLayoutSize();
892 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
894 // Set the offset for the horizontal alignment according to the text direction and outline width.
897 switch(mModel->GetHorizontalAlignment())
899 case HorizontalAlignment::BEGIN:
904 case HorizontalAlignment::CENTER:
906 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
909 case HorizontalAlignment::END:
911 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
916 // Set the offset for the vertical alignment.
919 switch(mModel->GetVerticalAlignment())
921 case VerticalAlignment::TOP:
926 case VerticalAlignment::CENTER:
928 penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
929 penY = penY < 0.f ? 0.f : penY;
932 case VerticalAlignment::BOTTOM:
934 penY = static_cast<int32_t>(size.height - layoutSize.height);
939 // Generate the image buffers of the text for each different style first,
940 // then combine all of them together as one final image buffer. We try to
941 // do all of these in CPU only, so that once the final texture is generated,
942 // no calculation is needed in GPU during each frame.
944 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
945 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
947 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
948 const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
950 //Elided text in ellipsis at START could start on index greater than 0
951 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
952 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
954 Devel::PixelBuffer imageBuffer;
956 if(RENDER_MASK == behaviour)
958 // Generate the image buffer as an alpha mask for color glyphs.
959 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
961 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
963 // Generate an empty image buffer so that it can been combined with the image buffers for styles
964 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
965 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
969 // Generate the image buffer for the text with no style.
970 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
973 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
975 // Generate the outline if enabled
976 const uint16_t outlineWidth = mModel->GetOutlineWidth();
977 if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
979 // Create the image buffer for outline
980 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
982 // Combine the two buffers
983 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
986 // @todo. Support shadow and underline for partial text later on.
988 // Generate the shadow if enabled
989 const Vector2& shadowOffset = mModel->GetShadowOffset();
990 if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
992 // Create the image buffer for shadow
993 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
995 // Check whether it will be a soft shadow
996 const float& blurRadius = mModel->GetShadowBlurRadius();
998 if(blurRadius > Math::MACHINE_EPSILON_1)
1000 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
1003 // Combine the two buffers
1004 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
1007 // Generate the underline if enabled
1008 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1009 if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
1011 // Create the image buffer for underline
1012 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1014 // Combine the two buffers
1015 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
1018 // Generate the background if enabled
1019 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
1020 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
1021 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
1023 Devel::PixelBuffer backgroundImageBuffer;
1025 if(backgroundEnabled)
1027 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1031 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1034 if(backgroundMarkupSet)
1036 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
1039 // Combine the two buffers
1040 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1043 // Generate the strikethrough if enabled
1044 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1045 if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
1047 // Create the image buffer for strikethrough
1048 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
1050 // Combine the two buffers
1051 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
1056 imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1059 // Create the final PixelData for the combined image buffer
1060 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
1065 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)
1067 // Retrieve lines, glyphs, positions and colors from the view model.
1068 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1069 const LineRun* const __restrict__ modelLinesBuffer = mModel->GetLines();
1070 const GlyphInfo* const __restrict__ glyphsBuffer = mModel->GetGlyphs();
1071 const Vector2* const __restrict__ positionBuffer = mModel->GetLayout();
1072 const Vector4* const __restrict__ colorsBuffer = mModel->GetColors();
1073 const ColorIndex* const __restrict__ colorIndexBuffer = mModel->GetColorIndices();
1074 const GlyphInfo* __restrict__ hyphens = mModel->GetHyphens();
1075 const Length* __restrict__ hyphenIndices = mModel->GetHyphenIndices();
1076 const Length hyphensCount = mModel->GetHyphensCount();
1078 // Elided text info. Indices according to elided text and Ellipsis position.
1079 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1080 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1081 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1082 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1083 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1085 // Whether to use the default color.
1086 const bool useDefaultColor = (NULL == colorsBuffer);
1087 const Vector4& defaultColor = mModel->GetDefaultColor();
1089 // Create and initialize the pixel buffer.
1090 GlyphData glyphData;
1091 glyphData.verticalOffset = verticalOffset;
1092 glyphData.width = bufferWidth;
1093 glyphData.height = bufferHeight;
1094 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1095 glyphData.horizontalOffset = 0;
1097 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1098 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1099 Length hyphenIndex = 0;
1101 const Character* __restrict__ textBuffer = mModel->GetTextBuffer();
1102 float calculatedAdvance = 0.f;
1103 const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1104 const CharacterIndex* __restrict__ glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1106 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1108 // Traverses the lines of the text.
1109 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1111 const LineRun& line = *(modelLinesBuffer + lineIndex);
1113 // Sets the horizontal offset of the line.
1114 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1115 glyphData.horizontalOffset += horizontalOffset;
1117 // Increases the vertical offset with the line's ascender.
1118 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1120 // Retrieves the glyph's outline width
1121 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1123 if(style == Typesetter::STYLE_OUTLINE)
1125 glyphData.horizontalOffset -= outlineWidth;
1128 // Only need to add the vertical outline offset for the first line
1129 glyphData.verticalOffset -= outlineWidth;
1132 else if(style == Typesetter::STYLE_SHADOW)
1134 const Vector2& shadowOffset = mModel->GetShadowOffset();
1135 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1139 // Only need to add the vertical shadow offset for first line
1140 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1144 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1145 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1146 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1148 // Get the character-spacing runs.
1149 const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1151 // Aggregate underline-style-properties from mModel
1152 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1153 mModel->GetUnderlineColor(),
1154 mModel->GetUnderlineHeight(),
1155 mModel->GetDashedUnderlineGap(),
1156 mModel->GetDashedUnderlineWidth(),
1163 // Aggregate strikethrough-style-properties from mModel
1164 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1165 mModel->GetStrikethroughHeight(),
1169 // Get the underline runs.
1170 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1171 Vector<UnderlinedGlyphRun> underlineRuns;
1172 underlineRuns.Resize(numberOfUnderlineRuns);
1173 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1175 // Get the strikethrough runs.
1176 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1177 Vector<StrikethroughGlyphRun> strikethroughRuns;
1178 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1179 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1181 bool thereAreUnderlinedGlyphs = false;
1182 bool thereAreStrikethroughGlyphs = false;
1184 float currentUnderlinePosition = 0.0f;
1185 float currentUnderlineHeight = modelUnderlineProperties.height;
1186 float maxUnderlineHeight = currentUnderlineHeight;
1187 auto currentUnderlineProperties = modelUnderlineProperties;
1189 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1190 float maxStrikethroughHeight = currentStrikethroughHeight;
1191 auto currentStrikethroughProperties = modelStrikethroughProperties;
1192 float strikethroughStartingYPosition = 0.0f;
1194 FontId lastFontId = 0;
1196 float lineExtentLeft = bufferWidth;
1197 float lineExtentRight = 0.0f;
1198 float baseline = 0.0f;
1199 bool addHyphen = false;
1201 // Traverses the glyphs of the line.
1202 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1203 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1204 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1206 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1208 //To handle START case of ellipsis, the first glyph has been shifted
1209 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1210 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1212 //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.
1213 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1215 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1216 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1218 // Ignore any glyph that removed for MIDDLE ellipsis
1221 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1223 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1227 // Retrieve the glyph's info.
1228 const GlyphInfo* glyphInfo;
1230 if(addHyphen && hyphens)
1232 glyphInfo = hyphens + hyphenIndex;
1237 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1240 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1241 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1243 // Nothing to do if the glyph's width or height is zero.
1247 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1248 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1249 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1250 currentUnderlineHeight = currentUnderlineProperties.height;
1251 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1253 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1254 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1255 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1256 currentStrikethroughHeight = currentStrikethroughProperties.height;
1257 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1259 // Are we still using the same fontId as previous
1260 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1262 // We need to fetch fresh font underline metrics
1263 FontMetrics fontMetrics;
1264 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1266 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1267 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1271 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1274 if(strikethroughGlyph)
1276 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1279 // Update lastFontId because fontId is changed
1280 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1283 // Retrieves the glyph's position.
1284 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1288 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1289 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1290 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1291 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1292 position.y = -glyphInfo->yBearing;
1295 if(baseline < position.y + glyphInfo->yBearing)
1297 baseline = position.y + glyphInfo->yBearing;
1300 // Calculate the positions of leftmost and rightmost glyphs in the current line
1301 if(position.x < lineExtentLeft)
1303 lineExtentLeft = position.x;
1306 if(position.x + glyphInfo->width > lineExtentRight)
1308 lineExtentRight = position.x + glyphInfo->width;
1311 // Retrieves the glyph's color.
1312 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1315 if(style == Typesetter::STYLE_SHADOW)
1317 color = mModel->GetShadowColor();
1319 else if(style == Typesetter::STYLE_OUTLINE)
1321 color = mModel->GetOutlineColor();
1325 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1328 // Premultiply alpha
1333 // Retrieves the glyph's bitmap.
1334 glyphData.glyphBitmap.buffer = NULL;
1335 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1336 glyphData.glyphBitmap.height = glyphInfo->height;
1338 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1340 // Don't render outline for other styles
1341 outlineWidth = 0.0f;
1344 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1346 fontClient.CreateBitmap(glyphInfo->fontId,
1348 glyphInfo->isItalicRequired,
1349 glyphInfo->isBoldRequired,
1350 glyphData.glyphBitmap,
1351 static_cast<int32_t>(outlineWidth));
1354 // Sets the glyph's bitmap into the bitmap of the whole text.
1355 if(NULL != glyphData.glyphBitmap.buffer)
1357 if(style == Typesetter::STYLE_OUTLINE)
1359 // Set the position offset for the current glyph
1360 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1361 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1364 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1365 TypesetGlyph(glyphData,
1371 if(style == Typesetter::STYLE_OUTLINE)
1373 // Reset the position offset for the next glyph
1374 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1375 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1378 // free the glyphBitmap.buffer if it is owner of buffer
1379 if(glyphData.glyphBitmap.isBufferOwned)
1381 free(glyphData.glyphBitmap.buffer);
1382 glyphData.glyphBitmap.isBufferOwned = false;
1384 glyphData.glyphBitmap.buffer = NULL;
1389 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1394 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1402 // Draw the underline from the leftmost glyph to the rightmost glyph
1403 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1405 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1408 // Draw the background color from the leftmost glyph to the rightmost glyph
1409 if(style == Typesetter::STYLE_BACKGROUND)
1411 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1414 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1415 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1417 //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.
1418 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.
1419 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1422 // Increases the vertical offset with the line's descender & line spacing.
1423 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1426 return glyphData.bitmapBuffer;
1429 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)
1431 // Underline-tags (this is for Markup case)
1432 // Get the underline runs.
1433 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1434 Vector<UnderlinedGlyphRun> underlineRuns;
1435 underlineRuns.Resize(numberOfUnderlineRuns);
1436 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1438 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1439 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1440 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1441 GlyphIndex startGlyphIndex, endGlyphIndex;
1443 //The outer loop to iterate on the separated chunks of underlined glyph runs
1444 while(itGlyphRun != endItGlyphRun)
1446 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1447 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1449 // Create the image buffer for underline
1450 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1451 // Combine the two buffers
1452 // Result pixel buffer will be stored into topPixelBuffer.
1453 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1458 return topPixelBuffer;
1461 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)
1463 // strikethrough-tags (this is for Markup case)
1464 // Get the strikethrough runs.
1465 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1466 Vector<StrikethroughGlyphRun> strikethroughRuns;
1467 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1468 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1470 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1471 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1472 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1473 GlyphIndex startGlyphIndex, endGlyphIndex;
1475 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1476 while(itGlyphRun != endItGlyphRun)
1478 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1479 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1481 // Create the image buffer for strikethrough
1482 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1483 // Combine the two buffers
1484 // Result pixel buffer will be stored into topPixelBuffer.
1485 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1490 return topPixelBuffer;
1493 Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(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)
1495 // Apply the markup-Processor if enabled
1496 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1497 if(markupProcessorEnabled)
1499 topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1501 topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1504 return topPixelBuffer;
1507 Typesetter::Typesetter(const ModelInterface* const model)
1508 : mModel(new ViewModel(model))
1512 Typesetter::~Typesetter()
1519 } // namespace Toolkit