2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/rendering/text-typesetter.h>
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/public-api/common/constants.h>
27 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
28 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
29 #include <dali-toolkit/internal/text/line-helper-functions.h>
30 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
31 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
32 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
33 #include <dali-toolkit/internal/text/rendering/view-model.h>
43 const float HALF(0.5f);
44 const float ONE_AND_A_HALF(1.5f);
47 * @brief Fast multiply & divide by 255. It wiil be useful when we applying alpha value in color
49 * @param x The value between [0..255]
50 * @param y The value between [0..255]
53 inline uint8_t MultiplyAndNormalizeColor(const uint8_t& x, const uint8_t& y) noexcept
55 const uint32_t xy = static_cast<const uint32_t>(x) * y;
56 return ((xy << 15) + (xy << 7) + xy) >> 23;
60 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
64 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
65 Vector2* position; ///< The position of the glyph.
66 TextAbstraction::FontClient::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
67 uint32_t width; ///< The bitmap's width.
68 uint32_t height; ///< The bitmap's height.
69 int32_t horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
70 int32_t verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
74 * @brief Sets the glyph's buffer into the bitmap's buffer.
76 * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
77 * @param[in] position The position of the glyph.
78 * @param[in] color The color of the glyph.
79 * @param[in] style The style of the text.
80 * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
82 void TypesetGlyph(GlyphData& data,
83 const Vector2* const position,
84 const Vector4* const color,
85 Typesetter::Style style,
86 Pixel::Format pixelFormat)
88 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
90 // Nothing to do if the width or height of the buffer is zero.
94 // Initial vertical / horizontal offset.
95 const int32_t yOffset = data.verticalOffset + position->y;
96 const int32_t xOffset = data.horizontalOffset + position->x;
98 // Whether the given glyph is a color one.
99 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
100 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
101 const uint32_t glyphAlphaIndex = glyphPixelSize - 1u;
103 // Determinate iterator range.
104 const int32_t lineIndexRangeMin = std::max(0, -yOffset);
105 const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
106 const int32_t indexRangeMin = std::max(0, -xOffset);
107 const int32_t indexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
109 // If current glyph don't need to be rendered, just ignore.
110 if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
115 if(Pixel::RGBA8888 == pixelFormat)
117 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
119 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
121 // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
122 // Just overwrite transparent color and return.
123 if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
125 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
127 // We can use memset here.
128 memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
129 bitmapBuffer += data.width;
134 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
136 // Pointer to the color glyph if there is one.
137 const uint8_t* glyphBuffer = data.glyphBitmap.buffer;
139 // Precalculate input color's packed result.
140 uint32_t packedInputColor = 0u;
141 uint8_t* packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
143 *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
144 *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
145 *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
146 *(packedInputColorBuffer) = static_cast<uint8_t>(color->r * 255);
148 // Skip basic line of glyph.
149 glyphBuffer += (lineIndexRangeMin) * static_cast<int32_t>(data.glyphBitmap.width) * glyphPixelSize;
151 // Traverse the pixels of the glyph line per line.
154 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
156 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
158 const int32_t xOffsetIndex = xOffset + index;
160 // Retrieves the color from the color glyph.
161 uint32_t packedColorGlyph = *(reinterpret_cast<const uint32_t*>(glyphBuffer + (index << 2)));
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 + xOffsetIndex) = packedColorGlyph;
197 bitmapBuffer += data.width;
198 glyphBuffer += data.glyphBitmap.width * glyphPixelSize;
203 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
205 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
207 // Update the alpha channel.
208 const uint8_t alpha = *(glyphBuffer + index * glyphPixelSize + glyphAlphaIndex);
210 // Copy non-transparent pixels only
213 const int32_t xOffsetIndex = xOffset + index;
215 // Check alpha of overlapped pixels
216 uint32_t& currentColor = *(bitmapBuffer + xOffsetIndex);
217 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
219 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
220 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
221 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
222 // happen, for example, in the RTL text when we copy glyphs from right to left).
223 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
224 currentAlpha = std::max(currentAlpha, alpha);
225 if(currentAlpha == 255)
227 // Fast-cut to avoid float type operation.
228 currentColor = packedInputColor;
232 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
233 // The format is RGBA8888.
234 uint32_t packedColor = 0u;
235 uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
237 // Color is pre-muliplied with its alpha.
238 *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
239 *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
240 *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
241 *(packedColorBuffer) = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
243 // Set the color into the final pixel buffer.
244 currentColor = packedColor;
248 bitmapBuffer += data.width;
249 glyphBuffer += data.glyphBitmap.width * glyphPixelSize;
255 // Below codes required only if not color glyph.
258 uint8_t* bitmapBuffer = data.bitmapBuffer.GetBuffer();
259 const uint8_t* glyphBuffer = data.glyphBitmap.buffer;
262 bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
263 glyphBuffer += (lineIndexRangeMin) * static_cast<int32_t>(data.glyphBitmap.width) * glyphPixelSize;
265 // Traverse the pixels of the glyph line per line.
266 for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
268 for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
270 const int32_t xOffsetIndex = xOffset + index;
272 // Update the alpha channel.
273 const uint8_t alpha = *(glyphBuffer + index * glyphPixelSize + glyphAlphaIndex);
275 // Copy non-transparent pixels only
278 // Check alpha of overlapped pixels
279 uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
281 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
282 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
283 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
284 // happen, for example, in the RTL text when we copy glyphs from right to left).
285 currentAlpha = std::max(currentAlpha, alpha);
289 bitmapBuffer += data.width;
290 glyphBuffer += data.glyphBitmap.width * glyphPixelSize;
296 /// Draws the specified underline color to the buffer
298 const uint32_t& bufferWidth,
299 const uint32_t& bufferHeight,
300 GlyphData& glyphData,
301 const float& baseline,
302 const float& currentUnderlinePosition,
303 const float& maxUnderlineHeight,
304 const float& lineExtentLeft,
305 const float& lineExtentRight,
306 const UnderlineStyleProperties& commonUnderlineProperties,
307 const UnderlineStyleProperties& currentUnderlineProperties,
310 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
311 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
312 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
313 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
315 int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
317 const uint32_t yRangeMin = underlineYOffset;
318 const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
319 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
320 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
322 // If current glyph don't need to be rendered, just ignore.
323 if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
328 // We can optimize by memset when underlineColor.a is near zero
329 uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
331 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
333 // Skip yRangeMin line.
334 bitmapBuffer += yRangeMin * glyphData.width;
336 // Note if underlineType is DASHED, we cannot setup color by memset.
337 if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
339 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
341 // We can use memset.
342 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
343 bitmapBuffer += glyphData.width;
345 if(underlineType == Text::Underline::DOUBLE)
347 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
348 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
349 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
351 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
352 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
354 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
356 // We can use memset.
357 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
358 bitmapBuffer += glyphData.width;
364 uint32_t packedUnderlineColor = 0u;
365 uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
367 // Write the color to the pixel buffer
368 *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
369 *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
370 *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
371 *(packedUnderlineColorBuffer) = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
373 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
375 if(underlineType == Text::Underline::DASHED)
377 float dashWidth = dashedUnderlineWidth;
380 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
382 if(dashGap == 0 && dashWidth > 0)
384 // Note : this is same logic as bitmap[y][x] = underlineColor;
385 *(bitmapBuffer + x) = packedUnderlineColor;
388 else if(dashGap < dashedUnderlineGap)
395 dashWidth = dashedUnderlineWidth;
402 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
404 // Note : this is same logic as bitmap[y][x] = underlineColor;
405 *(bitmapBuffer + x) = packedUnderlineColor;
408 bitmapBuffer += glyphData.width;
410 if(underlineType == Text::Underline::DOUBLE)
412 int32_t secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
413 const uint32_t secondYRangeMin = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
414 const uint32_t secondYRangeMax = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
416 // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
417 bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
419 for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
421 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
423 // Note : this is same logic as bitmap[y][x] = underlineColor;
424 *(bitmapBuffer + x) = packedUnderlineColor;
426 bitmapBuffer += glyphData.width;
432 /// Draws the background color to the buffer
433 void DrawBackgroundColor(
434 Vector4 backgroundColor,
435 const uint32_t& bufferWidth,
436 const uint32_t& bufferHeight,
437 GlyphData& glyphData,
438 const float& baseline,
440 const float& lineExtentLeft,
441 const float& lineExtentRight)
443 const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
444 const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
445 const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
446 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
448 // If current glyph don't need to be rendered, just ignore.
449 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
454 // We can optimize by memset when backgroundColor.a is near zero
455 uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
457 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
459 // Skip yRangeMin line.
460 bitmapBuffer += yRangeMin * glyphData.width;
462 if(backgroundColorAlpha == 0)
464 for(int32_t y = yRangeMin; y < yRangeMax; y++)
466 // We can use memset.
467 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
468 bitmapBuffer += glyphData.width;
473 uint32_t packedBackgroundColor = 0u;
474 uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
476 // Write the color to the pixel buffer
477 *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
478 *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
479 *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
480 *(packedBackgroundColorBuffer) = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
482 for(int32_t y = yRangeMin; y < yRangeMax; y++)
484 for(int32_t x = xRangeMin; x < xRangeMax; x++)
486 // Note : this is same logic as bitmap[y][x] = backgroundColor;
487 *(bitmapBuffer + x) = packedBackgroundColor;
489 bitmapBuffer += glyphData.width;
494 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)
496 // Retrieve lines, glyphs, positions and colors from the view model.
497 const Length modelNumberOfLines = model->GetNumberOfLines();
498 const LineRun* const modelLinesBuffer = model->GetLines();
499 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
500 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
501 const Vector2* const positionBuffer = model->GetLayout();
502 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
503 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
505 const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
507 // Create and initialize the pixel buffer.
509 glyphData.verticalOffset = verticalOffset;
510 glyphData.width = bufferWidth;
511 glyphData.height = bufferHeight;
512 glyphData.bitmapBuffer = buffer;
513 glyphData.horizontalOffset = 0;
515 ColorIndex prevBackgroundColorIndex = 0;
516 ColorIndex backgroundColorIndex = 0;
518 // Traverses the lines of the text.
519 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
521 const LineRun& line = *(modelLinesBuffer + lineIndex);
523 // Sets the horizontal offset of the line.
524 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
525 glyphData.horizontalOffset += horizontalOffset;
527 // Increases the vertical offset with the line's ascender.
528 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
530 float left = bufferWidth;
532 float baseline = 0.0f;
534 // Traverses the glyphs of the line.
535 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
536 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
538 // Retrieve the glyph's info.
539 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
541 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
542 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
544 // Nothing to do if default background color, the glyph's width or height is zero.
548 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
550 if((backgroundColorIndex != prevBackgroundColorIndex) &&
551 (prevBackgroundColorIndex != 0u))
553 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
554 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
557 if(backgroundColorIndex == 0u)
559 prevBackgroundColorIndex = backgroundColorIndex;
560 //if background color is the default do nothing
564 // Retrieves the glyph's position.
565 const Vector2* const position = positionBuffer + glyphIndex;
567 if(baseline < position->y + glyphInfo->yBearing)
569 baseline = position->y + glyphInfo->yBearing;
572 // Calculate the positions of leftmost and rightmost glyphs in the current line
573 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
575 left = position->x - glyphInfo->xBearing;
578 if(position->x + glyphInfo->width > right)
580 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
583 prevBackgroundColorIndex = backgroundColorIndex;
586 //draw last background at line end if not default
587 if(backgroundColorIndex != 0u)
589 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
590 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
593 // Increases the vertical offset with the line's descender.
594 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
597 return glyphData.bitmapBuffer;
600 /// Draws the specified strikethrough color to the buffer
601 void DrawStrikethrough(const uint32_t& bufferWidth,
602 const uint32_t& bufferHeight,
603 GlyphData& glyphData,
604 const float& baseline,
605 const float& strikethroughStartingYPosition,
606 const float& maxStrikethroughHeight,
607 const float& lineExtentLeft,
608 const float& lineExtentRight,
609 const StrikethroughStyleProperties& commonStrikethroughProperties,
610 const StrikethroughStyleProperties& currentStrikethroughProperties,
613 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
615 const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
616 const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
617 const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
618 const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
620 // If current glyph don't need to be rendered, just ignore.
621 if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
626 // We can optimize by memset when strikethroughColor.a is near zero
627 uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
629 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
631 // Skip yRangeMin line.
632 bitmapBuffer += yRangeMin * glyphData.width;
634 if(strikethroughColorAlpha == 0)
636 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
638 // We can use memset.
639 memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
640 bitmapBuffer += glyphData.width;
645 uint32_t packedStrikethroughColor = 0u;
646 uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
648 // Write the color to the pixel buffer
649 *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
650 *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
651 *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
652 *(packedStrikethroughColorBuffer) = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
654 for(uint32_t y = yRangeMin; y < yRangeMax; y++)
656 for(uint32_t x = xRangeMin; x < xRangeMax; x++)
658 // Note : this is same logic as bitmap[y][x] = strikethroughColor;
659 *(bitmapBuffer + x) = packedStrikethroughColor;
661 bitmapBuffer += glyphData.width;
667 * @brief Create an initialized image buffer filled with transparent color.
669 * Creates the pixel data used to generate the final image with the given size.
671 * @param[in] bufferWidth The width of the image buffer.
672 * @param[in] bufferHeight The height of the image buffer.
673 * @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).
675 * @return An image buffer.
677 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t& bufferWidth, const uint32_t& bufferHeight, const Pixel::Format& pixelFormat)
679 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
681 if(Pixel::RGBA8888 == pixelFormat)
683 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
684 const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
685 memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
689 memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
696 * @brief Combine the two RGBA image buffers together.
698 * The top layer buffer will blend over the bottom layer buffer:
699 * - If the pixel is not fully opaque from either buffer, it will be blended with
700 * the pixel from the other buffer and copied to the combined buffer.
701 * - If the pixels from both buffers are fully opaque, the pixels from the top layer
702 * buffer will be copied to the combined buffer.
704 * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
705 * We can determine which pixelBuffer's memory is destination
707 * @param[in, out] topPixelBuffer The top layer buffer.
708 * @param[in, out] bottomPixelBuffer The bottom layer buffer.
709 * @param[in] bufferWidth The width of the image buffer.
710 * @param[in] bufferHeight The height of the image buffer.
711 * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
712 * False if we store the combined image buffer result into bottomPixelBuffer.
715 void CombineImageBuffer(Devel::PixelBuffer& topPixelBuffer, Devel::PixelBuffer& bottomPixelBuffer, const uint32_t& bufferWidth, const uint32_t& bufferHeight, bool storeResultIntoTop)
717 // Assume that we always combine two RGBA images
718 // Jump with 4bytes for optimize runtime.
719 uint32_t* topBuffer = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
720 uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
722 if(topBuffer == NULL && bottomBuffer == NULL)
724 // Nothing to do if both buffers are empty.
728 if(topBuffer == NULL)
730 // Nothing to do if topBuffer is empty.
731 // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
732 if(storeResultIntoTop)
734 topPixelBuffer = bottomPixelBuffer;
739 if(bottomBuffer == NULL)
741 // Nothing to do if bottomBuffer is empty.
742 // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
743 if(!storeResultIntoTop)
745 bottomPixelBuffer = topPixelBuffer;
750 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
752 uint32_t* combinedBuffer = storeResultIntoTop ? topBuffer : bottomBuffer;
753 uint8_t* topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
755 for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
757 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
758 // Otherwise, copy pixel from topBuffer to combinedBuffer.
759 // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
761 uint8_t topAlpha = *topAlphaBufferPointer;
765 // Copy the pixel from bottomBuffer to combinedBuffer
766 if(storeResultIntoTop)
768 *(combinedBuffer) = *(bottomBuffer);
771 else if(topAlpha == 255)
773 // Copy the pixel from topBuffer to combinedBuffer
774 if(!storeResultIntoTop)
776 *(combinedBuffer) = *(topBuffer);
781 // At least one pixel is not fully opaque
782 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
783 uint32_t blendedBottomBufferColor = *(bottomBuffer);
784 uint8_t* blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
786 blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
787 blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
788 blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
789 blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
791 *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
794 // Increase each buffer's pointer.
798 topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
804 TypesetterPtr Typesetter::New(const ModelInterface* const model)
806 return TypesetterPtr(new Typesetter(model));
809 ViewModel* Typesetter::GetViewModel()
814 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
816 // @todo. This initial implementation for a TextLabel has only one visible page.
818 // Elides the text if needed.
819 mModel->ElideGlyphs();
821 // Retrieves the layout size.
822 const Size& layoutSize = mModel->GetLayoutSize();
824 const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
826 // Set the offset for the horizontal alignment according to the text direction and outline width.
829 switch(mModel->GetHorizontalAlignment())
831 case HorizontalAlignment::BEGIN:
836 case HorizontalAlignment::CENTER:
838 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
841 case HorizontalAlignment::END:
843 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
848 // Set the offset for the vertical alignment.
851 switch(mModel->GetVerticalAlignment())
853 case VerticalAlignment::TOP:
858 case VerticalAlignment::CENTER:
860 penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
861 penY = penY < 0.f ? 0.f : penY;
864 case VerticalAlignment::BOTTOM:
866 penY = static_cast<int32_t>(size.height - layoutSize.height);
871 // Generate the image buffers of the text for each different style first,
872 // then combine all of them together as one final image buffer. We try to
873 // do all of these in CPU only, so that once the final texture is generated,
874 // no calculation is needed in GPU during each frame.
876 const uint32_t bufferWidth = static_cast<uint32_t>(size.width);
877 const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
879 const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
880 const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
882 //Elided text in ellipsis at START could start on index greater than 0
883 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
884 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
886 Devel::PixelBuffer imageBuffer;
888 if(RENDER_MASK == behaviour)
890 // Generate the image buffer as an alpha mask for color glyphs.
891 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
893 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
895 // Generate an empty image buffer so that it can been combined with the image buffers for styles
896 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
897 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
901 // Generate the image buffer for the text with no style.
902 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
905 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
907 // Generate the outline if enabled
908 const uint16_t outlineWidth = mModel->GetOutlineWidth();
909 if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
911 // Create the image buffer for outline
912 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
914 // Combine the two buffers
915 CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
918 // @todo. Support shadow and underline for partial text later on.
920 // Generate the shadow if enabled
921 const Vector2& shadowOffset = mModel->GetShadowOffset();
922 if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
924 // Create the image buffer for shadow
925 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
927 // Check whether it will be a soft shadow
928 const float& blurRadius = mModel->GetShadowBlurRadius();
930 if(blurRadius > Math::MACHINE_EPSILON_1)
932 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
935 // Combine the two buffers
936 CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
939 // Generate the underline if enabled
940 const bool underlineEnabled = mModel->IsUnderlineEnabled();
941 if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
943 // Create the image buffer for underline
944 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
946 // Combine the two buffers
947 CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
950 // Generate the background if enabled
951 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
952 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
953 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
955 Devel::PixelBuffer backgroundImageBuffer;
957 if(backgroundEnabled)
959 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
963 backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
966 if(backgroundMarkupSet)
968 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
971 // Combine the two buffers
972 CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
975 // Generate the strikethrough if enabled
976 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
977 if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
979 // Create the image buffer for strikethrough
980 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
982 // Combine the two buffers
983 CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
988 imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
991 // Create the final PixelData for the combined image buffer
992 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
997 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)
999 // Retrieve lines, glyphs, positions and colors from the view model.
1000 const Length modelNumberOfLines = mModel->GetNumberOfLines();
1001 const LineRun* const modelLinesBuffer = mModel->GetLines();
1002 const GlyphInfo* const glyphsBuffer = mModel->GetGlyphs();
1003 const Vector2* const positionBuffer = mModel->GetLayout();
1004 const Vector4* const colorsBuffer = mModel->GetColors();
1005 const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
1006 const GlyphInfo* hyphens = mModel->GetHyphens();
1007 const Length* hyphenIndices = mModel->GetHyphenIndices();
1008 const Length hyphensCount = mModel->GetHyphensCount();
1010 // Elided text info. Indices according to elided text and Ellipsis position.
1011 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
1012 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
1013 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1014 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1015 const auto ellipsisPosition = mModel->GetEllipsisPosition();
1017 // Whether to use the default color.
1018 const bool useDefaultColor = (NULL == colorsBuffer);
1019 const Vector4& defaultColor = mModel->GetDefaultColor();
1021 // Create and initialize the pixel buffer.
1022 GlyphData glyphData;
1023 glyphData.verticalOffset = verticalOffset;
1024 glyphData.width = bufferWidth;
1025 glyphData.height = bufferHeight;
1026 glyphData.bitmapBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1027 glyphData.horizontalOffset = 0;
1029 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1030 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1031 Length hyphenIndex = 0;
1033 const Character* textBuffer = mModel->GetTextBuffer();
1034 float calculatedAdvance = 0.f;
1035 const Vector<CharacterIndex>& glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1036 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
1038 const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1040 // Traverses the lines of the text.
1041 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1043 const LineRun& line = *(modelLinesBuffer + lineIndex);
1045 // Sets the horizontal offset of the line.
1046 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1047 glyphData.horizontalOffset += horizontalOffset;
1049 // Increases the vertical offset with the line's ascender.
1050 glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1052 // Retrieves the glyph's outline width
1053 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1055 if(style == Typesetter::STYLE_OUTLINE)
1057 glyphData.horizontalOffset -= outlineWidth;
1060 // Only need to add the vertical outline offset for the first line
1061 glyphData.verticalOffset -= outlineWidth;
1064 else if(style == Typesetter::STYLE_SHADOW)
1066 const Vector2& shadowOffset = mModel->GetShadowOffset();
1067 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1071 // Only need to add the vertical shadow offset for first line
1072 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1076 const bool underlineEnabled = mModel->IsUnderlineEnabled();
1077 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
1078 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1080 // Get the character-spacing runs.
1081 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1083 // Aggregate underline-style-properties from mModel
1084 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1085 mModel->GetUnderlineColor(),
1086 mModel->GetUnderlineHeight(),
1087 mModel->GetDashedUnderlineGap(),
1088 mModel->GetDashedUnderlineWidth(),
1095 // Aggregate strikethrough-style-properties from mModel
1096 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1097 mModel->GetStrikethroughHeight(),
1101 // Get the underline runs.
1102 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1103 Vector<UnderlinedGlyphRun> underlineRuns;
1104 underlineRuns.Resize(numberOfUnderlineRuns);
1105 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1107 // Get the strikethrough runs.
1108 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1109 Vector<StrikethroughGlyphRun> strikethroughRuns;
1110 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1111 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1113 bool thereAreUnderlinedGlyphs = false;
1114 bool thereAreStrikethroughGlyphs = false;
1116 float currentUnderlinePosition = 0.0f;
1117 float currentUnderlineHeight = modelUnderlineProperties.height;
1118 float maxUnderlineHeight = currentUnderlineHeight;
1119 auto currentUnderlineProperties = modelUnderlineProperties;
1121 float currentStrikethroughHeight = modelStrikethroughProperties.height;
1122 float maxStrikethroughHeight = currentStrikethroughHeight;
1123 auto currentStrikethroughProperties = modelStrikethroughProperties;
1124 float strikethroughStartingYPosition = 0.0f;
1126 FontId lastFontId = 0;
1128 float lineExtentLeft = bufferWidth;
1129 float lineExtentRight = 0.0f;
1130 float baseline = 0.0f;
1131 bool addHyphen = false;
1133 // Traverses the glyphs of the line.
1134 const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1135 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1136 endGlyphIndex = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1138 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1140 //To handle START case of ellipsis, the first glyph has been shifted
1141 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1142 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1144 //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.
1145 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1147 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1148 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1150 // Ignore any glyph that removed for MIDDLE ellipsis
1153 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1155 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1159 // Retrieve the glyph's info.
1160 const GlyphInfo* glyphInfo;
1162 if(addHyphen && hyphens)
1164 glyphInfo = hyphens + hyphenIndex;
1169 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1172 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1173 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1175 // Nothing to do if the glyph's width or height is zero.
1179 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1180 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1181 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1182 currentUnderlineHeight = currentUnderlineProperties.height;
1183 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1185 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1186 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1187 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1188 currentStrikethroughHeight = currentStrikethroughProperties.height;
1189 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1191 // Are we still using the same fontId as previous
1192 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1194 // We need to fetch fresh font underline metrics
1195 FontMetrics fontMetrics;
1196 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1198 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1199 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1203 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1206 if(strikethroughGlyph)
1208 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1211 // Update lastFontId because fontId is changed
1212 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1215 // Retrieves the glyph's position.
1216 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1220 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1221 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1222 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1223 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1224 position.y = -glyphInfo->yBearing;
1227 if(baseline < position.y + glyphInfo->yBearing)
1229 baseline = position.y + glyphInfo->yBearing;
1232 // Calculate the positions of leftmost and rightmost glyphs in the current line
1233 if(position.x < lineExtentLeft)
1235 lineExtentLeft = position.x;
1238 if(position.x + glyphInfo->width > lineExtentRight)
1240 lineExtentRight = position.x + glyphInfo->width;
1243 // Retrieves the glyph's color.
1244 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1247 if(style == Typesetter::STYLE_SHADOW)
1249 color = mModel->GetShadowColor();
1251 else if(style == Typesetter::STYLE_OUTLINE)
1253 color = mModel->GetOutlineColor();
1257 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1260 // Premultiply alpha
1265 // Retrieves the glyph's bitmap.
1266 glyphData.glyphBitmap.buffer = NULL;
1267 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1268 glyphData.glyphBitmap.height = glyphInfo->height;
1270 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1272 // Don't render outline for other styles
1273 outlineWidth = 0.0f;
1276 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1278 fontClient.CreateBitmap(glyphInfo->fontId,
1280 glyphInfo->isItalicRequired,
1281 glyphInfo->isBoldRequired,
1282 glyphData.glyphBitmap,
1283 static_cast<int32_t>(outlineWidth));
1286 // Sets the glyph's bitmap into the bitmap of the whole text.
1287 if(NULL != glyphData.glyphBitmap.buffer)
1289 if(style == Typesetter::STYLE_OUTLINE)
1291 // Set the position offset for the current glyph
1292 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1293 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1296 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1297 TypesetGlyph(glyphData,
1303 if(style == Typesetter::STYLE_OUTLINE)
1305 // Reset the position offset for the next glyph
1306 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1307 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1310 // free the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1311 free(glyphData.glyphBitmap.buffer);
1312 glyphData.glyphBitmap.buffer = NULL;
1317 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1322 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1330 // Draw the underline from the leftmost glyph to the rightmost glyph
1331 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1333 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1336 // Draw the background color from the leftmost glyph to the rightmost glyph
1337 if(style == Typesetter::STYLE_BACKGROUND)
1339 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1342 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1343 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1345 //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.
1346 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.
1347 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1350 // Increases the vertical offset with the line's descender & line spacing.
1351 glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1354 return glyphData.bitmapBuffer;
1357 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)
1359 // Underline-tags (this is for Markup case)
1360 // Get the underline runs.
1361 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1362 Vector<UnderlinedGlyphRun> underlineRuns;
1363 underlineRuns.Resize(numberOfUnderlineRuns);
1364 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1366 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1367 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1368 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1369 GlyphIndex startGlyphIndex, endGlyphIndex;
1371 //The outer loop to iterate on the separated chunks of underlined glyph runs
1372 while(itGlyphRun != endItGlyphRun)
1374 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1375 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1377 // Create the image buffer for underline
1378 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1379 // Combine the two buffers
1380 // Result pixel buffer will be stored into topPixelBuffer.
1381 CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1386 return topPixelBuffer;
1389 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)
1391 // strikethrough-tags (this is for Markup case)
1392 // Get the strikethrough runs.
1393 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1394 Vector<StrikethroughGlyphRun> strikethroughRuns;
1395 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1396 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1398 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1399 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1400 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1401 GlyphIndex startGlyphIndex, endGlyphIndex;
1403 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1404 while(itGlyphRun != endItGlyphRun)
1406 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1407 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1409 // Create the image buffer for strikethrough
1410 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1411 // Combine the two buffers
1412 // Result pixel buffer will be stored into topPixelBuffer.
1413 CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1418 return topPixelBuffer;
1421 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)
1423 // Apply the markup-Processor if enabled
1424 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1425 if(markupProcessorEnabled)
1427 topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1429 topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1432 return topPixelBuffer;
1435 Typesetter::Typesetter(const ModelInterface* const model)
1436 : mModel(new ViewModel(model))
1440 Typesetter::~Typesetter()
1447 } // namespace Toolkit