+ // Calculate vertical line alignment
+ switch(mModel->GetVerticalLineAlignment())
+ {
+ case DevelText::VerticalLineAlignment::TOP:
+ {
+ break;
+ }
+ case DevelText::VerticalLineAlignment::MIDDLE:
+ {
+ const auto& line = *mModel->GetLines();
+ penY -= line.descender;
+ penY += static_cast<int>(line.lineSpacing * 0.5f + line.descender);
+ break;
+ }
+ case DevelText::VerticalLineAlignment::BOTTOM:
+ {
+ const auto& line = *mModel->GetLines();
+ const auto lineHeight = line.ascender + (-line.descender) + line.lineSpacing;
+ penY += static_cast<int>(lineHeight - (line.ascender - line.descender));
+ break;
+ }
+ }
+
+ // Generate the image buffers of the text for each different style first,
+ // then combine all of them together as one final image buffer. We try to
+ // do all of these in CPU only, so that once the final texture is generated,
+ // no calculation is needed in GPU during each frame.
+
+ const unsigned int bufferWidth = static_cast<unsigned int>(size.width);
+ const unsigned int bufferHeight = static_cast<unsigned int>(size.height);
+
+ const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
+ const unsigned int bufferSizeChar = 4u * bufferSizeInt;
+
+ //Elided text in ellipsis at START could start on index greater than 0
+ auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
+ auto endIndexOfGlyphs = mModel->GetEndIndexOfElidedGlyphs();
+
+ Devel::PixelBuffer imageBuffer;
+
+ if(RENDER_MASK == behaviour)
+ {
+ // Generate the image buffer as an alpha mask for color glyphs.
+ imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
+ }
+ else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
+ {
+ // Generate an empty image buffer so that it can been combined with the image buffers for styles
+ imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
+ memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
+ }
+ else
+ {
+ // Generate the image buffer for the text with no style.
+ imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
+ }
+
+ if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
+ {
+ // Generate the outline if enabled
+ const uint16_t outlineWidth = mModel->GetOutlineWidth();
+ if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
+ {
+ // Create the image buffer for outline
+ Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
+
+ // Combine the two buffers
+ imageBuffer = CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight);
+ }
+
+ // @todo. Support shadow and underline for partial text later on.
+
+ // Generate the shadow if enabled
+ const Vector2& shadowOffset = mModel->GetShadowOffset();
+ if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
+ {
+ // Create the image buffer for shadow
+ Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
+
+ // Check whether it will be a soft shadow
+ const float& blurRadius = mModel->GetShadowBlurRadius();
+
+ if(blurRadius > Math::MACHINE_EPSILON_1)
+ {
+ shadowImageBuffer.ApplyGaussianBlur(blurRadius);
+ }
+
+ // Combine the two buffers
+ imageBuffer = CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight);
+ }
+
+ // Generate the underline if enabled
+ const bool underlineEnabled = mModel->IsUnderlineEnabled();
+ if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
+ {
+ // Create the image buffer for underline
+ Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
+
+ // Combine the two buffers
+ imageBuffer = CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
+ }
+
+ // Generate the background if enabled
+ const bool backgroundEnabled = mModel->IsBackgroundEnabled();
+ const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
+ if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
+ {
+ Devel::PixelBuffer backgroundImageBuffer;
+
+ if(backgroundEnabled)
+ {
+ backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
+ }
+ else
+ {
+ backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
+ }
+
+ if(backgroundMarkupSet)
+ {
+ DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
+ }
+
+ // Combine the two buffers
+ imageBuffer = CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight);
+ }
+
+ // Generate the strikethrough if enabled
+ const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
+ if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
+ {
+ // Create the image buffer for strikethrough
+ Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
+
+ // Combine the two buffers
+ imageBuffer = CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight);
+ }
+
+ // Markup-Processor
+
+ imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
+ }
+
+ // Create the final PixelData for the combined image buffer
+ PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
+
+ return pixelData;
+}
+
+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)
+{