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);
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 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
118 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
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 // We can use memset here.
127 memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
128 bitmapBuffer += data.width;
133 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
135 // Pointer to the color glyph if there is one.
136 const uint8_t* glyphBuffer = data.glyphBitmap.buffer;
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 // Skip basic line of glyph.
148 glyphBuffer += (lineIndexRangeMin) * static_cast<int32_t>(data.glyphBitmap.width) * glyphPixelSize;
150 // Traverse the pixels of the glyph line per line.
153 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
155 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
157 const int32_t xOffsetIndex = xOffset + index;
159 // Retrieves the color from the color glyph.
160 uint32_t packedColorGlyph = *(reinterpret_cast<const uint32_t*>(glyphBuffer + (index << 2)));
161 uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
163 // Update the alpha channel.
164 const uint8_t colorAlpha = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
165 *(packedColorGlyphBuffer + 3u) = colorAlpha;
167 if(Typesetter::STYLE_SHADOW == style)
169 // The shadow of color glyph needs to have the shadow color.
170 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
171 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
172 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
178 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
181 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
182 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
183 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
185 if(data.glyphBitmap.isColorBitmap)
187 *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
188 *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
189 *packedColorGlyphBuffer = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
193 // Set the color into the final pixel buffer.
194 *(bitmapBuffer + xOffsetIndex) = packedColorGlyph;
196 bitmapBuffer += data.width;
197 glyphBuffer += data.glyphBitmap.width * glyphPixelSize;
202 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
204 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
206 // Update the alpha channel.
207 const uint8_t alpha = *(glyphBuffer + index);
209 // Copy non-transparent pixels only
212 const int32_t xOffsetIndex = xOffset + index;
214 // Check alpha of overlapped pixels
215 uint32_t& currentColor = *(bitmapBuffer + xOffsetIndex);
216 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
218 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
219 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
220 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
221 // happen, for example, in the RTL text when we copy glyphs from right to left).
222 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
223 currentAlpha = std::max(currentAlpha, alpha);
224 if(currentAlpha == 255)
226 // Fast-cut to avoid float type operation.
227 currentColor = packedInputColor;
231 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
232 // The format is RGBA8888.
233 uint32_t packedColor = 0u;
234 uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
236 // Color is pre-muliplied with its alpha.
237 *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
238 *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
239 *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
240 *(packedColorBuffer) = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
242 // Set the color into the final pixel buffer.
243 currentColor = packedColor;
247 bitmapBuffer += data.width;
248 glyphBuffer += data.glyphBitmap.width * glyphPixelSize;
254 // Below codes required only if not color glyph.
257 uint8_t* bitmapBuffer = data.bitmapBuffer.GetBuffer();
258 const uint8_t* glyphBuffer = data.glyphBitmap.buffer;
261 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
262 glyphBuffer += (lineIndexRangeMin) * static_cast<int32_t>(data.glyphBitmap.width);
264 // Traverse the pixels of the glyph line per line.
265 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
267 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
269 const int32_t xOffsetIndex = xOffset + index;
271 // Update the alpha channel.
272 const uint8_t alpha = *(glyphBuffer + index);
274 // Copy non-transparent pixels only
277 // Check alpha of overlapped pixels
278 uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
280 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
281 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
282 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
283 // happen, for example, in the RTL text when we copy glyphs from right to left).
284 currentAlpha = std::max(currentAlpha, alpha);
288 bitmapBuffer += data.width;
289 glyphBuffer += data.glyphBitmap.width;
295 /// Draws the specified underline color to the buffer
297 const uint32_t& bufferWidth,
298 const uint32_t& bufferHeight,
299 GlyphData& glyphData,
300 const float& baseline,
301 const float& currentUnderlinePosition,
302 const float& maxUnderlineHeight,
303 const float& lineExtentLeft,
304 const float& lineExtentRight,
305 const UnderlineStyleProperties& commonUnderlineProperties,
306 const UnderlineStyleProperties& currentUnderlineProperties,
309 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
310 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
311 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
312 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
314 int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
316 const uint32_t yRangeMin = underlineYOffset;
317 const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
318 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
319 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
321 // If current glyph don't need to be rendered, just ignore.
322 if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
327 // We can optimize by memset when underlineColor.a is near zero
328 uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
330 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
332 // Skip yRangeMin line.
333 bitmapBuffer += yRangeMin * glyphData.width;
335 // Note if underlineType is DASHED, we cannot setup color by memset.
336 if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
338 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
340 // We can use memset.
341 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
342 bitmapBuffer += glyphData.width;
344 if(underlineType == Text::Underline::DOUBLE)
346 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
347 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
348 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
350 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
351 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
353 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
355 // We can use memset.
356 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
357 bitmapBuffer += glyphData.width;
363 uint32_t packedUnderlineColor = 0u;
364 uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
366 // Write the color to the pixel buffer
367 *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
368 *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
369 *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
370 *(packedUnderlineColorBuffer) = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
372 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
374 if(underlineType == Text::Underline::DASHED)
376 float dashWidth = dashedUnderlineWidth;
379 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
381 if(dashGap == 0 && dashWidth > 0)
383 // Note : this is same logic as bitmap[y][x] = underlineColor;
384 *(bitmapBuffer + x) = packedUnderlineColor;
387 else if(dashGap < dashedUnderlineGap)
394 dashWidth = dashedUnderlineWidth;
401 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
403 // Note : this is same logic as bitmap[y][x] = underlineColor;
404 *(bitmapBuffer + x) = packedUnderlineColor;
407 bitmapBuffer += glyphData.width;
409 if(underlineType == Text::Underline::DOUBLE)
411 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
412 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
413 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
415 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
416 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
418 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
420 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
422 // Note : this is same logic as bitmap[y][x] = underlineColor;
423 *(bitmapBuffer + x) = packedUnderlineColor;
425 bitmapBuffer += glyphData.width;
431 /// Draws the background color to the buffer
432 void DrawBackgroundColor(
433 Vector4 backgroundColor,
434 const uint32_t& bufferWidth,
435 const uint32_t& bufferHeight,
436 GlyphData& glyphData,
437 const float& baseline,
439 const float& lineExtentLeft,
440 const float& lineExtentRight)
442 const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
443 const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
444 const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
445 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
447 // If current glyph don't need to be rendered, just ignore.
448 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
453 // We can optimize by memset when backgroundColor.a is near zero
454 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
456 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
458 // Skip yRangeMin line.
459 bitmapBuffer += yRangeMin * glyphData.width;
461 if(backgroundColorAlpha == 0)
463 for(int32_t y = yRangeMin; y < yRangeMax; y++)
465 // We can use memset.
466 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
467 bitmapBuffer += glyphData.width;
472 uint32_t packedBackgroundColor = 0u;
473 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
475 // Write the color to the pixel buffer
476 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
477 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
478 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
479 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
481 for(int32_t y = yRangeMin; y < yRangeMax; y++)
483 for(int32_t x = xRangeMin; x < xRangeMax; x++)
485 // Note : this is same logic as bitmap[y][x] = backgroundColor;
486 *(bitmapBuffer + x) = packedBackgroundColor;
488 bitmapBuffer += glyphData.width;
493 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)
495 // Retrieve lines, glyphs, positions and colors from the view model.
496 const Length modelNumberOfLines = model->GetNumberOfLines();
497 const LineRun* const modelLinesBuffer = model->GetLines();
498 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
499 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
500 const Vector2* const positionBuffer = model->GetLayout();
501 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
502 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
504 const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
506 // Create and initialize the pixel buffer.
508 glyphData.verticalOffset = verticalOffset;
509 glyphData.width = bufferWidth;
510 glyphData.height = bufferHeight;
511 glyphData.bitmapBuffer = buffer;
512 glyphData.horizontalOffset = 0;
514 ColorIndex prevBackgroundColorIndex = 0;
515 ColorIndex backgroundColorIndex = 0;
517 // Traverses the lines of the text.
518 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
520 const LineRun& line = *(modelLinesBuffer + lineIndex);
522 // Sets the horizontal offset of the line.
523 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
524 glyphData.horizontalOffset += horizontalOffset;
526 // Increases the vertical offset with the line's ascender.
527 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
529 float left = bufferWidth;
531 float baseline = 0.0f;
533 // Traverses the glyphs of the line.
534 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
535 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
537 // Retrieve the glyph's info.
538 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
540 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
541 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
543 // Nothing to do if default background color, the glyph's width or height is zero.
547 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
549 if((backgroundColorIndex != prevBackgroundColorIndex) &&
550 (prevBackgroundColorIndex != 0u))
552 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
553 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
556 if(backgroundColorIndex == 0u)
558 prevBackgroundColorIndex = backgroundColorIndex;
559 //if background color is the default do nothing
563 // Retrieves the glyph's position.
564 const Vector2* const position = positionBuffer + glyphIndex;
566 if(baseline < position->y + glyphInfo->yBearing)
568 baseline = position->y + glyphInfo->yBearing;
571 // Calculate the positions of leftmost and rightmost glyphs in the current line
572 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
574 left = position->x - glyphInfo->xBearing;
577 if(position->x + glyphInfo->width > right)
579 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
582 prevBackgroundColorIndex = backgroundColorIndex;
585 //draw last background at line end if not default
586 if(backgroundColorIndex != 0u)
588 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
589 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
592 // Increases the vertical offset with the line's descender.
593 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
596 return glyphData.bitmapBuffer;
599 /// Draws the specified strikethrough color to the buffer
600 void DrawStrikethrough(const uint32_t& bufferWidth,
601 const uint32_t& bufferHeight,
602 GlyphData& glyphData,
603 const float& baseline,
604 const float& strikethroughStartingYPosition,
605 const float& maxStrikethroughHeight,
606 const float& lineExtentLeft,
607 const float& lineExtentRight,
608 const StrikethroughStyleProperties& commonStrikethroughProperties,
609 const StrikethroughStyleProperties& currentStrikethroughProperties,
612 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
614 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
615 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
616 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
617 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
619 // If current glyph don't need to be rendered, just ignore.
620 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
625 // We can optimize by memset when strikethroughColor.a is near zero
626 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
628 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
630 // Skip yRangeMin line.
631 bitmapBuffer += yRangeMin * glyphData.width;
633 if(strikethroughColorAlpha == 0)
635 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
637 // We can use memset.
638 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
639 bitmapBuffer += glyphData.width;
644 uint32_t packedStrikethroughColor = 0u;
645 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
647 // Write the color to the pixel buffer
648 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
649 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
650 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
651 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
653 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
655 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
657 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
658 *(bitmapBuffer + x) = packedStrikethroughColor;
660 bitmapBuffer += glyphData.width;
666 * @brief Create an initialized image buffer filled with transparent color.
668 * Creates the pixel data used to generate the final image with the given size.
670 * @param[in] bufferWidth The width of the image buffer.
671 * @param[in] bufferHeight The height of the image buffer.
672 * @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).
674 * @return An image buffer.
676 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t& bufferWidth, const uint32_t& bufferHeight, const Pixel::Format& pixelFormat)
678 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
680 if(Pixel::RGBA8888 == pixelFormat)
682 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
683 const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
684 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
688 memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
695 * @brief Combine the two RGBA image buffers together.
697 * The top layer buffer will blend over the bottom layer buffer:
698 * - If the pixel is not fully opaque from either buffer, it will be blended with
699 * the pixel from the other buffer and copied to the combined buffer.
700 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
701 * buffer will be copied to the combined buffer.
703 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
704 * We can determine which pixelBuffer's memory is destination
706 * @param[in, out] topPixelBuffer The top layer buffer.
707 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
708 * @param[in] bufferWidth The width of the image buffer.
709 * @param[in] bufferHeight The height of the image buffer.
710 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
711 * False if we store the combined image buffer result into bottomPixelBuffer.
714 void CombineImageBuffer(Devel::PixelBuffer& topPixelBuffer, Devel::PixelBuffer& bottomPixelBuffer, const uint32_t& bufferWidth, const uint32_t& bufferHeight, bool storeResultIntoTop)
716 // Assume that we always combine two RGBA images
717 // Jump with 4bytes for optimize runtime.
718 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
719 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
721 if(topBuffer == NULL && bottomBuffer == NULL)
723 // Nothing to do if both buffers are empty.
727 if(topBuffer == NULL)
729 // Nothing to do if topBuffer is empty.
730 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
731 if(storeResultIntoTop)
733 topPixelBuffer = bottomPixelBuffer;
738 if(bottomBuffer == NULL)
740 // Nothing to do if bottomBuffer is empty.
741 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
742 if(!storeResultIntoTop)
744 bottomPixelBuffer = topPixelBuffer;
749 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
751 uint32_t* combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
752 uint8_t* topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
754 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
756 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
757 // Otherwise, copy pixel from topBuffer to combinedBuffer.
758 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
760 uint8_t topAlpha = *topAlphaBufferPointer;
764 // Copy the pixel from bottomBuffer to combinedBuffer
765 if(storeResultIntoTop)
767 *(combinedBuffer) = *(bottomBuffer);
770 else if(topAlpha == 255)
772 // Copy the pixel from topBuffer to combinedBuffer
773 if(!storeResultIntoTop)
775 *(combinedBuffer) = *(topBuffer);
780 // At least one pixel is not fully opaque
781 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
782 uint32_t blendedBottomBufferColor = *(bottomBuffer);
783 uint8_t* blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
785 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
786 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
787 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
788 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
790 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
793 // Increase each buffer's pointer.
797 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
803 TypesetterPtr Typesetter::New(const ModelInterface* const model)
805 return TypesetterPtr(new Typesetter(model));
808 ViewModel* Typesetter::GetViewModel()
813 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
815 // @todo. This initial implementation for a TextLabel has only one visible page.
817 // Elides the text if needed.
818 mModel->ElideGlyphs();
820 // Retrieves the layout size.
821 const Size& layoutSize = mModel->GetLayoutSize();
823 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
825 // Set the offset for the horizontal alignment according to the text direction and outline width.
828 switch(mModel->GetHorizontalAlignment())
830 case HorizontalAlignment::BEGIN:
835 case HorizontalAlignment::CENTER:
837 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
840 case HorizontalAlignment::END:
842 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
847 // Set the offset for the vertical alignment.
850 switch(mModel->GetVerticalAlignment())
852 case VerticalAlignment::TOP:
857 case VerticalAlignment::CENTER:
859 penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
860 penY = penY < 0.f ? 0.f : penY;
863 case VerticalAlignment::BOTTOM:
865 penY = static_cast<int32_t>(size.height - layoutSize.height);
870 // Generate the image buffers of the text for each different style first,
871 // then combine all of them together as one final image buffer. We try to
872 // do all of these in CPU only, so that once the final texture is generated,
873 // no calculation is needed in GPU during each frame.
875 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
876 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
878 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
879 const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
881 //Elided text in ellipsis at START could start on index greater than 0
882 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
883 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
885 Devel::PixelBuffer imageBuffer;
887 if(RENDER_MASK == behaviour)
889 // Generate the image buffer as an alpha mask for color glyphs.
890 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
892 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
894 // Generate an empty image buffer so that it can been combined with the image buffers for styles
895 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
896 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
900 // Generate the image buffer for the text with no style.
901 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
904 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
906 // Generate the outline if enabled
907 const uint16_t outlineWidth = mModel->GetOutlineWidth();
908 if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
910 // Create the image buffer for outline
911 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
913 // Combine the two buffers
914 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
917 // @todo. Support shadow and underline for partial text later on.
919 // Generate the shadow if enabled
920 const Vector2& shadowOffset = mModel->GetShadowOffset();
921 if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
923 // Create the image buffer for shadow
924 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
926 // Check whether it will be a soft shadow
927 const float& blurRadius = mModel->GetShadowBlurRadius();
929 if(blurRadius > Math::MACHINE_EPSILON_1)
931 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
934 // Combine the two buffers
935 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
938 // Generate the underline if enabled
939 const bool underlineEnabled = mModel->IsUnderlineEnabled();
940 if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
942 // Create the image buffer for underline
943 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
945 // Combine the two buffers
946 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
949 // Generate the background if enabled
950 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
951 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
952 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
954 Devel::PixelBuffer backgroundImageBuffer;
956 if(backgroundEnabled)
958 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
962 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
965 if(backgroundMarkupSet)
967 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
970 // Combine the two buffers
971 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
974 // Generate the strikethrough if enabled
975 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
976 if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
978 // Create the image buffer for strikethrough
979 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
981 // Combine the two buffers
982 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
987 imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
990 // Create the final PixelData for the combined image buffer
991 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
996 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)
998 // Retrieve lines, glyphs, positions and colors from the view model.
999 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1000 const LineRun* const modelLinesBuffer = mModel->GetLines();
1001 const GlyphInfo* const glyphsBuffer = mModel->GetGlyphs();
1002 const Vector2* const positionBuffer = mModel->GetLayout();
1003 const Vector4* const colorsBuffer = mModel->GetColors();
1004 const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
1005 const GlyphInfo* hyphens = mModel->GetHyphens();
1006 const Length* hyphenIndices = mModel->GetHyphenIndices();
1007 const Length hyphensCount = mModel->GetHyphensCount();
1009 // Elided text info. Indices according to elided text and Ellipsis position.
1010 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1011 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1012 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1013 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1014 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1016 // Whether to use the default color.
1017 const bool useDefaultColor = (NULL == colorsBuffer);
1018 const Vector4& defaultColor = mModel->GetDefaultColor();
1020 // Create and initialize the pixel buffer.
1021 GlyphData glyphData;
1022 glyphData.verticalOffset = verticalOffset;
1023 glyphData.width = bufferWidth;
1024 glyphData.height = bufferHeight;
1025 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1026 glyphData.horizontalOffset = 0;
1028 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1029 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1030 Length hyphenIndex = 0;
1032 const Character* textBuffer = mModel->GetTextBuffer();
1033 float calculatedAdvance = 0.f;
1034 const Vector<CharacterIndex>& glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1035 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1037 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1039 // Traverses the lines of the text.
1040 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1042 const LineRun& line = *(modelLinesBuffer + lineIndex);
1044 // Sets the horizontal offset of the line.
1045 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1046 glyphData.horizontalOffset += horizontalOffset;
1048 // Increases the vertical offset with the line's ascender.
1049 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1051 // Retrieves the glyph's outline width
1052 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1054 if(style == Typesetter::STYLE_OUTLINE)
1056 glyphData.horizontalOffset -= outlineWidth;
1059 // Only need to add the vertical outline offset for the first line
1060 glyphData.verticalOffset -= outlineWidth;
1063 else if(style == Typesetter::STYLE_SHADOW)
1065 const Vector2& shadowOffset = mModel->GetShadowOffset();
1066 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1070 // Only need to add the vertical shadow offset for first line
1071 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1075 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1076 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1077 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1079 // Get the character-spacing runs.
1080 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1082 // Aggregate underline-style-properties from mModel
1083 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1084 mModel->GetUnderlineColor(),
1085 mModel->GetUnderlineHeight(),
1086 mModel->GetDashedUnderlineGap(),
1087 mModel->GetDashedUnderlineWidth(),
1094 // Aggregate strikethrough-style-properties from mModel
1095 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1096 mModel->GetStrikethroughHeight(),
1100 // Get the underline runs.
1101 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1102 Vector<UnderlinedGlyphRun> underlineRuns;
1103 underlineRuns.Resize(numberOfUnderlineRuns);
1104 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1106 // Get the strikethrough runs.
1107 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1108 Vector<StrikethroughGlyphRun> strikethroughRuns;
1109 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1110 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1112 bool thereAreUnderlinedGlyphs = false;
1113 bool thereAreStrikethroughGlyphs = false;
1115 float currentUnderlinePosition = 0.0f;
1116 float currentUnderlineHeight = modelUnderlineProperties.height;
1117 float maxUnderlineHeight = currentUnderlineHeight;
1118 auto currentUnderlineProperties = modelUnderlineProperties;
1120 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1121 float maxStrikethroughHeight = currentStrikethroughHeight;
1122 auto currentStrikethroughProperties = modelStrikethroughProperties;
1123 float strikethroughStartingYPosition = 0.0f;
1125 FontId lastFontId = 0;
1127 float lineExtentLeft = bufferWidth;
1128 float lineExtentRight = 0.0f;
1129 float baseline = 0.0f;
1130 bool addHyphen = false;
1132 // Traverses the glyphs of the line.
1133 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1134 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1135 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1137 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1139 //To handle START case of ellipsis, the first glyph has been shifted
1140 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1141 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1143 //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.
1144 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1146 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1147 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1149 // Ignore any glyph that removed for MIDDLE ellipsis
1152 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1154 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1158 // Retrieve the glyph's info.
1159 const GlyphInfo* glyphInfo;
1161 if(addHyphen && hyphens)
1163 glyphInfo = hyphens + hyphenIndex;
1168 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1171 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1172 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1174 // Nothing to do if the glyph's width or height is zero.
1178 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1179 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1180 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1181 currentUnderlineHeight = currentUnderlineProperties.height;
1182 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1184 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1185 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1186 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1187 currentStrikethroughHeight = currentStrikethroughProperties.height;
1188 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1190 // Are we still using the same fontId as previous
1191 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1193 // We need to fetch fresh font underline metrics
1194 FontMetrics fontMetrics;
1195 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1197 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1198 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1202 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1205 if(strikethroughGlyph)
1207 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1210 // Update lastFontId because fontId is changed
1211 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1214 // Retrieves the glyph's position.
1215 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1219 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1220 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1221 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1222 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1223 position.y = -glyphInfo->yBearing;
1226 if(baseline < position.y + glyphInfo->yBearing)
1228 baseline = position.y + glyphInfo->yBearing;
1231 // Calculate the positions of leftmost and rightmost glyphs in the current line
1232 if(position.x < lineExtentLeft)
1234 lineExtentLeft = position.x;
1237 if(position.x + glyphInfo->width > lineExtentRight)
1239 lineExtentRight = position.x + glyphInfo->width;
1242 // Retrieves the glyph's color.
1243 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1246 if(style == Typesetter::STYLE_SHADOW)
1248 color = mModel->GetShadowColor();
1250 else if(style == Typesetter::STYLE_OUTLINE)
1252 color = mModel->GetOutlineColor();
1256 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1259 // Premultiply alpha
1264 // Retrieves the glyph's bitmap.
1265 glyphData.glyphBitmap.buffer = NULL;
1266 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1267 glyphData.glyphBitmap.height = glyphInfo->height;
1269 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1271 // Don't render outline for other styles
1272 outlineWidth = 0.0f;
1275 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1277 fontClient.CreateBitmap(glyphInfo->fontId,
1279 glyphInfo->isItalicRequired,
1280 glyphInfo->isBoldRequired,
1281 glyphData.glyphBitmap,
1282 static_cast<int32_t>(outlineWidth));
1285 // Sets the glyph's bitmap into the bitmap of the whole text.
1286 if(NULL != glyphData.glyphBitmap.buffer)
1288 if(style == Typesetter::STYLE_OUTLINE)
1290 // Set the position offset for the current glyph
1291 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1292 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1295 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1296 TypesetGlyph(glyphData,
1302 if(style == Typesetter::STYLE_OUTLINE)
1304 // Reset the position offset for the next glyph
1305 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1306 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1309 // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1310 delete[] glyphData.glyphBitmap.buffer;
1311 glyphData.glyphBitmap.buffer = NULL;
1316 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1321 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1329 // Draw the underline from the leftmost glyph to the rightmost glyph
1330 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1332 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1335 // Draw the background color from the leftmost glyph to the rightmost glyph
1336 if(style == Typesetter::STYLE_BACKGROUND)
1338 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1341 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1342 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1344 //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.
1345 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.
1346 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1349 // Increases the vertical offset with the line's descender & line spacing.
1350 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1353 return glyphData.bitmapBuffer;
1356 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)
1358 // Underline-tags (this is for Markup case)
1359 // Get the underline runs.
1360 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1361 Vector<UnderlinedGlyphRun> underlineRuns;
1362 underlineRuns.Resize(numberOfUnderlineRuns);
1363 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1365 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1366 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1367 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1368 GlyphIndex startGlyphIndex, endGlyphIndex;
1370 //The outer loop to iterate on the separated chunks of underlined glyph runs
1371 while(itGlyphRun != endItGlyphRun)
1373 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1374 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1376 // Create the image buffer for underline
1377 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1378 // Combine the two buffers
1379 // Result pixel buffer will be stored into topPixelBuffer.
1380 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1385 return topPixelBuffer;
1388 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)
1390 // strikethrough-tags (this is for Markup case)
1391 // Get the strikethrough runs.
1392 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1393 Vector<StrikethroughGlyphRun> strikethroughRuns;
1394 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1395 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1397 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1398 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1399 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1400 GlyphIndex startGlyphIndex, endGlyphIndex;
1402 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1403 while(itGlyphRun != endItGlyphRun)
1405 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1406 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1408 // Create the image buffer for strikethrough
1409 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1410 // Combine the two buffers
1411 // Result pixel buffer will be stored into topPixelBuffer.
1412 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1417 return topPixelBuffer;
1420 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)
1422 // Apply the markup-Processor if enabled
1423 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1424 if(markupProcessorEnabled)
1426 topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1428 topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1431 return topPixelBuffer;
1434 Typesetter::Typesetter(const ModelInterface* const model)
1435 : mModel(new ViewModel(model))
1439 Typesetter::~Typesetter()
1446 } // namespace Toolkit