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 float GetPreOffsetVerticalLineAlignment(LineRun line, DevelText::VerticalLineAlignment::Type verLineAlign)
480 // Calculate vertical line alignment
485 case DevelText::VerticalLineAlignment::TOP:
489 case DevelText::VerticalLineAlignment::MIDDLE:
491 offset = line.lineSpacing * 0.5f;
494 case DevelText::VerticalLineAlignment::BOTTOM:
496 offset = line.lineSpacing;
504 float GetPostOffsetVerticalLineAlignment(LineRun line, DevelText::VerticalLineAlignment::Type verLineAlign)
506 // Calculate vertical line alignment
511 case DevelText::VerticalLineAlignment::TOP:
513 offset = line.lineSpacing;
516 case DevelText::VerticalLineAlignment::MIDDLE:
518 offset = line.lineSpacing * 0.5f;
521 case DevelText::VerticalLineAlignment::BOTTOM:
530 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)
532 // Retrieve lines, glyphs, positions and colors from the view model.
533 const Length modelNumberOfLines = model->GetNumberOfLines();
534 const LineRun* const modelLinesBuffer = model->GetLines();
535 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
536 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
537 const Vector2* const positionBuffer = model->GetLayout();
538 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
539 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
541 const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
543 // Create and initialize the pixel buffer.
545 glyphData.verticalOffset = verticalOffset;
546 glyphData.width = bufferWidth;
547 glyphData.height = bufferHeight;
548 glyphData.bitmapBuffer = buffer;
549 glyphData.horizontalOffset = 0;
551 ColorIndex prevBackgroundColorIndex = 0;
552 ColorIndex backgroundColorIndex = 0;
554 // Traverses the lines of the text.
555 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
557 const LineRun& line = *(modelLinesBuffer + lineIndex);
559 // Sets the horizontal offset of the line.
560 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
561 glyphData.horizontalOffset += horizontalOffset;
563 // Increases the vertical offset with the line's ascender.
564 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
566 float left = bufferWidth;
568 float baseline = 0.0f;
570 // Traverses the glyphs of the line.
571 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
572 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
574 // Retrieve the glyph's info.
575 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
577 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
578 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
580 // Nothing to do if default background color, the glyph's width or height is zero.
584 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
586 if((backgroundColorIndex != prevBackgroundColorIndex) &&
587 (prevBackgroundColorIndex != 0u))
589 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
590 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
593 if(backgroundColorIndex == 0u)
595 prevBackgroundColorIndex = backgroundColorIndex;
596 //if background color is the default do nothing
600 // Retrieves the glyph's position.
601 const Vector2* const position = positionBuffer + glyphIndex;
603 if(baseline < position->y + glyphInfo->yBearing)
605 baseline = position->y + glyphInfo->yBearing;
608 // Calculate the positions of leftmost and rightmost glyphs in the current line
609 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
611 left = position->x - glyphInfo->xBearing;
614 if(position->x + glyphInfo->width > right)
616 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
619 prevBackgroundColorIndex = backgroundColorIndex;
622 //draw last background at line end if not default
623 if(backgroundColorIndex != 0u)
625 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
626 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
629 // Increases the vertical offset with the line's descender.
630 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
633 return glyphData.bitmapBuffer;
636 /// Draws the specified strikethrough color to the buffer
637 void DrawStrikethrough(const uint32_t& bufferWidth,
638 const uint32_t& bufferHeight,
639 GlyphData& glyphData,
640 const float& baseline,
641 const float& strikethroughStartingYPosition,
642 const float& maxStrikethroughHeight,
643 const float& lineExtentLeft,
644 const float& lineExtentRight,
645 const StrikethroughStyleProperties& commonStrikethroughProperties,
646 const StrikethroughStyleProperties& currentStrikethroughProperties,
649 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
651 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
652 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
653 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
654 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
656 // If current glyph don't need to be rendered, just ignore.
657 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
662 // We can optimize by memset when strikethroughColor.a is near zero
663 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
665 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
667 // Skip yRangeMin line.
668 bitmapBuffer += yRangeMin * glyphData.width;
670 if(strikethroughColorAlpha == 0)
672 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
674 // We can use memset.
675 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
676 bitmapBuffer += glyphData.width;
681 uint32_t packedStrikethroughColor = 0u;
682 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
684 // Write the color to the pixel buffer
685 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
686 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
687 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
688 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
690 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
692 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
694 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
695 *(bitmapBuffer + x) = packedStrikethroughColor;
697 bitmapBuffer += glyphData.width;
703 * @brief Create an initialized image buffer filled with transparent color.
705 * Creates the pixel data used to generate the final image with the given size.
707 * @param[in] bufferWidth The width of the image buffer.
708 * @param[in] bufferHeight The height of the image buffer.
709 * @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).
711 * @return An image buffer.
713 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t& bufferWidth, const uint32_t& bufferHeight, const Pixel::Format& pixelFormat)
715 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
717 if(Pixel::RGBA8888 == pixelFormat)
719 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
720 const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
721 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
725 memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
732 * @brief Combine the two RGBA image buffers together.
734 * The top layer buffer will blend over the bottom layer buffer:
735 * - If the pixel is not fully opaque from either buffer, it will be blended with
736 * the pixel from the other buffer and copied to the combined buffer.
737 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
738 * buffer will be copied to the combined buffer.
740 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
741 * We can determine which pixelBuffer's memory is destination
743 * @param[in, out] topPixelBuffer The top layer buffer.
744 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
745 * @param[in] bufferWidth The width of the image buffer.
746 * @param[in] bufferHeight The height of the image buffer.
747 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
748 * False if we store the combined image buffer result into bottomPixelBuffer.
751 void CombineImageBuffer(Devel::PixelBuffer& topPixelBuffer, Devel::PixelBuffer& bottomPixelBuffer, const uint32_t& bufferWidth, const uint32_t& bufferHeight, bool storeResultIntoTop)
753 // Assume that we always combine two RGBA images
754 // Jump with 4bytes for optimize runtime.
755 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
756 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
758 if(topBuffer == NULL && bottomBuffer == NULL)
760 // Nothing to do if both buffers are empty.
764 if(topBuffer == NULL)
766 // Nothing to do if topBuffer is empty.
767 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
768 if(storeResultIntoTop)
770 topPixelBuffer = bottomPixelBuffer;
775 if(bottomBuffer == NULL)
777 // Nothing to do if bottomBuffer is empty.
778 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
779 if(!storeResultIntoTop)
781 bottomPixelBuffer = topPixelBuffer;
786 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
788 uint32_t* combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
789 uint8_t* topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
791 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
793 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
794 // Otherwise, copy pixel from topBuffer to combinedBuffer.
795 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
797 uint8_t topAlpha = *topAlphaBufferPointer;
801 // Copy the pixel from bottomBuffer to combinedBuffer
802 if(storeResultIntoTop)
804 *(combinedBuffer) = *(bottomBuffer);
807 else if(topAlpha == 255)
809 // Copy the pixel from topBuffer to combinedBuffer
810 if(!storeResultIntoTop)
812 *(combinedBuffer) = *(topBuffer);
817 // At least one pixel is not fully opaque
818 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
819 uint32_t blendedBottomBufferColor = *(bottomBuffer);
820 uint8_t* blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
822 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
823 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
824 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
825 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
827 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
830 // Increase each buffer's pointer.
834 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
840 TypesetterPtr Typesetter::New(const ModelInterface* const model)
842 return TypesetterPtr(new Typesetter(model));
845 ViewModel* Typesetter::GetViewModel()
850 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
852 // @todo. This initial implementation for a TextLabel has only one visible page.
854 // Elides the text if needed.
855 mModel->ElideGlyphs();
857 // Retrieves the layout size.
858 const Size& layoutSize = mModel->GetLayoutSize();
860 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
862 // Set the offset for the horizontal alignment according to the text direction and outline width.
865 switch(mModel->GetHorizontalAlignment())
867 case HorizontalAlignment::BEGIN:
872 case HorizontalAlignment::CENTER:
874 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
877 case HorizontalAlignment::END:
879 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
884 // Set the offset for the vertical alignment.
887 switch(mModel->GetVerticalAlignment())
889 case VerticalAlignment::TOP:
894 case VerticalAlignment::CENTER:
896 penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
897 penY = penY < 0.f ? 0.f : penY;
900 case VerticalAlignment::BOTTOM:
902 penY = static_cast<int32_t>(size.height - layoutSize.height);
907 // Generate the image buffers of the text for each different style first,
908 // then combine all of them together as one final image buffer. We try to
909 // do all of these in CPU only, so that once the final texture is generated,
910 // no calculation is needed in GPU during each frame.
912 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
913 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
915 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
916 const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
918 //Elided text in ellipsis at START could start on index greater than 0
919 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
920 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
922 Devel::PixelBuffer imageBuffer;
924 if(RENDER_MASK == behaviour)
926 // Generate the image buffer as an alpha mask for color glyphs.
927 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
929 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
931 // Generate an empty image buffer so that it can been combined with the image buffers for styles
932 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
933 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
937 // Generate the image buffer for the text with no style.
938 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
941 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
943 // Generate the outline if enabled
944 const uint16_t outlineWidth = mModel->GetOutlineWidth();
945 if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
947 // Create the image buffer for outline
948 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
950 // Combine the two buffers
951 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
954 // @todo. Support shadow and underline for partial text later on.
956 // Generate the shadow if enabled
957 const Vector2& shadowOffset = mModel->GetShadowOffset();
958 if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
960 // Create the image buffer for shadow
961 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
963 // Check whether it will be a soft shadow
964 const float& blurRadius = mModel->GetShadowBlurRadius();
966 if(blurRadius > Math::MACHINE_EPSILON_1)
968 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
971 // Combine the two buffers
972 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
975 // Generate the underline if enabled
976 const bool underlineEnabled = mModel->IsUnderlineEnabled();
977 if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
979 // Create the image buffer for underline
980 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
982 // Combine the two buffers
983 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
986 // Generate the background if enabled
987 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
988 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
989 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
991 Devel::PixelBuffer backgroundImageBuffer;
993 if(backgroundEnabled)
995 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
999 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1002 if(backgroundMarkupSet)
1004 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
1007 // Combine the two buffers
1008 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1011 // Generate the strikethrough if enabled
1012 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1013 if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
1015 // Create the image buffer for strikethrough
1016 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
1018 // Combine the two buffers
1019 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
1024 imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1027 // Create the final PixelData for the combined image buffer
1028 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
1033 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)
1035 // Retrieve lines, glyphs, positions and colors from the view model.
1036 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1037 const LineRun* const modelLinesBuffer = mModel->GetLines();
1038 const GlyphInfo* const glyphsBuffer = mModel->GetGlyphs();
1039 const Vector2* const positionBuffer = mModel->GetLayout();
1040 const Vector4* const colorsBuffer = mModel->GetColors();
1041 const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
1042 const GlyphInfo* hyphens = mModel->GetHyphens();
1043 const Length* hyphenIndices = mModel->GetHyphenIndices();
1044 const Length hyphensCount = mModel->GetHyphensCount();
1046 // Elided text info. Indices according to elided text and Ellipsis position.
1047 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1048 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1049 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1050 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1051 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1053 // Whether to use the default color.
1054 const bool useDefaultColor = (NULL == colorsBuffer);
1055 const Vector4& defaultColor = mModel->GetDefaultColor();
1057 // Create and initialize the pixel buffer.
1058 GlyphData glyphData;
1059 glyphData.verticalOffset = verticalOffset;
1060 glyphData.width = bufferWidth;
1061 glyphData.height = bufferHeight;
1062 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1063 glyphData.horizontalOffset = 0;
1065 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1066 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1067 Length hyphenIndex = 0;
1069 const Character* textBuffer = mModel->GetTextBuffer();
1070 float calculatedAdvance = 0.f;
1071 const Vector<CharacterIndex>& glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1072 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1074 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1076 // Traverses the lines of the text.
1077 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1079 const LineRun& line = *(modelLinesBuffer + lineIndex);
1081 // Sets the horizontal offset of the line.
1082 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1083 glyphData.horizontalOffset += horizontalOffset;
1085 // Increases the vertical offset with the line's ascender.
1086 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1088 // Retrieves the glyph's outline width
1089 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1091 if(style == Typesetter::STYLE_OUTLINE)
1093 glyphData.horizontalOffset -= outlineWidth;
1096 // Only need to add the vertical outline offset for the first line
1097 glyphData.verticalOffset -= outlineWidth;
1100 else if(style == Typesetter::STYLE_SHADOW)
1102 const Vector2& shadowOffset = mModel->GetShadowOffset();
1103 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1107 // Only need to add the vertical shadow offset for first line
1108 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1112 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1113 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1114 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1116 // Get the character-spacing runs.
1117 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1119 // Aggregate underline-style-properties from mModel
1120 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1121 mModel->GetUnderlineColor(),
1122 mModel->GetUnderlineHeight(),
1123 mModel->GetDashedUnderlineGap(),
1124 mModel->GetDashedUnderlineWidth(),
1131 // Aggregate strikethrough-style-properties from mModel
1132 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1133 mModel->GetStrikethroughHeight(),
1137 // Get the underline runs.
1138 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1139 Vector<UnderlinedGlyphRun> underlineRuns;
1140 underlineRuns.Resize(numberOfUnderlineRuns);
1141 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1143 // Get the strikethrough runs.
1144 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1145 Vector<StrikethroughGlyphRun> strikethroughRuns;
1146 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1147 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1149 bool thereAreUnderlinedGlyphs = false;
1150 bool thereAreStrikethroughGlyphs = false;
1152 float currentUnderlinePosition = 0.0f;
1153 float currentUnderlineHeight = modelUnderlineProperties.height;
1154 float maxUnderlineHeight = currentUnderlineHeight;
1155 auto currentUnderlineProperties = modelUnderlineProperties;
1157 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1158 float maxStrikethroughHeight = currentStrikethroughHeight;
1159 auto currentStrikethroughProperties = modelStrikethroughProperties;
1160 float strikethroughStartingYPosition = 0.0f;
1162 FontId lastFontId = 0;
1164 float lineExtentLeft = bufferWidth;
1165 float lineExtentRight = 0.0f;
1166 float baseline = 0.0f;
1167 bool addHyphen = false;
1169 // Traverses the glyphs of the line.
1170 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1171 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1172 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1174 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1176 //To handle START case of ellipsis, the first glyph has been shifted
1177 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1178 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1180 //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.
1181 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1183 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1184 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1186 // Ignore any glyph that removed for MIDDLE ellipsis
1189 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1191 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1195 // Retrieve the glyph's info.
1196 const GlyphInfo* glyphInfo;
1198 if(addHyphen && hyphens)
1200 glyphInfo = hyphens + hyphenIndex;
1205 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1208 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1209 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1211 // Nothing to do if the glyph's width or height is zero.
1215 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1216 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1217 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1218 currentUnderlineHeight = currentUnderlineProperties.height;
1219 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1221 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1222 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1223 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1224 currentStrikethroughHeight = currentStrikethroughProperties.height;
1225 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1227 // Are we still using the same fontId as previous
1228 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1230 // We need to fetch fresh font underline metrics
1231 FontMetrics fontMetrics;
1232 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1234 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1235 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1239 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1242 if(strikethroughGlyph)
1244 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1247 // Update lastFontId because fontId is changed
1248 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1251 // Retrieves the glyph's position.
1252 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1256 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1257 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1258 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1259 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1260 position.y = -glyphInfo->yBearing;
1263 if(baseline < position.y + glyphInfo->yBearing)
1265 baseline = position.y + glyphInfo->yBearing;
1268 // Calculate the positions of leftmost and rightmost glyphs in the current line
1269 if(position.x < lineExtentLeft)
1271 lineExtentLeft = position.x;
1274 if(position.x + glyphInfo->width > lineExtentRight)
1276 lineExtentRight = position.x + glyphInfo->width;
1279 // Retrieves the glyph's color.
1280 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1283 if(style == Typesetter::STYLE_SHADOW)
1285 color = mModel->GetShadowColor();
1287 else if(style == Typesetter::STYLE_OUTLINE)
1289 color = mModel->GetOutlineColor();
1293 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1296 // Premultiply alpha
1301 // Retrieves the glyph's bitmap.
1302 glyphData.glyphBitmap.buffer = NULL;
1303 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1304 glyphData.glyphBitmap.height = glyphInfo->height;
1306 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1308 // Don't render outline for other styles
1309 outlineWidth = 0.0f;
1312 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1314 fontClient.CreateBitmap(glyphInfo->fontId,
1316 glyphInfo->isItalicRequired,
1317 glyphInfo->isBoldRequired,
1318 glyphData.glyphBitmap,
1319 static_cast<int32_t>(outlineWidth));
1322 // Sets the glyph's bitmap into the bitmap of the whole text.
1323 if(NULL != glyphData.glyphBitmap.buffer)
1325 if(style == Typesetter::STYLE_OUTLINE)
1327 // Set the position offset for the current glyph
1328 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1329 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1332 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1333 TypesetGlyph(glyphData,
1339 if(style == Typesetter::STYLE_OUTLINE)
1341 // Reset the position offset for the next glyph
1342 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1343 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1346 // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1347 delete[] glyphData.glyphBitmap.buffer;
1348 glyphData.glyphBitmap.buffer = NULL;
1353 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1358 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1366 // Draw the underline from the leftmost glyph to the rightmost glyph
1367 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1369 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1372 // Draw the background color from the leftmost glyph to the rightmost glyph
1373 if(style == Typesetter::STYLE_BACKGROUND)
1375 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1378 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1379 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1381 //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.
1382 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.
1383 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1386 // Increases the vertical offset with the line's descender & line spacing.
1387 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1390 return glyphData.bitmapBuffer;
1393 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)
1395 // Underline-tags (this is for Markup case)
1396 // Get the underline runs.
1397 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1398 Vector<UnderlinedGlyphRun> underlineRuns;
1399 underlineRuns.Resize(numberOfUnderlineRuns);
1400 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1402 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1403 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1404 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1405 GlyphIndex startGlyphIndex, endGlyphIndex;
1407 //The outer loop to iterate on the separated chunks of underlined glyph runs
1408 while(itGlyphRun != endItGlyphRun)
1410 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1411 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1413 // Create the image buffer for underline
1414 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1415 // Combine the two buffers
1416 // Result pixel buffer will be stored into topPixelBuffer.
1417 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1422 return topPixelBuffer;
1425 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)
1427 // strikethrough-tags (this is for Markup case)
1428 // Get the strikethrough runs.
1429 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1430 Vector<StrikethroughGlyphRun> strikethroughRuns;
1431 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1432 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1434 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1435 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1436 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1437 GlyphIndex startGlyphIndex, endGlyphIndex;
1439 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1440 while(itGlyphRun != endItGlyphRun)
1442 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1443 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1445 // Create the image buffer for strikethrough
1446 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1447 // Combine the two buffers
1448 // Result pixel buffer will be stored into topPixelBuffer.
1449 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1454 return topPixelBuffer;
1457 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)
1459 // Apply the markup-Processor if enabled
1460 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1461 if(markupProcessorEnabled)
1463 topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1465 topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1468 return topPixelBuffer;
1471 Typesetter::Typesetter(const ModelInterface* const model)
1472 : mModel(new ViewModel(model))
1476 Typesetter::~Typesetter()
1483 } // namespace Toolkit