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/view-model.h>
39 const float HALF(0.5f);
41 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
45 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
46 Vector2* position; ///< The position of the glyph.
47 TextAbstraction::FontClient::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
48 unsigned int width; ///< The bitmap's width.
49 unsigned int height; ///< The bitmap's height.
50 int horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
51 int verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
55 * @brief Sets the glyph's buffer into the bitmap's buffer.
57 * @param[in] data Struct which contains the glyph's data and the bitmap's data.
58 * @param[in] position The position of the glyph.
59 * @param[in] color The color of the glyph.
60 * @param[in] style The style of the text.
61 * @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).
63 void TypesetGlyph(GlyphData& data,
64 const Vector2* const position,
65 const Vector4* const color,
66 Typesetter::Style style,
67 Pixel::Format pixelFormat)
69 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
71 // Nothing to do if the width or height of the buffer is zero.
75 const int widthMinusOne = static_cast<int>(data.width - 1u);
76 const int heightMinusOne = static_cast<int>(data.height - 1u);
78 if(Pixel::RGBA8888 == pixelFormat)
80 // Whether the given glyph is a color one.
81 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
82 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
83 const uint32_t alphaIndex = glyphPixelSize - 1u;
84 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
86 // Pointer to the color glyph if there is one.
87 const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>(data.glyphBitmap.buffer) : NULL;
89 // Initial vertical offset.
90 const int yOffset = data.verticalOffset + position->y;
92 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
94 // Traverse the pixels of the glyph line per line.
95 for(int lineIndex = 0, glyphHeight = static_cast<int>(data.glyphBitmap.height); lineIndex < glyphHeight; ++lineIndex)
97 const int yOffsetIndex = yOffset + lineIndex;
98 if((0 > yOffsetIndex) || (yOffsetIndex > heightMinusOne))
100 // Do not write out of bounds.
104 const int verticalOffset = yOffsetIndex * data.width;
105 const int xOffset = data.horizontalOffset + position->x;
106 const int glyphBufferOffset = lineIndex * static_cast<int>(data.glyphBitmap.width);
107 for(int index = 0, glyphWidth = static_cast<int>(data.glyphBitmap.width); index < glyphWidth; ++index)
109 const int xOffsetIndex = xOffset + index;
110 if((0 > xOffsetIndex) || (xOffsetIndex > widthMinusOne))
112 // Don't write out of bounds.
118 // Retrieves the color from the color glyph.
119 uint32_t packedColorGlyph = *(colorGlyphBuffer + glyphBufferOffset + index);
120 uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
122 // Update the alpha channel.
123 if(Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style) // Outline not shown for color glyph
125 // Create an alpha mask for color glyph.
126 *(packedColorGlyphBuffer + 3u) = 0u;
127 *(packedColorGlyphBuffer + 2u) = 0u;
128 *(packedColorGlyphBuffer + 1u) = 0u;
129 *packedColorGlyphBuffer = 0u;
133 const uint8_t colorAlpha = static_cast<uint8_t>(color->a * static_cast<float>(*(packedColorGlyphBuffer + 3u)));
134 *(packedColorGlyphBuffer + 3u) = colorAlpha;
136 if(Typesetter::STYLE_SHADOW == style)
138 // The shadow of color glyph needs to have the shadow color.
139 *(packedColorGlyphBuffer + 2u) = static_cast<uint8_t>(color->b * colorAlpha);
140 *(packedColorGlyphBuffer + 1u) = static_cast<uint8_t>(color->g * colorAlpha);
141 *packedColorGlyphBuffer = static_cast<uint8_t>(color->r * colorAlpha);
147 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
150 *(packedColorGlyphBuffer + 2u) = (*(packedColorGlyphBuffer + 2u) * colorAlpha / 255);
151 *(packedColorGlyphBuffer + 1u) = (*(packedColorGlyphBuffer + 1u) * colorAlpha / 255);
152 *packedColorGlyphBuffer = (*(packedColorGlyphBuffer)*colorAlpha / 255);
154 if(data.glyphBitmap.isColorBitmap)
156 *(packedColorGlyphBuffer + 2u) = static_cast<uint8_t>(*(packedColorGlyphBuffer + 2u) * color->b);
157 *(packedColorGlyphBuffer + 1u) = static_cast<uint8_t>(*(packedColorGlyphBuffer + 1u) * color->g);
158 *packedColorGlyphBuffer = static_cast<uint8_t>(*packedColorGlyphBuffer * color->r);
163 // Set the color into the final pixel buffer.
164 *(bitmapBuffer + verticalOffset + xOffsetIndex) = packedColorGlyph;
168 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
169 // The format is RGBA8888.
170 uint32_t packedColor = 0u;
171 uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
173 // Update the alpha channel.
174 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
176 // Copy non-transparent pixels only
179 // Check alpha of overlapped pixels
180 uint32_t& currentColor = *(bitmapBuffer + verticalOffset + xOffsetIndex);
181 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
183 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
184 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
185 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
186 // happen, for example, in the RTL text when we copy glyphs from right to left).
187 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
188 currentAlpha = std::max(currentAlpha, alpha);
190 // Color is pre-muliplied with its alpha.
191 *(packedColorBuffer + 3u) = static_cast<uint8_t>(color->a * currentAlpha);
192 *(packedColorBuffer + 2u) = static_cast<uint8_t>(color->b * currentAlpha);
193 *(packedColorBuffer + 1u) = static_cast<uint8_t>(color->g * currentAlpha);
194 *(packedColorBuffer) = static_cast<uint8_t>(color->r * currentAlpha);
196 // Set the color into the final pixel buffer.
197 currentColor = packedColor;
205 // Whether the given glyph is a color one.
206 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
207 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
208 const uint32_t alphaIndex = glyphPixelSize - 1u;
210 // Initial vertical offset.
211 const int yOffset = data.verticalOffset + position->y;
213 uint8_t* bitmapBuffer = reinterpret_cast<uint8_t*>(data.bitmapBuffer.GetBuffer());
215 // Traverse the pixels of the glyph line per line.
216 for(int lineIndex = 0, glyphHeight = static_cast<int>(data.glyphBitmap.height); lineIndex < glyphHeight; ++lineIndex)
218 const int yOffsetIndex = yOffset + lineIndex;
219 if((0 > yOffsetIndex) || (yOffsetIndex > heightMinusOne))
221 // Do not write out of bounds.
225 const int verticalOffset = yOffsetIndex * data.width;
226 const int xOffset = data.horizontalOffset + position->x;
227 const int glyphBufferOffset = lineIndex * static_cast<int>(data.glyphBitmap.width);
228 for(int index = 0, glyphWidth = static_cast<int>(data.glyphBitmap.width); index < glyphWidth; ++index)
230 const int xOffsetIndex = xOffset + index;
231 if((0 > xOffsetIndex) || (xOffsetIndex > widthMinusOne))
233 // Don't write out of bounds.
239 // Update the alpha channel.
240 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
242 // Copy non-transparent pixels only
245 // Check alpha of overlapped pixels
246 uint8_t& currentAlpha = *(bitmapBuffer + verticalOffset + xOffsetIndex);
248 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
249 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
250 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
251 // happen, for example, in the RTL text when we copy glyphs from right to left).
252 currentAlpha = std::max(currentAlpha, alpha);
260 bool IsGlyphUnderlined(GlyphIndex index,
261 const Vector<GlyphRun>& underlineRuns)
263 for(Vector<GlyphRun>::ConstIterator it = underlineRuns.Begin(),
264 endIt = underlineRuns.End();
268 const GlyphRun& run = *it;
270 if((run.glyphIndex <= index) && (index < run.glyphIndex + run.numberOfGlyphs))
279 bool doGlyphHaveStrikethrough(GlyphIndex index,
280 const Vector<StrikethroughGlyphRun>& strikethroughRuns,
281 Vector4& strikethroughColor)
283 for(Vector<StrikethroughGlyphRun>::ConstIterator it = strikethroughRuns.Begin(),
284 endIt = strikethroughRuns.End();
288 const StrikethroughGlyphRun& run = *it;
290 if((run.glyphRun.glyphIndex <= index) && (index < run.glyphRun.glyphIndex + run.glyphRun.numberOfGlyphs))
294 strikethroughColor = run.color;
304 /// Helper method to fetch the underline metrics for the specified font glyph
305 void FetchFontDecorationlinesMetrics(
306 TextAbstraction::FontClient& fontClient,
307 const GlyphInfo* const glyphInfo,
308 float& currentUnderlinePosition,
309 const float underlineHeight,
310 float& currentUnderlineThickness,
311 float& maxUnderlineThickness,
312 FontId& lastlinedFontId,
313 const float strikethroughHeight,
314 float& currentStrikethroughThickness,
315 float& maxStrikethroughThickness)
317 FontMetrics fontMetrics;
318 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
319 currentUnderlinePosition = ceil(fabsf(fontMetrics.underlinePosition));
320 const float descender = ceil(fabsf(fontMetrics.descender));
322 if(fabsf(underlineHeight) < Math::MACHINE_EPSILON_1000)
324 currentUnderlineThickness = fontMetrics.underlineThickness;
326 // Ensure underline will be at least a pixel high
327 if(currentUnderlineThickness < 1.0f)
329 currentUnderlineThickness = 1.0f;
333 currentUnderlineThickness = ceil(currentUnderlineThickness);
337 if(fabsf(strikethroughHeight) < Math::MACHINE_EPSILON_1000)
339 // Ensure strikethrough will be at least a pixel high
340 if(currentStrikethroughThickness < 1.0f)
342 currentStrikethroughThickness = 1.0f;
346 currentStrikethroughThickness = ceil(currentStrikethroughThickness);
350 // The underline thickness should be the max underline thickness of all glyphs of the line.
351 if(currentUnderlineThickness > maxUnderlineThickness)
353 maxUnderlineThickness = currentUnderlineThickness;
356 // The strikethrough thickness should be the max strikethrough thickness of all glyphs of the line.
357 if(currentStrikethroughThickness > maxStrikethroughThickness)
359 maxStrikethroughThickness = currentStrikethroughThickness;
362 // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
363 if(currentUnderlinePosition > descender)
365 currentUnderlinePosition = descender;
368 if(fabsf(currentUnderlinePosition) < Math::MACHINE_EPSILON_1000)
370 // Move offset down by one ( EFL behavior )
371 currentUnderlinePosition = 1.0f;
374 lastlinedFontId = glyphInfo->fontId;
377 /// Draws the specified color to the pixel buffer
378 void WriteColorToPixelBuffer(
379 GlyphData& glyphData,
380 uint32_t* bitmapBuffer,
381 const Vector4& color,
382 const unsigned int x,
383 const unsigned int y)
385 // Always RGBA image for text with styles
386 uint32_t pixel = *(bitmapBuffer + y * glyphData.width + x);
387 uint8_t* pixelBuffer = reinterpret_cast<uint8_t*>(&pixel);
389 // Write the color to the pixel buffer
390 uint8_t colorAlpha = static_cast<uint8_t>(color.a * 255.f);
391 *(pixelBuffer + 3u) = colorAlpha;
392 *(pixelBuffer + 2u) = static_cast<uint8_t>(color.b * colorAlpha);
393 *(pixelBuffer + 1u) = static_cast<uint8_t>(color.g * colorAlpha);
394 *(pixelBuffer) = static_cast<uint8_t>(color.r * colorAlpha);
396 *(bitmapBuffer + y * glyphData.width + x) = pixel;
399 /// Draws the specified underline color to the buffer
401 const Vector4& underlineColor,
402 const unsigned int bufferWidth,
403 const unsigned int bufferHeight,
404 GlyphData& glyphData,
405 const float baseline,
406 const float currentUnderlinePosition,
407 const float maxUnderlineThickness,
408 const float lineExtentLeft,
409 const float lineExtentRight,
410 const Text::Underline::Type underlineType,
411 const float dashedUnderlineWidth,
412 const float dashedUnderlineGap,
415 int underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
416 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
418 for(unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineThickness; y++)
420 if(y > bufferHeight - 1)
422 // Do not write out of bounds.
425 if(underlineType == Text::Underline::DASHED)
427 float dashWidth = dashedUnderlineWidth;
430 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
432 if(x > bufferWidth - 1)
434 // Do not write out of bounds.
437 if(dashGap == 0 && dashWidth > 0)
439 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
442 else if(dashGap < dashedUnderlineGap)
449 dashWidth = dashedUnderlineWidth;
456 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
458 if(x > bufferWidth - 1)
460 // Do not write out of bounds.
463 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
467 if(underlineType == Text::Underline::DOUBLE)
469 int secondUnderlineYOffset = glyphData.verticalOffset - line.descender - maxUnderlineThickness;
470 for(unsigned int y = secondUnderlineYOffset; y < secondUnderlineYOffset + maxUnderlineThickness; y++)
472 if(y > bufferHeight - 1)
474 // Do not write out of bounds.
477 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
479 if(x > bufferWidth - 1)
481 // Do not write out of bounds.
484 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
490 /// Draws the background color to the buffer
491 void DrawBackgroundColor(
492 Vector4 backgroundColor,
493 const unsigned int bufferWidth,
494 const unsigned int bufferHeight,
495 GlyphData& glyphData,
496 const float baseline,
498 const float lineExtentLeft,
499 const float lineExtentRight)
501 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
503 for(int y = glyphData.verticalOffset + baseline - line.ascender; y < glyphData.verticalOffset + baseline - line.descender; y++)
505 if((y < 0) || (y > static_cast<int>(bufferHeight - 1)))
507 // Do not write out of bounds.
511 for(int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
513 if((x < 0) || (x > static_cast<int>(bufferWidth - 1)))
515 // Do not write out of bounds.
519 WriteColorToPixelBuffer(glyphData, bitmapBuffer, backgroundColor, x, y);
524 Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuffer& buffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, int horizontalOffset, int verticalOffset)
526 // Retrieve lines, glyphs, positions and colors from the view model.
527 const Length modelNumberOfLines = model->GetNumberOfLines();
528 const LineRun* const modelLinesBuffer = model->GetLines();
529 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
530 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
531 const Vector2* const positionBuffer = model->GetLayout();
532 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
533 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
535 // Create and initialize the pixel buffer.
537 glyphData.verticalOffset = verticalOffset;
538 glyphData.width = bufferWidth;
539 glyphData.height = bufferHeight;
540 glyphData.bitmapBuffer = buffer;
541 glyphData.horizontalOffset = 0;
543 ColorIndex prevBackgroundColorIndex = 0;
544 ColorIndex backgroundColorIndex = 0;
546 // Traverses the lines of the text.
547 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
549 const LineRun& line = *(modelLinesBuffer + lineIndex);
551 // Sets the horizontal offset of the line.
552 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
553 glyphData.horizontalOffset += horizontalOffset;
555 // Increases the vertical offset with the line's ascender.
556 glyphData.verticalOffset += static_cast<int>(line.ascender);
558 // Include line spacing after first line
561 glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
564 float left = bufferWidth;
566 float baseline = 0.0f;
568 // Traverses the glyphs of the line.
569 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
570 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
572 // Retrieve the glyph's info.
573 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
575 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
576 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
578 // Nothing to do if default background color, the glyph's width or height is zero.
582 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
584 if((backgroundColorIndex != prevBackgroundColorIndex) &&
585 (prevBackgroundColorIndex != 0u))
587 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
588 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
591 if(backgroundColorIndex == 0u)
593 prevBackgroundColorIndex = backgroundColorIndex;
594 //if background color is the default do nothing
598 // Retrieves the glyph's position.
599 const Vector2* const position = positionBuffer + glyphIndex;
601 if(baseline < position->y + glyphInfo->yBearing)
603 baseline = position->y + glyphInfo->yBearing;
606 // Calculate the positions of leftmost and rightmost glyphs in the current line
607 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
609 left = position->x - glyphInfo->xBearing;
612 if(position->x + glyphInfo->width > right)
614 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
617 prevBackgroundColorIndex = backgroundColorIndex;
620 //draw last background at line end if not default
621 if(backgroundColorIndex != 0u)
623 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
624 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
627 // Increases the vertical offset with the line's descender.
628 glyphData.verticalOffset += static_cast<int>(-line.descender);
631 return glyphData.bitmapBuffer;
634 /// Draws the specified strikethrough color to the buffer
635 void DrawStrikethrough(
636 const Vector4& strikethroughColor,
637 const unsigned int bufferWidth,
638 const unsigned int bufferHeight,
639 GlyphData& glyphData,
640 const float baseline,
642 const float maxStrikethroughThickness,
643 const float lineExtentLeft,
644 const float lineExtentRight,
645 float strikethroughStartingYPosition)
647 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
649 for(unsigned int y = strikethroughStartingYPosition; y < strikethroughStartingYPosition + maxStrikethroughThickness; y++)
651 if(y > bufferHeight - 1)
653 // Do not write out of bounds.
657 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
659 if(x > bufferWidth - 1)
661 // Do not write out of bounds.
665 WriteColorToPixelBuffer(glyphData, bitmapBuffer, strikethroughColor, x, y);
672 TypesetterPtr Typesetter::New(const ModelInterface* const model)
674 return TypesetterPtr(new Typesetter(model));
677 ViewModel* Typesetter::GetViewModel()
682 Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, const unsigned int bufferHeight, Pixel::Format pixelFormat)
684 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
686 if(Pixel::RGBA8888 == pixelFormat)
688 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
689 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
690 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
694 memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
700 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
702 // @todo. This initial implementation for a TextLabel has only one visible page.
704 // Elides the text if needed.
705 mModel->ElideGlyphs();
707 // Retrieves the layout size.
708 const Size& layoutSize = mModel->GetLayoutSize();
710 const int outlineWidth = static_cast<int>(mModel->GetOutlineWidth());
712 // Set the offset for the horizontal alignment according to the text direction and outline width.
715 switch(mModel->GetHorizontalAlignment())
717 case HorizontalAlignment::BEGIN:
722 case HorizontalAlignment::CENTER:
724 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
727 case HorizontalAlignment::END:
729 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
734 // Set the offset for the vertical alignment.
737 switch(mModel->GetVerticalAlignment())
739 case VerticalAlignment::TOP:
744 case VerticalAlignment::CENTER:
746 penY = static_cast<int>(0.5f * (size.height - layoutSize.height));
747 penY = penY < 0.f ? 0.f : penY;
750 case VerticalAlignment::BOTTOM:
752 penY = static_cast<int>(size.height - layoutSize.height);
757 // Calculate vertical line alignment
758 switch(mModel->GetVerticalLineAlignment())
760 case DevelText::VerticalLineAlignment::TOP:
764 case DevelText::VerticalLineAlignment::MIDDLE:
766 const auto& line = *mModel->GetLines();
767 penY -= line.descender;
768 penY += static_cast<int>(line.lineSpacing * 0.5f + line.descender);
771 case DevelText::VerticalLineAlignment::BOTTOM:
773 const auto& line = *mModel->GetLines();
774 const auto lineHeight = line.ascender + (-line.descender) + line.lineSpacing;
775 penY += static_cast<int>(lineHeight - (line.ascender - line.descender));
780 // Generate the image buffers of the text for each different style first,
781 // then combine all of them together as one final image buffer. We try to
782 // do all of these in CPU only, so that once the final texture is generated,
783 // no calculation is needed in GPU during each frame.
785 const unsigned int bufferWidth = static_cast<unsigned int>(size.width);
786 const unsigned int bufferHeight = static_cast<unsigned int>(size.height);
788 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
789 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
791 //Elided text in ellipsis at START could start on index greater than 0
792 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
793 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
795 Devel::PixelBuffer imageBuffer;
797 if(RENDER_MASK == behaviour)
799 // Generate the image buffer as an alpha mask for color glyphs.
800 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
802 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
804 // Generate an empty image buffer so that it can been combined with the image buffers for styles
805 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
806 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
810 // Generate the image buffer for the text with no style.
811 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
814 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
816 // Generate the outline if enabled
817 const uint16_t outlineWidth = mModel->GetOutlineWidth();
818 if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
820 // Create the image buffer for outline
821 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
823 // Combine the two buffers
824 imageBuffer = CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight);
827 // @todo. Support shadow and underline for partial text later on.
829 // Generate the shadow if enabled
830 const Vector2& shadowOffset = mModel->GetShadowOffset();
831 if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
833 // Create the image buffer for shadow
834 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
836 // Check whether it will be a soft shadow
837 const float& blurRadius = mModel->GetShadowBlurRadius();
839 if(blurRadius > Math::MACHINE_EPSILON_1)
841 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
844 // Combine the two buffers
845 imageBuffer = CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight);
848 // Generate the underline if enabled
849 const bool underlineEnabled = mModel->IsUnderlineEnabled();
850 if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
852 // Create the image buffer for underline
853 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
855 // Combine the two buffers
856 imageBuffer = CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
859 // Generate the background if enabled
860 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
861 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
862 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
864 Devel::PixelBuffer backgroundImageBuffer;
866 if(backgroundEnabled)
868 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
872 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
875 if(backgroundMarkupSet)
877 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
880 // Combine the two buffers
881 imageBuffer = CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight);
884 // Generate the strikethrough if enabled
885 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
886 if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
888 // Create the image buffer for strikethrough
889 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
891 // Combine the two buffers
892 imageBuffer = CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight);
897 imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
900 // Create the final PixelData for the combined image buffer
901 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
906 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)
908 // Retrieve lines, glyphs, positions and colors from the view model.
909 const Length modelNumberOfLines = mModel->GetNumberOfLines();
910 const LineRun* const modelLinesBuffer = mModel->GetLines();
911 const GlyphInfo* const glyphsBuffer = mModel->GetGlyphs();
912 const Vector2* const positionBuffer = mModel->GetLayout();
913 const Vector4* const colorsBuffer = mModel->GetColors();
914 const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
915 const GlyphInfo* hyphens = mModel->GetHyphens();
916 const Length* hyphenIndices = mModel->GetHyphenIndices();
917 const Length hyphensCount = mModel->GetHyphensCount();
919 // Elided text info. Indices according to elided text and Ellipsis position.
920 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
921 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
922 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
923 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
924 const auto ellipsisPosition = mModel->GetEllipsisPosition();
926 // Whether to use the default color.
927 const bool useDefaultColor = (NULL == colorsBuffer);
928 const Vector4& defaultColor = mModel->GetDefaultColor();
929 Vector4 currentStrikethroughColor;
931 // Create and initialize the pixel buffer.
933 glyphData.verticalOffset = verticalOffset;
934 glyphData.width = bufferWidth;
935 glyphData.height = bufferHeight;
936 glyphData.bitmapBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
937 glyphData.horizontalOffset = 0;
939 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
940 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
941 Length hyphenIndex = 0;
943 const Character* textBuffer = mModel->GetTextBuffer();
944 float calculatedAdvance = 0.f;
945 const Vector<CharacterIndex>& glyphToCharacterMap = mModel->GetGlyphsToCharacters();
946 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
948 // Traverses the lines of the text.
949 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
951 const LineRun& line = *(modelLinesBuffer + lineIndex);
953 // Sets the horizontal offset of the line.
954 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
955 glyphData.horizontalOffset += horizontalOffset;
957 // Increases the vertical offset with the line's ascender.
958 glyphData.verticalOffset += static_cast<int>(line.ascender);
960 // Include line spacing after first line
963 glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
966 // Retrieves the glyph's outline width
967 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
969 if(style == Typesetter::STYLE_OUTLINE)
971 glyphData.horizontalOffset -= outlineWidth;
974 // Only need to add the vertical outline offset for the first line
975 glyphData.verticalOffset -= outlineWidth;
978 else if(style == Typesetter::STYLE_SHADOW)
980 const Vector2& shadowOffset = mModel->GetShadowOffset();
981 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
985 // Only need to add the vertical shadow offset for first line
986 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
990 const bool underlineEnabled = mModel->IsUnderlineEnabled();
991 const Vector4& underlineColor = mModel->GetUnderlineColor();
992 const float underlineHeight = mModel->GetUnderlineHeight();
993 const Text::Underline::Type underlineType = mModel->GetUnderlineType();
994 const float dashedUnderlineWidth = mModel->GetDashedUnderlineWidth();
995 const float dashedUnderlineGap = mModel->GetDashedUnderlineGap();
996 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
997 const Vector4& strikethroughColor = mModel->GetStrikethroughColor();
998 const float strikethroughHeight = mModel->GetStrikethroughHeight();
999 const float characterSpacing = mModel->GetCharacterSpacing();
1001 // Get the underline runs.
1002 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1003 Vector<GlyphRun> underlineRuns;
1004 underlineRuns.Resize(numberOfUnderlineRuns);
1005 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1007 // Get the strikethrough runs.
1008 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1009 Vector<StrikethroughGlyphRun> strikethroughRuns;
1010 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1011 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1013 bool thereAreUnderlinedGlyphs = false;
1014 bool strikethroughGlyphsExist = false;
1016 float currentUnderlinePosition = 0.0f;
1017 float currentUnderlineThickness = underlineHeight;
1018 float maxUnderlineThickness = currentUnderlineThickness;
1019 float currentStrikethroughThickness = strikethroughHeight;
1020 float maxStrikethroughThickness = currentStrikethroughThickness;
1021 float strikethroughStartingYPosition = 0.0f;
1023 FontId lastUnderlinedFontId = 0;
1025 float lineExtentLeft = bufferWidth;
1026 float lineExtentRight = 0.0f;
1027 float baseline = 0.0f;
1028 bool addHyphen = false;
1030 // Traverses the glyphs of the line.
1031 const GlyphIndex startGlyphIndex = std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs);
1032 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1033 endGlyphIndex = std::min(endGlyphIndex, endIndexOfGlyphs);
1035 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1037 if(glyphIndex < fromGlyphIndex || glyphIndex > toGlyphIndex)
1039 // Ignore any glyph that out of the specified range
1043 //To handle START case of ellipsis, the first glyph has been shifted
1044 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1045 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1047 //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.
1048 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1050 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1051 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1053 // Ignore any glyph that removed for MIDDLE ellipsis
1056 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1058 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1062 // Retrieve the glyph's info.
1063 const GlyphInfo* glyphInfo;
1065 if(addHyphen && hyphens)
1067 glyphInfo = hyphens + hyphenIndex;
1072 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1075 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1076 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1078 // Nothing to do if the glyph's width or height is zero.
1082 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns);
1083 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1085 currentStrikethroughColor = strikethroughColor;
1086 const bool strikethroughGlyph = strikethroughEnabled || doGlyphHaveStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughColor);
1087 strikethroughGlyphsExist = strikethroughGlyphsExist || strikethroughGlyph;
1089 // Are we still using the same fontId as previous
1090 if((strikethroughGlyph || underlineGlyph) && (glyphInfo->fontId != lastUnderlinedFontId))
1092 // We need to fetch fresh font underline metrics
1093 FetchFontDecorationlinesMetrics(fontClient, glyphInfo, currentUnderlinePosition, underlineHeight, currentUnderlineThickness, maxUnderlineThickness, lastUnderlinedFontId, strikethroughHeight, currentStrikethroughThickness, maxStrikethroughThickness);
1096 // Retrieves the glyph's position.
1097 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1101 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1102 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1103 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1104 position.y = -glyphInfo->yBearing;
1107 if(baseline < position.y + glyphInfo->yBearing)
1109 baseline = position.y + glyphInfo->yBearing;
1112 // Calculate the positions of leftmost and rightmost glyphs in the current line
1113 if(position.x < lineExtentLeft)
1115 lineExtentLeft = position.x;
1118 if(position.x + glyphInfo->width > lineExtentRight)
1120 lineExtentRight = position.x + glyphInfo->width;
1123 // Retrieves the glyph's color.
1124 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1127 if(style == Typesetter::STYLE_SHADOW)
1129 color = mModel->GetShadowColor();
1131 else if(style == Typesetter::STYLE_OUTLINE)
1133 color = mModel->GetOutlineColor();
1137 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1140 // Premultiply alpha
1145 // Retrieves the glyph's bitmap.
1146 glyphData.glyphBitmap.buffer = NULL;
1147 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1148 glyphData.glyphBitmap.height = glyphInfo->height;
1150 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1152 // Don't render outline for other styles
1153 outlineWidth = 0.0f;
1156 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1158 fontClient.CreateBitmap(glyphInfo->fontId,
1160 glyphInfo->isItalicRequired,
1161 glyphInfo->isBoldRequired,
1162 glyphData.glyphBitmap,
1163 static_cast<int>(outlineWidth));
1166 // Sets the glyph's bitmap into the bitmap of the whole text.
1167 if(NULL != glyphData.glyphBitmap.buffer)
1169 if(style == Typesetter::STYLE_OUTLINE)
1171 // Set the position offset for the current glyph
1172 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1173 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1176 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1177 TypesetGlyph(glyphData,
1183 if(style == Typesetter::STYLE_OUTLINE)
1185 // Reset the position offset for the next glyph
1186 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1187 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1190 // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1191 delete[] glyphData.glyphBitmap.buffer;
1192 glyphData.glyphBitmap.buffer = NULL;
1197 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1202 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1210 // Draw the underline from the leftmost glyph to the rightmost glyph
1211 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1213 DrawUnderline(underlineColor, bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineThickness, lineExtentLeft, lineExtentRight, underlineType, dashedUnderlineWidth, dashedUnderlineGap, line);
1216 // Draw the background color from the leftmost glyph to the rightmost glyph
1217 if(style == Typesetter::STYLE_BACKGROUND)
1219 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1222 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1223 if(strikethroughGlyphsExist && style == Typesetter::STYLE_STRIKETHROUGH)
1225 //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.
1226 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.
1227 DrawStrikethrough(currentStrikethroughColor, bufferWidth, bufferHeight, glyphData, baseline, line, maxStrikethroughThickness, lineExtentLeft, lineExtentRight, strikethroughStartingYPosition);
1230 // Increases the vertical offset with the line's descender.
1231 glyphData.verticalOffset += static_cast<int>(-line.descender);
1234 return glyphData.bitmapBuffer;
1237 Devel::PixelBuffer Typesetter::CombineImageBuffer(Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight)
1239 unsigned char* topBuffer = topPixelBuffer.GetBuffer();
1240 unsigned char* bottomBuffer = bottomPixelBuffer.GetBuffer();
1242 Devel::PixelBuffer combinedPixelBuffer;
1244 if(topBuffer == NULL && bottomBuffer == NULL)
1246 // Nothing to do if both buffers are empty.
1247 return combinedPixelBuffer;
1250 if(topBuffer == NULL)
1252 // Nothing to do if topBuffer is empty.
1253 return bottomPixelBuffer;
1256 if(bottomBuffer == NULL)
1258 // Nothing to do if bottomBuffer is empty.
1259 return topPixelBuffer;
1262 // Always combine two RGBA images
1263 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
1264 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
1266 combinedPixelBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1267 uint8_t* combinedBuffer = reinterpret_cast<uint8_t*>(combinedPixelBuffer.GetBuffer());
1268 memset(combinedBuffer, 0u, bufferSizeChar);
1270 for(unsigned int pixelIndex = 0; pixelIndex < bufferSizeInt; pixelIndex++)
1272 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
1273 // Otherwise, copy pixel from topBuffer to combinedBuffer.
1275 unsigned int alphaBuffer1 = topBuffer[pixelIndex * 4 + 3];
1277 if(alphaBuffer1 != 255)
1279 // At least one pixel is not fully opaque
1280 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
1281 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4] + (bottomBuffer[pixelIndex * 4] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1282 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1] + (bottomBuffer[pixelIndex * 4 + 1] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1283 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2] + (bottomBuffer[pixelIndex * 4 + 2] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1284 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3] + (bottomBuffer[pixelIndex * 4 + 3] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1288 // Copy the pixel from topBuffer to combinedBuffer
1289 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4];
1290 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1];
1291 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2];
1292 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3];
1296 return combinedPixelBuffer;
1299 Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1301 // Underline-tags (this is for Markup case)
1302 // Get the underline runs.
1303 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1304 Vector<GlyphRun> underlineRuns;
1305 underlineRuns.Resize(numberOfUnderlineRuns);
1306 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1308 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1309 Vector<GlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1310 Vector<GlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1311 GlyphIndex startGlyphIndex, endGlyphIndex;
1313 //The outer loop to iterate on the separated chunks of underlined glyph runs
1314 while(itGlyphRun != endItGlyphRun)
1316 startGlyphIndex = itGlyphRun->glyphIndex;
1317 endGlyphIndex = startGlyphIndex;
1318 //The inner loop to make a connected underline for the consecutive characters
1321 endGlyphIndex += itGlyphRun->numberOfGlyphs;
1323 } while(itGlyphRun != endItGlyphRun && itGlyphRun->glyphIndex == endGlyphIndex);
1327 // Create the image buffer for underline
1328 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1329 // Combine the two buffers
1330 topPixelBuffer = CombineImageBuffer(topPixelBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
1333 return topPixelBuffer;
1336 Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1338 // strikethrough-tags (this is for Markup case)
1339 // Get the strikethrough runs.
1340 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1341 Vector<StrikethroughGlyphRun> strikethroughRuns;
1342 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1343 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1345 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1346 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1347 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1348 GlyphIndex startGlyphIndex, endGlyphIndex;
1350 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1351 while(itGlyphRun != endItGlyphRun)
1353 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1354 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1356 // Create the image buffer for strikethrough
1357 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1358 // Combine the two buffers
1359 topPixelBuffer = CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight);
1364 return topPixelBuffer;
1367 Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1369 // Apply the markup-Processor if enabled
1370 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1371 if(markupProcessorEnabled)
1373 topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1375 topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1378 return topPixelBuffer;
1381 Typesetter::Typesetter(const ModelInterface* const model)
1382 : mModel(new ViewModel(model))
1386 Typesetter::~Typesetter()
1393 } // namespace Toolkit