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/rendering/styles/character-spacing-helper-functions.h>
30 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
31 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
32 #include <dali-toolkit/internal/text/rendering/view-model.h>
42 const float HALF(0.5f);
43 const float ONE_AND_A_HALF(1.5f);
46 * @brief Fast multiply & divide by 255. It wiil be useful when we applying alpha value in color
48 * @param x The value between [0..255]
49 * @param y The value between [0..255]
52 inline uint8_t MultiplyAndNormalizeColor(const uint8_t& x, const uint8_t& y) noexcept
54 const uint32_t xy = static_cast<const uint32_t>(x) * y;
55 return ((xy << 15) + (xy << 7) + xy) >> 23;
59 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
63 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
64 Vector2* position; ///< The position of the glyph.
65 TextAbstraction::FontClient::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
66 uint32_t width; ///< The bitmap's width.
67 uint32_t height; ///< The bitmap's height.
68 int32_t horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
69 int32_t verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
73 * @brief Sets the glyph's buffer into the bitmap's buffer.
75 * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
76 * @param[in] position The position of the glyph.
77 * @param[in] color The color of the glyph.
78 * @param[in] style The style of the text.
79 * @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).
81 void TypesetGlyph(GlyphData& data,
82 const Vector2* const position,
83 const Vector4* const color,
84 Typesetter::Style style,
85 Pixel::Format pixelFormat)
87 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
89 // Nothing to do if the width or height of the buffer is zero.
93 // Initial vertical / horizontal offset.
94 const int32_t yOffset = data.verticalOffset + position->y;
95 const int32_t xOffset = data.horizontalOffset + position->x;
97 // Whether the given glyph is a color one.
98 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
99 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
100 const uint32_t alphaIndex = glyphPixelSize - 1u;
102 // Determinate iterator range.
103 const int32_t lineIndexRangeMin = std::max(0, -yOffset);
104 const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
105 const int32_t indexRangeMin = std::max(0, -xOffset);
106 const int32_t indexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
108 // If current glyph don't need to be rendered, just ignore.
109 if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
114 if(Pixel::RGBA8888 == pixelFormat)
116 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
118 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
120 // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
121 // Just overwrite transparent color and return.
122 if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
124 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
126 const int32_t yOffsetIndex = yOffset + lineIndex;
127 const int32_t verticalOffset = yOffsetIndex * data.width;
129 // We can use memset here.
130 memset(bitmapBuffer + verticalOffset + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
135 // Pointer to the color glyph if there is one.
136 const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>(data.glyphBitmap.buffer) : NULL;
138 // Precalculate input color's packed result.
139 uint32_t packedInputColor = 0u;
140 uint8_t* packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
142 *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
143 *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
144 *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
145 *(packedInputColorBuffer) = static_cast<uint8_t>(color->r * 255);
147 // Traverse the pixels of the glyph line per line.
148 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
150 const int32_t yOffsetIndex = yOffset + lineIndex;
152 const int32_t verticalOffset = yOffsetIndex * data.width;
153 const int32_t glyphBufferOffset = lineIndex * static_cast<int32_t>(data.glyphBitmap.width);
154 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
156 const int32_t xOffsetIndex = xOffset + index;
160 // Retrieves the color from the color glyph.
161 uint32_t packedColorGlyph = *(colorGlyphBuffer + glyphBufferOffset + index);
162 uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
164 // Update the alpha channel.
165 const uint8_t colorAlpha = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
166 *(packedColorGlyphBuffer + 3u) = colorAlpha;
168 if(Typesetter::STYLE_SHADOW == style)
170 // The shadow of color glyph needs to have the shadow color.
171 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
172 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
173 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
179 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
182 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
183 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
184 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
186 if(data.glyphBitmap.isColorBitmap)
188 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
189 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
190 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
194 // Set the color into the final pixel buffer.
195 *(bitmapBuffer + verticalOffset + xOffsetIndex) = packedColorGlyph;
199 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
200 // The format is RGBA8888.
201 uint32_t packedColor = 0u;
202 uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
204 // Update the alpha channel.
205 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
207 // Copy non-transparent pixels only
210 // Check alpha of overlapped pixels
211 uint32_t& currentColor = *(bitmapBuffer + verticalOffset + xOffsetIndex);
212 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
214 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
215 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
216 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
217 // happen, for example, in the RTL text when we copy glyphs from right to left).
218 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
219 currentAlpha = std::max(currentAlpha, alpha);
220 if(currentAlpha == 255)
222 // Fast-cut to avoid float type operation.
223 currentColor = packedInputColor;
227 // Color is pre-muliplied with its alpha.
228 *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
229 *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
230 *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
231 *(packedColorBuffer) = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
233 // Set the color into the final pixel buffer.
234 currentColor = packedColor;
243 // Below codes required only if not color glyph.
246 uint8_t* bitmapBuffer = reinterpret_cast<uint8_t*>(data.bitmapBuffer.GetBuffer());
248 // Traverse the pixels of the glyph line per line.
249 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
251 const int32_t yOffsetIndex = yOffset + lineIndex;
253 const int32_t verticalOffset = yOffsetIndex * data.width;
254 const int32_t glyphBufferOffset = lineIndex * static_cast<int32_t>(data.glyphBitmap.width);
255 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
257 const int32_t xOffsetIndex = xOffset + index;
259 // Update the alpha channel.
260 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
262 // Copy non-transparent pixels only
265 // Check alpha of overlapped pixels
266 uint8_t& currentAlpha = *(bitmapBuffer + verticalOffset + xOffsetIndex);
268 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
269 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
270 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
271 // happen, for example, in the RTL text when we copy glyphs from right to left).
272 currentAlpha = std::max(currentAlpha, alpha);
280 /// Draws the specified underline color to the buffer
282 const uint32_t& bufferWidth,
283 const uint32_t& bufferHeight,
284 GlyphData& glyphData,
285 const float& baseline,
286 const float& currentUnderlinePosition,
287 const float& maxUnderlineHeight,
288 const float& lineExtentLeft,
289 const float& lineExtentRight,
290 const UnderlineStyleProperties& commonUnderlineProperties,
291 const UnderlineStyleProperties& currentUnderlineProperties,
294 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
295 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
296 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
297 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
299 int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
301 const uint32_t yRangeMin = underlineYOffset;
302 const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
303 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
304 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
306 // If current glyph don't need to be rendered, just ignore.
307 if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
312 // We can optimize by memset when underlineColor.a is near zero
313 uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
315 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
317 // Skip yRangeMin line.
318 bitmapBuffer += yRangeMin * glyphData.width;
320 // Note if underlineType is DASHED, we cannot setup color by memset.
321 if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
323 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
325 // We can use memset.
326 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
327 bitmapBuffer += glyphData.width;
329 if(underlineType == Text::Underline::DOUBLE)
331 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
332 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
333 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
335 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
336 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
338 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
340 // We can use memset.
341 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
342 bitmapBuffer += glyphData.width;
348 uint32_t packedUnderlineColor = 0u;
349 uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
351 // Write the color to the pixel buffer
352 *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
353 *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
354 *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
355 *(packedUnderlineColorBuffer) = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
357 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
359 if(underlineType == Text::Underline::DASHED)
361 float dashWidth = dashedUnderlineWidth;
364 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
366 if(dashGap == 0 && dashWidth > 0)
368 // Note : this is same logic as bitmap[y][x] = underlineColor;
369 *(bitmapBuffer + x) = packedUnderlineColor;
372 else if(dashGap < dashedUnderlineGap)
379 dashWidth = dashedUnderlineWidth;
386 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
388 // Note : this is same logic as bitmap[y][x] = underlineColor;
389 *(bitmapBuffer + x) = packedUnderlineColor;
392 bitmapBuffer += glyphData.width;
394 if(underlineType == Text::Underline::DOUBLE)
396 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
397 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
398 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
400 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
401 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
403 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
405 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
407 // Note : this is same logic as bitmap[y][x] = underlineColor;
408 *(bitmapBuffer + x) = packedUnderlineColor;
410 bitmapBuffer += glyphData.width;
416 /// Draws the background color to the buffer
417 void DrawBackgroundColor(
418 Vector4 backgroundColor,
419 const uint32_t& bufferWidth,
420 const uint32_t& bufferHeight,
421 GlyphData& glyphData,
422 const float& baseline,
424 const float& lineExtentLeft,
425 const float& lineExtentRight)
427 const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
428 const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
429 const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
430 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
432 // If current glyph don't need to be rendered, just ignore.
433 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
438 // We can optimize by memset when backgroundColor.a is near zero
439 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
441 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
443 // Skip yRangeMin line.
444 bitmapBuffer += yRangeMin * glyphData.width;
446 if(backgroundColorAlpha == 0)
448 for(int32_t y = yRangeMin; y < yRangeMax; y++)
450 // We can use memset.
451 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
452 bitmapBuffer += glyphData.width;
457 uint32_t packedBackgroundColor = 0u;
458 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
460 // Write the color to the pixel buffer
461 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
462 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
463 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
464 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
466 for(int32_t y = yRangeMin; y < yRangeMax; y++)
468 for(int32_t x = xRangeMin; x < xRangeMax; x++)
470 // Note : this is same logic as bitmap[y][x] = backgroundColor;
471 *(bitmapBuffer + x) = packedBackgroundColor;
473 bitmapBuffer += glyphData.width;
478 Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuffer& buffer, const uint32_t& bufferWidth, const uint32_t& bufferHeight, bool ignoreHorizontalAlignment, int32_t horizontalOffset, int32_t verticalOffset)
480 // Retrieve lines, glyphs, positions and colors from the view model.
481 const Length modelNumberOfLines = model->GetNumberOfLines();
482 const LineRun* const modelLinesBuffer = model->GetLines();
483 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
484 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
485 const Vector2* const positionBuffer = model->GetLayout();
486 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
487 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
489 // Create and initialize the pixel buffer.
491 glyphData.verticalOffset = verticalOffset;
492 glyphData.width = bufferWidth;
493 glyphData.height = bufferHeight;
494 glyphData.bitmapBuffer = buffer;
495 glyphData.horizontalOffset = 0;
497 ColorIndex prevBackgroundColorIndex = 0;
498 ColorIndex backgroundColorIndex = 0;
500 // Traverses the lines of the text.
501 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
503 const LineRun& line = *(modelLinesBuffer + lineIndex);
505 // Sets the horizontal offset of the line.
506 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
507 glyphData.horizontalOffset += horizontalOffset;
509 // Increases the vertical offset with the line's ascender.
510 glyphData.verticalOffset += static_cast<int32_t>(line.ascender);
512 // Include line spacing after first line
515 glyphData.verticalOffset += static_cast<int32_t>(line.lineSpacing);
518 float left = bufferWidth;
520 float baseline = 0.0f;
522 // Traverses the glyphs of the line.
523 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
524 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
526 // Retrieve the glyph's info.
527 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
529 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
530 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
532 // Nothing to do if default background color, the glyph's width or height is zero.
536 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
538 if((backgroundColorIndex != prevBackgroundColorIndex) &&
539 (prevBackgroundColorIndex != 0u))
541 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
542 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
545 if(backgroundColorIndex == 0u)
547 prevBackgroundColorIndex = backgroundColorIndex;
548 //if background color is the default do nothing
552 // Retrieves the glyph's position.
553 const Vector2* const position = positionBuffer + glyphIndex;
555 if(baseline < position->y + glyphInfo->yBearing)
557 baseline = position->y + glyphInfo->yBearing;
560 // Calculate the positions of leftmost and rightmost glyphs in the current line
561 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
563 left = position->x - glyphInfo->xBearing;
566 if(position->x + glyphInfo->width > right)
568 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
571 prevBackgroundColorIndex = backgroundColorIndex;
574 //draw last background at line end if not default
575 if(backgroundColorIndex != 0u)
577 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
578 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
581 // Increases the vertical offset with the line's descender.
582 glyphData.verticalOffset += static_cast<int32_t>(-line.descender);
585 return glyphData.bitmapBuffer;
588 /// Draws the specified strikethrough color to the buffer
589 void DrawStrikethrough(const uint32_t& bufferWidth,
590 const uint32_t& bufferHeight,
591 GlyphData& glyphData,
592 const float& baseline,
593 const float& strikethroughStartingYPosition,
594 const float& maxStrikethroughHeight,
595 const float& lineExtentLeft,
596 const float& lineExtentRight,
597 const StrikethroughStyleProperties& commonStrikethroughProperties,
598 const StrikethroughStyleProperties& currentStrikethroughProperties,
601 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
603 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
604 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
605 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
606 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
608 // If current glyph don't need to be rendered, just ignore.
609 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
614 // We can optimize by memset when strikethroughColor.a is near zero
615 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
617 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
619 // Skip yRangeMin line.
620 bitmapBuffer += yRangeMin * glyphData.width;
622 if(strikethroughColorAlpha == 0)
624 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
626 // We can use memset.
627 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
628 bitmapBuffer += glyphData.width;
633 uint32_t packedStrikethroughColor = 0u;
634 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
636 // Write the color to the pixel buffer
637 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
638 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
639 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
640 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
642 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
644 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
646 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
647 *(bitmapBuffer + x) = packedStrikethroughColor;
649 bitmapBuffer += glyphData.width;
655 * @brief Create an initialized image buffer filled with transparent color.
657 * Creates the pixel data used to generate the final image with the given size.
659 * @param[in] bufferWidth The width of the image buffer.
660 * @param[in] bufferHeight The height of the image buffer.
661 * @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).
663 * @return An image buffer.
665 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t& bufferWidth, const uint32_t& bufferHeight, const Pixel::Format& pixelFormat)
667 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
669 if(Pixel::RGBA8888 == pixelFormat)
671 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
672 const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
673 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
677 memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
684 * @brief Combine the two RGBA image buffers together.
686 * The top layer buffer will blend over the bottom layer buffer:
687 * - If the pixel is not fully opaque from either buffer, it will be blended with
688 * the pixel from the other buffer and copied to the combined buffer.
689 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
690 * buffer will be copied to the combined buffer.
692 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
693 * We can determine which pixelBuffer's memory is destination
695 * @param[in, out] topPixelBuffer The top layer buffer.
696 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
697 * @param[in] bufferWidth The width of the image buffer.
698 * @param[in] bufferHeight The height of the image buffer.
699 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
700 * False if we store the combined image buffer result into bottomPixelBuffer.
703 void CombineImageBuffer(Devel::PixelBuffer& topPixelBuffer, Devel::PixelBuffer& bottomPixelBuffer, const uint32_t& bufferWidth, const uint32_t& bufferHeight, bool storeResultIntoTop)
705 // Assume that we always combine two RGBA images
706 // Jump with 4bytes for optimize runtime.
707 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
708 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
710 if(topBuffer == NULL && bottomBuffer == NULL)
712 // Nothing to do if both buffers are empty.
716 if(topBuffer == NULL)
718 // Nothing to do if topBuffer is empty.
719 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
720 if(storeResultIntoTop)
722 topPixelBuffer = bottomPixelBuffer;
727 if(bottomBuffer == NULL)
729 // Nothing to do if bottomBuffer is empty.
730 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
731 if(!storeResultIntoTop)
733 bottomPixelBuffer = topPixelBuffer;
738 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
740 uint32_t* combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
741 uint8_t* topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
743 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
745 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
746 // Otherwise, copy pixel from topBuffer to combinedBuffer.
747 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
749 uint8_t topAlpha = *topAlphaBufferPointer;
753 // Copy the pixel from bottomBuffer to combinedBuffer
754 if(storeResultIntoTop)
756 *(combinedBuffer) = *(bottomBuffer);
759 else if(topAlpha == 255)
761 // Copy the pixel from topBuffer to combinedBuffer
762 if(!storeResultIntoTop)
764 *(combinedBuffer) = *(topBuffer);
769 // At least one pixel is not fully opaque
770 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
771 uint32_t blendedBottomBufferColor = *(bottomBuffer);
772 uint8_t* blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
774 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
775 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
776 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
777 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
779 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
782 // Increase each buffer's pointer.
786 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
792 TypesetterPtr Typesetter::New(const ModelInterface* const model)
794 return TypesetterPtr(new Typesetter(model));
797 ViewModel* Typesetter::GetViewModel()
802 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
804 // @todo. This initial implementation for a TextLabel has only one visible page.
806 // Elides the text if needed.
807 mModel->ElideGlyphs();
809 // Retrieves the layout size.
810 const Size& layoutSize = mModel->GetLayoutSize();
812 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
814 // Set the offset for the horizontal alignment according to the text direction and outline width.
817 switch(mModel->GetHorizontalAlignment())
819 case HorizontalAlignment::BEGIN:
824 case HorizontalAlignment::CENTER:
826 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
829 case HorizontalAlignment::END:
831 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
836 // Set the offset for the vertical alignment.
839 switch(mModel->GetVerticalAlignment())
841 case VerticalAlignment::TOP:
846 case VerticalAlignment::CENTER:
848 penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
849 penY = penY < 0.f ? 0.f : penY;
852 case VerticalAlignment::BOTTOM:
854 penY = static_cast<int32_t>(size.height - layoutSize.height);
859 // Calculate vertical line alignment
860 switch(mModel->GetVerticalLineAlignment())
862 case DevelText::VerticalLineAlignment::TOP:
866 case DevelText::VerticalLineAlignment::MIDDLE:
868 const auto& line = *mModel->GetLines();
869 penY -= line.descender;
870 penY += static_cast<int32_t>(line.lineSpacing * 0.5f + line.descender);
873 case DevelText::VerticalLineAlignment::BOTTOM:
875 const auto& line = *mModel->GetLines();
876 const auto lineHeight = line.ascender + (-line.descender) + line.lineSpacing;
877 penY += static_cast<int32_t>(lineHeight - (line.ascender - line.descender));
882 // Generate the image buffers of the text for each different style first,
883 // then combine all of them together as one final image buffer. We try to
884 // do all of these in CPU only, so that once the final texture is generated,
885 // no calculation is needed in GPU during each frame.
887 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
888 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
890 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
891 const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
893 //Elided text in ellipsis at START could start on index greater than 0
894 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
895 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
897 Devel::PixelBuffer imageBuffer;
899 if(RENDER_MASK == behaviour)
901 // Generate the image buffer as an alpha mask for color glyphs.
902 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
904 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
906 // Generate an empty image buffer so that it can been combined with the image buffers for styles
907 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
908 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
912 // Generate the image buffer for the text with no style.
913 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
916 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
918 // Generate the outline if enabled
919 const uint16_t outlineWidth = mModel->GetOutlineWidth();
920 if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
922 // Create the image buffer for outline
923 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
925 // Combine the two buffers
926 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
929 // @todo. Support shadow and underline for partial text later on.
931 // Generate the shadow if enabled
932 const Vector2& shadowOffset = mModel->GetShadowOffset();
933 if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
935 // Create the image buffer for shadow
936 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
938 // Check whether it will be a soft shadow
939 const float& blurRadius = mModel->GetShadowBlurRadius();
941 if(blurRadius > Math::MACHINE_EPSILON_1)
943 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
946 // Combine the two buffers
947 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
950 // Generate the underline if enabled
951 const bool underlineEnabled = mModel->IsUnderlineEnabled();
952 if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
954 // Create the image buffer for underline
955 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
957 // Combine the two buffers
958 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
961 // Generate the background if enabled
962 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
963 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
964 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
966 Devel::PixelBuffer backgroundImageBuffer;
968 if(backgroundEnabled)
970 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
974 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
977 if(backgroundMarkupSet)
979 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
982 // Combine the two buffers
983 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
986 // Generate the strikethrough if enabled
987 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
988 if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
990 // Create the image buffer for strikethrough
991 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
993 // Combine the two buffers
994 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
999 imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1002 // Create the final PixelData for the combined image buffer
1003 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
1008 Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t& bufferWidth, const uint32_t& bufferHeight, Typesetter::Style style, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, const int32_t& horizontalOffset, const int32_t& verticalOffset, GlyphIndex fromGlyphIndex, GlyphIndex toGlyphIndex)
1010 // Retrieve lines, glyphs, positions and colors from the view model.
1011 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1012 const LineRun* const modelLinesBuffer = mModel->GetLines();
1013 const GlyphInfo* const glyphsBuffer = mModel->GetGlyphs();
1014 const Vector2* const positionBuffer = mModel->GetLayout();
1015 const Vector4* const colorsBuffer = mModel->GetColors();
1016 const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
1017 const GlyphInfo* hyphens = mModel->GetHyphens();
1018 const Length* hyphenIndices = mModel->GetHyphenIndices();
1019 const Length hyphensCount = mModel->GetHyphensCount();
1021 // Elided text info. Indices according to elided text and Ellipsis position.
1022 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1023 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1024 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1025 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1026 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1028 // Whether to use the default color.
1029 const bool useDefaultColor = (NULL == colorsBuffer);
1030 const Vector4& defaultColor = mModel->GetDefaultColor();
1032 // Create and initialize the pixel buffer.
1033 GlyphData glyphData;
1034 glyphData.verticalOffset = verticalOffset;
1035 glyphData.width = bufferWidth;
1036 glyphData.height = bufferHeight;
1037 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1038 glyphData.horizontalOffset = 0;
1040 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1041 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1042 Length hyphenIndex = 0;
1044 const Character* textBuffer = mModel->GetTextBuffer();
1045 float calculatedAdvance = 0.f;
1046 const Vector<CharacterIndex>& glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1047 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1049 // Traverses the lines of the text.
1050 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1052 const LineRun& line = *(modelLinesBuffer + lineIndex);
1054 // Sets the horizontal offset of the line.
1055 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1056 glyphData.horizontalOffset += horizontalOffset;
1058 // Increases the vertical offset with the line's ascender.
1059 glyphData.verticalOffset += static_cast<int32_t>(line.ascender);
1061 // Retrieves the glyph's outline width
1062 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1064 if(style == Typesetter::STYLE_OUTLINE)
1066 glyphData.horizontalOffset -= outlineWidth;
1069 // Only need to add the vertical outline offset for the first line
1070 glyphData.verticalOffset -= outlineWidth;
1073 else if(style == Typesetter::STYLE_SHADOW)
1075 const Vector2& shadowOffset = mModel->GetShadowOffset();
1076 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1080 // Only need to add the vertical shadow offset for first line
1081 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1085 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1086 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1087 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1089 // Get the character-spacing runs.
1090 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1092 // Aggregate underline-style-properties from mModel
1093 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1094 mModel->GetUnderlineColor(),
1095 mModel->GetUnderlineHeight(),
1096 mModel->GetDashedUnderlineGap(),
1097 mModel->GetDashedUnderlineWidth(),
1104 // Aggregate strikethrough-style-properties from mModel
1105 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1106 mModel->GetStrikethroughHeight(),
1110 // Get the underline runs.
1111 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1112 Vector<UnderlinedGlyphRun> underlineRuns;
1113 underlineRuns.Resize(numberOfUnderlineRuns);
1114 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1116 // Get the strikethrough runs.
1117 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1118 Vector<StrikethroughGlyphRun> strikethroughRuns;
1119 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1120 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1122 bool thereAreUnderlinedGlyphs = false;
1123 bool thereAreStrikethroughGlyphs = false;
1125 float currentUnderlinePosition = 0.0f;
1126 float currentUnderlineHeight = modelUnderlineProperties.height;
1127 float maxUnderlineHeight = currentUnderlineHeight;
1128 auto currentUnderlineProperties = modelUnderlineProperties;
1130 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1131 float maxStrikethroughHeight = currentStrikethroughHeight;
1132 auto currentStrikethroughProperties = modelStrikethroughProperties;
1133 float strikethroughStartingYPosition = 0.0f;
1135 FontId lastFontId = 0;
1137 float lineExtentLeft = bufferWidth;
1138 float lineExtentRight = 0.0f;
1139 float baseline = 0.0f;
1140 bool addHyphen = false;
1142 // Traverses the glyphs of the line.
1143 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1144 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1145 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1147 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1149 //To handle START case of ellipsis, the first glyph has been shifted
1150 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1151 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1153 //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.
1154 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1156 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1157 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1159 // Ignore any glyph that removed for MIDDLE ellipsis
1162 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1164 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1168 // Retrieve the glyph's info.
1169 const GlyphInfo* glyphInfo;
1171 if(addHyphen && hyphens)
1173 glyphInfo = hyphens + hyphenIndex;
1178 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1181 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1182 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1184 // Nothing to do if the glyph's width or height is zero.
1188 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1189 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1190 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1191 currentUnderlineHeight = currentUnderlineProperties.height;
1192 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1194 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1195 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1196 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1197 currentStrikethroughHeight = currentStrikethroughProperties.height;
1198 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1200 // Are we still using the same fontId as previous
1201 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1203 // We need to fetch fresh font underline metrics
1204 FontMetrics fontMetrics;
1205 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1207 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1208 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1212 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1215 if(strikethroughGlyph)
1217 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1220 // Update lastFontId because fontId is changed
1221 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1224 // Retrieves the glyph's position.
1225 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1229 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1230 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1231 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1232 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1233 position.y = -glyphInfo->yBearing;
1236 if(baseline < position.y + glyphInfo->yBearing)
1238 baseline = position.y + glyphInfo->yBearing;
1241 // Calculate the positions of leftmost and rightmost glyphs in the current line
1242 if(position.x < lineExtentLeft)
1244 lineExtentLeft = position.x;
1247 if(position.x + glyphInfo->width > lineExtentRight)
1249 lineExtentRight = position.x + glyphInfo->width;
1252 // Retrieves the glyph's color.
1253 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1256 if(style == Typesetter::STYLE_SHADOW)
1258 color = mModel->GetShadowColor();
1260 else if(style == Typesetter::STYLE_OUTLINE)
1262 color = mModel->GetOutlineColor();
1266 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1269 // Premultiply alpha
1274 // Retrieves the glyph's bitmap.
1275 glyphData.glyphBitmap.buffer = NULL;
1276 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1277 glyphData.glyphBitmap.height = glyphInfo->height;
1279 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1281 // Don't render outline for other styles
1282 outlineWidth = 0.0f;
1285 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1287 fontClient.CreateBitmap(glyphInfo->fontId,
1289 glyphInfo->isItalicRequired,
1290 glyphInfo->isBoldRequired,
1291 glyphData.glyphBitmap,
1292 static_cast<int32_t>(outlineWidth));
1295 // Sets the glyph's bitmap into the bitmap of the whole text.
1296 if(NULL != glyphData.glyphBitmap.buffer)
1298 if(style == Typesetter::STYLE_OUTLINE)
1300 // Set the position offset for the current glyph
1301 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1302 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1305 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1306 TypesetGlyph(glyphData,
1312 if(style == Typesetter::STYLE_OUTLINE)
1314 // Reset the position offset for the next glyph
1315 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1316 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1319 // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1320 delete[] glyphData.glyphBitmap.buffer;
1321 glyphData.glyphBitmap.buffer = NULL;
1326 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1331 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1339 // Draw the underline from the leftmost glyph to the rightmost glyph
1340 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1342 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1345 // Draw the background color from the leftmost glyph to the rightmost glyph
1346 if(style == Typesetter::STYLE_BACKGROUND)
1348 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1351 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1352 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1354 //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.
1355 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.
1356 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1359 // Increases the vertical offset with the line's descender & line spacing.
1360 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + line.lineSpacing);
1363 return glyphData.bitmapBuffer;
1366 Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const uint32_t& bufferWidth, const uint32_t& bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, const int32_t& horizontalOffset, const int32_t& verticalOffset)
1368 // Underline-tags (this is for Markup case)
1369 // Get the underline runs.
1370 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1371 Vector<UnderlinedGlyphRun> underlineRuns;
1372 underlineRuns.Resize(numberOfUnderlineRuns);
1373 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1375 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1376 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1377 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1378 GlyphIndex startGlyphIndex, endGlyphIndex;
1380 //The outer loop to iterate on the separated chunks of underlined glyph runs
1381 while(itGlyphRun != endItGlyphRun)
1383 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1384 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1386 // Create the image buffer for underline
1387 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1388 // Combine the two buffers
1389 // Result pixel buffer will be stored into topPixelBuffer.
1390 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1395 return topPixelBuffer;
1398 Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const uint32_t& bufferWidth, const uint32_t& bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, const int32_t& horizontalOffset, const int32_t& verticalOffset)
1400 // strikethrough-tags (this is for Markup case)
1401 // Get the strikethrough runs.
1402 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1403 Vector<StrikethroughGlyphRun> strikethroughRuns;
1404 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1405 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1407 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1408 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1409 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1410 GlyphIndex startGlyphIndex, endGlyphIndex;
1412 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1413 while(itGlyphRun != endItGlyphRun)
1415 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1416 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1418 // Create the image buffer for strikethrough
1419 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1420 // Combine the two buffers
1421 // Result pixel buffer will be stored into topPixelBuffer.
1422 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1427 return topPixelBuffer;
1430 Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const uint32_t& bufferWidth, const uint32_t& bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, const int32_t& horizontalOffset, const int32_t& verticalOffset)
1432 // Apply the markup-Processor if enabled
1433 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1434 if(markupProcessorEnabled)
1436 topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1438 topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1441 return topPixelBuffer;
1444 Typesetter::Typesetter(const ModelInterface* const model)
1445 : mModel(new ViewModel(model))
1449 Typesetter::~Typesetter()
1456 } // namespace Toolkit