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 // Include line spacing after first line
853 glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
856 // Retrieves the glyph's outline width
857 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
859 if(style == Typesetter::STYLE_OUTLINE)
861 glyphData.horizontalOffset -= outlineWidth;
864 // Only need to add the vertical outline offset for the first line
865 glyphData.verticalOffset -= outlineWidth;
868 else if(style == Typesetter::STYLE_SHADOW)
870 const Vector2& shadowOffset = mModel->GetShadowOffset();
871 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
875 // Only need to add the vertical shadow offset for first line
876 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
880 const bool underlineEnabled = mModel->IsUnderlineEnabled();
881 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
882 const float characterSpacing = mModel->GetCharacterSpacing();
884 // Aggregate underline-style-properties from mModel
885 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
886 mModel->GetUnderlineColor(),
887 mModel->GetUnderlineHeight(),
888 mModel->GetDashedUnderlineGap(),
889 mModel->GetDashedUnderlineWidth(),
896 // Aggregate strikethrough-style-properties from mModel
897 const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
898 mModel->GetStrikethroughHeight(),
902 // Get the underline runs.
903 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
904 Vector<UnderlinedGlyphRun> underlineRuns;
905 underlineRuns.Resize(numberOfUnderlineRuns);
906 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
908 // Get the strikethrough runs.
909 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
910 Vector<StrikethroughGlyphRun> strikethroughRuns;
911 strikethroughRuns.Resize(numberOfStrikethroughRuns);
912 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
914 bool thereAreUnderlinedGlyphs = false;
915 bool thereAreStrikethroughGlyphs = false;
917 float currentUnderlinePosition = 0.0f;
918 float currentUnderlineHeight = modelUnderlineProperties.height;
919 float maxUnderlineHeight = currentUnderlineHeight;
920 auto currentUnderlineProperties = modelUnderlineProperties;
922 float currentStrikethroughHeight = modelStrikethroughProperties.height;
923 float maxStrikethroughHeight = currentStrikethroughHeight;
924 auto currentStrikethroughProperties = modelStrikethroughProperties;
925 float strikethroughStartingYPosition = 0.0f;
927 FontId lastFontId = 0;
929 float lineExtentLeft = bufferWidth;
930 float lineExtentRight = 0.0f;
931 float baseline = 0.0f;
932 bool addHyphen = false;
934 // Traverses the glyphs of the line.
935 const GlyphIndex startGlyphIndex = std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs);
936 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
937 endGlyphIndex = std::min(endGlyphIndex, endIndexOfGlyphs);
939 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
941 if(glyphIndex < fromGlyphIndex || glyphIndex > toGlyphIndex)
943 // Ignore any glyph that out of the specified range
947 //To handle START case of ellipsis, the first glyph has been shifted
948 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
949 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
951 //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.
952 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
954 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
955 glyphIndex < secondMiddleIndexOfElidedGlyphs)
957 // Ignore any glyph that removed for MIDDLE ellipsis
960 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
962 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
966 // Retrieve the glyph's info.
967 const GlyphInfo* glyphInfo;
969 if(addHyphen && hyphens)
971 glyphInfo = hyphens + hyphenIndex;
976 glyphInfo = glyphsBuffer + elidedGlyphIndex;
979 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
980 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
982 // Nothing to do if the glyph's width or height is zero.
986 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
987 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
988 currentUnderlineProperties = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
989 currentUnderlineHeight = currentUnderlineProperties.height;
990 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
992 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
993 const bool strikethroughGlyph = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
994 currentStrikethroughProperties = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
995 currentStrikethroughHeight = currentStrikethroughProperties.height;
996 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || strikethroughGlyph;
998 // Are we still using the same fontId as previous
999 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1001 // We need to fetch fresh font underline metrics
1002 FontMetrics fontMetrics;
1003 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1005 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1006 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1010 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1013 if(strikethroughGlyph)
1015 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1018 // Update lastFontId because fontId is changed
1019 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1022 // Retrieves the glyph's position.
1023 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1027 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1028 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1029 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1030 position.y = -glyphInfo->yBearing;
1033 if(baseline < position.y + glyphInfo->yBearing)
1035 baseline = position.y + glyphInfo->yBearing;
1038 // Calculate the positions of leftmost and rightmost glyphs in the current line
1039 if(position.x < lineExtentLeft)
1041 lineExtentLeft = position.x;
1044 if(position.x + glyphInfo->width > lineExtentRight)
1046 lineExtentRight = position.x + glyphInfo->width;
1049 // Retrieves the glyph's color.
1050 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1053 if(style == Typesetter::STYLE_SHADOW)
1055 color = mModel->GetShadowColor();
1057 else if(style == Typesetter::STYLE_OUTLINE)
1059 color = mModel->GetOutlineColor();
1063 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1066 // Premultiply alpha
1071 // Retrieves the glyph's bitmap.
1072 glyphData.glyphBitmap.buffer = NULL;
1073 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1074 glyphData.glyphBitmap.height = glyphInfo->height;
1076 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1078 // Don't render outline for other styles
1079 outlineWidth = 0.0f;
1082 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1084 fontClient.CreateBitmap(glyphInfo->fontId,
1086 glyphInfo->isItalicRequired,
1087 glyphInfo->isBoldRequired,
1088 glyphData.glyphBitmap,
1089 static_cast<int>(outlineWidth));
1092 // Sets the glyph's bitmap into the bitmap of the whole text.
1093 if(NULL != glyphData.glyphBitmap.buffer)
1095 if(style == Typesetter::STYLE_OUTLINE)
1097 // Set the position offset for the current glyph
1098 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1099 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1102 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1103 TypesetGlyph(glyphData,
1109 if(style == Typesetter::STYLE_OUTLINE)
1111 // Reset the position offset for the next glyph
1112 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1113 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1116 // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1117 delete[] glyphData.glyphBitmap.buffer;
1118 glyphData.glyphBitmap.buffer = NULL;
1123 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1128 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1136 // Draw the underline from the leftmost glyph to the rightmost glyph
1137 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1139 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1142 // Draw the background color from the leftmost glyph to the rightmost glyph
1143 if(style == Typesetter::STYLE_BACKGROUND)
1145 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1148 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1149 if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1151 //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.
1152 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.
1153 DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1156 // Increases the vertical offset with the line's descender.
1157 glyphData.verticalOffset += static_cast<int>(-line.descender);
1160 return glyphData.bitmapBuffer;
1163 Devel::PixelBuffer Typesetter::CombineImageBuffer(Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight)
1165 unsigned char* topBuffer = topPixelBuffer.GetBuffer();
1166 unsigned char* bottomBuffer = bottomPixelBuffer.GetBuffer();
1168 Devel::PixelBuffer combinedPixelBuffer;
1170 if(topBuffer == NULL && bottomBuffer == NULL)
1172 // Nothing to do if both buffers are empty.
1173 return combinedPixelBuffer;
1176 if(topBuffer == NULL)
1178 // Nothing to do if topBuffer is empty.
1179 return bottomPixelBuffer;
1182 if(bottomBuffer == NULL)
1184 // Nothing to do if bottomBuffer is empty.
1185 return topPixelBuffer;
1188 // Always combine two RGBA images
1189 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
1190 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
1192 combinedPixelBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1193 uint8_t* combinedBuffer = reinterpret_cast<uint8_t*>(combinedPixelBuffer.GetBuffer());
1194 memset(combinedBuffer, 0u, bufferSizeChar);
1196 for(unsigned int pixelIndex = 0; pixelIndex < bufferSizeInt; pixelIndex++)
1198 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
1199 // Otherwise, copy pixel from topBuffer to combinedBuffer.
1201 unsigned int alphaBuffer1 = topBuffer[pixelIndex * 4 + 3];
1203 if(alphaBuffer1 != 255)
1205 // At least one pixel is not fully opaque
1206 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
1207 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4] + (bottomBuffer[pixelIndex * 4] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1208 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1] + (bottomBuffer[pixelIndex * 4 + 1] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1209 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2] + (bottomBuffer[pixelIndex * 4 + 2] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1210 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3] + (bottomBuffer[pixelIndex * 4 + 3] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1214 // Copy the pixel from topBuffer to combinedBuffer
1215 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4];
1216 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1];
1217 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2];
1218 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3];
1222 return combinedPixelBuffer;
1225 Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1227 // Underline-tags (this is for Markup case)
1228 // Get the underline runs.
1229 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1230 Vector<UnderlinedGlyphRun> underlineRuns;
1231 underlineRuns.Resize(numberOfUnderlineRuns);
1232 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1234 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1235 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1236 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1237 GlyphIndex startGlyphIndex, endGlyphIndex;
1239 //The outer loop to iterate on the separated chunks of underlined glyph runs
1240 while(itGlyphRun != endItGlyphRun)
1242 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1243 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1245 // Create the image buffer for underline
1246 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1247 // Combine the two buffers
1248 topPixelBuffer = CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight);
1253 return topPixelBuffer;
1256 Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1258 // strikethrough-tags (this is for Markup case)
1259 // Get the strikethrough runs.
1260 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1261 Vector<StrikethroughGlyphRun> strikethroughRuns;
1262 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1263 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1265 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1266 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1267 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1268 GlyphIndex startGlyphIndex, endGlyphIndex;
1270 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1271 while(itGlyphRun != endItGlyphRun)
1273 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1274 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1276 // Create the image buffer for strikethrough
1277 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1278 // Combine the two buffers
1279 topPixelBuffer = CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight);
1284 return topPixelBuffer;
1287 Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1289 // Apply the markup-Processor if enabled
1290 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1291 if(markupProcessorEnabled)
1293 topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1295 topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1298 return topPixelBuffer;
1301 Typesetter::Typesetter(const ModelInterface* const model)
1302 : mModel(new ViewModel(model))
1306 Typesetter::~Typesetter()
1313 } // namespace Toolkit