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/rendering/view-model.h>
38 const float HALF(0.5f);
40 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
44 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
45 Vector2* position; ///< The position of the glyph.
46 TextAbstraction::FontClient::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
47 unsigned int width; ///< The bitmap's width.
48 unsigned int height; ///< The bitmap's height.
49 int horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
50 int verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
54 * @brief Sets the glyph's buffer into the bitmap's buffer.
56 * @param[in] data Struct which contains the glyph's data and the bitmap's data.
57 * @param[in] position The position of the glyph.
58 * @param[in] color The color of the glyph.
59 * @param[in] style The style of the text.
60 * @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).
62 void TypesetGlyph(GlyphData& data,
63 const Vector2* const position,
64 const Vector4* const color,
65 Typesetter::Style style,
66 Pixel::Format pixelFormat)
68 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
70 // Nothing to do if the width or height of the buffer is zero.
74 const int widthMinusOne = static_cast<int>(data.width - 1u);
75 const int heightMinusOne = static_cast<int>(data.height - 1u);
77 if(Pixel::RGBA8888 == pixelFormat)
79 // Whether the given glyph is a color one.
80 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
81 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
82 const uint32_t alphaIndex = glyphPixelSize - 1u;
83 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
85 // Pointer to the color glyph if there is one.
86 const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>(data.glyphBitmap.buffer) : NULL;
88 // Initial vertical offset.
89 const int yOffset = data.verticalOffset + position->y;
91 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
93 // Traverse the pixels of the glyph line per line.
94 for(int lineIndex = 0, glyphHeight = static_cast<int>(data.glyphBitmap.height); lineIndex < glyphHeight; ++lineIndex)
96 const int yOffsetIndex = yOffset + lineIndex;
97 if((0 > yOffsetIndex) || (yOffsetIndex > heightMinusOne))
99 // Do not write out of bounds.
103 const int verticalOffset = yOffsetIndex * data.width;
104 const int xOffset = data.horizontalOffset + position->x;
105 const int glyphBufferOffset = lineIndex * static_cast<int>(data.glyphBitmap.width);
106 for(int index = 0, glyphWidth = static_cast<int>(data.glyphBitmap.width); index < glyphWidth; ++index)
108 const int xOffsetIndex = xOffset + index;
109 if((0 > xOffsetIndex) || (xOffsetIndex > widthMinusOne))
111 // Don't write out of bounds.
117 // Retrieves the color from the color glyph.
118 uint32_t packedColorGlyph = *(colorGlyphBuffer + glyphBufferOffset + index);
119 uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
121 // Update the alpha channel.
122 if(Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style) // Outline not shown for color glyph
124 // Create an alpha mask for color glyph.
125 *(packedColorGlyphBuffer + 3u) = 0u;
126 *(packedColorGlyphBuffer + 2u) = 0u;
127 *(packedColorGlyphBuffer + 1u) = 0u;
128 *packedColorGlyphBuffer = 0u;
132 const uint8_t colorAlpha = static_cast<uint8_t>(color->a * static_cast<float>(*(packedColorGlyphBuffer + 3u)));
133 *(packedColorGlyphBuffer + 3u) = colorAlpha;
135 if(Typesetter::STYLE_SHADOW == style)
137 // The shadow of color glyph needs to have the shadow color.
138 *(packedColorGlyphBuffer + 2u) = static_cast<uint8_t>(color->b * colorAlpha);
139 *(packedColorGlyphBuffer + 1u) = static_cast<uint8_t>(color->g * colorAlpha);
140 *packedColorGlyphBuffer = static_cast<uint8_t>(color->r * colorAlpha);
146 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
149 *(packedColorGlyphBuffer + 2u) = (*(packedColorGlyphBuffer + 2u) * colorAlpha / 255);
150 *(packedColorGlyphBuffer + 1u) = (*(packedColorGlyphBuffer + 1u) * colorAlpha / 255);
151 *packedColorGlyphBuffer = (*(packedColorGlyphBuffer)*colorAlpha / 255);
153 if(data.glyphBitmap.isColorBitmap)
155 *(packedColorGlyphBuffer + 2u) = static_cast<uint8_t>(*(packedColorGlyphBuffer + 2u) * color->b);
156 *(packedColorGlyphBuffer + 1u) = static_cast<uint8_t>(*(packedColorGlyphBuffer + 1u) * color->g);
157 *packedColorGlyphBuffer = static_cast<uint8_t>(*packedColorGlyphBuffer * color->r);
162 // Set the color into the final pixel buffer.
163 *(bitmapBuffer + verticalOffset + xOffsetIndex) = packedColorGlyph;
167 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
168 // The format is RGBA8888.
169 uint32_t packedColor = 0u;
170 uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
172 // Update the alpha channel.
173 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
175 // Copy non-transparent pixels only
178 // Check alpha of overlapped pixels
179 uint32_t& currentColor = *(bitmapBuffer + verticalOffset + xOffsetIndex);
180 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
182 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
183 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
184 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
185 // happen, for example, in the RTL text when we copy glyphs from right to left).
186 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
187 currentAlpha = std::max(currentAlpha, alpha);
189 // Color is pre-muliplied with its alpha.
190 *(packedColorBuffer + 3u) = static_cast<uint8_t>(color->a * currentAlpha);
191 *(packedColorBuffer + 2u) = static_cast<uint8_t>(color->b * currentAlpha);
192 *(packedColorBuffer + 1u) = static_cast<uint8_t>(color->g * currentAlpha);
193 *(packedColorBuffer) = static_cast<uint8_t>(color->r * currentAlpha);
195 // Set the color into the final pixel buffer.
196 currentColor = packedColor;
204 // Whether the given glyph is a color one.
205 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
206 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
207 const uint32_t alphaIndex = glyphPixelSize - 1u;
209 // Initial vertical offset.
210 const int yOffset = data.verticalOffset + position->y;
212 uint8_t* bitmapBuffer = reinterpret_cast<uint8_t*>(data.bitmapBuffer.GetBuffer());
214 // Traverse the pixels of the glyph line per line.
215 for(int lineIndex = 0, glyphHeight = static_cast<int>(data.glyphBitmap.height); lineIndex < glyphHeight; ++lineIndex)
217 const int yOffsetIndex = yOffset + lineIndex;
218 if((0 > yOffsetIndex) || (yOffsetIndex > heightMinusOne))
220 // Do not write out of bounds.
224 const int verticalOffset = yOffsetIndex * data.width;
225 const int xOffset = data.horizontalOffset + position->x;
226 const int glyphBufferOffset = lineIndex * static_cast<int>(data.glyphBitmap.width);
227 for(int index = 0, glyphWidth = static_cast<int>(data.glyphBitmap.width); index < glyphWidth; ++index)
229 const int xOffsetIndex = xOffset + index;
230 if((0 > xOffsetIndex) || (xOffsetIndex > widthMinusOne))
232 // Don't write out of bounds.
238 // Update the alpha channel.
239 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
241 // Copy non-transparent pixels only
244 // Check alpha of overlapped pixels
245 uint8_t& currentAlpha = *(bitmapBuffer + verticalOffset + xOffsetIndex);
247 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
248 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
249 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
250 // happen, for example, in the RTL text when we copy glyphs from right to left).
251 currentAlpha = std::max(currentAlpha, alpha);
259 bool IsGlyphUnderlined(GlyphIndex index,
260 const Vector<GlyphRun>& underlineRuns)
262 for(Vector<GlyphRun>::ConstIterator it = underlineRuns.Begin(),
263 endIt = underlineRuns.End();
267 const GlyphRun& run = *it;
269 if((run.glyphIndex <= index) && (index < run.glyphIndex + run.numberOfGlyphs))
278 bool doGlyphHaveStrikethrough(GlyphIndex index,
279 const Vector<StrikethroughGlyphRun>& strikethroughRuns,
280 Vector4& strikethroughColor)
282 for(Vector<StrikethroughGlyphRun>::ConstIterator it = strikethroughRuns.Begin(),
283 endIt = strikethroughRuns.End();
287 const StrikethroughGlyphRun& run = *it;
289 if((run.glyphRun.glyphIndex <= index) && (index < run.glyphRun.glyphIndex + run.glyphRun.numberOfGlyphs))
293 strikethroughColor = run.color;
303 /// Helper method to fetch the underline metrics for the specified font glyph
304 void FetchFontDecorationlinesMetrics(
305 TextAbstraction::FontClient& fontClient,
306 const GlyphInfo* const glyphInfo,
307 float& currentUnderlinePosition,
308 const float underlineHeight,
309 float& currentUnderlineThickness,
310 float& maxUnderlineThickness,
311 FontId& lastlinedFontId,
312 const float strikethroughHeight,
313 float& currentStrikethroughThickness,
314 float& maxStrikethroughThickness)
316 FontMetrics fontMetrics;
317 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
318 currentUnderlinePosition = ceil(fabsf(fontMetrics.underlinePosition));
319 const float descender = ceil(fabsf(fontMetrics.descender));
321 if(fabsf(underlineHeight) < Math::MACHINE_EPSILON_1000)
323 currentUnderlineThickness = fontMetrics.underlineThickness;
325 // Ensure underline will be at least a pixel high
326 if(currentUnderlineThickness < 1.0f)
328 currentUnderlineThickness = 1.0f;
332 currentUnderlineThickness = ceil(currentUnderlineThickness);
336 if(fabsf(strikethroughHeight) < Math::MACHINE_EPSILON_1000)
338 // Ensure strikethrough will be at least a pixel high
339 if(currentStrikethroughThickness < 1.0f)
341 currentStrikethroughThickness = 1.0f;
345 currentStrikethroughThickness = ceil(currentStrikethroughThickness);
349 // The underline thickness should be the max underline thickness of all glyphs of the line.
350 if(currentUnderlineThickness > maxUnderlineThickness)
352 maxUnderlineThickness = currentUnderlineThickness;
355 // The strikethrough thickness should be the max strikethrough thickness of all glyphs of the line.
356 if(currentStrikethroughThickness > maxStrikethroughThickness)
358 maxStrikethroughThickness = currentStrikethroughThickness;
361 // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
362 if(currentUnderlinePosition > descender)
364 currentUnderlinePosition = descender;
367 if(fabsf(currentUnderlinePosition) < Math::MACHINE_EPSILON_1000)
369 // Move offset down by one ( EFL behavior )
370 currentUnderlinePosition = 1.0f;
373 lastlinedFontId = glyphInfo->fontId;
376 /// Draws the specified color to the pixel buffer
377 void WriteColorToPixelBuffer(
378 GlyphData& glyphData,
379 uint32_t* bitmapBuffer,
380 const Vector4& color,
381 const unsigned int x,
382 const unsigned int y)
384 // Always RGBA image for text with styles
385 uint32_t pixel = *(bitmapBuffer + y * glyphData.width + x);
386 uint8_t* pixelBuffer = reinterpret_cast<uint8_t*>(&pixel);
388 // Write the color to the pixel buffer
389 uint8_t colorAlpha = static_cast<uint8_t>(color.a * 255.f);
390 *(pixelBuffer + 3u) = colorAlpha;
391 *(pixelBuffer + 2u) = static_cast<uint8_t>(color.b * colorAlpha);
392 *(pixelBuffer + 1u) = static_cast<uint8_t>(color.g * colorAlpha);
393 *(pixelBuffer) = static_cast<uint8_t>(color.r * colorAlpha);
395 *(bitmapBuffer + y * glyphData.width + x) = pixel;
398 /// Draws the specified underline color to the buffer
400 const Vector4& underlineColor,
401 const unsigned int bufferWidth,
402 const unsigned int bufferHeight,
403 GlyphData& glyphData,
404 const float baseline,
405 const float currentUnderlinePosition,
406 const float maxUnderlineThickness,
407 const float lineExtentLeft,
408 const float lineExtentRight,
409 const Text::Underline::Type underlineType,
410 const float dashedUnderlineWidth,
411 const float dashedUnderlineGap,
414 int underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
415 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
417 for(unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineThickness; y++)
419 if(y > bufferHeight - 1)
421 // Do not write out of bounds.
424 if(underlineType == Text::Underline::DASHED)
426 float dashWidth = dashedUnderlineWidth;
429 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
431 if(x > bufferWidth - 1)
433 // Do not write out of bounds.
436 if(dashGap == 0 && dashWidth > 0)
438 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
441 else if(dashGap < dashedUnderlineGap)
448 dashWidth = dashedUnderlineWidth;
455 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
457 if(x > bufferWidth - 1)
459 // Do not write out of bounds.
462 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
466 if(underlineType == Text::Underline::DOUBLE)
468 int secondUnderlineYOffset = glyphData.verticalOffset - line.descender - maxUnderlineThickness;
469 for(unsigned int y = secondUnderlineYOffset; y < secondUnderlineYOffset + maxUnderlineThickness; y++)
471 if(y > bufferHeight - 1)
473 // Do not write out of bounds.
476 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
478 if(x > bufferWidth - 1)
480 // Do not write out of bounds.
483 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
489 /// Draws the background color to the buffer
490 void DrawBackgroundColor(
491 Vector4 backgroundColor,
492 const unsigned int bufferWidth,
493 const unsigned int bufferHeight,
494 GlyphData& glyphData,
495 const float baseline,
497 const float lineExtentLeft,
498 const float lineExtentRight)
500 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
502 for(int y = glyphData.verticalOffset + baseline - line.ascender; y < glyphData.verticalOffset + baseline - line.descender; y++)
504 if((y < 0) || (y > static_cast<int>(bufferHeight - 1)))
506 // Do not write out of bounds.
510 for(int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
512 if((x < 0) || (x > static_cast<int>(bufferWidth - 1)))
514 // Do not write out of bounds.
518 WriteColorToPixelBuffer(glyphData, bitmapBuffer, backgroundColor, x, y);
523 Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuffer& buffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, int horizontalOffset, int verticalOffset)
525 // Retrieve lines, glyphs, positions and colors from the view model.
526 const Length modelNumberOfLines = model->GetNumberOfLines();
527 const LineRun* const modelLinesBuffer = model->GetLines();
528 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
529 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
530 const Vector2* const positionBuffer = model->GetLayout();
531 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
532 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
534 // Create and initialize the pixel buffer.
536 glyphData.verticalOffset = verticalOffset;
537 glyphData.width = bufferWidth;
538 glyphData.height = bufferHeight;
539 glyphData.bitmapBuffer = buffer;
540 glyphData.horizontalOffset = 0;
542 ColorIndex prevBackgroundColorIndex = 0;
543 ColorIndex backgroundColorIndex = 0;
545 // Traverses the lines of the text.
546 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
548 const LineRun& line = *(modelLinesBuffer + lineIndex);
550 // Sets the horizontal offset of the line.
551 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
552 glyphData.horizontalOffset += horizontalOffset;
554 // Increases the vertical offset with the line's ascender.
555 glyphData.verticalOffset += static_cast<int>(line.ascender);
557 // Include line spacing after first line
560 glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
563 float left = bufferWidth;
565 float baseline = 0.0f;
567 // Traverses the glyphs of the line.
568 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
569 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
571 // Retrieve the glyph's info.
572 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
574 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
575 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
577 // Nothing to do if default background color, the glyph's width or height is zero.
581 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
583 if((backgroundColorIndex != prevBackgroundColorIndex) &&
584 (prevBackgroundColorIndex != 0u))
586 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
587 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
590 if(backgroundColorIndex == 0u)
592 prevBackgroundColorIndex = backgroundColorIndex;
593 //if background color is the default do nothing
597 // Retrieves the glyph's position.
598 const Vector2* const position = positionBuffer + glyphIndex;
600 if(baseline < position->y + glyphInfo->yBearing)
602 baseline = position->y + glyphInfo->yBearing;
605 // Calculate the positions of leftmost and rightmost glyphs in the current line
606 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
608 left = position->x - glyphInfo->xBearing;
611 if(position->x + glyphInfo->width > right)
613 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
616 prevBackgroundColorIndex = backgroundColorIndex;
619 //draw last background at line end if not default
620 if(backgroundColorIndex != 0u)
622 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
623 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
626 // Increases the vertical offset with the line's descender.
627 glyphData.verticalOffset += static_cast<int>(-line.descender);
630 return glyphData.bitmapBuffer;
633 /// Draws the specified strikethrough color to the buffer
634 void DrawStrikethrough(
635 const Vector4& strikethroughColor,
636 const unsigned int bufferWidth,
637 const unsigned int bufferHeight,
638 GlyphData& glyphData,
639 const float baseline,
641 const float maxStrikethroughThickness,
642 const float lineExtentLeft,
643 const float lineExtentRight,
644 float strikethroughStartingYPosition)
646 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
648 for(unsigned int y = strikethroughStartingYPosition; y < strikethroughStartingYPosition + maxStrikethroughThickness; y++)
650 if(y > bufferHeight - 1)
652 // Do not write out of bounds.
656 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
658 if(x > bufferWidth - 1)
660 // Do not write out of bounds.
664 WriteColorToPixelBuffer(glyphData, bitmapBuffer, strikethroughColor, x, y);
671 TypesetterPtr Typesetter::New(const ModelInterface* const model)
673 return TypesetterPtr(new Typesetter(model));
676 ViewModel* Typesetter::GetViewModel()
681 Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, const unsigned int bufferHeight, Pixel::Format pixelFormat)
683 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
685 if(Pixel::RGBA8888 == pixelFormat)
687 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
688 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
689 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
693 memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
699 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
701 // @todo. This initial implementation for a TextLabel has only one visible page.
703 // Elides the text if needed.
704 mModel->ElideGlyphs();
706 // Retrieves the layout size.
707 const Size& layoutSize = mModel->GetLayoutSize();
709 const int outlineWidth = static_cast<int>(mModel->GetOutlineWidth());
711 // Set the offset for the horizontal alignment according to the text direction and outline width.
714 switch(mModel->GetHorizontalAlignment())
716 case HorizontalAlignment::BEGIN:
721 case HorizontalAlignment::CENTER:
723 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
726 case HorizontalAlignment::END:
728 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
733 // Set the offset for the vertical alignment.
736 switch(mModel->GetVerticalAlignment())
738 case VerticalAlignment::TOP:
743 case VerticalAlignment::CENTER:
745 penY = static_cast<int>(0.5f * (size.height - layoutSize.height));
746 penY = penY < 0.f ? 0.f : penY;
749 case VerticalAlignment::BOTTOM:
751 penY = static_cast<int>(size.height - layoutSize.height);
756 // Calculate vertical line alignment
757 switch(mModel->GetVerticalLineAlignment())
759 case DevelText::VerticalLineAlignment::TOP:
763 case DevelText::VerticalLineAlignment::MIDDLE:
765 const auto& line = *mModel->GetLines();
766 penY -= line.descender;
767 penY += static_cast<int>(line.lineSpacing * 0.5f + line.descender);
770 case DevelText::VerticalLineAlignment::BOTTOM:
772 const auto& line = *mModel->GetLines();
773 const auto lineHeight = line.ascender + (-line.descender) + line.lineSpacing;
774 penY += static_cast<int>(lineHeight - (line.ascender - line.descender));
779 // Generate the image buffers of the text for each different style first,
780 // then combine all of them together as one final image buffer. We try to
781 // do all of these in CPU only, so that once the final texture is generated,
782 // no calculation is needed in GPU during each frame.
784 const unsigned int bufferWidth = static_cast<unsigned int>(size.width);
785 const unsigned int bufferHeight = static_cast<unsigned int>(size.height);
787 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
788 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
790 //Elided text in ellipsis at START could start on index greater than 0
791 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
792 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
794 Devel::PixelBuffer imageBuffer;
796 if(RENDER_MASK == behaviour)
798 // Generate the image buffer as an alpha mask for color glyphs.
799 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
801 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
803 // Generate an empty image buffer so that it can been combined with the image buffers for styles
804 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
805 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
809 // Generate the image buffer for the text with no style.
810 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
813 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
815 // Generate the outline if enabled
816 const uint16_t outlineWidth = mModel->GetOutlineWidth();
817 if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
819 // Create the image buffer for outline
820 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
822 // Combine the two buffers
823 imageBuffer = CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight);
826 // @todo. Support shadow and underline for partial text later on.
828 // Generate the shadow if enabled
829 const Vector2& shadowOffset = mModel->GetShadowOffset();
830 if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
832 // Create the image buffer for shadow
833 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
835 // Check whether it will be a soft shadow
836 const float& blurRadius = mModel->GetShadowBlurRadius();
838 if(blurRadius > Math::MACHINE_EPSILON_1)
840 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
843 // Combine the two buffers
844 imageBuffer = CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight);
847 // Generate the underline if enabled
848 const bool underlineEnabled = mModel->IsUnderlineEnabled();
849 if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
851 // Create the image buffer for underline
852 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
854 // Combine the two buffers
855 imageBuffer = CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
858 // Generate the background if enabled
859 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
860 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
861 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
863 Devel::PixelBuffer backgroundImageBuffer;
865 if(backgroundEnabled)
867 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
871 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
874 if(backgroundMarkupSet)
876 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
879 // Combine the two buffers
880 imageBuffer = CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight);
883 // Generate the strikethrough if enabled
884 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
885 if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
887 // Create the image buffer for strikethrough
888 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
890 // Combine the two buffers
891 imageBuffer = CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight);
896 imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
899 // Create the final PixelData for the combined image buffer
900 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
905 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)
907 // Retrieve lines, glyphs, positions and colors from the view model.
908 const Length modelNumberOfLines = mModel->GetNumberOfLines();
909 const LineRun* const modelLinesBuffer = mModel->GetLines();
910 const GlyphInfo* const glyphsBuffer = mModel->GetGlyphs();
911 const Vector2* const positionBuffer = mModel->GetLayout();
912 const Vector4* const colorsBuffer = mModel->GetColors();
913 const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
914 const GlyphInfo* hyphens = mModel->GetHyphens();
915 const Length* hyphenIndices = mModel->GetHyphenIndices();
916 const Length hyphensCount = mModel->GetHyphensCount();
918 // Elided text info. Indices according to elided text and Ellipsis position.
919 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
920 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
921 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
922 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
923 const auto ellipsisPosition = mModel->GetEllipsisPosition();
925 // Whether to use the default color.
926 const bool useDefaultColor = (NULL == colorsBuffer);
927 const Vector4& defaultColor = mModel->GetDefaultColor();
928 Vector4 currentStrikethroughColor;
930 // Create and initialize the pixel buffer.
932 glyphData.verticalOffset = verticalOffset;
933 glyphData.width = bufferWidth;
934 glyphData.height = bufferHeight;
935 glyphData.bitmapBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
936 glyphData.horizontalOffset = 0;
938 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
939 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
940 Length hyphenIndex = 0;
942 // Traverses the lines of the text.
943 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
945 const LineRun& line = *(modelLinesBuffer + lineIndex);
947 // Sets the horizontal offset of the line.
948 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
949 glyphData.horizontalOffset += horizontalOffset;
951 // Increases the vertical offset with the line's ascender.
952 glyphData.verticalOffset += static_cast<int>(line.ascender);
954 // Include line spacing after first line
957 glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
960 // Retrieves the glyph's outline width
961 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
963 if(style == Typesetter::STYLE_OUTLINE)
965 glyphData.horizontalOffset -= outlineWidth;
968 // Only need to add the vertical outline offset for the first line
969 glyphData.verticalOffset -= outlineWidth;
972 else if(style == Typesetter::STYLE_SHADOW)
974 const Vector2& shadowOffset = mModel->GetShadowOffset();
975 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
979 // Only need to add the vertical shadow offset for first line
980 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
984 const bool underlineEnabled = mModel->IsUnderlineEnabled();
985 const Vector4& underlineColor = mModel->GetUnderlineColor();
986 const float underlineHeight = mModel->GetUnderlineHeight();
987 const Text::Underline::Type underlineType = mModel->GetUnderlineType();
988 const float dashedUnderlineWidth = mModel->GetDashedUnderlineWidth();
989 const float dashedUnderlineGap = mModel->GetDashedUnderlineGap();
991 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
992 const Vector4& strikethroughColor = mModel->GetStrikethroughColor();
993 const float strikethroughHeight = mModel->GetStrikethroughHeight();
995 // Get the underline runs.
996 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
997 Vector<GlyphRun> underlineRuns;
998 underlineRuns.Resize(numberOfUnderlineRuns);
999 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1001 // Get the strikethrough runs.
1002 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1003 Vector<StrikethroughGlyphRun> strikethroughRuns;
1004 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1005 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1007 bool thereAreUnderlinedGlyphs = false;
1008 bool strikethroughGlyphsExist = false;
1010 float currentUnderlinePosition = 0.0f;
1011 float currentUnderlineThickness = underlineHeight;
1012 float maxUnderlineThickness = currentUnderlineThickness;
1013 float currentStrikethroughThickness = strikethroughHeight;
1014 float maxStrikethroughThickness = currentStrikethroughThickness;
1015 float strikethroughStartingYPosition = 0.0f;
1017 FontId lastUnderlinedFontId = 0;
1019 float lineExtentLeft = bufferWidth;
1020 float lineExtentRight = 0.0f;
1021 float baseline = 0.0f;
1022 bool addHyphen = false;
1024 // Traverses the glyphs of the line.
1025 const GlyphIndex startGlyphIndex = std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs);
1026 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1027 endGlyphIndex = std::min(endGlyphIndex, endIndexOfGlyphs);
1029 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1031 if(glyphIndex < fromGlyphIndex || glyphIndex > toGlyphIndex)
1033 // Ignore any glyph that out of the specified range
1037 //To handle START case of ellipsis, the first glyph has been shifted
1038 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1039 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1041 //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.
1042 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1044 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1045 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1047 // Ignore any glyph that removed for MIDDLE ellipsis
1050 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1052 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1056 // Retrieve the glyph's info.
1057 const GlyphInfo* glyphInfo;
1059 if(addHyphen && hyphens)
1061 glyphInfo = hyphens + hyphenIndex;
1066 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1069 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1070 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1072 // Nothing to do if the glyph's width or height is zero.
1076 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns);
1077 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1079 currentStrikethroughColor = strikethroughColor;
1080 const bool strikethroughGlyph = strikethroughEnabled || doGlyphHaveStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughColor);
1081 strikethroughGlyphsExist = strikethroughGlyphsExist || strikethroughGlyph;
1083 // Are we still using the same fontId as previous
1084 if((strikethroughGlyph || underlineGlyph) && (glyphInfo->fontId != lastUnderlinedFontId))
1086 // We need to fetch fresh font underline metrics
1087 FetchFontDecorationlinesMetrics(fontClient, glyphInfo, currentUnderlinePosition, underlineHeight, currentUnderlineThickness, maxUnderlineThickness, lastUnderlinedFontId, strikethroughHeight, currentStrikethroughThickness, maxStrikethroughThickness);
1090 // Retrieves the glyph's position.
1091 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1095 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1096 position.x = position.x + tempInfo.advance - tempInfo.xBearing + glyphInfo->xBearing;
1097 position.y = -glyphInfo->yBearing;
1100 if(baseline < position.y + glyphInfo->yBearing)
1102 baseline = position.y + glyphInfo->yBearing;
1105 // Calculate the positions of leftmost and rightmost glyphs in the current line
1106 if(position.x < lineExtentLeft)
1108 lineExtentLeft = position.x;
1111 if(position.x + glyphInfo->width > lineExtentRight)
1113 lineExtentRight = position.x + glyphInfo->width;
1116 // Retrieves the glyph's color.
1117 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1120 if(style == Typesetter::STYLE_SHADOW)
1122 color = mModel->GetShadowColor();
1124 else if(style == Typesetter::STYLE_OUTLINE)
1126 color = mModel->GetOutlineColor();
1130 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1133 // Premultiply alpha
1138 // Retrieves the glyph's bitmap.
1139 glyphData.glyphBitmap.buffer = NULL;
1140 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1141 glyphData.glyphBitmap.height = glyphInfo->height;
1143 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1145 // Don't render outline for other styles
1146 outlineWidth = 0.0f;
1149 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1151 fontClient.CreateBitmap(glyphInfo->fontId,
1153 glyphInfo->isItalicRequired,
1154 glyphInfo->isBoldRequired,
1155 glyphData.glyphBitmap,
1156 static_cast<int>(outlineWidth));
1159 // Sets the glyph's bitmap into the bitmap of the whole text.
1160 if(NULL != glyphData.glyphBitmap.buffer)
1162 if(style == Typesetter::STYLE_OUTLINE)
1164 // Set the position offset for the current glyph
1165 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1166 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1169 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1170 TypesetGlyph(glyphData,
1176 if(style == Typesetter::STYLE_OUTLINE)
1178 // Reset the position offset for the next glyph
1179 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1180 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1183 // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1184 delete[] glyphData.glyphBitmap.buffer;
1185 glyphData.glyphBitmap.buffer = NULL;
1190 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1195 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1203 // Draw the underline from the leftmost glyph to the rightmost glyph
1204 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1206 DrawUnderline(underlineColor, bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineThickness, lineExtentLeft, lineExtentRight, underlineType, dashedUnderlineWidth, dashedUnderlineGap, line);
1209 // Draw the background color from the leftmost glyph to the rightmost glyph
1210 if(style == Typesetter::STYLE_BACKGROUND)
1212 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1215 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1216 if(strikethroughGlyphsExist && style == Typesetter::STYLE_STRIKETHROUGH)
1218 //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.
1219 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.
1220 DrawStrikethrough(strikethroughColor, bufferWidth, bufferHeight, glyphData, baseline, line, maxStrikethroughThickness, lineExtentLeft, lineExtentRight, strikethroughStartingYPosition);
1223 // Increases the vertical offset with the line's descender.
1224 glyphData.verticalOffset += static_cast<int>(-line.descender);
1227 return glyphData.bitmapBuffer;
1230 Devel::PixelBuffer Typesetter::CombineImageBuffer(Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight)
1232 unsigned char* topBuffer = topPixelBuffer.GetBuffer();
1233 unsigned char* bottomBuffer = bottomPixelBuffer.GetBuffer();
1235 Devel::PixelBuffer combinedPixelBuffer;
1237 if(topBuffer == NULL && bottomBuffer == NULL)
1239 // Nothing to do if both buffers are empty.
1240 return combinedPixelBuffer;
1243 if(topBuffer == NULL)
1245 // Nothing to do if topBuffer is empty.
1246 return bottomPixelBuffer;
1249 if(bottomBuffer == NULL)
1251 // Nothing to do if bottomBuffer is empty.
1252 return topPixelBuffer;
1255 // Always combine two RGBA images
1256 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
1257 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
1259 combinedPixelBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1260 uint8_t* combinedBuffer = reinterpret_cast<uint8_t*>(combinedPixelBuffer.GetBuffer());
1261 memset(combinedBuffer, 0u, bufferSizeChar);
1263 for(unsigned int pixelIndex = 0; pixelIndex < bufferSizeInt; pixelIndex++)
1265 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
1266 // Otherwise, copy pixel from topBuffer to combinedBuffer.
1268 unsigned int alphaBuffer1 = topBuffer[pixelIndex * 4 + 3];
1270 if(alphaBuffer1 != 255)
1272 // At least one pixel is not fully opaque
1273 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
1274 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4] + (bottomBuffer[pixelIndex * 4] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1275 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1] + (bottomBuffer[pixelIndex * 4 + 1] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1276 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2] + (bottomBuffer[pixelIndex * 4 + 2] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1277 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3] + (bottomBuffer[pixelIndex * 4 + 3] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1281 // Copy the pixel from topBuffer to combinedBuffer
1282 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4];
1283 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1];
1284 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2];
1285 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3];
1289 return combinedPixelBuffer;
1292 Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1294 // Underline-tags (this is for Markup case)
1295 // Get the underline runs.
1296 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1297 Vector<GlyphRun> underlineRuns;
1298 underlineRuns.Resize(numberOfUnderlineRuns);
1299 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1301 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1302 Vector<GlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1303 Vector<GlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1304 GlyphIndex startGlyphIndex, endGlyphIndex;
1306 //The outer loop to iterate on the separated chunks of underlined glyph runs
1307 while(itGlyphRun != endItGlyphRun)
1309 startGlyphIndex = itGlyphRun->glyphIndex;
1310 endGlyphIndex = startGlyphIndex;
1311 //The inner loop to make a connected underline for the consecutive characters
1314 endGlyphIndex += itGlyphRun->numberOfGlyphs;
1316 } while(itGlyphRun != endItGlyphRun && itGlyphRun->glyphIndex == endGlyphIndex);
1320 // Create the image buffer for underline
1321 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1322 // Combine the two buffers
1323 topPixelBuffer = CombineImageBuffer(topPixelBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
1326 return topPixelBuffer;
1329 Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1331 // strikethrough-tags (this is for Markup case)
1332 // Get the strikethrough runs.
1333 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1334 Vector<StrikethroughGlyphRun> strikethroughRuns;
1335 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1336 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1338 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1339 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1340 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1341 GlyphIndex startGlyphIndex, endGlyphIndex;
1343 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1344 while(itGlyphRun != endItGlyphRun)
1346 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1347 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1349 // Create the image buffer for strikethrough
1350 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1351 // Combine the two buffers
1352 topPixelBuffer = CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight);
1357 return topPixelBuffer;
1360 Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1362 // Apply the markup-Processor if enabled
1363 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1364 if(markupProcessorEnabled)
1366 topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1368 topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1371 return topPixelBuffer;
1374 Typesetter::Typesetter(const ModelInterface* const model)
1375 : mModel(new ViewModel(model))
1379 Typesetter::~Typesetter()
1386 } // namespace Toolkit