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& data,
83 const Vector2* const position,
84 const Vector4* const color,
85 Typesetter::Style style,
86 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 alphaIndex = 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 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
119 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
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 const int32_t yOffsetIndex = yOffset + lineIndex;
128 const int32_t verticalOffset = yOffsetIndex * data.width;
130 // We can use memset here.
131 memset(bitmapBuffer + verticalOffset + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
136 // Pointer to the color glyph if there is one.
137 const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>(data.glyphBitmap.buffer) : NULL;
139 // Precalculate input color's packed result.
140 uint32_t packedInputColor = 0u;
141 uint8_t* packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
143 *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
144 *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
145 *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
146 *(packedInputColorBuffer) = static_cast<uint8_t>(color->r * 255);
148 // Traverse the pixels of the glyph line per line.
149 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
151 const int32_t yOffsetIndex = yOffset + lineIndex;
153 const int32_t verticalOffset = yOffsetIndex * data.width;
154 const int32_t glyphBufferOffset = lineIndex * static_cast<int32_t>(data.glyphBitmap.width);
155 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
157 const int32_t xOffsetIndex = xOffset + index;
161 // Retrieves the color from the color glyph.
162 uint32_t packedColorGlyph = *(colorGlyphBuffer + glyphBufferOffset + index);
163 uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
165 // Update the alpha channel.
166 const uint8_t colorAlpha = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
167 *(packedColorGlyphBuffer + 3u) = colorAlpha;
169 if(Typesetter::STYLE_SHADOW == style)
171 // The shadow of color glyph needs to have the shadow color.
172 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
173 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
174 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
180 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
183 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
184 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
185 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
187 if(data.glyphBitmap.isColorBitmap)
189 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
190 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
191 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
195 // Set the color into the final pixel buffer.
196 *(bitmapBuffer + verticalOffset + xOffsetIndex) = packedColorGlyph;
200 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
201 // The format is RGBA8888.
202 uint32_t packedColor = 0u;
203 uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
205 // Update the alpha channel.
206 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
208 // Copy non-transparent pixels only
211 // Check alpha of overlapped pixels
212 uint32_t& currentColor = *(bitmapBuffer + verticalOffset + xOffsetIndex);
213 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
215 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
216 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
217 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
218 // happen, for example, in the RTL text when we copy glyphs from right to left).
219 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
220 currentAlpha = std::max(currentAlpha, alpha);
221 if(currentAlpha == 255)
223 // Fast-cut to avoid float type operation.
224 currentColor = packedInputColor;
228 // Color is pre-muliplied with its alpha.
229 *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
230 *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
231 *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
232 *(packedColorBuffer) = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
234 // Set the color into the final pixel buffer.
235 currentColor = packedColor;
244 // Below codes required only if not color glyph.
247 uint8_t* bitmapBuffer = reinterpret_cast<uint8_t*>(data.bitmapBuffer.GetBuffer());
249 // Traverse the pixels of the glyph line per line.
250 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
252 const int32_t yOffsetIndex = yOffset + lineIndex;
254 const int32_t verticalOffset = yOffsetIndex * data.width;
255 const int32_t glyphBufferOffset = lineIndex * static_cast<int32_t>(data.glyphBitmap.width);
256 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
258 const int32_t xOffsetIndex = xOffset + index;
260 // Update the alpha channel.
261 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
263 // Copy non-transparent pixels only
266 // Check alpha of overlapped pixels
267 uint8_t& currentAlpha = *(bitmapBuffer + verticalOffset + xOffsetIndex);
269 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
270 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
271 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
272 // happen, for example, in the RTL text when we copy glyphs from right to left).
273 currentAlpha = std::max(currentAlpha, alpha);
281 /// Draws the specified underline color to the buffer
283 const uint32_t& bufferWidth,
284 const uint32_t& bufferHeight,
285 GlyphData& glyphData,
286 const float& baseline,
287 const float& currentUnderlinePosition,
288 const float& maxUnderlineHeight,
289 const float& lineExtentLeft,
290 const float& lineExtentRight,
291 const UnderlineStyleProperties& commonUnderlineProperties,
292 const UnderlineStyleProperties& currentUnderlineProperties,
295 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
296 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
297 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
298 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
300 int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
302 const uint32_t yRangeMin = underlineYOffset;
303 const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
304 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
305 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
307 // If current glyph don't need to be rendered, just ignore.
308 if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
313 // We can optimize by memset when underlineColor.a is near zero
314 uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
316 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
318 // Skip yRangeMin line.
319 bitmapBuffer += yRangeMin * glyphData.width;
321 // Note if underlineType is DASHED, we cannot setup color by memset.
322 if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
324 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
326 // We can use memset.
327 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
328 bitmapBuffer += glyphData.width;
330 if(underlineType == Text::Underline::DOUBLE)
332 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
333 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
334 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
336 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
337 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
339 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
341 // We can use memset.
342 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
343 bitmapBuffer += glyphData.width;
349 uint32_t packedUnderlineColor = 0u;
350 uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
352 // Write the color to the pixel buffer
353 *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
354 *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
355 *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
356 *(packedUnderlineColorBuffer) = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
358 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
360 if(underlineType == Text::Underline::DASHED)
362 float dashWidth = dashedUnderlineWidth;
365 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
367 if(dashGap == 0 && dashWidth > 0)
369 // Note : this is same logic as bitmap[y][x] = underlineColor;
370 *(bitmapBuffer + x) = packedUnderlineColor;
373 else if(dashGap < dashedUnderlineGap)
380 dashWidth = dashedUnderlineWidth;
387 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
389 // Note : this is same logic as bitmap[y][x] = underlineColor;
390 *(bitmapBuffer + x) = packedUnderlineColor;
393 bitmapBuffer += glyphData.width;
395 if(underlineType == Text::Underline::DOUBLE)
397 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
398 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
399 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
401 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
402 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
404 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
406 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
408 // Note : this is same logic as bitmap[y][x] = underlineColor;
409 *(bitmapBuffer + x) = packedUnderlineColor;
411 bitmapBuffer += glyphData.width;
417 /// Draws the background color to the buffer
418 void DrawBackgroundColor(
419 Vector4 backgroundColor,
420 const uint32_t& bufferWidth,
421 const uint32_t& bufferHeight,
422 GlyphData& glyphData,
423 const float& baseline,
425 const float& lineExtentLeft,
426 const float& lineExtentRight)
428 const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
429 const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
430 const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
431 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
433 // If current glyph don't need to be rendered, just ignore.
434 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
439 // We can optimize by memset when backgroundColor.a is near zero
440 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
442 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
444 // Skip yRangeMin line.
445 bitmapBuffer += yRangeMin * glyphData.width;
447 if(backgroundColorAlpha == 0)
449 for(int32_t y = yRangeMin; y < yRangeMax; y++)
451 // We can use memset.
452 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
453 bitmapBuffer += glyphData.width;
458 uint32_t packedBackgroundColor = 0u;
459 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
461 // Write the color to the pixel buffer
462 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
463 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
464 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
465 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
467 for(int32_t y = yRangeMin; y < yRangeMax; y++)
469 for(int32_t x = xRangeMin; x < xRangeMax; x++)
471 // Note : this is same logic as bitmap[y][x] = backgroundColor;
472 *(bitmapBuffer + x) = packedBackgroundColor;
474 bitmapBuffer += glyphData.width;
479 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)
481 // Retrieve lines, glyphs, positions and colors from the view model.
482 const Length modelNumberOfLines = model->GetNumberOfLines();
483 const LineRun* const modelLinesBuffer = model->GetLines();
484 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
485 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
486 const Vector2* const positionBuffer = model->GetLayout();
487 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
488 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
490 const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
492 // Create and initialize the pixel buffer.
494 glyphData.verticalOffset = verticalOffset;
495 glyphData.width = bufferWidth;
496 glyphData.height = bufferHeight;
497 glyphData.bitmapBuffer = buffer;
498 glyphData.horizontalOffset = 0;
500 ColorIndex prevBackgroundColorIndex = 0;
501 ColorIndex backgroundColorIndex = 0;
503 // Traverses the lines of the text.
504 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
506 const LineRun& line = *(modelLinesBuffer + lineIndex);
508 // Sets the horizontal offset of the line.
509 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
510 glyphData.horizontalOffset += horizontalOffset;
512 // Increases the vertical offset with the line's ascender.
513 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
515 float left = bufferWidth;
517 float baseline = 0.0f;
519 // Traverses the glyphs of the line.
520 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
521 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
523 // Retrieve the glyph's info.
524 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
526 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
527 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
529 // Nothing to do if default background color, the glyph's width or height is zero.
533 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
535 if((backgroundColorIndex != prevBackgroundColorIndex) &&
536 (prevBackgroundColorIndex != 0u))
538 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
539 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
542 if(backgroundColorIndex == 0u)
544 prevBackgroundColorIndex = backgroundColorIndex;
545 //if background color is the default do nothing
549 // Retrieves the glyph's position.
550 const Vector2* const position = positionBuffer + glyphIndex;
552 if(baseline < position->y + glyphInfo->yBearing)
554 baseline = position->y + glyphInfo->yBearing;
557 // Calculate the positions of leftmost and rightmost glyphs in the current line
558 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
560 left = position->x - glyphInfo->xBearing;
563 if(position->x + glyphInfo->width > right)
565 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
568 prevBackgroundColorIndex = backgroundColorIndex;
571 //draw last background at line end if not default
572 if(backgroundColorIndex != 0u)
574 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
575 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
578 // Increases the vertical offset with the line's descender.
579 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
582 return glyphData.bitmapBuffer;
585 /// Draws the specified strikethrough color to the buffer
586 void DrawStrikethrough(const uint32_t& bufferWidth,
587 const uint32_t& bufferHeight,
588 GlyphData& glyphData,
589 const float& baseline,
590 const float& strikethroughStartingYPosition,
591 const float& maxStrikethroughHeight,
592 const float& lineExtentLeft,
593 const float& lineExtentRight,
594 const StrikethroughStyleProperties& commonStrikethroughProperties,
595 const StrikethroughStyleProperties& currentStrikethroughProperties,
598 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
600 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
601 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
602 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
603 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
605 // If current glyph don't need to be rendered, just ignore.
606 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
611 // We can optimize by memset when strikethroughColor.a is near zero
612 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
614 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
616 // Skip yRangeMin line.
617 bitmapBuffer += yRangeMin * glyphData.width;
619 if(strikethroughColorAlpha == 0)
621 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
623 // We can use memset.
624 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
625 bitmapBuffer += glyphData.width;
630 uint32_t packedStrikethroughColor = 0u;
631 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
633 // Write the color to the pixel buffer
634 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
635 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
636 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
637 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
639 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
641 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
643 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
644 *(bitmapBuffer + x) = packedStrikethroughColor;
646 bitmapBuffer += glyphData.width;
652 * @brief Create an initialized image buffer filled with transparent color.
654 * Creates the pixel data used to generate the final image with the given size.
656 * @param[in] bufferWidth The width of the image buffer.
657 * @param[in] bufferHeight The height of the image buffer.
658 * @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).
660 * @return An image buffer.
662 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t& bufferWidth, const uint32_t& bufferHeight, const Pixel::Format& pixelFormat)
664 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
666 if(Pixel::RGBA8888 == pixelFormat)
668 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
669 const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
670 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
674 memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
681 * @brief Combine the two RGBA image buffers together.
683 * The top layer buffer will blend over the bottom layer buffer:
684 * - If the pixel is not fully opaque from either buffer, it will be blended with
685 * the pixel from the other buffer and copied to the combined buffer.
686 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
687 * buffer will be copied to the combined buffer.
689 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
690 * We can determine which pixelBuffer's memory is destination
692 * @param[in, out] topPixelBuffer The top layer buffer.
693 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
694 * @param[in] bufferWidth The width of the image buffer.
695 * @param[in] bufferHeight The height of the image buffer.
696 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
697 * False if we store the combined image buffer result into bottomPixelBuffer.
700 void CombineImageBuffer(Devel::PixelBuffer& topPixelBuffer, Devel::PixelBuffer& bottomPixelBuffer, const uint32_t& bufferWidth, const uint32_t& bufferHeight, bool storeResultIntoTop)
702 // Assume that we always combine two RGBA images
703 // Jump with 4bytes for optimize runtime.
704 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
705 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
707 if(topBuffer == NULL && bottomBuffer == NULL)
709 // Nothing to do if both buffers are empty.
713 if(topBuffer == NULL)
715 // Nothing to do if topBuffer is empty.
716 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
717 if(storeResultIntoTop)
719 topPixelBuffer = bottomPixelBuffer;
724 if(bottomBuffer == NULL)
726 // Nothing to do if bottomBuffer is empty.
727 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
728 if(!storeResultIntoTop)
730 bottomPixelBuffer = topPixelBuffer;
735 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
737 uint32_t* combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
738 uint8_t* topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
740 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
742 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
743 // Otherwise, copy pixel from topBuffer to combinedBuffer.
744 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
746 uint8_t topAlpha = *topAlphaBufferPointer;
750 // Copy the pixel from bottomBuffer to combinedBuffer
751 if(storeResultIntoTop)
753 *(combinedBuffer) = *(bottomBuffer);
756 else if(topAlpha == 255)
758 // Copy the pixel from topBuffer to combinedBuffer
759 if(!storeResultIntoTop)
761 *(combinedBuffer) = *(topBuffer);
766 // At least one pixel is not fully opaque
767 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
768 uint32_t blendedBottomBufferColor = *(bottomBuffer);
769 uint8_t* blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
771 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
772 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
773 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
774 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
776 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
779 // Increase each buffer's pointer.
783 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
789 TypesetterPtr Typesetter::New(const ModelInterface* const model)
791 return TypesetterPtr(new Typesetter(model));
794 ViewModel* Typesetter::GetViewModel()
799 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
801 // @todo. This initial implementation for a TextLabel has only one visible page.
803 // Elides the text if needed.
804 mModel->ElideGlyphs();
806 // Retrieves the layout size.
807 const Size& layoutSize = mModel->GetLayoutSize();
809 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
811 // Set the offset for the horizontal alignment according to the text direction and outline width.
814 switch(mModel->GetHorizontalAlignment())
816 case HorizontalAlignment::BEGIN:
821 case HorizontalAlignment::CENTER:
823 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
826 case HorizontalAlignment::END:
828 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
833 // Set the offset for the vertical alignment.
836 switch(mModel->GetVerticalAlignment())
838 case VerticalAlignment::TOP:
843 case VerticalAlignment::CENTER:
845 penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
846 penY = penY < 0.f ? 0.f : penY;
849 case VerticalAlignment::BOTTOM:
851 penY = static_cast<int32_t>(size.height - layoutSize.height);
856 // Generate the image buffers of the text for each different style first,
857 // then combine all of them together as one final image buffer. We try to
858 // do all of these in CPU only, so that once the final texture is generated,
859 // no calculation is needed in GPU during each frame.
861 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
862 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
864 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
865 const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
867 //Elided text in ellipsis at START could start on index greater than 0
868 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
869 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
871 Devel::PixelBuffer imageBuffer;
873 if(RENDER_MASK == behaviour)
875 // Generate the image buffer as an alpha mask for color glyphs.
876 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
878 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
880 // Generate an empty image buffer so that it can been combined with the image buffers for styles
881 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
882 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
886 // Generate the image buffer for the text with no style.
887 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
890 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
892 // Generate the outline if enabled
893 const uint16_t outlineWidth = mModel->GetOutlineWidth();
894 if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
896 // Create the image buffer for outline
897 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
899 // Combine the two buffers
900 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
903 // @todo. Support shadow and underline for partial text later on.
905 // Generate the shadow if enabled
906 const Vector2& shadowOffset = mModel->GetShadowOffset();
907 if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
909 // Create the image buffer for shadow
910 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
912 // Check whether it will be a soft shadow
913 const float& blurRadius = mModel->GetShadowBlurRadius();
915 if(blurRadius > Math::MACHINE_EPSILON_1)
917 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
920 // Combine the two buffers
921 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
924 // Generate the underline if enabled
925 const bool underlineEnabled = mModel->IsUnderlineEnabled();
926 if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
928 // Create the image buffer for underline
929 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
931 // Combine the two buffers
932 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
935 // Generate the background if enabled
936 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
937 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
938 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
940 Devel::PixelBuffer backgroundImageBuffer;
942 if(backgroundEnabled)
944 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
948 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
951 if(backgroundMarkupSet)
953 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
956 // Combine the two buffers
957 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
960 // Generate the strikethrough if enabled
961 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
962 if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
964 // Create the image buffer for strikethrough
965 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
967 // Combine the two buffers
968 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
973 imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
976 // Create the final PixelData for the combined image buffer
977 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
982 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)
984 // Retrieve lines, glyphs, positions and colors from the view model.
985 const Length modelNumberOfLines = mModel->GetNumberOfLines();
986 const LineRun* const modelLinesBuffer = mModel->GetLines();
987 const GlyphInfo* const glyphsBuffer = mModel->GetGlyphs();
988 const Vector2* const positionBuffer = mModel->GetLayout();
989 const Vector4* const colorsBuffer = mModel->GetColors();
990 const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
991 const GlyphInfo* hyphens = mModel->GetHyphens();
992 const Length* hyphenIndices = mModel->GetHyphenIndices();
993 const Length hyphensCount = mModel->GetHyphensCount();
995 // Elided text info. Indices according to elided text and Ellipsis position.
996 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
997 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
998 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
999 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1000 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1002 // Whether to use the default color.
1003 const bool useDefaultColor = (NULL == colorsBuffer);
1004 const Vector4& defaultColor = mModel->GetDefaultColor();
1006 // Create and initialize the pixel buffer.
1007 GlyphData glyphData;
1008 glyphData.verticalOffset = verticalOffset;
1009 glyphData.width = bufferWidth;
1010 glyphData.height = bufferHeight;
1011 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1012 glyphData.horizontalOffset = 0;
1014 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1015 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1016 Length hyphenIndex = 0;
1018 const Character* textBuffer = mModel->GetTextBuffer();
1019 float calculatedAdvance = 0.f;
1020 const Vector<CharacterIndex>& glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1021 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1023 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1025 // Traverses the lines of the text.
1026 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1028 const LineRun& line = *(modelLinesBuffer + lineIndex);
1030 // Sets the horizontal offset of the line.
1031 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1032 glyphData.horizontalOffset += horizontalOffset;
1034 // Increases the vertical offset with the line's ascender.
1035 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1037 // Retrieves the glyph's outline width
1038 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1040 if(style == Typesetter::STYLE_OUTLINE)
1042 glyphData.horizontalOffset -= outlineWidth;
1045 // Only need to add the vertical outline offset for the first line
1046 glyphData.verticalOffset -= outlineWidth;
1049 else if(style == Typesetter::STYLE_SHADOW)
1051 const Vector2& shadowOffset = mModel->GetShadowOffset();
1052 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1056 // Only need to add the vertical shadow offset for first line
1057 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1061 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1062 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1063 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1065 // Get the character-spacing runs.
1066 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1068 // Aggregate underline-style-properties from mModel
1069 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1070 mModel->GetUnderlineColor(),
1071 mModel->GetUnderlineHeight(),
1072 mModel->GetDashedUnderlineGap(),
1073 mModel->GetDashedUnderlineWidth(),
1080 // Aggregate strikethrough-style-properties from mModel
1081 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1082 mModel->GetStrikethroughHeight(),
1086 // Get the underline runs.
1087 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1088 Vector<UnderlinedGlyphRun> underlineRuns;
1089 underlineRuns.Resize(numberOfUnderlineRuns);
1090 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1092 // Get the strikethrough runs.
1093 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1094 Vector<StrikethroughGlyphRun> strikethroughRuns;
1095 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1096 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1098 bool thereAreUnderlinedGlyphs = false;
1099 bool thereAreStrikethroughGlyphs = false;
1101 float currentUnderlinePosition = 0.0f;
1102 float currentUnderlineHeight = modelUnderlineProperties.height;
1103 float maxUnderlineHeight = currentUnderlineHeight;
1104 auto currentUnderlineProperties = modelUnderlineProperties;
1106 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1107 float maxStrikethroughHeight = currentStrikethroughHeight;
1108 auto currentStrikethroughProperties = modelStrikethroughProperties;
1109 float strikethroughStartingYPosition = 0.0f;
1111 FontId lastFontId = 0;
1113 float lineExtentLeft = bufferWidth;
1114 float lineExtentRight = 0.0f;
1115 float baseline = 0.0f;
1116 bool addHyphen = false;
1118 // Traverses the glyphs of the line.
1119 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1120 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1121 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1123 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1125 //To handle START case of ellipsis, the first glyph has been shifted
1126 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1127 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1129 //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.
1130 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1132 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1133 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1135 // Ignore any glyph that removed for MIDDLE ellipsis
1138 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1140 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1144 // Retrieve the glyph's info.
1145 const GlyphInfo* glyphInfo;
1147 if(addHyphen && hyphens)
1149 glyphInfo = hyphens + hyphenIndex;
1154 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1157 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1158 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1160 // Nothing to do if the glyph's width or height is zero.
1164 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1165 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1166 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1167 currentUnderlineHeight = currentUnderlineProperties.height;
1168 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1170 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1171 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1172 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1173 currentStrikethroughHeight = currentStrikethroughProperties.height;
1174 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1176 // Are we still using the same fontId as previous
1177 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1179 // We need to fetch fresh font underline metrics
1180 FontMetrics fontMetrics;
1181 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1183 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1184 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1188 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1191 if(strikethroughGlyph)
1193 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1196 // Update lastFontId because fontId is changed
1197 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1200 // Retrieves the glyph's position.
1201 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1205 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1206 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1207 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1208 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1209 position.y = -glyphInfo->yBearing;
1212 if(baseline < position.y + glyphInfo->yBearing)
1214 baseline = position.y + glyphInfo->yBearing;
1217 // Calculate the positions of leftmost and rightmost glyphs in the current line
1218 if(position.x < lineExtentLeft)
1220 lineExtentLeft = position.x;
1223 if(position.x + glyphInfo->width > lineExtentRight)
1225 lineExtentRight = position.x + glyphInfo->width;
1228 // Retrieves the glyph's color.
1229 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1232 if(style == Typesetter::STYLE_SHADOW)
1234 color = mModel->GetShadowColor();
1236 else if(style == Typesetter::STYLE_OUTLINE)
1238 color = mModel->GetOutlineColor();
1242 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1245 // Premultiply alpha
1250 // Retrieves the glyph's bitmap.
1251 glyphData.glyphBitmap.buffer = NULL;
1252 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1253 glyphData.glyphBitmap.height = glyphInfo->height;
1255 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1257 // Don't render outline for other styles
1258 outlineWidth = 0.0f;
1261 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1263 fontClient.CreateBitmap(glyphInfo->fontId,
1265 glyphInfo->isItalicRequired,
1266 glyphInfo->isBoldRequired,
1267 glyphData.glyphBitmap,
1268 static_cast<int32_t>(outlineWidth));
1271 // Sets the glyph's bitmap into the bitmap of the whole text.
1272 if(NULL != glyphData.glyphBitmap.buffer)
1274 if(style == Typesetter::STYLE_OUTLINE)
1276 // Set the position offset for the current glyph
1277 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1278 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1281 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1282 TypesetGlyph(glyphData,
1288 if(style == Typesetter::STYLE_OUTLINE)
1290 // Reset the position offset for the next glyph
1291 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1292 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1295 // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1296 delete[] glyphData.glyphBitmap.buffer;
1297 glyphData.glyphBitmap.buffer = NULL;
1302 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1307 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1315 // Draw the underline from the leftmost glyph to the rightmost glyph
1316 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1318 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1321 // Draw the background color from the leftmost glyph to the rightmost glyph
1322 if(style == Typesetter::STYLE_BACKGROUND)
1324 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1327 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1328 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1330 //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.
1331 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.
1332 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1335 // Increases the vertical offset with the line's descender & line spacing.
1336 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1339 return glyphData.bitmapBuffer;
1342 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)
1344 // Underline-tags (this is for Markup case)
1345 // Get the underline runs.
1346 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1347 Vector<UnderlinedGlyphRun> underlineRuns;
1348 underlineRuns.Resize(numberOfUnderlineRuns);
1349 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1351 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1352 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1353 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1354 GlyphIndex startGlyphIndex, endGlyphIndex;
1356 //The outer loop to iterate on the separated chunks of underlined glyph runs
1357 while(itGlyphRun != endItGlyphRun)
1359 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1360 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1362 // Create the image buffer for underline
1363 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1364 // Combine the two buffers
1365 // Result pixel buffer will be stored into topPixelBuffer.
1366 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1371 return topPixelBuffer;
1374 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)
1376 // strikethrough-tags (this is for Markup case)
1377 // Get the strikethrough runs.
1378 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1379 Vector<StrikethroughGlyphRun> strikethroughRuns;
1380 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1381 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1383 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1384 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1385 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1386 GlyphIndex startGlyphIndex, endGlyphIndex;
1388 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1389 while(itGlyphRun != endItGlyphRun)
1391 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1392 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1394 // Create the image buffer for strikethrough
1395 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1396 // Combine the two buffers
1397 // Result pixel buffer will be stored into topPixelBuffer.
1398 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1403 return topPixelBuffer;
1406 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)
1408 // Apply the markup-Processor if enabled
1409 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1410 if(markupProcessorEnabled)
1412 topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1414 topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1417 return topPixelBuffer;
1420 Typesetter::Typesetter(const ModelInterface* const model)
1421 : mModel(new ViewModel(model))
1425 Typesetter::~Typesetter()
1432 } // namespace Toolkit