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/strikethrough-helper-functions.h>
30 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
31 #include <dali-toolkit/internal/text/rendering/view-model.h>
41 const float HALF(0.5f);
42 const float ONE_AND_A_HALF(1.5f);
44 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
48 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
49 Vector2* position; ///< The position of the glyph.
50 TextAbstraction::FontClient::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
51 unsigned int width; ///< The bitmap's width.
52 unsigned int height; ///< The bitmap's height.
53 int horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
54 int verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
58 * @brief Sets the glyph's buffer into the bitmap's buffer.
60 * @param[in] data Struct which contains the glyph's data and the bitmap's data.
61 * @param[in] position The position of the glyph.
62 * @param[in] color The color of the glyph.
63 * @param[in] style The style of the text.
64 * @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).
66 void TypesetGlyph(GlyphData& data,
67 const Vector2* const position,
68 const Vector4* const color,
69 Typesetter::Style style,
70 Pixel::Format pixelFormat)
72 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
74 // Nothing to do if the width or height of the buffer is zero.
78 const int widthMinusOne = static_cast<int>(data.width - 1u);
79 const int heightMinusOne = static_cast<int>(data.height - 1u);
81 if(Pixel::RGBA8888 == pixelFormat)
83 // Whether the given glyph is a color one.
84 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
85 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
86 const uint32_t alphaIndex = glyphPixelSize - 1u;
87 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
89 // Pointer to the color glyph if there is one.
90 const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>(data.glyphBitmap.buffer) : NULL;
92 // Initial vertical offset.
93 const int yOffset = data.verticalOffset + position->y;
95 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
97 // Traverse the pixels of the glyph line per line.
98 for(int lineIndex = 0, glyphHeight = static_cast<int>(data.glyphBitmap.height); lineIndex < glyphHeight; ++lineIndex)
100 const int yOffsetIndex = yOffset + lineIndex;
101 if((0 > yOffsetIndex) || (yOffsetIndex > heightMinusOne))
103 // Do not write out of bounds.
107 const int verticalOffset = yOffsetIndex * data.width;
108 const int xOffset = data.horizontalOffset + position->x;
109 const int glyphBufferOffset = lineIndex * static_cast<int>(data.glyphBitmap.width);
110 for(int index = 0, glyphWidth = static_cast<int>(data.glyphBitmap.width); index < glyphWidth; ++index)
112 const int xOffsetIndex = xOffset + index;
113 if((0 > xOffsetIndex) || (xOffsetIndex > widthMinusOne))
115 // Don't write out of bounds.
121 // Retrieves the color from the color glyph.
122 uint32_t packedColorGlyph = *(colorGlyphBuffer + glyphBufferOffset + index);
123 uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
125 // Update the alpha channel.
126 if(Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style) // Outline not shown for color glyph
128 // Create an alpha mask for color glyph.
129 *(packedColorGlyphBuffer + 3u) = 0u;
130 *(packedColorGlyphBuffer + 2u) = 0u;
131 *(packedColorGlyphBuffer + 1u) = 0u;
132 *packedColorGlyphBuffer = 0u;
136 const uint8_t colorAlpha = static_cast<uint8_t>(color->a * static_cast<float>(*(packedColorGlyphBuffer + 3u)));
137 *(packedColorGlyphBuffer + 3u) = colorAlpha;
139 if(Typesetter::STYLE_SHADOW == style)
141 // The shadow of color glyph needs to have the shadow color.
142 *(packedColorGlyphBuffer + 2u) = static_cast<uint8_t>(color->b * colorAlpha);
143 *(packedColorGlyphBuffer + 1u) = static_cast<uint8_t>(color->g * colorAlpha);
144 *packedColorGlyphBuffer = static_cast<uint8_t>(color->r * colorAlpha);
150 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
153 *(packedColorGlyphBuffer + 2u) = (*(packedColorGlyphBuffer + 2u) * colorAlpha / 255);
154 *(packedColorGlyphBuffer + 1u) = (*(packedColorGlyphBuffer + 1u) * colorAlpha / 255);
155 *packedColorGlyphBuffer = (*(packedColorGlyphBuffer)*colorAlpha / 255);
157 if(data.glyphBitmap.isColorBitmap)
159 *(packedColorGlyphBuffer + 2u) = static_cast<uint8_t>(*(packedColorGlyphBuffer + 2u) * color->b);
160 *(packedColorGlyphBuffer + 1u) = static_cast<uint8_t>(*(packedColorGlyphBuffer + 1u) * color->g);
161 *packedColorGlyphBuffer = static_cast<uint8_t>(*packedColorGlyphBuffer * color->r);
166 // Set the color into the final pixel buffer.
167 *(bitmapBuffer + verticalOffset + xOffsetIndex) = packedColorGlyph;
171 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
172 // The format is RGBA8888.
173 uint32_t packedColor = 0u;
174 uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
176 // Update the alpha channel.
177 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
179 // Copy non-transparent pixels only
182 // Check alpha of overlapped pixels
183 uint32_t& currentColor = *(bitmapBuffer + verticalOffset + xOffsetIndex);
184 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
186 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
187 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
188 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
189 // happen, for example, in the RTL text when we copy glyphs from right to left).
190 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
191 currentAlpha = std::max(currentAlpha, alpha);
193 // Color is pre-muliplied with its alpha.
194 *(packedColorBuffer + 3u) = static_cast<uint8_t>(color->a * currentAlpha);
195 *(packedColorBuffer + 2u) = static_cast<uint8_t>(color->b * currentAlpha);
196 *(packedColorBuffer + 1u) = static_cast<uint8_t>(color->g * currentAlpha);
197 *(packedColorBuffer) = static_cast<uint8_t>(color->r * currentAlpha);
199 // Set the color into the final pixel buffer.
200 currentColor = packedColor;
208 // Whether the given glyph is a color one.
209 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
210 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
211 const uint32_t alphaIndex = glyphPixelSize - 1u;
213 // Initial vertical offset.
214 const int yOffset = data.verticalOffset + position->y;
216 uint8_t* bitmapBuffer = reinterpret_cast<uint8_t*>(data.bitmapBuffer.GetBuffer());
218 // Traverse the pixels of the glyph line per line.
219 for(int lineIndex = 0, glyphHeight = static_cast<int>(data.glyphBitmap.height); lineIndex < glyphHeight; ++lineIndex)
221 const int yOffsetIndex = yOffset + lineIndex;
222 if((0 > yOffsetIndex) || (yOffsetIndex > heightMinusOne))
224 // Do not write out of bounds.
228 const int verticalOffset = yOffsetIndex * data.width;
229 const int xOffset = data.horizontalOffset + position->x;
230 const int glyphBufferOffset = lineIndex * static_cast<int>(data.glyphBitmap.width);
231 for(int index = 0, glyphWidth = static_cast<int>(data.glyphBitmap.width); index < glyphWidth; ++index)
233 const int xOffsetIndex = xOffset + index;
234 if((0 > xOffsetIndex) || (xOffsetIndex > widthMinusOne))
236 // Don't write out of bounds.
242 // Update the alpha channel.
243 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
245 // Copy non-transparent pixels only
248 // Check alpha of overlapped pixels
249 uint8_t& currentAlpha = *(bitmapBuffer + verticalOffset + xOffsetIndex);
251 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
252 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
253 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
254 // happen, for example, in the RTL text when we copy glyphs from right to left).
255 currentAlpha = std::max(currentAlpha, alpha);
263 /// Draws the specified color to the pixel buffer
264 void WriteColorToPixelBuffer(
265 GlyphData& glyphData,
266 uint32_t* bitmapBuffer,
267 const Vector4& color,
268 const unsigned int x,
269 const unsigned int y)
271 // Always RGBA image for text with styles
272 uint32_t pixel = *(bitmapBuffer + y * glyphData.width + x);
273 uint8_t* pixelBuffer = reinterpret_cast<uint8_t*>(&pixel);
275 // Write the color to the pixel buffer
276 uint8_t colorAlpha = static_cast<uint8_t>(color.a * 255.f);
277 *(pixelBuffer + 3u) = colorAlpha;
278 *(pixelBuffer + 2u) = static_cast<uint8_t>(color.b * colorAlpha);
279 *(pixelBuffer + 1u) = static_cast<uint8_t>(color.g * colorAlpha);
280 *(pixelBuffer) = static_cast<uint8_t>(color.r * colorAlpha);
282 *(bitmapBuffer + y * glyphData.width + x) = pixel;
285 /// Draws the specified underline color to the buffer
287 const unsigned int bufferWidth,
288 const unsigned int bufferHeight,
289 GlyphData& glyphData,
290 const float baseline,
291 const float currentUnderlinePosition,
292 const float maxUnderlineHeight,
293 const float lineExtentLeft,
294 const float lineExtentRight,
295 const UnderlineStyleProperties& commonUnderlineProperties,
296 const UnderlineStyleProperties& currentUnderlineProperties,
299 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
300 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
301 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
302 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
304 int underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
305 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
307 for(unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineHeight; y++)
309 if(y > bufferHeight - 1)
311 // Do not write out of bounds.
314 if(underlineType == Text::Underline::DASHED)
316 float dashWidth = dashedUnderlineWidth;
319 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
321 if(x > bufferWidth - 1)
323 // Do not write out of bounds.
326 if(dashGap == 0 && dashWidth > 0)
328 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
331 else if(dashGap < dashedUnderlineGap)
338 dashWidth = dashedUnderlineWidth;
345 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
347 if(x > bufferWidth - 1)
349 // Do not write out of bounds.
352 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
356 if(underlineType == Text::Underline::DOUBLE)
358 int secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
359 for(unsigned int y = secondUnderlineYOffset; y < secondUnderlineYOffset + maxUnderlineHeight; y++)
361 if(y > bufferHeight - 1)
363 // Do not write out of bounds.
366 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
368 if(x > bufferWidth - 1)
370 // Do not write out of bounds.
373 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
379 /// Draws the background color to the buffer
380 void DrawBackgroundColor(
381 Vector4 backgroundColor,
382 const unsigned int bufferWidth,
383 const unsigned int bufferHeight,
384 GlyphData& glyphData,
385 const float baseline,
387 const float lineExtentLeft,
388 const float lineExtentRight)
390 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
392 for(int y = glyphData.verticalOffset + baseline - line.ascender; y < glyphData.verticalOffset + baseline - line.descender; y++)
394 if((y < 0) || (y > static_cast<int>(bufferHeight - 1)))
396 // Do not write out of bounds.
400 for(int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
402 if((x < 0) || (x > static_cast<int>(bufferWidth - 1)))
404 // Do not write out of bounds.
408 WriteColorToPixelBuffer(glyphData, bitmapBuffer, backgroundColor, x, y);
413 Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuffer& buffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, int horizontalOffset, int verticalOffset)
415 // Retrieve lines, glyphs, positions and colors from the view model.
416 const Length modelNumberOfLines = model->GetNumberOfLines();
417 const LineRun* const modelLinesBuffer = model->GetLines();
418 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
419 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
420 const Vector2* const positionBuffer = model->GetLayout();
421 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
422 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
424 // Create and initialize the pixel buffer.
426 glyphData.verticalOffset = verticalOffset;
427 glyphData.width = bufferWidth;
428 glyphData.height = bufferHeight;
429 glyphData.bitmapBuffer = buffer;
430 glyphData.horizontalOffset = 0;
432 ColorIndex prevBackgroundColorIndex = 0;
433 ColorIndex backgroundColorIndex = 0;
435 // Traverses the lines of the text.
436 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
438 const LineRun& line = *(modelLinesBuffer + lineIndex);
440 // Sets the horizontal offset of the line.
441 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
442 glyphData.horizontalOffset += horizontalOffset;
444 // Increases the vertical offset with the line's ascender.
445 glyphData.verticalOffset += static_cast<int>(line.ascender);
447 // Include line spacing after first line
450 glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
453 float left = bufferWidth;
455 float baseline = 0.0f;
457 // Traverses the glyphs of the line.
458 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
459 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
461 // Retrieve the glyph's info.
462 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
464 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
465 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
467 // Nothing to do if default background color, the glyph's width or height is zero.
471 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
473 if((backgroundColorIndex != prevBackgroundColorIndex) &&
474 (prevBackgroundColorIndex != 0u))
476 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
477 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
480 if(backgroundColorIndex == 0u)
482 prevBackgroundColorIndex = backgroundColorIndex;
483 //if background color is the default do nothing
487 // Retrieves the glyph's position.
488 const Vector2* const position = positionBuffer + glyphIndex;
490 if(baseline < position->y + glyphInfo->yBearing)
492 baseline = position->y + glyphInfo->yBearing;
495 // Calculate the positions of leftmost and rightmost glyphs in the current line
496 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
498 left = position->x - glyphInfo->xBearing;
501 if(position->x + glyphInfo->width > right)
503 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
506 prevBackgroundColorIndex = backgroundColorIndex;
509 //draw last background at line end if not default
510 if(backgroundColorIndex != 0u)
512 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
513 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
516 // Increases the vertical offset with the line's descender.
517 glyphData.verticalOffset += static_cast<int>(-line.descender);
520 return glyphData.bitmapBuffer;
523 /// Draws the specified strikethrough color to the buffer
524 void DrawStrikethrough(const unsigned int bufferWidth,
525 const unsigned int bufferHeight,
526 GlyphData& glyphData,
527 const float baseline,
528 const float strikethroughStartingYPosition,
529 const float maxStrikethroughHeight,
530 const float lineExtentLeft,
531 const float lineExtentRight,
532 const StrikethroughStyleProperties& commonStrikethroughProperties,
533 const StrikethroughStyleProperties& currentStrikethroughProperties,
536 const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
538 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
540 for(unsigned int y = strikethroughStartingYPosition; y < strikethroughStartingYPosition + maxStrikethroughHeight; y++)
542 if(y > bufferHeight - 1)
544 // Do not write out of bounds.
548 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
550 if(x > bufferWidth - 1)
552 // Do not write out of bounds.
556 WriteColorToPixelBuffer(glyphData, bitmapBuffer, strikethroughColor, x, y);
563 TypesetterPtr Typesetter::New(const ModelInterface* const model)
565 return TypesetterPtr(new Typesetter(model));
568 ViewModel* Typesetter::GetViewModel()
573 Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, const unsigned int bufferHeight, Pixel::Format pixelFormat)
575 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
577 if(Pixel::RGBA8888 == pixelFormat)
579 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
580 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
581 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
585 memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
591 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
593 // @todo. This initial implementation for a TextLabel has only one visible page.
595 // Elides the text if needed.
596 mModel->ElideGlyphs();
598 // Retrieves the layout size.
599 const Size& layoutSize = mModel->GetLayoutSize();
601 const int outlineWidth = static_cast<int>(mModel->GetOutlineWidth());
603 // Set the offset for the horizontal alignment according to the text direction and outline width.
606 switch(mModel->GetHorizontalAlignment())
608 case HorizontalAlignment::BEGIN:
613 case HorizontalAlignment::CENTER:
615 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
618 case HorizontalAlignment::END:
620 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
625 // Set the offset for the vertical alignment.
628 switch(mModel->GetVerticalAlignment())
630 case VerticalAlignment::TOP:
635 case VerticalAlignment::CENTER:
637 penY = static_cast<int>(0.5f * (size.height - layoutSize.height));
638 penY = penY < 0.f ? 0.f : penY;
641 case VerticalAlignment::BOTTOM:
643 penY = static_cast<int>(size.height - layoutSize.height);
648 // Calculate vertical line alignment
649 switch(mModel->GetVerticalLineAlignment())
651 case DevelText::VerticalLineAlignment::TOP:
655 case DevelText::VerticalLineAlignment::MIDDLE:
657 const auto& line = *mModel->GetLines();
658 penY -= line.descender;
659 penY += static_cast<int>(line.lineSpacing * 0.5f + line.descender);
662 case DevelText::VerticalLineAlignment::BOTTOM:
664 const auto& line = *mModel->GetLines();
665 const auto lineHeight = line.ascender + (-line.descender) + line.lineSpacing;
666 penY += static_cast<int>(lineHeight - (line.ascender - line.descender));
671 // Generate the image buffers of the text for each different style first,
672 // then combine all of them together as one final image buffer. We try to
673 // do all of these in CPU only, so that once the final texture is generated,
674 // no calculation is needed in GPU during each frame.
676 const unsigned int bufferWidth = static_cast<unsigned int>(size.width);
677 const unsigned int bufferHeight = static_cast<unsigned int>(size.height);
679 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
680 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
682 //Elided text in ellipsis at START could start on index greater than 0
683 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
684 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
686 Devel::PixelBuffer imageBuffer;
688 if(RENDER_MASK == behaviour)
690 // Generate the image buffer as an alpha mask for color glyphs.
691 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
693 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
695 // Generate an empty image buffer so that it can been combined with the image buffers for styles
696 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
697 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
701 // Generate the image buffer for the text with no style.
702 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
705 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
707 // Generate the outline if enabled
708 const uint16_t outlineWidth = mModel->GetOutlineWidth();
709 if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
711 // Create the image buffer for outline
712 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
714 // Combine the two buffers
715 imageBuffer = CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight);
718 // @todo. Support shadow and underline for partial text later on.
720 // Generate the shadow if enabled
721 const Vector2& shadowOffset = mModel->GetShadowOffset();
722 if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
724 // Create the image buffer for shadow
725 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
727 // Check whether it will be a soft shadow
728 const float& blurRadius = mModel->GetShadowBlurRadius();
730 if(blurRadius > Math::MACHINE_EPSILON_1)
732 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
735 // Combine the two buffers
736 imageBuffer = CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight);
739 // Generate the underline if enabled
740 const bool underlineEnabled = mModel->IsUnderlineEnabled();
741 if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
743 // Create the image buffer for underline
744 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
746 // Combine the two buffers
747 imageBuffer = CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
750 // Generate the background if enabled
751 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
752 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
753 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
755 Devel::PixelBuffer backgroundImageBuffer;
757 if(backgroundEnabled)
759 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
763 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
766 if(backgroundMarkupSet)
768 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
771 // Combine the two buffers
772 imageBuffer = CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight);
775 // Generate the strikethrough if enabled
776 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
777 if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
779 // Create the image buffer for strikethrough
780 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
782 // Combine the two buffers
783 imageBuffer = CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight);
788 imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
791 // Create the final PixelData for the combined image buffer
792 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
797 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)
799 // Retrieve lines, glyphs, positions and colors from the view model.
800 const Length modelNumberOfLines = mModel->GetNumberOfLines();
801 const LineRun* const modelLinesBuffer = mModel->GetLines();
802 const GlyphInfo* const glyphsBuffer = mModel->GetGlyphs();
803 const Vector2* const positionBuffer = mModel->GetLayout();
804 const Vector4* const colorsBuffer = mModel->GetColors();
805 const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
806 const GlyphInfo* hyphens = mModel->GetHyphens();
807 const Length* hyphenIndices = mModel->GetHyphenIndices();
808 const Length hyphensCount = mModel->GetHyphensCount();
810 // Elided text info. Indices according to elided text and Ellipsis position.
811 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
812 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
813 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
814 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
815 const auto ellipsisPosition = mModel->GetEllipsisPosition();
817 // Whether to use the default color.
818 const bool useDefaultColor = (NULL == colorsBuffer);
819 const Vector4& defaultColor = mModel->GetDefaultColor();
821 // Create and initialize the pixel buffer.
823 glyphData.verticalOffset = verticalOffset;
824 glyphData.width = bufferWidth;
825 glyphData.height = bufferHeight;
826 glyphData.bitmapBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
827 glyphData.horizontalOffset = 0;
829 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
830 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
831 Length hyphenIndex = 0;
833 const Character* textBuffer = mModel->GetTextBuffer();
834 float calculatedAdvance = 0.f;
835 const Vector<CharacterIndex>& glyphToCharacterMap = mModel->GetGlyphsToCharacters();
836 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
838 // Traverses the lines of the text.
839 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
841 const LineRun& line = *(modelLinesBuffer + lineIndex);
843 // Sets the horizontal offset of the line.
844 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
845 glyphData.horizontalOffset += horizontalOffset;
847 // Increases the vertical offset with the line's ascender.
848 glyphData.verticalOffset += static_cast<int>(line.ascender);
850 // Retrieves the glyph's outline width
851 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
853 if(style == Typesetter::STYLE_OUTLINE)
855 glyphData.horizontalOffset -= outlineWidth;
858 // Only need to add the vertical outline offset for the first line
859 glyphData.verticalOffset -= outlineWidth;
862 else if(style == Typesetter::STYLE_SHADOW)
864 const Vector2& shadowOffset = mModel->GetShadowOffset();
865 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
869 // Only need to add the vertical shadow offset for first line
870 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
874 const bool underlineEnabled = mModel->IsUnderlineEnabled();
875 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
876 const float characterSpacing = mModel->GetCharacterSpacing();
878 // Aggregate underline-style-properties from mModel
879 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
880 mModel->GetUnderlineColor(),
881 mModel->GetUnderlineHeight(),
882 mModel->GetDashedUnderlineGap(),
883 mModel->GetDashedUnderlineWidth(),
890 // Aggregate strikethrough-style-properties from mModel
891 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
892 mModel->GetStrikethroughHeight(),
896 // Get the underline runs.
897 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
898 Vector<UnderlinedGlyphRun> underlineRuns;
899 underlineRuns.Resize(numberOfUnderlineRuns);
900 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
902 // Get the strikethrough runs.
903 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
904 Vector<StrikethroughGlyphRun> strikethroughRuns;
905 strikethroughRuns.Resize(numberOfStrikethroughRuns);
906 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
908 bool thereAreUnderlinedGlyphs = false;
909 bool thereAreStrikethroughGlyphs = false;
911 float currentUnderlinePosition = 0.0f;
912 float currentUnderlineHeight = modelUnderlineProperties.height;
913 float maxUnderlineHeight = currentUnderlineHeight;
914 auto currentUnderlineProperties = modelUnderlineProperties;
916 float currentStrikethroughHeight = modelStrikethroughProperties.height;
917 float maxStrikethroughHeight = currentStrikethroughHeight;
918 auto currentStrikethroughProperties = modelStrikethroughProperties;
919 float strikethroughStartingYPosition = 0.0f;
921 FontId lastFontId = 0;
923 float lineExtentLeft = bufferWidth;
924 float lineExtentRight = 0.0f;
925 float baseline = 0.0f;
926 bool addHyphen = false;
928 // Traverses the glyphs of the line.
929 const GlyphIndex startGlyphIndex = std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs);
930 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
931 endGlyphIndex = std::min(endGlyphIndex, endIndexOfGlyphs);
933 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
935 if(glyphIndex < fromGlyphIndex || glyphIndex > toGlyphIndex)
937 // Ignore any glyph that out of the specified range
941 //To handle START case of ellipsis, the first glyph has been shifted
942 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
943 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
945 //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.
946 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
948 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
949 glyphIndex < secondMiddleIndexOfElidedGlyphs)
951 // Ignore any glyph that removed for MIDDLE ellipsis
954 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
956 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
960 // Retrieve the glyph's info.
961 const GlyphInfo* glyphInfo;
963 if(addHyphen && hyphens)
965 glyphInfo = hyphens + hyphenIndex;
970 glyphInfo = glyphsBuffer + elidedGlyphIndex;
973 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
974 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
976 // Nothing to do if the glyph's width or height is zero.
980 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
981 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
982 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
983 currentUnderlineHeight = currentUnderlineProperties.height;
984 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
986 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
987 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
988 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
989 currentStrikethroughHeight = currentStrikethroughProperties.height;
990 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
992 // Are we still using the same fontId as previous
993 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
995 // We need to fetch fresh font underline metrics
996 FontMetrics fontMetrics;
997 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
999 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1000 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1004 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1007 if(strikethroughGlyph)
1009 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1012 // Update lastFontId because fontId is changed
1013 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1016 // Retrieves the glyph's position.
1017 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1021 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1022 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1023 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1024 position.y = -glyphInfo->yBearing;
1027 if(baseline < position.y + glyphInfo->yBearing)
1029 baseline = position.y + glyphInfo->yBearing;
1032 // Calculate the positions of leftmost and rightmost glyphs in the current line
1033 if(position.x < lineExtentLeft)
1035 lineExtentLeft = position.x;
1038 if(position.x + glyphInfo->width > lineExtentRight)
1040 lineExtentRight = position.x + glyphInfo->width;
1043 // Retrieves the glyph's color.
1044 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1047 if(style == Typesetter::STYLE_SHADOW)
1049 color = mModel->GetShadowColor();
1051 else if(style == Typesetter::STYLE_OUTLINE)
1053 color = mModel->GetOutlineColor();
1057 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1060 // Premultiply alpha
1065 // Retrieves the glyph's bitmap.
1066 glyphData.glyphBitmap.buffer = NULL;
1067 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1068 glyphData.glyphBitmap.height = glyphInfo->height;
1070 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1072 // Don't render outline for other styles
1073 outlineWidth = 0.0f;
1076 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1078 fontClient.CreateBitmap(glyphInfo->fontId,
1080 glyphInfo->isItalicRequired,
1081 glyphInfo->isBoldRequired,
1082 glyphData.glyphBitmap,
1083 static_cast<int>(outlineWidth));
1086 // Sets the glyph's bitmap into the bitmap of the whole text.
1087 if(NULL != glyphData.glyphBitmap.buffer)
1089 if(style == Typesetter::STYLE_OUTLINE)
1091 // Set the position offset for the current glyph
1092 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1093 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1096 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1097 TypesetGlyph(glyphData,
1103 if(style == Typesetter::STYLE_OUTLINE)
1105 // Reset the position offset for the next glyph
1106 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1107 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1110 // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1111 delete[] glyphData.glyphBitmap.buffer;
1112 glyphData.glyphBitmap.buffer = NULL;
1117 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1122 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1130 // Draw the underline from the leftmost glyph to the rightmost glyph
1131 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1133 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1136 // Draw the background color from the leftmost glyph to the rightmost glyph
1137 if(style == Typesetter::STYLE_BACKGROUND)
1139 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1142 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1143 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1145 //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.
1146 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.
1147 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1150 // Increases the vertical offset with the line's descender & line spacing.
1151 glyphData.verticalOffset += static_cast<int>(-line.descender+line.lineSpacing);
1154 return glyphData.bitmapBuffer;
1157 Devel::PixelBuffer Typesetter::CombineImageBuffer(Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight)
1159 unsigned char* topBuffer = topPixelBuffer.GetBuffer();
1160 unsigned char* bottomBuffer = bottomPixelBuffer.GetBuffer();
1162 Devel::PixelBuffer combinedPixelBuffer;
1164 if(topBuffer == NULL && bottomBuffer == NULL)
1166 // Nothing to do if both buffers are empty.
1167 return combinedPixelBuffer;
1170 if(topBuffer == NULL)
1172 // Nothing to do if topBuffer is empty.
1173 return bottomPixelBuffer;
1176 if(bottomBuffer == NULL)
1178 // Nothing to do if bottomBuffer is empty.
1179 return topPixelBuffer;
1182 // Always combine two RGBA images
1183 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
1184 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
1186 combinedPixelBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1187 uint8_t* combinedBuffer = reinterpret_cast<uint8_t*>(combinedPixelBuffer.GetBuffer());
1188 memset(combinedBuffer, 0u, bufferSizeChar);
1190 for(unsigned int pixelIndex = 0; pixelIndex < bufferSizeInt; pixelIndex++)
1192 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
1193 // Otherwise, copy pixel from topBuffer to combinedBuffer.
1195 unsigned int alphaBuffer1 = topBuffer[pixelIndex * 4 + 3];
1197 if(alphaBuffer1 != 255)
1199 // At least one pixel is not fully opaque
1200 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
1201 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4] + (bottomBuffer[pixelIndex * 4] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1202 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1] + (bottomBuffer[pixelIndex * 4 + 1] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1203 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2] + (bottomBuffer[pixelIndex * 4 + 2] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1204 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3] + (bottomBuffer[pixelIndex * 4 + 3] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1208 // Copy the pixel from topBuffer to combinedBuffer
1209 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4];
1210 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1];
1211 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2];
1212 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3];
1216 return combinedPixelBuffer;
1219 Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1221 // Underline-tags (this is for Markup case)
1222 // Get the underline runs.
1223 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1224 Vector<UnderlinedGlyphRun> underlineRuns;
1225 underlineRuns.Resize(numberOfUnderlineRuns);
1226 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1228 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1229 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1230 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1231 GlyphIndex startGlyphIndex, endGlyphIndex;
1233 //The outer loop to iterate on the separated chunks of underlined glyph runs
1234 while(itGlyphRun != endItGlyphRun)
1236 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1237 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1239 // Create the image buffer for underline
1240 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1241 // Combine the two buffers
1242 topPixelBuffer = CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight);
1247 return topPixelBuffer;
1250 Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1252 // strikethrough-tags (this is for Markup case)
1253 // Get the strikethrough runs.
1254 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1255 Vector<StrikethroughGlyphRun> strikethroughRuns;
1256 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1257 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1259 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1260 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1261 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1262 GlyphIndex startGlyphIndex, endGlyphIndex;
1264 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1265 while(itGlyphRun != endItGlyphRun)
1267 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1268 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1270 // Create the image buffer for strikethrough
1271 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1272 // Combine the two buffers
1273 topPixelBuffer = CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight);
1278 return topPixelBuffer;
1281 Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1283 // Apply the markup-Processor if enabled
1284 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1285 if(markupProcessorEnabled)
1287 topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1289 topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1292 return topPixelBuffer;
1295 Typesetter::Typesetter(const ModelInterface* const model)
1296 : mModel(new ViewModel(model))
1300 Typesetter::~Typesetter()
1307 } // namespace Toolkit