+ // 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;
+
+ Length numberOfGlyphs = mModel->GetNumberOfGlyphs();
+
+ 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, 0u, numberOfGlyphs - 1 );
+ }
+ else if( RENDER_NO_TEXT == 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, 0u, numberOfGlyphs -1 );
+ }
+
+ if ( ( RENDER_NO_STYLES != behaviour ) && ( RENDER_MASK != behaviour ) )
+ {
+
+ // Generate the outline if enabled
+ const float outlineWidth = mModel->GetOutlineWidth();
+ if ( outlineWidth > Math::MACHINE_EPSILON_1 )
+ {
+ // Create the image buffer for outline
+ Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, numberOfGlyphs -1 );
+
+ // 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 ( 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, 0u, numberOfGlyphs - 1 );
+
+ // 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 )
+ {
+ // Create the image buffer for underline
+ Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, numberOfGlyphs - 1 );
+
+ // Combine the two buffers
+ imageBuffer = CombineImageBuffer( imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight );
+ }
+
+ // Generate the background if enabled
+ const bool backgroundEnabled = mModel->IsBackgroundEnabled();
+ if ( backgroundEnabled )
+ {
+ Devel::PixelBuffer backgroundImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, numberOfGlyphs -1 );
+
+ // Combine the two buffers
+ imageBuffer = CombineImageBuffer( imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight );
+ }
+ }
+
+ // 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 )
+{