2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/rendering/text-typesetter.h>
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/public-api/common/constants.h>
27 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
28 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
29 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
30 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
31 #include <dali-toolkit/internal/text/rendering/view-model.h>
41 const float HALF(0.5f);
42 const float ONE_AND_A_HALF(1.5f);
44 * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
48 Devel::PixelBuffer bitmapBuffer; ///< The buffer of the whole bitmap. The format is RGBA8888.
49 Vector2* position; ///< The position of the glyph.
50 TextAbstraction::FontClient::GlyphBufferData glyphBitmap; ///< The glyph's bitmap.
51 unsigned int width; ///< The bitmap's width.
52 unsigned int height; ///< The bitmap's height.
53 int horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
54 int verticalOffset; ///< The vertical offset to be added to the 'y' glyph's position.
58 * @brief Sets the glyph's buffer into the bitmap's buffer.
60 * @param[in] data Struct which contains the glyph's data and the bitmap's data.
61 * @param[in] position The position of the glyph.
62 * @param[in] color The color of the glyph.
63 * @param[in] style The style of the text.
64 * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
66 void TypesetGlyph(GlyphData& data,
67 const Vector2* const position,
68 const Vector4* const color,
69 Typesetter::Style style,
70 Pixel::Format pixelFormat)
72 if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
74 // Nothing to do if the width or height of the buffer is zero.
78 const int widthMinusOne = static_cast<int>(data.width - 1u);
79 const int heightMinusOne = static_cast<int>(data.height - 1u);
81 if(Pixel::RGBA8888 == pixelFormat)
83 // Whether the given glyph is a color one.
84 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
85 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
86 const uint32_t alphaIndex = glyphPixelSize - 1u;
87 const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
89 // Pointer to the color glyph if there is one.
90 const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>(data.glyphBitmap.buffer) : NULL;
92 // Initial vertical offset.
93 const int yOffset = data.verticalOffset + position->y;
95 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
97 // Traverse the pixels of the glyph line per line.
98 for(int lineIndex = 0, glyphHeight = static_cast<int>(data.glyphBitmap.height); lineIndex < glyphHeight; ++lineIndex)
100 const int yOffsetIndex = yOffset + lineIndex;
101 if((0 > yOffsetIndex) || (yOffsetIndex > heightMinusOne))
103 // Do not write out of bounds.
107 const int verticalOffset = yOffsetIndex * data.width;
108 const int xOffset = data.horizontalOffset + position->x;
109 const int glyphBufferOffset = lineIndex * static_cast<int>(data.glyphBitmap.width);
110 for(int index = 0, glyphWidth = static_cast<int>(data.glyphBitmap.width); index < glyphWidth; ++index)
112 const int xOffsetIndex = xOffset + index;
113 if((0 > xOffsetIndex) || (xOffsetIndex > widthMinusOne))
115 // Don't write out of bounds.
121 // Retrieves the color from the color glyph.
122 uint32_t packedColorGlyph = *(colorGlyphBuffer + glyphBufferOffset + index);
123 uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
125 // Update the alpha channel.
126 if(Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style) // Outline not shown for color glyph
128 // Create an alpha mask for color glyph.
129 *(packedColorGlyphBuffer + 3u) = 0u;
130 *(packedColorGlyphBuffer + 2u) = 0u;
131 *(packedColorGlyphBuffer + 1u) = 0u;
132 *packedColorGlyphBuffer = 0u;
136 const uint8_t colorAlpha = static_cast<uint8_t>(color->a * static_cast<float>(*(packedColorGlyphBuffer + 3u)));
137 *(packedColorGlyphBuffer + 3u) = colorAlpha;
139 if(Typesetter::STYLE_SHADOW == style)
141 // The shadow of color glyph needs to have the shadow color.
142 *(packedColorGlyphBuffer + 2u) = static_cast<uint8_t>(color->b * colorAlpha);
143 *(packedColorGlyphBuffer + 1u) = static_cast<uint8_t>(color->g * colorAlpha);
144 *packedColorGlyphBuffer = static_cast<uint8_t>(color->r * colorAlpha);
150 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
153 *(packedColorGlyphBuffer + 2u) = (*(packedColorGlyphBuffer + 2u) * colorAlpha / 255);
154 *(packedColorGlyphBuffer + 1u) = (*(packedColorGlyphBuffer + 1u) * colorAlpha / 255);
155 *packedColorGlyphBuffer = (*(packedColorGlyphBuffer)*colorAlpha / 255);
157 if(data.glyphBitmap.isColorBitmap)
159 *(packedColorGlyphBuffer + 2u) = static_cast<uint8_t>(*(packedColorGlyphBuffer + 2u) * color->b);
160 *(packedColorGlyphBuffer + 1u) = static_cast<uint8_t>(*(packedColorGlyphBuffer + 1u) * color->g);
161 *packedColorGlyphBuffer = static_cast<uint8_t>(*packedColorGlyphBuffer * color->r);
166 // Set the color into the final pixel buffer.
167 *(bitmapBuffer + verticalOffset + xOffsetIndex) = packedColorGlyph;
171 // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
172 // The format is RGBA8888.
173 uint32_t packedColor = 0u;
174 uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
176 // Update the alpha channel.
177 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
179 // Copy non-transparent pixels only
182 // Check alpha of overlapped pixels
183 uint32_t& currentColor = *(bitmapBuffer + verticalOffset + xOffsetIndex);
184 uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(¤tColor);
186 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
187 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
188 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
189 // happen, for example, in the RTL text when we copy glyphs from right to left).
190 uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
191 currentAlpha = std::max(currentAlpha, alpha);
193 // Color is pre-muliplied with its alpha.
194 *(packedColorBuffer + 3u) = static_cast<uint8_t>(color->a * currentAlpha);
195 *(packedColorBuffer + 2u) = static_cast<uint8_t>(color->b * currentAlpha);
196 *(packedColorBuffer + 1u) = static_cast<uint8_t>(color->g * currentAlpha);
197 *(packedColorBuffer) = static_cast<uint8_t>(color->r * currentAlpha);
199 // Set the color into the final pixel buffer.
200 currentColor = packedColor;
208 // Whether the given glyph is a color one.
209 const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
210 const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
211 const uint32_t alphaIndex = glyphPixelSize - 1u;
213 // Initial vertical offset.
214 const int yOffset = data.verticalOffset + position->y;
216 uint8_t* bitmapBuffer = reinterpret_cast<uint8_t*>(data.bitmapBuffer.GetBuffer());
218 // Traverse the pixels of the glyph line per line.
219 for(int lineIndex = 0, glyphHeight = static_cast<int>(data.glyphBitmap.height); lineIndex < glyphHeight; ++lineIndex)
221 const int yOffsetIndex = yOffset + lineIndex;
222 if((0 > yOffsetIndex) || (yOffsetIndex > heightMinusOne))
224 // Do not write out of bounds.
228 const int verticalOffset = yOffsetIndex * data.width;
229 const int xOffset = data.horizontalOffset + position->x;
230 const int glyphBufferOffset = lineIndex * static_cast<int>(data.glyphBitmap.width);
231 for(int index = 0, glyphWidth = static_cast<int>(data.glyphBitmap.width); index < glyphWidth; ++index)
233 const int xOffsetIndex = xOffset + index;
234 if((0 > xOffsetIndex) || (xOffsetIndex > widthMinusOne))
236 // Don't write out of bounds.
242 // Update the alpha channel.
243 const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
245 // Copy non-transparent pixels only
248 // Check alpha of overlapped pixels
249 uint8_t& currentAlpha = *(bitmapBuffer + verticalOffset + xOffsetIndex);
251 // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
252 // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
253 // semi-transparent gaps between joint glyphs with overlapped pixels, which could
254 // happen, for example, in the RTL text when we copy glyphs from right to left).
255 currentAlpha = std::max(currentAlpha, alpha);
263 bool doGlyphHaveStrikethrough(GlyphIndex index,
264 const Vector<StrikethroughGlyphRun>& strikethroughRuns,
265 Vector4& strikethroughColor)
267 for(Vector<StrikethroughGlyphRun>::ConstIterator it = strikethroughRuns.Begin(),
268 endIt = strikethroughRuns.End();
272 const StrikethroughGlyphRun& run = *it;
274 if((run.glyphRun.glyphIndex <= index) && (index < run.glyphRun.glyphIndex + run.glyphRun.numberOfGlyphs))
278 strikethroughColor = run.color;
288 /// Draws the specified color to the pixel buffer
289 void WriteColorToPixelBuffer(
290 GlyphData& glyphData,
291 uint32_t* bitmapBuffer,
292 const Vector4& color,
293 const unsigned int x,
294 const unsigned int y)
296 // Always RGBA image for text with styles
297 uint32_t pixel = *(bitmapBuffer + y * glyphData.width + x);
298 uint8_t* pixelBuffer = reinterpret_cast<uint8_t*>(&pixel);
300 // Write the color to the pixel buffer
301 uint8_t colorAlpha = static_cast<uint8_t>(color.a * 255.f);
302 *(pixelBuffer + 3u) = colorAlpha;
303 *(pixelBuffer + 2u) = static_cast<uint8_t>(color.b * colorAlpha);
304 *(pixelBuffer + 1u) = static_cast<uint8_t>(color.g * colorAlpha);
305 *(pixelBuffer) = static_cast<uint8_t>(color.r * colorAlpha);
307 *(bitmapBuffer + y * glyphData.width + x) = pixel;
310 /// Draws the specified underline color to the buffer
312 const unsigned int bufferWidth,
313 const unsigned int bufferHeight,
314 GlyphData& glyphData,
315 const float baseline,
316 const float currentUnderlinePosition,
317 const float maxUnderlineHeight,
318 const float lineExtentLeft,
319 const float lineExtentRight,
320 const UnderlineStyleProperties& commonUnderlineProperties,
321 const UnderlineStyleProperties& currentUnderlineProperties,
324 const Vector4& underlineColor = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
325 const Text::Underline::Type underlineType = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
326 const float dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
327 const float dashedUnderlineGap = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
329 int underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
330 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
332 for(unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineHeight; y++)
334 if(y > bufferHeight - 1)
336 // Do not write out of bounds.
339 if(underlineType == Text::Underline::DASHED)
341 float dashWidth = dashedUnderlineWidth;
344 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
346 if(x > bufferWidth - 1)
348 // Do not write out of bounds.
351 if(dashGap == 0 && dashWidth > 0)
353 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
356 else if(dashGap < dashedUnderlineGap)
363 dashWidth = dashedUnderlineWidth;
370 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
372 if(x > bufferWidth - 1)
374 // Do not write out of bounds.
377 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
381 if(underlineType == Text::Underline::DOUBLE)
383 int secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
384 for(unsigned int y = secondUnderlineYOffset; y < secondUnderlineYOffset + maxUnderlineHeight; y++)
386 if(y > bufferHeight - 1)
388 // Do not write out of bounds.
391 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
393 if(x > bufferWidth - 1)
395 // Do not write out of bounds.
398 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
404 /// Draws the background color to the buffer
405 void DrawBackgroundColor(
406 Vector4 backgroundColor,
407 const unsigned int bufferWidth,
408 const unsigned int bufferHeight,
409 GlyphData& glyphData,
410 const float baseline,
412 const float lineExtentLeft,
413 const float lineExtentRight)
415 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
417 for(int y = glyphData.verticalOffset + baseline - line.ascender; y < glyphData.verticalOffset + baseline - line.descender; y++)
419 if((y < 0) || (y > static_cast<int>(bufferHeight - 1)))
421 // Do not write out of bounds.
425 for(int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
427 if((x < 0) || (x > static_cast<int>(bufferWidth - 1)))
429 // Do not write out of bounds.
433 WriteColorToPixelBuffer(glyphData, bitmapBuffer, backgroundColor, x, y);
438 Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuffer& buffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, int horizontalOffset, int verticalOffset)
440 // Retrieve lines, glyphs, positions and colors from the view model.
441 const Length modelNumberOfLines = model->GetNumberOfLines();
442 const LineRun* const modelLinesBuffer = model->GetLines();
443 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
444 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
445 const Vector2* const positionBuffer = model->GetLayout();
446 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
447 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
449 // Create and initialize the pixel buffer.
451 glyphData.verticalOffset = verticalOffset;
452 glyphData.width = bufferWidth;
453 glyphData.height = bufferHeight;
454 glyphData.bitmapBuffer = buffer;
455 glyphData.horizontalOffset = 0;
457 ColorIndex prevBackgroundColorIndex = 0;
458 ColorIndex backgroundColorIndex = 0;
460 // Traverses the lines of the text.
461 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
463 const LineRun& line = *(modelLinesBuffer + lineIndex);
465 // Sets the horizontal offset of the line.
466 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
467 glyphData.horizontalOffset += horizontalOffset;
469 // Increases the vertical offset with the line's ascender.
470 glyphData.verticalOffset += static_cast<int>(line.ascender);
472 // Include line spacing after first line
475 glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
478 float left = bufferWidth;
480 float baseline = 0.0f;
482 // Traverses the glyphs of the line.
483 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
484 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
486 // Retrieve the glyph's info.
487 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
489 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
490 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
492 // Nothing to do if default background color, the glyph's width or height is zero.
496 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
498 if((backgroundColorIndex != prevBackgroundColorIndex) &&
499 (prevBackgroundColorIndex != 0u))
501 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
502 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
505 if(backgroundColorIndex == 0u)
507 prevBackgroundColorIndex = backgroundColorIndex;
508 //if background color is the default do nothing
512 // Retrieves the glyph's position.
513 const Vector2* const position = positionBuffer + glyphIndex;
515 if(baseline < position->y + glyphInfo->yBearing)
517 baseline = position->y + glyphInfo->yBearing;
520 // Calculate the positions of leftmost and rightmost glyphs in the current line
521 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
523 left = position->x - glyphInfo->xBearing;
526 if(position->x + glyphInfo->width > right)
528 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
531 prevBackgroundColorIndex = backgroundColorIndex;
534 //draw last background at line end if not default
535 if(backgroundColorIndex != 0u)
537 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
538 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
541 // Increases the vertical offset with the line's descender.
542 glyphData.verticalOffset += static_cast<int>(-line.descender);
545 return glyphData.bitmapBuffer;
548 /// Draws the specified strikethrough color to the buffer
549 void DrawStrikethrough(
550 const Vector4& strikethroughColor,
551 const unsigned int bufferWidth,
552 const unsigned int bufferHeight,
553 GlyphData& glyphData,
554 const float baseline,
556 const float maxStrikethroughHeight,
557 const float lineExtentLeft,
558 const float lineExtentRight,
559 float strikethroughStartingYPosition)
561 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
563 for(unsigned int y = strikethroughStartingYPosition; y < strikethroughStartingYPosition + maxStrikethroughHeight; y++)
565 if(y > bufferHeight - 1)
567 // Do not write out of bounds.
571 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
573 if(x > bufferWidth - 1)
575 // Do not write out of bounds.
579 WriteColorToPixelBuffer(glyphData, bitmapBuffer, strikethroughColor, x, y);
586 TypesetterPtr Typesetter::New(const ModelInterface* const model)
588 return TypesetterPtr(new Typesetter(model));
591 ViewModel* Typesetter::GetViewModel()
596 Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, const unsigned int bufferHeight, Pixel::Format pixelFormat)
598 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
600 if(Pixel::RGBA8888 == pixelFormat)
602 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
603 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
604 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
608 memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
614 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
616 // @todo. This initial implementation for a TextLabel has only one visible page.
618 // Elides the text if needed.
619 mModel->ElideGlyphs();
621 // Retrieves the layout size.
622 const Size& layoutSize = mModel->GetLayoutSize();
624 const int outlineWidth = static_cast<int>(mModel->GetOutlineWidth());
626 // Set the offset for the horizontal alignment according to the text direction and outline width.
629 switch(mModel->GetHorizontalAlignment())
631 case HorizontalAlignment::BEGIN:
636 case HorizontalAlignment::CENTER:
638 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
641 case HorizontalAlignment::END:
643 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
648 // Set the offset for the vertical alignment.
651 switch(mModel->GetVerticalAlignment())
653 case VerticalAlignment::TOP:
658 case VerticalAlignment::CENTER:
660 penY = static_cast<int>(0.5f * (size.height - layoutSize.height));
661 penY = penY < 0.f ? 0.f : penY;
664 case VerticalAlignment::BOTTOM:
666 penY = static_cast<int>(size.height - layoutSize.height);
671 // Calculate vertical line alignment
672 switch(mModel->GetVerticalLineAlignment())
674 case DevelText::VerticalLineAlignment::TOP:
678 case DevelText::VerticalLineAlignment::MIDDLE:
680 const auto& line = *mModel->GetLines();
681 penY -= line.descender;
682 penY += static_cast<int>(line.lineSpacing * 0.5f + line.descender);
685 case DevelText::VerticalLineAlignment::BOTTOM:
687 const auto& line = *mModel->GetLines();
688 const auto lineHeight = line.ascender + (-line.descender) + line.lineSpacing;
689 penY += static_cast<int>(lineHeight - (line.ascender - line.descender));
694 // Generate the image buffers of the text for each different style first,
695 // then combine all of them together as one final image buffer. We try to
696 // do all of these in CPU only, so that once the final texture is generated,
697 // no calculation is needed in GPU during each frame.
699 const unsigned int bufferWidth = static_cast<unsigned int>(size.width);
700 const unsigned int bufferHeight = static_cast<unsigned int>(size.height);
702 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
703 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
705 //Elided text in ellipsis at START could start on index greater than 0
706 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
707 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
709 Devel::PixelBuffer imageBuffer;
711 if(RENDER_MASK == behaviour)
713 // Generate the image buffer as an alpha mask for color glyphs.
714 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
716 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
718 // Generate an empty image buffer so that it can been combined with the image buffers for styles
719 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
720 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
724 // Generate the image buffer for the text with no style.
725 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
728 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
730 // Generate the outline if enabled
731 const uint16_t outlineWidth = mModel->GetOutlineWidth();
732 if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
734 // Create the image buffer for outline
735 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
737 // Combine the two buffers
738 imageBuffer = CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight);
741 // @todo. Support shadow and underline for partial text later on.
743 // Generate the shadow if enabled
744 const Vector2& shadowOffset = mModel->GetShadowOffset();
745 if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
747 // Create the image buffer for shadow
748 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
750 // Check whether it will be a soft shadow
751 const float& blurRadius = mModel->GetShadowBlurRadius();
753 if(blurRadius > Math::MACHINE_EPSILON_1)
755 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
758 // Combine the two buffers
759 imageBuffer = CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight);
762 // Generate the underline if enabled
763 const bool underlineEnabled = mModel->IsUnderlineEnabled();
764 if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
766 // Create the image buffer for underline
767 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
769 // Combine the two buffers
770 imageBuffer = CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
773 // Generate the background if enabled
774 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
775 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
776 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
778 Devel::PixelBuffer backgroundImageBuffer;
780 if(backgroundEnabled)
782 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
786 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
789 if(backgroundMarkupSet)
791 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
794 // Combine the two buffers
795 imageBuffer = CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight);
798 // Generate the strikethrough if enabled
799 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
800 if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
802 // Create the image buffer for strikethrough
803 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
805 // Combine the two buffers
806 imageBuffer = CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight);
811 imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
814 // Create the final PixelData for the combined image buffer
815 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
820 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)
822 // Retrieve lines, glyphs, positions and colors from the view model.
823 const Length modelNumberOfLines = mModel->GetNumberOfLines();
824 const LineRun* const modelLinesBuffer = mModel->GetLines();
825 const GlyphInfo* const glyphsBuffer = mModel->GetGlyphs();
826 const Vector2* const positionBuffer = mModel->GetLayout();
827 const Vector4* const colorsBuffer = mModel->GetColors();
828 const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
829 const GlyphInfo* hyphens = mModel->GetHyphens();
830 const Length* hyphenIndices = mModel->GetHyphenIndices();
831 const Length hyphensCount = mModel->GetHyphensCount();
833 // Elided text info. Indices according to elided text and Ellipsis position.
834 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
835 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
836 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
837 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
838 const auto ellipsisPosition = mModel->GetEllipsisPosition();
840 // Whether to use the default color.
841 const bool useDefaultColor = (NULL == colorsBuffer);
842 const Vector4& defaultColor = mModel->GetDefaultColor();
843 Vector4 currentStrikethroughColor;
845 // Create and initialize the pixel buffer.
847 glyphData.verticalOffset = verticalOffset;
848 glyphData.width = bufferWidth;
849 glyphData.height = bufferHeight;
850 glyphData.bitmapBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
851 glyphData.horizontalOffset = 0;
853 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
854 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
855 Length hyphenIndex = 0;
857 const Character* textBuffer = mModel->GetTextBuffer();
858 float calculatedAdvance = 0.f;
859 const Vector<CharacterIndex>& glyphToCharacterMap = mModel->GetGlyphsToCharacters();
860 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
862 // Traverses the lines of the text.
863 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
865 const LineRun& line = *(modelLinesBuffer + lineIndex);
867 // Sets the horizontal offset of the line.
868 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
869 glyphData.horizontalOffset += horizontalOffset;
871 // Increases the vertical offset with the line's ascender.
872 glyphData.verticalOffset += static_cast<int>(line.ascender);
874 // Include line spacing after first line
877 glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
880 // Retrieves the glyph's outline width
881 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
883 if(style == Typesetter::STYLE_OUTLINE)
885 glyphData.horizontalOffset -= outlineWidth;
888 // Only need to add the vertical outline offset for the first line
889 glyphData.verticalOffset -= outlineWidth;
892 else if(style == Typesetter::STYLE_SHADOW)
894 const Vector2& shadowOffset = mModel->GetShadowOffset();
895 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
899 // Only need to add the vertical shadow offset for first line
900 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
904 const bool underlineEnabled = mModel->IsUnderlineEnabled();
905 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
906 const Vector4& strikethroughColor = mModel->GetStrikethroughColor();
907 const float strikethroughHeight = mModel->GetStrikethroughHeight();
908 const float characterSpacing = mModel->GetCharacterSpacing();
910 // Aggregate underline-style-properties from mModel
911 const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
912 mModel->GetUnderlineColor(),
913 mModel->GetUnderlineHeight(),
914 mModel->GetDashedUnderlineGap(),
915 mModel->GetDashedUnderlineWidth(),
922 // Get the underline runs.
923 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
924 Vector<UnderlinedGlyphRun> underlineRuns;
925 underlineRuns.Resize(numberOfUnderlineRuns);
926 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
928 // Get the strikethrough runs.
929 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
930 Vector<StrikethroughGlyphRun> strikethroughRuns;
931 strikethroughRuns.Resize(numberOfStrikethroughRuns);
932 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
934 bool thereAreUnderlinedGlyphs = false;
935 bool strikethroughGlyphsExist = false;
937 float currentUnderlinePosition = 0.0f;
938 float currentUnderlineHeight = modelUnderlineProperties.height;
939 float maxUnderlineHeight = currentUnderlineHeight;
940 auto currentUnderlineProperties = modelUnderlineProperties;
942 float currentStrikethroughHeight = strikethroughHeight;
943 float maxStrikethroughHeight = currentStrikethroughHeight;
944 float strikethroughStartingYPosition = 0.0f;
946 FontId lastFontId = 0;
948 float lineExtentLeft = bufferWidth;
949 float lineExtentRight = 0.0f;
950 float baseline = 0.0f;
951 bool addHyphen = false;
953 // Traverses the glyphs of the line.
954 const GlyphIndex startGlyphIndex = std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs);
955 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
956 endGlyphIndex = std::min(endGlyphIndex, endIndexOfGlyphs);
958 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
960 if(glyphIndex < fromGlyphIndex || glyphIndex > toGlyphIndex)
962 // Ignore any glyph that out of the specified range
966 //To handle START case of ellipsis, the first glyph has been shifted
967 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
968 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
970 //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.
971 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
973 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
974 glyphIndex < secondMiddleIndexOfElidedGlyphs)
976 // Ignore any glyph that removed for MIDDLE ellipsis
979 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
981 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
985 // Retrieve the glyph's info.
986 const GlyphInfo* glyphInfo;
988 if(addHyphen && hyphens)
990 glyphInfo = hyphens + hyphenIndex;
995 glyphInfo = glyphsBuffer + elidedGlyphIndex;
998 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
999 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1001 // Nothing to do if the glyph's width or height is zero.
1005 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1006 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1007 currentUnderlineProperties = GetCurrentUnderlineProperties(underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1008 currentUnderlineHeight = GetCurrentUnderlineHeight(underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties.height);
1009 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1011 currentStrikethroughColor = strikethroughColor;
1012 const bool strikethroughGlyph = strikethroughEnabled || doGlyphHaveStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughColor);
1013 strikethroughGlyphsExist = strikethroughGlyphsExist || strikethroughGlyph;
1015 // Are we still using the same fontId as previous
1016 if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1018 // We need to fetch fresh font underline metrics
1019 FontMetrics fontMetrics;
1020 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1022 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1023 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1027 CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1030 if(strikethroughGlyph)
1032 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1035 // Update lastFontId because fontId is changed
1036 lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1039 // Retrieves the glyph's position.
1040 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1044 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1045 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1046 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1047 position.y = -glyphInfo->yBearing;
1050 if(baseline < position.y + glyphInfo->yBearing)
1052 baseline = position.y + glyphInfo->yBearing;
1055 // Calculate the positions of leftmost and rightmost glyphs in the current line
1056 if(position.x < lineExtentLeft)
1058 lineExtentLeft = position.x;
1061 if(position.x + glyphInfo->width > lineExtentRight)
1063 lineExtentRight = position.x + glyphInfo->width;
1066 // Retrieves the glyph's color.
1067 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1070 if(style == Typesetter::STYLE_SHADOW)
1072 color = mModel->GetShadowColor();
1074 else if(style == Typesetter::STYLE_OUTLINE)
1076 color = mModel->GetOutlineColor();
1080 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1083 // Premultiply alpha
1088 // Retrieves the glyph's bitmap.
1089 glyphData.glyphBitmap.buffer = NULL;
1090 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1091 glyphData.glyphBitmap.height = glyphInfo->height;
1093 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1095 // Don't render outline for other styles
1096 outlineWidth = 0.0f;
1099 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1101 fontClient.CreateBitmap(glyphInfo->fontId,
1103 glyphInfo->isItalicRequired,
1104 glyphInfo->isBoldRequired,
1105 glyphData.glyphBitmap,
1106 static_cast<int>(outlineWidth));
1109 // Sets the glyph's bitmap into the bitmap of the whole text.
1110 if(NULL != glyphData.glyphBitmap.buffer)
1112 if(style == Typesetter::STYLE_OUTLINE)
1114 // Set the position offset for the current glyph
1115 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1116 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1119 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1120 TypesetGlyph(glyphData,
1126 if(style == Typesetter::STYLE_OUTLINE)
1128 // Reset the position offset for the next glyph
1129 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1130 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1133 // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1134 delete[] glyphData.glyphBitmap.buffer;
1135 glyphData.glyphBitmap.buffer = NULL;
1140 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1145 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1153 // Draw the underline from the leftmost glyph to the rightmost glyph
1154 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1156 DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1159 // Draw the background color from the leftmost glyph to the rightmost glyph
1160 if(style == Typesetter::STYLE_BACKGROUND)
1162 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1165 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1166 if(strikethroughGlyphsExist && style == Typesetter::STYLE_STRIKETHROUGH)
1168 //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.
1169 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.
1170 DrawStrikethrough(currentStrikethroughColor, bufferWidth, bufferHeight, glyphData, baseline, line, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, strikethroughStartingYPosition);
1173 // Increases the vertical offset with the line's descender.
1174 glyphData.verticalOffset += static_cast<int>(-line.descender);
1177 return glyphData.bitmapBuffer;
1180 Devel::PixelBuffer Typesetter::CombineImageBuffer(Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight)
1182 unsigned char* topBuffer = topPixelBuffer.GetBuffer();
1183 unsigned char* bottomBuffer = bottomPixelBuffer.GetBuffer();
1185 Devel::PixelBuffer combinedPixelBuffer;
1187 if(topBuffer == NULL && bottomBuffer == NULL)
1189 // Nothing to do if both buffers are empty.
1190 return combinedPixelBuffer;
1193 if(topBuffer == NULL)
1195 // Nothing to do if topBuffer is empty.
1196 return bottomPixelBuffer;
1199 if(bottomBuffer == NULL)
1201 // Nothing to do if bottomBuffer is empty.
1202 return topPixelBuffer;
1205 // Always combine two RGBA images
1206 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
1207 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
1209 combinedPixelBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1210 uint8_t* combinedBuffer = reinterpret_cast<uint8_t*>(combinedPixelBuffer.GetBuffer());
1211 memset(combinedBuffer, 0u, bufferSizeChar);
1213 for(unsigned int pixelIndex = 0; pixelIndex < bufferSizeInt; pixelIndex++)
1215 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
1216 // Otherwise, copy pixel from topBuffer to combinedBuffer.
1218 unsigned int alphaBuffer1 = topBuffer[pixelIndex * 4 + 3];
1220 if(alphaBuffer1 != 255)
1222 // At least one pixel is not fully opaque
1223 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
1224 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4] + (bottomBuffer[pixelIndex * 4] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1225 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1] + (bottomBuffer[pixelIndex * 4 + 1] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1226 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2] + (bottomBuffer[pixelIndex * 4 + 2] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1227 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3] + (bottomBuffer[pixelIndex * 4 + 3] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1231 // Copy the pixel from topBuffer to combinedBuffer
1232 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4];
1233 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1];
1234 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2];
1235 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3];
1239 return combinedPixelBuffer;
1242 Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1244 // Underline-tags (this is for Markup case)
1245 // Get the underline runs.
1246 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1247 Vector<UnderlinedGlyphRun> underlineRuns;
1248 underlineRuns.Resize(numberOfUnderlineRuns);
1249 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1251 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1252 Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1253 Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1254 GlyphIndex startGlyphIndex, endGlyphIndex;
1256 //The outer loop to iterate on the separated chunks of underlined glyph runs
1257 while(itGlyphRun != endItGlyphRun)
1259 const UnderlineStyleProperties& firstUnderlineStyleProperties = itGlyphRun->properties;
1261 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1262 endGlyphIndex = startGlyphIndex;
1263 //The inner loop to make a connected underline for the consecutive characters
1266 endGlyphIndex += itGlyphRun->glyphRun.numberOfGlyphs;
1268 } while(itGlyphRun != endItGlyphRun && itGlyphRun->glyphRun.glyphIndex == endGlyphIndex &&
1269 (firstUnderlineStyleProperties == itGlyphRun->properties));
1273 // Create the image buffer for underline
1274 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1275 // Combine the two buffers
1276 topPixelBuffer = CombineImageBuffer(topPixelBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
1279 return topPixelBuffer;
1282 Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1284 // strikethrough-tags (this is for Markup case)
1285 // Get the strikethrough runs.
1286 const Length numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1287 Vector<StrikethroughGlyphRun> strikethroughRuns;
1288 strikethroughRuns.Resize(numberOfStrikethroughRuns);
1289 mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1291 // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1292 Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun = strikethroughRuns.Begin();
1293 Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1294 GlyphIndex startGlyphIndex, endGlyphIndex;
1296 //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1297 while(itGlyphRun != endItGlyphRun)
1299 startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1300 endGlyphIndex = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1302 // Create the image buffer for strikethrough
1303 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1304 // Combine the two buffers
1305 topPixelBuffer = CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight);
1310 return topPixelBuffer;
1313 Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1315 // Apply the markup-Processor if enabled
1316 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1317 if(markupProcessorEnabled)
1319 topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1321 topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1324 return topPixelBuffer;
1327 Typesetter::Typesetter(const ModelInterface* const model)
1328 : mModel(new ViewModel(model))
1332 Typesetter::~Typesetter()
1339 } // namespace Toolkit