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