2 * Copyright (c) 2021 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 /// Helper method to fetch the underline metrics for the specified font glyph
279 void FetchFontDecorationlinesMetrics(
280 TextAbstraction::FontClient& fontClient,
281 const GlyphInfo* const glyphInfo,
282 float& currentUnderlinePosition,
283 const float underlineHeight,
284 float& currentUnderlineThickness,
285 float& maxUnderlineThickness,
286 FontId& lastlinedFontId,
287 const float strikethroughHeight,
288 float& currentStrikethroughThickness,
289 float& maxStrikethroughThickness)
291 FontMetrics fontMetrics;
292 fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
293 currentUnderlinePosition = ceil(fabsf(fontMetrics.underlinePosition));
294 const float descender = ceil(fabsf(fontMetrics.descender));
296 if(fabsf(underlineHeight) < Math::MACHINE_EPSILON_1000)
298 currentUnderlineThickness = fontMetrics.underlineThickness;
300 // Ensure underline will be at least a pixel high
301 if(currentUnderlineThickness < 1.0f)
303 currentUnderlineThickness = 1.0f;
307 currentUnderlineThickness = ceil(currentUnderlineThickness);
311 if(fabsf(strikethroughHeight) < Math::MACHINE_EPSILON_1000)
313 // Ensure strikethrough will be at least a pixel high
314 if(currentStrikethroughThickness < 1.0f)
316 currentStrikethroughThickness = 1.0f;
320 currentStrikethroughThickness = ceil(currentStrikethroughThickness);
324 // The underline thickness should be the max underline thickness of all glyphs of the line.
325 if(currentUnderlineThickness > maxUnderlineThickness)
327 maxUnderlineThickness = currentUnderlineThickness;
330 // The strikethrough thickness should be the max strikethrough thickness of all glyphs of the line.
331 if(currentStrikethroughThickness > maxStrikethroughThickness)
333 maxStrikethroughThickness = currentStrikethroughThickness;
336 // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
337 if(currentUnderlinePosition > descender)
339 currentUnderlinePosition = descender;
342 if(fabsf(currentUnderlinePosition) < Math::MACHINE_EPSILON_1000)
344 // Move offset down by one ( EFL behavior )
345 currentUnderlinePosition = 1.0f;
348 lastlinedFontId = glyphInfo->fontId;
351 /// Draws the specified color to the pixel buffer
352 void WriteColorToPixelBuffer(
353 GlyphData& glyphData,
354 uint32_t* bitmapBuffer,
355 const Vector4& color,
356 const unsigned int x,
357 const unsigned int y)
359 // Always RGBA image for text with styles
360 uint32_t pixel = *(bitmapBuffer + y * glyphData.width + x);
361 uint8_t* pixelBuffer = reinterpret_cast<uint8_t*>(&pixel);
363 // Write the color to the pixel buffer
364 uint8_t colorAlpha = static_cast<uint8_t>(color.a * 255.f);
365 *(pixelBuffer + 3u) = colorAlpha;
366 *(pixelBuffer + 2u) = static_cast<uint8_t>(color.b * colorAlpha);
367 *(pixelBuffer + 1u) = static_cast<uint8_t>(color.g * colorAlpha);
368 *(pixelBuffer) = static_cast<uint8_t>(color.r * colorAlpha);
370 *(bitmapBuffer + y * glyphData.width + x) = pixel;
373 /// Draws the specified underline color to the buffer
375 const Vector4& underlineColor,
376 const unsigned int bufferWidth,
377 const unsigned int bufferHeight,
378 GlyphData& glyphData,
379 const float baseline,
380 const float currentUnderlinePosition,
381 const float maxUnderlineThickness,
382 const float lineExtentLeft,
383 const float lineExtentRight,
384 const Text::Underline::Type underlineType,
385 const float dashedUnderlineWidth,
386 const float dashedUnderlineGap,
389 int underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
390 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
392 for(unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineThickness; y++)
394 if(y > bufferHeight - 1)
396 // Do not write out of bounds.
399 if(underlineType == Text::Underline::DASHED)
401 float dashWidth = dashedUnderlineWidth;
404 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
406 if(x > bufferWidth - 1)
408 // Do not write out of bounds.
411 if(dashGap == 0 && dashWidth > 0)
413 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
416 else if(dashGap < dashedUnderlineGap)
423 dashWidth = dashedUnderlineWidth;
430 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
432 if(x > bufferWidth - 1)
434 // Do not write out of bounds.
437 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
441 if(underlineType == Text::Underline::DOUBLE)
443 int secondUnderlineYOffset = glyphData.verticalOffset - line.descender - maxUnderlineThickness;
444 for(unsigned int y = secondUnderlineYOffset; y < secondUnderlineYOffset + maxUnderlineThickness; y++)
446 if(y > bufferHeight - 1)
448 // Do not write out of bounds.
451 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
453 if(x > bufferWidth - 1)
455 // Do not write out of bounds.
458 WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
464 /// Draws the background color to the buffer
465 void DrawBackgroundColor(
466 Vector4 backgroundColor,
467 const unsigned int bufferWidth,
468 const unsigned int bufferHeight,
469 GlyphData& glyphData,
470 const float baseline,
472 const float lineExtentLeft,
473 const float lineExtentRight)
475 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
477 for(int y = glyphData.verticalOffset + baseline - line.ascender; y < glyphData.verticalOffset + baseline - line.descender; y++)
479 if((y < 0) || (y > static_cast<int>(bufferHeight - 1)))
481 // Do not write out of bounds.
485 for(int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
487 if((x < 0) || (x > static_cast<int>(bufferWidth - 1)))
489 // Do not write out of bounds.
493 WriteColorToPixelBuffer(glyphData, bitmapBuffer, backgroundColor, x, y);
498 Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuffer& buffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, int horizontalOffset, int verticalOffset)
500 // Retrieve lines, glyphs, positions and colors from the view model.
501 const Length modelNumberOfLines = model->GetNumberOfLines();
502 const LineRun* const modelLinesBuffer = model->GetLines();
503 const Length numberOfGlyphs = model->GetNumberOfGlyphs();
504 const GlyphInfo* const glyphsBuffer = model->GetGlyphs();
505 const Vector2* const positionBuffer = model->GetLayout();
506 const Vector4* const backgroundColorsBuffer = model->GetBackgroundColors();
507 const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
509 // Create and initialize the pixel buffer.
511 glyphData.verticalOffset = verticalOffset;
512 glyphData.width = bufferWidth;
513 glyphData.height = bufferHeight;
514 glyphData.bitmapBuffer = buffer;
515 glyphData.horizontalOffset = 0;
517 ColorIndex prevBackgroundColorIndex = 0;
518 ColorIndex backgroundColorIndex = 0;
520 // Traverses the lines of the text.
521 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
523 const LineRun& line = *(modelLinesBuffer + lineIndex);
525 // Sets the horizontal offset of the line.
526 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
527 glyphData.horizontalOffset += horizontalOffset;
529 // Increases the vertical offset with the line's ascender.
530 glyphData.verticalOffset += static_cast<int>(line.ascender);
532 // Include line spacing after first line
535 glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
538 float left = bufferWidth;
540 float baseline = 0.0f;
542 // Traverses the glyphs of the line.
543 const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
544 for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
546 // Retrieve the glyph's info.
547 const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
549 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
550 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
552 // Nothing to do if default background color, the glyph's width or height is zero.
556 backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
558 if((backgroundColorIndex != prevBackgroundColorIndex) &&
559 (prevBackgroundColorIndex != 0u))
561 const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
562 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
565 if(backgroundColorIndex == 0u)
567 prevBackgroundColorIndex = backgroundColorIndex;
568 //if background color is the default do nothing
572 // Retrieves the glyph's position.
573 const Vector2* const position = positionBuffer + glyphIndex;
575 if(baseline < position->y + glyphInfo->yBearing)
577 baseline = position->y + glyphInfo->yBearing;
580 // Calculate the positions of leftmost and rightmost glyphs in the current line
581 if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
583 left = position->x - glyphInfo->xBearing;
586 if(position->x + glyphInfo->width > right)
588 right = position->x - glyphInfo->xBearing + glyphInfo->advance;
591 prevBackgroundColorIndex = backgroundColorIndex;
594 //draw last background at line end if not default
595 if(backgroundColorIndex != 0u)
597 const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
598 DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
601 // Increases the vertical offset with the line's descender.
602 glyphData.verticalOffset += static_cast<int>(-line.descender);
605 return glyphData.bitmapBuffer;
608 /// Draws the specified strikethrough color to the buffer
609 void DrawStrikethrough(
610 const Vector4& strikethroughColor,
611 const unsigned int bufferWidth,
612 const unsigned int bufferHeight,
613 GlyphData& glyphData,
614 const float baseline,
616 const float maxStrikethroughThickness,
617 const float lineExtentLeft,
618 const float lineExtentRight,
619 float strikethroughStartingYPosition)
621 uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
623 for(unsigned int y = strikethroughStartingYPosition; y < strikethroughStartingYPosition + maxStrikethroughThickness; y++)
625 if(y > bufferHeight - 1)
627 // Do not write out of bounds.
631 for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
633 if(x > bufferWidth - 1)
635 // Do not write out of bounds.
639 WriteColorToPixelBuffer(glyphData, bitmapBuffer, strikethroughColor, x, y);
646 TypesetterPtr Typesetter::New(const ModelInterface* const model)
648 return TypesetterPtr(new Typesetter(model));
651 ViewModel* Typesetter::GetViewModel()
656 Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, const unsigned int bufferHeight, Pixel::Format pixelFormat)
658 Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
660 if(Pixel::RGBA8888 == pixelFormat)
662 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
663 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
664 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
668 memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
674 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
676 // @todo. This initial implementation for a TextLabel has only one visible page.
678 // Elides the text if needed.
679 mModel->ElideGlyphs();
681 // Retrieves the layout size.
682 const Size& layoutSize = mModel->GetLayoutSize();
684 const int outlineWidth = static_cast<int>(mModel->GetOutlineWidth());
686 // Set the offset for the horizontal alignment according to the text direction and outline width.
689 switch(mModel->GetHorizontalAlignment())
691 case HorizontalAlignment::BEGIN:
696 case HorizontalAlignment::CENTER:
698 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
701 case HorizontalAlignment::END:
703 penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
708 // Set the offset for the vertical alignment.
711 switch(mModel->GetVerticalAlignment())
713 case VerticalAlignment::TOP:
718 case VerticalAlignment::CENTER:
720 penY = static_cast<int>(0.5f * (size.height - layoutSize.height));
721 penY = penY < 0.f ? 0.f : penY;
724 case VerticalAlignment::BOTTOM:
726 penY = static_cast<int>(size.height - layoutSize.height);
731 // Calculate vertical line alignment
732 switch(mModel->GetVerticalLineAlignment())
734 case DevelText::VerticalLineAlignment::TOP:
738 case DevelText::VerticalLineAlignment::MIDDLE:
740 const auto& line = *mModel->GetLines();
741 penY -= line.descender;
742 penY += static_cast<int>(line.lineSpacing * 0.5f + line.descender);
745 case DevelText::VerticalLineAlignment::BOTTOM:
747 const auto& line = *mModel->GetLines();
748 const auto lineHeight = line.ascender + (-line.descender) + line.lineSpacing;
749 penY += static_cast<int>(lineHeight - (line.ascender - line.descender));
754 // Generate the image buffers of the text for each different style first,
755 // then combine all of them together as one final image buffer. We try to
756 // do all of these in CPU only, so that once the final texture is generated,
757 // no calculation is needed in GPU during each frame.
759 const unsigned int bufferWidth = static_cast<unsigned int>(size.width);
760 const unsigned int bufferHeight = static_cast<unsigned int>(size.height);
762 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
763 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
765 //Elided text in ellipsis at START could start on index greater than 0
766 auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
767 auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
769 Devel::PixelBuffer imageBuffer;
771 if(RENDER_MASK == behaviour)
773 // Generate the image buffer as an alpha mask for color glyphs.
774 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
776 else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
778 // Generate an empty image buffer so that it can been combined with the image buffers for styles
779 imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
780 memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
784 // Generate the image buffer for the text with no style.
785 imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
788 if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
790 // Generate the outline if enabled
791 const uint16_t outlineWidth = mModel->GetOutlineWidth();
792 if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
794 // Create the image buffer for outline
795 Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
797 // Combine the two buffers
798 imageBuffer = CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight);
801 // @todo. Support shadow and underline for partial text later on.
803 // Generate the shadow if enabled
804 const Vector2& shadowOffset = mModel->GetShadowOffset();
805 if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
807 // Create the image buffer for shadow
808 Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
810 // Check whether it will be a soft shadow
811 const float& blurRadius = mModel->GetShadowBlurRadius();
813 if(blurRadius > Math::MACHINE_EPSILON_1)
815 shadowImageBuffer.ApplyGaussianBlur(blurRadius);
818 // Combine the two buffers
819 imageBuffer = CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight);
822 // Generate the underline if enabled
823 const bool underlineEnabled = mModel->IsUnderlineEnabled();
824 if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
826 // Create the image buffer for underline
827 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
829 // Combine the two buffers
830 imageBuffer = CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
833 // Generate the background if enabled
834 const bool backgroundEnabled = mModel->IsBackgroundEnabled();
835 const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
836 if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
838 Devel::PixelBuffer backgroundImageBuffer;
840 if(backgroundEnabled)
842 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
846 backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
849 if(backgroundMarkupSet)
851 DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
854 // Combine the two buffers
855 imageBuffer = CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight);
858 // Generate the strikethrough if enabled
859 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
860 if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
862 // Create the image buffer for strikethrough
863 Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
865 // Combine the two buffers
866 imageBuffer = CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight);
871 imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
874 // Create the final PixelData for the combined image buffer
875 PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
880 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)
882 // Retrieve lines, glyphs, positions and colors from the view model.
883 const Length modelNumberOfLines = mModel->GetNumberOfLines();
884 const LineRun* const modelLinesBuffer = mModel->GetLines();
885 const GlyphInfo* const glyphsBuffer = mModel->GetGlyphs();
886 const Vector2* const positionBuffer = mModel->GetLayout();
887 const Vector4* const colorsBuffer = mModel->GetColors();
888 const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
889 const GlyphInfo* hyphens = mModel->GetHyphens();
890 const Length* hyphenIndices = mModel->GetHyphenIndices();
891 const Length hyphensCount = mModel->GetHyphensCount();
893 // Elided text info. Indices according to elided text and Ellipsis position.
894 const auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
895 const auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
896 const auto firstMiddleIndexOfElidedGlyphs = mModel->GetFirstMiddleIndexOfElidedGlyphs();
897 const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
898 const auto ellipsisPosition = mModel->GetEllipsisPosition();
900 // Whether to use the default color.
901 const bool useDefaultColor = (NULL == colorsBuffer);
902 const Vector4& defaultColor = mModel->GetDefaultColor();
904 // Create and initialize the pixel buffer.
906 glyphData.verticalOffset = verticalOffset;
907 glyphData.width = bufferWidth;
908 glyphData.height = bufferHeight;
909 glyphData.bitmapBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
910 glyphData.horizontalOffset = 0;
912 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
913 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
914 Length hyphenIndex = 0;
916 // Traverses the lines of the text.
917 for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
919 const LineRun& line = *(modelLinesBuffer + lineIndex);
921 // Sets the horizontal offset of the line.
922 glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
923 glyphData.horizontalOffset += horizontalOffset;
925 // Increases the vertical offset with the line's ascender.
926 glyphData.verticalOffset += static_cast<int>(line.ascender);
928 // Include line spacing after first line
931 glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
934 // Retrieves the glyph's outline width
935 float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
937 if(style == Typesetter::STYLE_OUTLINE)
939 glyphData.horizontalOffset -= outlineWidth;
942 // Only need to add the vertical outline offset for the first line
943 glyphData.verticalOffset -= outlineWidth;
946 else if(style == Typesetter::STYLE_SHADOW)
948 const Vector2& shadowOffset = mModel->GetShadowOffset();
949 glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
953 // Only need to add the vertical shadow offset for first line
954 glyphData.verticalOffset += shadowOffset.y - outlineWidth;
958 const bool underlineEnabled = mModel->IsUnderlineEnabled();
959 const Vector4& underlineColor = mModel->GetUnderlineColor();
960 const float underlineHeight = mModel->GetUnderlineHeight();
961 const Text::Underline::Type underlineType = mModel->GetUnderlineType();
962 const float dashedUnderlineWidth = mModel->GetDashedUnderlineWidth();
963 const float dashedUnderlineGap = mModel->GetDashedUnderlineGap();
965 const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
966 const Vector4& strikethroughColor = mModel->GetStrikethroughColor();
967 const float strikethroughHeight = mModel->GetStrikethroughHeight();
969 // Get the underline runs.
970 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
971 Vector<GlyphRun> underlineRuns;
972 underlineRuns.Resize(numberOfUnderlineRuns);
973 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
975 bool thereAreUnderlinedGlyphs = false;
976 bool strikethroughGlyphsExist = false;
978 float currentUnderlinePosition = 0.0f;
979 float currentUnderlineThickness = underlineHeight;
980 float maxUnderlineThickness = currentUnderlineThickness;
981 float currentStrikethroughThickness = strikethroughHeight;
982 float maxStrikethroughThickness = currentStrikethroughThickness;
983 float strikethroughStartingYPosition = 0.0f;
985 FontId lastUnderlinedFontId = 0;
987 float lineExtentLeft = bufferWidth;
988 float lineExtentRight = 0.0f;
989 float baseline = 0.0f;
990 bool addHyphen = false;
992 // Traverses the glyphs of the line.
993 const GlyphIndex startGlyphIndex = std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs);
994 GlyphIndex endGlyphIndex = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
995 endGlyphIndex = std::min(endGlyphIndex, endIndexOfGlyphs);
997 for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
999 if(glyphIndex < fromGlyphIndex || glyphIndex > toGlyphIndex)
1001 // Ignore any glyph that out of the specified range
1005 //To handle START case of ellipsis, the first glyph has been shifted
1006 //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1007 GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1009 //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.
1010 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1012 if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1013 glyphIndex < secondMiddleIndexOfElidedGlyphs)
1015 // Ignore any glyph that removed for MIDDLE ellipsis
1018 if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1020 elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1024 // Retrieve the glyph's info.
1025 const GlyphInfo* glyphInfo;
1027 if(addHyphen && hyphens)
1029 glyphInfo = hyphens + hyphenIndex;
1034 glyphInfo = glyphsBuffer + elidedGlyphIndex;
1037 if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1038 (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1040 // Nothing to do if the glyph's width or height is zero.
1044 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns);
1045 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
1047 strikethroughGlyphsExist = strikethroughGlyphsExist || strikethroughEnabled;
1049 // Are we still using the same fontId as previous
1050 if((strikethroughEnabled || underlineGlyph) && (glyphInfo->fontId != lastUnderlinedFontId))
1052 // We need to fetch fresh font underline metrics
1053 FetchFontDecorationlinesMetrics(fontClient, glyphInfo, currentUnderlinePosition, underlineHeight, currentUnderlineThickness, maxUnderlineThickness, lastUnderlinedFontId, strikethroughHeight, currentStrikethroughThickness, maxStrikethroughThickness);
1056 // Retrieves the glyph's position.
1057 Vector2 position = *(positionBuffer + elidedGlyphIndex);
1061 GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1062 position.x = position.x + tempInfo.advance - tempInfo.xBearing + glyphInfo->xBearing;
1063 position.y = -glyphInfo->yBearing;
1066 if(baseline < position.y + glyphInfo->yBearing)
1068 baseline = position.y + glyphInfo->yBearing;
1071 // Calculate the positions of leftmost and rightmost glyphs in the current line
1072 if(position.x < lineExtentLeft)
1074 lineExtentLeft = position.x;
1077 if(position.x + glyphInfo->width > lineExtentRight)
1079 lineExtentRight = position.x + glyphInfo->width;
1082 // Retrieves the glyph's color.
1083 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1086 if(style == Typesetter::STYLE_SHADOW)
1088 color = mModel->GetShadowColor();
1090 else if(style == Typesetter::STYLE_OUTLINE)
1092 color = mModel->GetOutlineColor();
1096 color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1099 // Premultiply alpha
1104 // Retrieves the glyph's bitmap.
1105 glyphData.glyphBitmap.buffer = NULL;
1106 glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
1107 glyphData.glyphBitmap.height = glyphInfo->height;
1109 if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1111 // Don't render outline for other styles
1112 outlineWidth = 0.0f;
1115 if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1117 fontClient.CreateBitmap(glyphInfo->fontId,
1119 glyphInfo->isItalicRequired,
1120 glyphInfo->isBoldRequired,
1121 glyphData.glyphBitmap,
1122 static_cast<int>(outlineWidth));
1125 // Sets the glyph's bitmap into the bitmap of the whole text.
1126 if(NULL != glyphData.glyphBitmap.buffer)
1128 if(style == Typesetter::STYLE_OUTLINE)
1130 // Set the position offset for the current glyph
1131 glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1132 glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1135 // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1136 TypesetGlyph(glyphData,
1142 if(style == Typesetter::STYLE_OUTLINE)
1144 // Reset the position offset for the next glyph
1145 glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1146 glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1149 // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1150 delete[] glyphData.glyphBitmap.buffer;
1151 glyphData.glyphBitmap.buffer = NULL;
1156 while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1161 addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1169 // Draw the underline from the leftmost glyph to the rightmost glyph
1170 if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1172 DrawUnderline(underlineColor, bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineThickness, lineExtentLeft, lineExtentRight, underlineType, dashedUnderlineWidth, dashedUnderlineGap, line);
1175 // Draw the background color from the leftmost glyph to the rightmost glyph
1176 if(style == Typesetter::STYLE_BACKGROUND)
1178 DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1181 // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1182 if(strikethroughGlyphsExist && style == Typesetter::STYLE_STRIKETHROUGH)
1184 //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.
1185 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.
1186 DrawStrikethrough(strikethroughColor, bufferWidth, bufferHeight, glyphData, baseline, line, maxStrikethroughThickness, lineExtentLeft, lineExtentRight, strikethroughStartingYPosition);
1189 // Increases the vertical offset with the line's descender.
1190 glyphData.verticalOffset += static_cast<int>(-line.descender);
1193 return glyphData.bitmapBuffer;
1196 Devel::PixelBuffer Typesetter::CombineImageBuffer(Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight)
1198 unsigned char* topBuffer = topPixelBuffer.GetBuffer();
1199 unsigned char* bottomBuffer = bottomPixelBuffer.GetBuffer();
1201 Devel::PixelBuffer combinedPixelBuffer;
1203 if(topBuffer == NULL && bottomBuffer == NULL)
1205 // Nothing to do if both buffers are empty.
1206 return combinedPixelBuffer;
1209 if(topBuffer == NULL)
1211 // Nothing to do if topBuffer is empty.
1212 return bottomPixelBuffer;
1215 if(bottomBuffer == NULL)
1217 // Nothing to do if bottomBuffer is empty.
1218 return topPixelBuffer;
1221 // Always combine two RGBA images
1222 const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
1223 const unsigned int bufferSizeChar = 4u * bufferSizeInt;
1225 combinedPixelBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1226 uint8_t* combinedBuffer = reinterpret_cast<uint8_t*>(combinedPixelBuffer.GetBuffer());
1227 memset(combinedBuffer, 0u, bufferSizeChar);
1229 for(unsigned int pixelIndex = 0; pixelIndex < bufferSizeInt; pixelIndex++)
1231 // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
1232 // Otherwise, copy pixel from topBuffer to combinedBuffer.
1234 unsigned int alphaBuffer1 = topBuffer[pixelIndex * 4 + 3];
1236 if(alphaBuffer1 != 255)
1238 // At least one pixel is not fully opaque
1239 // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
1240 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4] + (bottomBuffer[pixelIndex * 4] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1241 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1] + (bottomBuffer[pixelIndex * 4 + 1] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1242 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2] + (bottomBuffer[pixelIndex * 4 + 2] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1243 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3] + (bottomBuffer[pixelIndex * 4 + 3] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1247 // Copy the pixel from topBuffer to combinedBuffer
1248 combinedBuffer[pixelIndex * 4] = topBuffer[pixelIndex * 4];
1249 combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1];
1250 combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2];
1251 combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3];
1255 return combinedPixelBuffer;
1258 Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1260 // Apply the markup-Processor if enabled
1261 const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1262 if(markupProcessorEnabled)
1264 // Underline-tags (this is for Markup case)
1265 // Get the underline runs.
1266 const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1267 Vector<GlyphRun> underlineRuns;
1268 underlineRuns.Resize(numberOfUnderlineRuns);
1269 mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1271 // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1272 Vector<GlyphRun>::ConstIterator itGlyphRun = underlineRuns.Begin();
1273 Vector<GlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1274 GlyphIndex startGlyphIndex, endGlyphIndex;
1276 //The outer loop to iterate on the separated chunks of underlined glyph runs
1277 while(itGlyphRun != endItGlyphRun)
1279 startGlyphIndex = itGlyphRun->glyphIndex;
1280 endGlyphIndex = startGlyphIndex;
1281 //The inner loop to make a connected underline for the consecutive characters
1284 endGlyphIndex += itGlyphRun->numberOfGlyphs;
1286 } while(itGlyphRun != endItGlyphRun && itGlyphRun->glyphIndex == endGlyphIndex);
1290 // Create the image buffer for underline
1291 Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1292 // Combine the two buffers
1293 topPixelBuffer = CombineImageBuffer(topPixelBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
1297 return topPixelBuffer;
1300 Typesetter::Typesetter(const ModelInterface* const model)
1301 : mModel(new ViewModel(model))
1305 Typesetter::~Typesetter()
1312 } // namespace Toolkit