2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/rendering/text-typesetter.h>
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/public-api/common/constants.h>
27 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
28 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
29 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
30 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
31 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
32 #include <dali-toolkit/internal/text/rendering/view-model.h>
42 const float HALF(0.5f);
43 const float ONE_AND_A_HALF(1.5f);
45 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
49 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
50 Vector2* position; ///< The position of the glyph.
51 TextAbstraction::FontClient::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
52 unsigned int width; ///< The bitmap's width.
53 unsigned int height; ///< The bitmap's height.
54 int horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
55 int verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
59 * @brief Sets the glyph's buffer into the bitmap's buffer.
61 * @param[in] data Struct which contains the glyph's data and the bitmap's data.
62 * @param[in] position The position of the glyph.
63 * @param[in] color The color of the glyph.
64 * @param[in] style The style of the text.
65 * @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).
67 void TypesetGlyph(GlyphData& data,
68 const Vector2* const position,
69 const Vector4* const color,
70 Typesetter::Style style,
71 Pixel::Format pixelFormat)
73 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
75 // Nothing to do if the width or height of the buffer is zero.
79 const int widthMinusOne = static_cast<int>(data.width - 1u);
80 const int heightMinusOne = static_cast<int>(data.height - 1u);
82 if(Pixel::RGBA8888 == pixelFormat)
84 // Whether the given glyph is a color one.
85 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
86 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
87 const uint32_t alphaIndex = glyphPixelSize - 1u;
88 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
90 // Pointer to the color glyph if there is one.
91 const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>(data.glyphBitmap.buffer) : NULL;
93 // Initial vertical offset.
94 const int yOffset = data.verticalOffset + position->y;
96 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
98 // Traverse the pixels of the glyph line per line.
99 for(int lineIndex = 0, glyphHeight = static_cast<int>(data.glyphBitmap.height); lineIndex < glyphHeight; ++lineIndex)
101 const int yOffsetIndex = yOffset + lineIndex;
102 if((0 > yOffsetIndex) || (yOffsetIndex > heightMinusOne))
104 // Do not write out of bounds.
108 const int verticalOffset = yOffsetIndex * data.width;
109 const int xOffset = data.horizontalOffset + position->x;
110 const int glyphBufferOffset = lineIndex * static_cast<int>(data.glyphBitmap.width);
111 for(int index = 0, glyphWidth = static_cast<int>(data.glyphBitmap.width); index < glyphWidth; ++index)
113 const int xOffsetIndex = xOffset + index;
114 if((0 > xOffsetIndex) || (xOffsetIndex > widthMinusOne))
116 // Don't write out of bounds.
122 // Retrieves the color from the color glyph.
123 uint32_t packedColorGlyph = *(colorGlyphBuffer + glyphBufferOffset + index);
124 uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
126 // Update the alpha channel.
127 if(Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style) // Outline not shown for color glyph
129 // Create an alpha mask for color glyph.
130 *(packedColorGlyphBuffer + 3u) = 0u;
131 *(packedColorGlyphBuffer + 2u) = 0u;
132 *(packedColorGlyphBuffer + 1u) = 0u;
133 *packedColorGlyphBuffer = 0u;
137 const uint8_t colorAlpha = static_cast<uint8_t>(color->a * static_cast<float>(*(packedColorGlyphBuffer + 3u)));
138 *(packedColorGlyphBuffer + 3u) = colorAlpha;
140 if(Typesetter::STYLE_SHADOW == style)
142 // The shadow of color glyph needs to have the shadow color.
143 *(packedColorGlyphBuffer + 2u) = static_cast<uint8_t>(color->b * colorAlpha);
144 *(packedColorGlyphBuffer + 1u) = static_cast<uint8_t>(color->g * colorAlpha);
145 *packedColorGlyphBuffer = static_cast<uint8_t>(color->r * colorAlpha);
151 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
154 *(packedColorGlyphBuffer + 2u) = (*(packedColorGlyphBuffer + 2u) * colorAlpha / 255);
155 *(packedColorGlyphBuffer + 1u) = (*(packedColorGlyphBuffer + 1u) * colorAlpha / 255);
156 *packedColorGlyphBuffer = (*(packedColorGlyphBuffer)*colorAlpha / 255);
158 if(data.glyphBitmap.isColorBitmap)
160 *(packedColorGlyphBuffer + 2u) = static_cast<uint8_t>(*(packedColorGlyphBuffer + 2u) * color->b);
161 *(packedColorGlyphBuffer + 1u) = static_cast<uint8_t>(*(packedColorGlyphBuffer + 1u) * color->g);
162 *packedColorGlyphBuffer = static_cast<uint8_t>(*packedColorGlyphBuffer * color->r);
167 // Set the color into the final pixel buffer.
168 *(bitmapBuffer + verticalOffset + xOffsetIndex) = packedColorGlyph;
172 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
173 // The format is RGBA8888.
174 uint32_t packedColor = 0u;
175 uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
177 // Update the alpha channel.
178 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
180 // Copy non-transparent pixels only
183 // Check alpha of overlapped pixels
184 uint32_t& currentColor = *(bitmapBuffer + verticalOffset + xOffsetIndex);
185 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
187 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
188 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
189 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
190 // happen, for example, in the RTL text when we copy glyphs from right to left).
191 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
192 currentAlpha = std::max(currentAlpha, alpha);
194 // Color is pre-muliplied with its alpha.
195 *(packedColorBuffer + 3u) = static_cast<uint8_t>(color->a * currentAlpha);
196 *(packedColorBuffer + 2u) = static_cast<uint8_t>(color->b * currentAlpha);
197 *(packedColorBuffer + 1u) = static_cast<uint8_t>(color->g * currentAlpha);
198 *(packedColorBuffer) = static_cast<uint8_t>(color->r * currentAlpha);
200 // Set the color into the final pixel buffer.
201 currentColor = packedColor;
209 // Whether the given glyph is a color one.
210 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
211 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
212 const uint32_t alphaIndex = glyphPixelSize - 1u;
214 // Initial vertical offset.
215 const int yOffset = data.verticalOffset + position->y;
217 uint8_t* bitmapBuffer = reinterpret_cast<uint8_t*>(data.bitmapBuffer.GetBuffer());
219 // Traverse the pixels of the glyph line per line.
220 for(int lineIndex = 0, glyphHeight = static_cast<int>(data.glyphBitmap.height); lineIndex < glyphHeight; ++lineIndex)
222 const int yOffsetIndex = yOffset + lineIndex;
223 if((0 > yOffsetIndex) || (yOffsetIndex > heightMinusOne))
225 // Do not write out of bounds.
229 const int verticalOffset = yOffsetIndex * data.width;
230 const int xOffset = data.horizontalOffset + position->x;
231 const int glyphBufferOffset = lineIndex * static_cast<int>(data.glyphBitmap.width);
232 for(int index = 0, glyphWidth = static_cast<int>(data.glyphBitmap.width); index < glyphWidth; ++index)
234 const int xOffsetIndex = xOffset + index;
235 if((0 > xOffsetIndex) || (xOffsetIndex > widthMinusOne))
237 // Don't write out of bounds.
243 // Update the alpha channel.
244 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
246 // Copy non-transparent pixels only
249 // Check alpha of overlapped pixels
250 uint8_t& currentAlpha = *(bitmapBuffer + verticalOffset + xOffsetIndex);
252 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
253 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
254 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
255 // happen, for example, in the RTL text when we copy glyphs from right to left).
256 currentAlpha = std::max(currentAlpha, alpha);
264 /// Draws the specified color to the pixel buffer
265 void WriteColorToPixelBuffer(
266 GlyphData& glyphData,
267 uint32_t* bitmapBuffer,
268 const Vector4& color,
269 const unsigned int x,
270 const unsigned int y)
272 // Always RGBA image for text with styles
273 uint32_t pixel = *(bitmapBuffer + y * glyphData.width + x);
274 uint8_t* pixelBuffer = reinterpret_cast<uint8_t*>(&pixel);
276 // Write the color to the pixel buffer
277 uint8_t colorAlpha = static_cast<uint8_t>(color.a * 255.f);
278 *(pixelBuffer + 3u) = colorAlpha;
279 *(pixelBuffer + 2u) = static_cast<uint8_t>(color.b * colorAlpha);
280 *(pixelBuffer + 1u) = static_cast<uint8_t>(color.g * colorAlpha);
281 *(pixelBuffer) = static_cast<uint8_t>(color.r * colorAlpha);
283 *(bitmapBuffer + y * glyphData.width + x) = pixel;
286 /// Draws the specified underline color to the buffer
288 const unsigned int bufferWidth,
289 const unsigned int bufferHeight,
290 GlyphData& glyphData,
291 const float baseline,
292 const float currentUnderlinePosition,
293 const float maxUnderlineHeight,
294 const float lineExtentLeft,
295 const float lineExtentRight,
296 const UnderlineStyleProperties& commonUnderlineProperties,
297 const UnderlineStyleProperties& currentUnderlineProperties,
300 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
301 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
302 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
303 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
305 int underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
306 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
308 for(unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineHeight; y++)
310 if(y > bufferHeight - 1)
312 // Do not write out of bounds.
315 if(underlineType == Text::Underline::DASHED)
317 float dashWidth = dashedUnderlineWidth;
320 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
322 if(x > bufferWidth - 1)
324 // Do not write out of bounds.
327 if(dashGap == 0 && dashWidth > 0)
329 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
332 else if(dashGap < dashedUnderlineGap)
339 dashWidth = dashedUnderlineWidth;
346 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
348 if(x > bufferWidth - 1)
350 // Do not write out of bounds.
353 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
357 if(underlineType == Text::Underline::DOUBLE)
359 int secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
360 for(unsigned int y = secondUnderlineYOffset; y < secondUnderlineYOffset + maxUnderlineHeight; y++)
362 if(y > bufferHeight - 1)
364 // Do not write out of bounds.
367 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
369 if(x > bufferWidth - 1)
371 // Do not write out of bounds.
374 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
380 /// Draws the background color to the buffer
381 void DrawBackgroundColor(
382 Vector4 backgroundColor,
383 const unsigned int bufferWidth,
384 const unsigned int bufferHeight,
385 GlyphData& glyphData,
386 const float baseline,
388 const float lineExtentLeft,
389 const float lineExtentRight)
391 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
393 for(int y = glyphData.verticalOffset + baseline - line.ascender; y < glyphData.verticalOffset + baseline - line.descender; y++)
395 if((y < 0) || (y > static_cast<int>(bufferHeight - 1)))
397 // Do not write out of bounds.
401 for(int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
403 if((x < 0) || (x > static_cast<int>(bufferWidth - 1)))
405 // Do not write out of bounds.
409 WriteColorToPixelBuffer(glyphData, bitmapBuffer, backgroundColor, x, y);
414 Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuffer& buffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, int horizontalOffset, int verticalOffset)
416 // Retrieve lines, glyphs, positions and colors from the view model.
417 const Length modelNumberOfLines = model->GetNumberOfLines();
418 const LineRun* const modelLinesBuffer = model->GetLines();
419 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
420 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
421 const Vector2* const positionBuffer = model->GetLayout();
422 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
423 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
425 // Create and initialize the pixel buffer.
427 glyphData.verticalOffset = verticalOffset;
428 glyphData.width = bufferWidth;
429 glyphData.height = bufferHeight;
430 glyphData.bitmapBuffer = buffer;
431 glyphData.horizontalOffset = 0;
433 ColorIndex prevBackgroundColorIndex = 0;
434 ColorIndex backgroundColorIndex = 0;
436 // Traverses the lines of the text.
437 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
439 const LineRun& line = *(modelLinesBuffer + lineIndex);
441 // Sets the horizontal offset of the line.
442 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
443 glyphData.horizontalOffset += horizontalOffset;
445 // Increases the vertical offset with the line's ascender.
446 glyphData.verticalOffset += static_cast<int>(line.ascender);
448 // Include line spacing after first line
451 glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
454 float left = bufferWidth;
456 float baseline = 0.0f;
458 // Traverses the glyphs of the line.
459 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
460 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
462 // Retrieve the glyph's info.
463 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
465 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
466 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
468 // Nothing to do if default background color, the glyph's width or height is zero.
472 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
474 if((backgroundColorIndex != prevBackgroundColorIndex) &&
475 (prevBackgroundColorIndex != 0u))
477 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
478 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
481 if(backgroundColorIndex == 0u)
483 prevBackgroundColorIndex = backgroundColorIndex;
484 //if background color is the default do nothing
488 // Retrieves the glyph's position.
489 const Vector2* const position = positionBuffer + glyphIndex;
491 if(baseline < position->y + glyphInfo->yBearing)
493 baseline = position->y + glyphInfo->yBearing;
496 // Calculate the positions of leftmost and rightmost glyphs in the current line
497 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
499 left = position->x - glyphInfo->xBearing;
502 if(position->x + glyphInfo->width > right)
504 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
507 prevBackgroundColorIndex = backgroundColorIndex;
510 //draw last background at line end if not default
511 if(backgroundColorIndex != 0u)
513 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
514 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
517 // Increases the vertical offset with the line's descender.
518 glyphData.verticalOffset += static_cast<int>(-line.descender);
521 return glyphData.bitmapBuffer;
524 /// Draws the specified strikethrough color to the buffer
525 void DrawStrikethrough(const unsigned int bufferWidth,
526 const unsigned int bufferHeight,
527 GlyphData& glyphData,
528 const float baseline,
529 const float strikethroughStartingYPosition,
530 const float maxStrikethroughHeight,
531 const float lineExtentLeft,
532 const float lineExtentRight,
533 const StrikethroughStyleProperties& commonStrikethroughProperties,
534 const StrikethroughStyleProperties& currentStrikethroughProperties,
537 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
539 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
541 for(unsigned int y = strikethroughStartingYPosition; y < strikethroughStartingYPosition + maxStrikethroughHeight; y++)
543 if(y > bufferHeight - 1)
545 // Do not write out of bounds.
549 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
551 if(x > bufferWidth - 1)
553 // Do not write out of bounds.
557 WriteColorToPixelBuffer(glyphData, bitmapBuffer, strikethroughColor, x, y);
564 TypesetterPtr Typesetter::New(const ModelInterface* const model)
566 return TypesetterPtr(new Typesetter(model));
569 ViewModel* Typesetter::GetViewModel()
574 Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, const unsigned int bufferHeight, Pixel::Format pixelFormat)
576 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
578 if(Pixel::RGBA8888 == pixelFormat)
580 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
581 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
582 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
586 memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
592 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
594 // @todo. This initial implementation for a TextLabel has only one visible page.
596 // Elides the text if needed.
597 mModel->ElideGlyphs();
599 // Retrieves the layout size.
600 const Size& layoutSize = mModel->GetLayoutSize();
602 const int outlineWidth = static_cast<int>(mModel->GetOutlineWidth());
604 // Set the offset for the horizontal alignment according to the text direction and outline width.
607 switch(mModel->GetHorizontalAlignment())
609 case HorizontalAlignment::BEGIN:
614 case HorizontalAlignment::CENTER:
616 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
619 case HorizontalAlignment::END:
621 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
626 // Set the offset for the vertical alignment.
629 switch(mModel->GetVerticalAlignment())
631 case VerticalAlignment::TOP:
636 case VerticalAlignment::CENTER:
638 penY = static_cast<int>(0.5f * (size.height - layoutSize.height));
639 penY = penY < 0.f ? 0.f : penY;
642 case VerticalAlignment::BOTTOM:
644 penY = static_cast<int>(size.height - layoutSize.height);
649 // Calculate vertical line alignment
650 switch(mModel->GetVerticalLineAlignment())
652 case DevelText::VerticalLineAlignment::TOP:
656 case DevelText::VerticalLineAlignment::MIDDLE:
658 const auto& line = *mModel->GetLines();
659 penY -= line.descender;
660 penY += static_cast<int>(line.lineSpacing * 0.5f + line.descender);
663 case DevelText::VerticalLineAlignment::BOTTOM:
665 const auto& line = *mModel->GetLines();
666 const auto lineHeight = line.ascender + (-line.descender) + line.lineSpacing;
667 penY += static_cast<int>(lineHeight - (line.ascender - line.descender));
672 // Generate the image buffers of the text for each different style first,
673 // then combine all of them together as one final image buffer. We try to
674 // do all of these in CPU only, so that once the final texture is generated,
675 // no calculation is needed in GPU during each frame.
677 const unsigned int bufferWidth = static_cast<unsigned int>(size.width);
678 const unsigned int bufferHeight = static_cast<unsigned int>(size.height);
680 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
681 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
683 //Elided text in ellipsis at START could start on index greater than 0
684 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
685 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
687 Devel::PixelBuffer imageBuffer;
689 if(RENDER_MASK == behaviour)
691 // Generate the image buffer as an alpha mask for color glyphs.
692 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
694 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
696 // Generate an empty image buffer so that it can been combined with the image buffers for styles
697 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
698 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
702 // Generate the image buffer for the text with no style.
703 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
706 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
708 // Generate the outline if enabled
709 const uint16_t outlineWidth = mModel->GetOutlineWidth();
710 if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
712 // Create the image buffer for outline
713 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
715 // Combine the two buffers
716 imageBuffer = CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight);
719 // @todo. Support shadow and underline for partial text later on.
721 // Generate the shadow if enabled
722 const Vector2& shadowOffset = mModel->GetShadowOffset();
723 if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
725 // Create the image buffer for shadow
726 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
728 // Check whether it will be a soft shadow
729 const float& blurRadius = mModel->GetShadowBlurRadius();
731 if(blurRadius > Math::MACHINE_EPSILON_1)
733 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
736 // Combine the two buffers
737 imageBuffer = CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight);
740 // Generate the underline if enabled
741 const bool underlineEnabled = mModel->IsUnderlineEnabled();
742 if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
744 // Create the image buffer for underline
745 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
747 // Combine the two buffers
748 imageBuffer = CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
751 // Generate the background if enabled
752 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
753 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
754 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
756 Devel::PixelBuffer backgroundImageBuffer;
758 if(backgroundEnabled)
760 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
764 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
767 if(backgroundMarkupSet)
769 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
772 // Combine the two buffers
773 imageBuffer = CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight);
776 // Generate the strikethrough if enabled
777 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
778 if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
780 // Create the image buffer for strikethrough
781 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
783 // Combine the two buffers
784 imageBuffer = CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight);
789 imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
792 // Create the final PixelData for the combined image buffer
793 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
798 Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, const unsigned int bufferHeight, Typesetter::Style style, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset, GlyphIndex fromGlyphIndex, GlyphIndex toGlyphIndex)
800 // Retrieve lines, glyphs, positions and colors from the view model.
801 const Length modelNumberOfLines = mModel->GetNumberOfLines();
802 const LineRun* const modelLinesBuffer = mModel->GetLines();
803 const GlyphInfo* const glyphsBuffer = mModel->GetGlyphs();
804 const Vector2* const positionBuffer = mModel->GetLayout();
805 const Vector4* const colorsBuffer = mModel->GetColors();
806 const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
807 const GlyphInfo* hyphens = mModel->GetHyphens();
808 const Length* hyphenIndices = mModel->GetHyphenIndices();
809 const Length hyphensCount = mModel->GetHyphensCount();
811 // Elided text info. Indices according to elided text and Ellipsis position.
812 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
813 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
814 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
815 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
816 const auto ellipsisPosition = mModel->GetEllipsisPosition();
818 // Whether to use the default color.
819 const bool useDefaultColor = (NULL == colorsBuffer);
820 const Vector4& defaultColor = mModel->GetDefaultColor();
822 // Create and initialize the pixel buffer.
824 glyphData.verticalOffset = verticalOffset;
825 glyphData.width = bufferWidth;
826 glyphData.height = bufferHeight;
827 glyphData.bitmapBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
828 glyphData.horizontalOffset = 0;
830 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
831 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
832 Length hyphenIndex = 0;
834 const Character* textBuffer = mModel->GetTextBuffer();
835 float calculatedAdvance = 0.f;
836 const Vector<CharacterIndex>& glyphToCharacterMap = mModel->GetGlyphsToCharacters();
837 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
839 // Traverses the lines of the text.
840 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
842 const LineRun& line = *(modelLinesBuffer + lineIndex);
844 // Sets the horizontal offset of the line.
845 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
846 glyphData.horizontalOffset += horizontalOffset;
848 // Increases the vertical offset with the line's ascender.
849 glyphData.verticalOffset += static_cast<int>(line.ascender);
851 // Include line spacing after first line
854 glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
857 // Retrieves the glyph's outline width
858 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
860 if(style == Typesetter::STYLE_OUTLINE)
862 glyphData.horizontalOffset -= outlineWidth;
865 // Only need to add the vertical outline offset for the first line
866 glyphData.verticalOffset -= outlineWidth;
869 else if(style == Typesetter::STYLE_SHADOW)
871 const Vector2& shadowOffset = mModel->GetShadowOffset();
872 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
876 // Only need to add the vertical shadow offset for first line
877 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
881 const bool underlineEnabled = mModel->IsUnderlineEnabled();
882 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
883 const float modelCharacterSpacing = mModel->GetCharacterSpacing();
885 // Get the character-spacing runs.
886 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
888 // Aggregate underline-style-properties from mModel
889 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
890 mModel->GetUnderlineColor(),
891 mModel->GetUnderlineHeight(),
892 mModel->GetDashedUnderlineGap(),
893 mModel->GetDashedUnderlineWidth(),
900 // Aggregate strikethrough-style-properties from mModel
901 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
902 mModel->GetStrikethroughHeight(),
906 // Get the underline runs.
907 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
908 Vector<UnderlinedGlyphRun> underlineRuns;
909 underlineRuns.Resize(numberOfUnderlineRuns);
910 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
912 // Get the strikethrough runs.
913 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
914 Vector<StrikethroughGlyphRun> strikethroughRuns;
915 strikethroughRuns.Resize(numberOfStrikethroughRuns);
916 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
918 bool thereAreUnderlinedGlyphs = false;
919 bool thereAreStrikethroughGlyphs = false;
921 float currentUnderlinePosition = 0.0f;
922 float currentUnderlineHeight = modelUnderlineProperties.height;
923 float maxUnderlineHeight = currentUnderlineHeight;
924 auto currentUnderlineProperties = modelUnderlineProperties;
926 float currentStrikethroughHeight = modelStrikethroughProperties.height;
927 float maxStrikethroughHeight = currentStrikethroughHeight;
928 auto currentStrikethroughProperties = modelStrikethroughProperties;
929 float strikethroughStartingYPosition = 0.0f;
931 FontId lastFontId = 0;
933 float lineExtentLeft = bufferWidth;
934 float lineExtentRight = 0.0f;
935 float baseline = 0.0f;
936 bool addHyphen = false;
938 // Traverses the glyphs of the line.
939 const GlyphIndex startGlyphIndex = std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs);
940 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
941 endGlyphIndex = std::min(endGlyphIndex, endIndexOfGlyphs);
943 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
945 if(glyphIndex < fromGlyphIndex || glyphIndex > toGlyphIndex)
947 // Ignore any glyph that out of the specified range
951 //To handle START case of ellipsis, the first glyph has been shifted
952 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
953 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
955 //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.
956 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
958 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
959 glyphIndex < secondMiddleIndexOfElidedGlyphs)
961 // Ignore any glyph that removed for MIDDLE ellipsis
964 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
966 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
970 // Retrieve the glyph's info.
971 const GlyphInfo* glyphInfo;
973 if(addHyphen && hyphens)
975 glyphInfo = hyphens + hyphenIndex;
980 glyphInfo = glyphsBuffer + elidedGlyphIndex;
983 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
984 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
986 // Nothing to do if the glyph's width or height is zero.
990 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
991 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
992 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
993 currentUnderlineHeight = currentUnderlineProperties.height;
994 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
996 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
997 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
998 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
999 currentStrikethroughHeight = currentStrikethroughProperties.height;
1000 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
1002 // Are we still using the same fontId as previous
1003 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1005 // We need to fetch fresh font underline metrics
1006 FontMetrics fontMetrics;
1007 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1009 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1010 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1014 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1017 if(strikethroughGlyph)
1019 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1022 // Update lastFontId because fontId is changed
1023 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1026 // Retrieves the glyph's position.
1027 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1031 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1032 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1033 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1034 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1035 position.y = -glyphInfo->yBearing;
1038 if(baseline < position.y + glyphInfo->yBearing)
1040 baseline = position.y + glyphInfo->yBearing;
1043 // Calculate the positions of leftmost and rightmost glyphs in the current line
1044 if(position.x < lineExtentLeft)
1046 lineExtentLeft = position.x;
1049 if(position.x + glyphInfo->width > lineExtentRight)
1051 lineExtentRight = position.x + glyphInfo->width;
1054 // Retrieves the glyph's color.
1055 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1058 if(style == Typesetter::STYLE_SHADOW)
1060 color = mModel->GetShadowColor();
1062 else if(style == Typesetter::STYLE_OUTLINE)
1064 color = mModel->GetOutlineColor();
1068 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1071 // Premultiply alpha
1076 // Retrieves the glyph's bitmap.
1077 glyphData.glyphBitmap.buffer = NULL;
1078 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1079 glyphData.glyphBitmap.height = glyphInfo->height;
1081 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1083 // Don't render outline for other styles
1084 outlineWidth = 0.0f;
1087 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1089 fontClient.CreateBitmap(glyphInfo->fontId,
1091 glyphInfo->isItalicRequired,
1092 glyphInfo->isBoldRequired,
1093 glyphData.glyphBitmap,
1094 static_cast<int>(outlineWidth));
1097 // Sets the glyph's bitmap into the bitmap of the whole text.
1098 if(NULL != glyphData.glyphBitmap.buffer)
1100 if(style == Typesetter::STYLE_OUTLINE)
1102 // Set the position offset for the current glyph
1103 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1104 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1107 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1108 TypesetGlyph(glyphData,
1114 if(style == Typesetter::STYLE_OUTLINE)
1116 // Reset the position offset for the next glyph
1117 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1118 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1121 // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1122 delete[] glyphData.glyphBitmap.buffer;
1123 glyphData.glyphBitmap.buffer = NULL;
1128 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1133 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1141 // Draw the underline from the leftmost glyph to the rightmost glyph
1142 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1144 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1147 // Draw the background color from the leftmost glyph to the rightmost glyph
1148 if(style == Typesetter::STYLE_BACKGROUND)
1150 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1153 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1154 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1156 //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.
1157 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.
1158 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1161 // Increases the vertical offset with the line's descender.
1162 glyphData.verticalOffset += static_cast<int>(-line.descender);
1165 return glyphData.bitmapBuffer;
1168 Devel::PixelBuffer Typesetter::CombineImageBuffer(Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight)
1170 unsigned char* topBuffer = topPixelBuffer.GetBuffer();
1171 unsigned char* bottomBuffer = bottomPixelBuffer.GetBuffer();
1173 Devel::PixelBuffer combinedPixelBuffer;
1175 if(topBuffer == NULL && bottomBuffer == NULL)
1177 // Nothing to do if both buffers are empty.
1178 return combinedPixelBuffer;
1181 if(topBuffer == NULL)
1183 // Nothing to do if topBuffer is empty.
1184 return bottomPixelBuffer;
1187 if(bottomBuffer == NULL)
1189 // Nothing to do if bottomBuffer is empty.
1190 return topPixelBuffer;
1193 // Always combine two RGBA images
1194 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
1195 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
1197 combinedPixelBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1198 uint8_t* combinedBuffer = reinterpret_cast<uint8_t*>(combinedPixelBuffer.GetBuffer());
1199 memset(combinedBuffer, 0u, bufferSizeChar);
1201 for(unsigned int pixelIndex = 0; pixelIndex < bufferSizeInt; pixelIndex++)
1203 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
1204 // Otherwise, copy pixel from topBuffer to combinedBuffer.
1206 unsigned int alphaBuffer1 = topBuffer[pixelIndex * 4 + 3];
1208 if(alphaBuffer1 != 255)
1210 // At least one pixel is not fully opaque
1211 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
1212 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4] + (bottomBuffer[pixelIndex * 4] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1213 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1] + (bottomBuffer[pixelIndex * 4 + 1] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1214 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2] + (bottomBuffer[pixelIndex * 4 + 2] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1215 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3] + (bottomBuffer[pixelIndex * 4 + 3] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1219 // Copy the pixel from topBuffer to combinedBuffer
1220 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4];
1221 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1];
1222 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2];
1223 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3];
1227 return combinedPixelBuffer;
1230 Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1232 // Underline-tags (this is for Markup case)
1233 // Get the underline runs.
1234 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1235 Vector<UnderlinedGlyphRun> underlineRuns;
1236 underlineRuns.Resize(numberOfUnderlineRuns);
1237 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1239 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1240 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1241 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1242 GlyphIndex startGlyphIndex, endGlyphIndex;
1244 //The outer loop to iterate on the separated chunks of underlined glyph runs
1245 while(itGlyphRun != endItGlyphRun)
1247 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1248 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1250 // Create the image buffer for underline
1251 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1252 // Combine the two buffers
1253 topPixelBuffer = CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight);
1258 return topPixelBuffer;
1261 Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1263 // strikethrough-tags (this is for Markup case)
1264 // Get the strikethrough runs.
1265 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1266 Vector<StrikethroughGlyphRun> strikethroughRuns;
1267 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1268 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1270 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1271 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1272 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1273 GlyphIndex startGlyphIndex, endGlyphIndex;
1275 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1276 while(itGlyphRun != endItGlyphRun)
1278 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1279 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1281 // Create the image buffer for strikethrough
1282 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1283 // Combine the two buffers
1284 topPixelBuffer = CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight);
1289 return topPixelBuffer;
1292 Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1294 // Apply the markup-Processor if enabled
1295 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1296 if(markupProcessorEnabled)
1298 topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1300 topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1303 return topPixelBuffer;
1306 Typesetter::Typesetter(const ModelInterface* const model)
1307 : mModel(new ViewModel(model))
1311 Typesetter::~Typesetter()
1318 } // namespace Toolkit