/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <dali-toolkit/internal/text/rendering/text-typesetter.h>
// EXTERNAL INCLUDES
-#include <dali/devel-api/text-abstraction/font-client.h>
#include <memory.h>
+#include <dali/devel-api/text-abstraction/font-client.h>
#include <dali/public-api/common/constants.h>
// INTERNAL INCLUDES
#include <dali-toolkit/internal/text/rendering/view-model.h>
+#include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
namespace Dali
{
// Pointer to the color glyph if there is one.
const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>( data.glyphBitmap.buffer ) : NULL;
- // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
- // The format is RGBA8888.
- uint32_t packedColor = 0u;
- uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>( &packedColor );
- *( packedColorBuffer + 2 ) = static_cast<uint8_t>( color->b * 255.f );
- *( packedColorBuffer + 1 ) = static_cast<uint8_t>( color->g * 255.f );
- *packedColorBuffer = static_cast<uint8_t>( color->r * 255.f );
-
// Initial vertical offset.
const int yOffset = data.verticalOffset + position->y;
if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
{
// Do not write out of bounds.
- break;
+ continue;
}
const int verticalOffset = yOffsetIndex * data.width;
if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
{
// Don't write out of bounds.
- break;
+ continue;
}
uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( data.bitmapBuffer.GetBuffer() );
uint32_t packedColorGlyph = *( colorGlyphBuffer + glyphBufferOffset + index );
uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>( &packedColorGlyph );
- if( Typesetter::STYLE_SHADOW == style )
- {
- // The shadow of color glyph needs to have the shadow color.
- *( packedColorGlyphBuffer + 2 ) = static_cast<uint8_t>( color->b * 255.f );
- *( packedColorGlyphBuffer + 1 ) = static_cast<uint8_t>( color->g * 255.f );
- *packedColorGlyphBuffer = static_cast<uint8_t>( color->r * 255.f );
- }
- else
- {
- std::swap( *packedColorGlyphBuffer, *( packedColorGlyphBuffer + 2u ) ); // Swap B and R.
- }
-
// Update the alpha channel.
- if( Typesetter::STYLE_MASK == style )
+ if( Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style ) // Outline not shown for color glyph
{
// Create an alpha mask for color glyph.
*( packedColorGlyphBuffer + 3u ) = 0u;
+ *( packedColorGlyphBuffer + 2u ) = 0u;
+ *( packedColorGlyphBuffer + 1u ) = 0u;
+ *packedColorGlyphBuffer = 0u;
}
else
{
- *( packedColorGlyphBuffer + 3u ) = static_cast<uint8_t>( color->a * static_cast<float>( *( packedColorGlyphBuffer + 3u ) ) );
+ uint8_t colorAlpha = static_cast<uint8_t>( color->a * static_cast<float>( *( packedColorGlyphBuffer + 3u ) ) );
+ *( packedColorGlyphBuffer + 3u ) = colorAlpha;
+
+ if( Typesetter::STYLE_SHADOW == style )
+ {
+ // The shadow of color glyph needs to have the shadow color.
+ *( packedColorGlyphBuffer + 2u ) = static_cast<uint8_t>( color->b * colorAlpha );
+ *( packedColorGlyphBuffer + 1u ) = static_cast<uint8_t>( color->g * colorAlpha );
+ *packedColorGlyphBuffer = static_cast<uint8_t>( color->r * colorAlpha );
+ }
+ else
+ {
+ std::swap( *packedColorGlyphBuffer, *( packedColorGlyphBuffer + 2u ) ); // Swap B and R.
+
+ *( packedColorGlyphBuffer + 2u ) = ( *( packedColorGlyphBuffer + 2u ) * colorAlpha / 255 );
+ *( packedColorGlyphBuffer + 1u ) = ( *( packedColorGlyphBuffer + 1u ) * colorAlpha / 255 );
+ *packedColorGlyphBuffer = ( *( packedColorGlyphBuffer ) * colorAlpha / 255 );
+ }
}
// Set the color into the final pixel buffer.
}
else
{
+ // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
+ // The format is RGBA8888.
+ uint32_t packedColor = 0u;
+ uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>( &packedColor );
+
// Update the alpha channel.
const uint8_t alpha = *( data.glyphBitmap.buffer + glyphBufferOffset + index );
uint32_t& currentColor = *( bitmapBuffer + verticalOffset + xOffsetIndex );
uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>( ¤tColor );
- uint8_t currentAlpha = *( packedCurrentColorBuffer + 3u );
- uint8_t newAlpha = static_cast<uint8_t>( color->a * static_cast<float>( alpha ) );
-
// For any pixel overlapped with the pixel in previous glyphs, make sure we don't
// overwrite a previous bigger alpha with a smaller alpha (in order to avoid
// semi-transparent gaps between joint glyphs with overlapped pixels, which could
// happen, for example, in the RTL text when we copy glyphs from right to left).
- *( packedColorBuffer + 3u ) = std::max( currentAlpha, newAlpha );
+ uint8_t currentAlpha = *( packedCurrentColorBuffer + 3u );
+ currentAlpha = std::max( currentAlpha, alpha );
+
+ // Color is pre-muliplied with its alpha.
+ *( packedColorBuffer + 3u ) = static_cast<uint8_t>( color->a * currentAlpha );
+ *( packedColorBuffer + 2u ) = static_cast<uint8_t>( color->b * currentAlpha );
+ *( packedColorBuffer + 1u ) = static_cast<uint8_t>( color->g * currentAlpha );
+ *( packedColorBuffer ) = static_cast<uint8_t>( color->r * currentAlpha );
// Set the color into the final pixel buffer.
currentColor = packedColor;
}
else
{
+ // Whether the given glyph is a color one.
+ const bool isColorGlyph = Pixel::BGRA8888 == data.glyphBitmap.format;
+
// Initial vertical offset.
const int yOffset = data.verticalOffset + position->y;
if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
{
// Do not write out of bounds.
- break;
+ continue;
}
const int verticalOffset = yOffsetIndex * data.width;
if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
{
// Don't write out of bounds.
- break;
+ continue;
}
uint8_t* bitmapBuffer = reinterpret_cast< uint8_t* >( data.bitmapBuffer.GetBuffer() );
- // Update the alpha channel.
- const uint8_t alpha = *( data.glyphBitmap.buffer + glyphBufferOffset + index );
-
- // Copy non-transparent pixels only
- if ( alpha > 0u )
+ if ( !isColorGlyph )
{
- // Check alpha of overlapped pixels
- uint8_t& currentAlpha = *( bitmapBuffer + verticalOffset + xOffsetIndex );
- uint8_t newAlpha = static_cast<uint8_t>( color->a * static_cast<float>( alpha ) );
-// printf("y: %d, x: %d: alpha: %u, currentAlpha: %u, newAlpha: %u, a: %u\n", yOffsetIndex, xOffsetIndex, alpha, currentAlpha, newAlpha, std::max( currentAlpha, newAlpha ) );
-
- // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
- // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
- // semi-transparent gaps between joint glyphs with overlapped pixels, which could
- // happen, for example, in the RTL text when we copy glyphs from right to left).
- *( bitmapBuffer + verticalOffset + xOffsetIndex ) = std::max( currentAlpha, newAlpha );
+ // Update the alpha channel.
+ const uint8_t alpha = *( data.glyphBitmap.buffer + glyphBufferOffset + index );
+
+ // Copy non-transparent pixels only
+ if ( alpha > 0u )
+ {
+ // Check alpha of overlapped pixels
+ uint8_t& currentAlpha = *( bitmapBuffer + verticalOffset + xOffsetIndex );
+
+ // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
+ // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
+ // semi-transparent gaps between joint glyphs with overlapped pixels, which could
+ // happen, for example, in the RTL text when we copy glyphs from right to left).
+ *( bitmapBuffer + verticalOffset + xOffsetIndex ) = std::max( currentAlpha, alpha );
+ }
}
}
}
return mModel;
}
-PixelData Typesetter::Render( const Vector2& size, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat )
+PixelData Typesetter::Render( const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat )
{
// @todo. This initial implementation for a TextLabel has only one visible page.
// Retrieves the layout size.
const Size& layoutSize = mModel->GetLayoutSize();
+ const float outlineWidth = mModel->GetOutlineWidth();
+
+ // Set the offset for the horizontal alignment according to the text direction and outline width.
+ int penX = 0;
+
+ switch( mModel->GetHorizontalAlignment() )
+ {
+ case HorizontalAlignment::BEGIN:
+ {
+ // No offset to add.
+ break;
+ }
+ case HorizontalAlignment::CENTER:
+ {
+ penX += ( textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT ) ? -outlineWidth : outlineWidth;
+ break;
+ }
+ case HorizontalAlignment::END:
+ {
+ penX += ( textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT ) ? -outlineWidth * 2.0f : outlineWidth * 2.0f;
+ break;
+ }
+ }
+
// Set the offset for the vertical alignment.
- int penY = 0u;
+ int penY = 0;
switch( mModel->GetVerticalAlignment() )
{
- case Layout::VERTICAL_ALIGN_TOP:
+ case VerticalAlignment::TOP:
{
// No offset to add.
break;
}
- case Layout::VERTICAL_ALIGN_CENTER:
+ case VerticalAlignment::CENTER:
{
penY = static_cast<int>( 0.5f * ( size.height - layoutSize.height ) );
break;
}
- case Layout::VERTICAL_ALIGN_BOTTOM:
+ case VerticalAlignment::BOTTOM:
{
penY = static_cast<int>( size.height - layoutSize.height );
break;
}
}
+ // 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,
if( RENDER_MASK == behaviour )
{
// Generate the image buffer as an alpha mask for color glyphs.
- imageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penY, 0u, numberOfGlyphs - 1 );
+ imageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, numberOfGlyphs - 1 );
}
else if( RENDER_NO_TEXT == behaviour )
{
else
{
// Generate the image buffer for the text with no style.
- imageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penY, 0u, numberOfGlyphs -1 );
+ 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
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, penY, 0u, numberOfGlyphs - 1 );
+ 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 );
if ( underlineEnabled )
{
// Create the image buffer for underline
- Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penY, 0u, numberOfGlyphs - 1 );
+ 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
return pixelData;
}
-Devel::PixelBuffer Typesetter::CreateImageBuffer( const unsigned int bufferWidth, const unsigned int bufferHeight, Typesetter::Style style, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int verticalOffset, GlyphIndex fromGlyphIndex, GlyphIndex toGlyphIndex )
+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 )
{
// Retrieve lines, glyphs, positions and colors from the view model.
const Length modelNumberOfLines = mModel->GetNumberOfLines();
glyphData.width = bufferWidth;
glyphData.height = bufferHeight;
glyphData.bitmapBuffer = Devel::PixelBuffer::New( bufferWidth, bufferHeight, pixelFormat );
+ glyphData.horizontalOffset = 0;
if ( Pixel::RGBA8888 == pixelFormat )
{
// Sets the horizontal offset of the line.
glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>( line.alignmentOffset );
+ glyphData.horizontalOffset += horizontalOffset;
// Increases the vertical offset with the line's ascender.
glyphData.verticalOffset += static_cast<int>( line.ascender );
- if ( style == Typesetter::STYLE_SHADOW )
+ // Include line spacing after first line
+ if( lineIndex > 0u )
+ {
+ glyphData.verticalOffset += static_cast<int>( line.lineSpacing );
+ }
+
+ // Retrieves the glyph's outline width
+ float outlineWidth = mModel->GetOutlineWidth();
+
+ if( style == Typesetter::STYLE_OUTLINE )
+ {
+ glyphData.horizontalOffset -= outlineWidth;
+ if( lineIndex == 0u )
+ {
+ // Only need to add the vertical outline offset for the first line
+ glyphData.verticalOffset -= outlineWidth;
+ }
+ }
+ else if ( style == Typesetter::STYLE_SHADOW )
{
const Vector2& shadowOffset = mModel->GetShadowOffset();
- glyphData.horizontalOffset += shadowOffset.x;
+ glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
+
if ( lineIndex == 0u )
{
- // Only need to add the vertical shadow offset for once
- glyphData.verticalOffset += shadowOffset.y;
+ // Only need to add the vertical shadow offset for first line
+ glyphData.verticalOffset += shadowOffset.y - outlineWidth;
}
}
// Retrieves the glyph's color.
const ColorIndex colorIndex = *( colorIndexBuffer + glyphIndex );
- const Vector4* color;
+ Vector4 color;
if ( style == Typesetter::STYLE_SHADOW )
{
- color = &( mModel->GetShadowColor() );
+ color = mModel->GetShadowColor();
+ }
+ else if ( style == Typesetter::STYLE_OUTLINE )
+ {
+ color = mModel->GetOutlineColor();
}
else
{
- color = ( useDefaultColor || ( 0u == colorIndex ) ) ? &defaultColor : colorsBuffer + ( colorIndex - 1u );
+ color = ( useDefaultColor || ( 0u == colorIndex ) ) ? defaultColor : *( colorsBuffer + ( colorIndex - 1u ) );
}
+ // Premultiply alpha
+ color.r *= color.a;
+ color.g *= color.a;
+ color.b *= color.a;
+
// Retrieves the glyph's bitmap.
glyphData.glyphBitmap.buffer = NULL;
glyphData.glyphBitmap.width = glyphInfo->width; // Desired width and height.
glyphData.glyphBitmap.height = glyphInfo->height;
- fontClient.CreateBitmap( glyphInfo->fontId,
- glyphInfo->index,
- glyphData.glyphBitmap );
+
+ if( style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW )
+ {
+ // Don't render outline for other styles
+ outlineWidth = 0.0f;
+ }
+ if( style != Typesetter::STYLE_UNDERLINE )
+ {
+ fontClient.CreateBitmap( glyphInfo->fontId,
+ glyphInfo->index,
+ glyphData.glyphBitmap,
+ outlineWidth );
+ }
+
// Sets the glyph's bitmap into the bitmap of the whole text.
if( NULL != glyphData.glyphBitmap.buffer )
{
TypesetGlyph( glyphData,
position,
- color,
+ &color,
style,
pixelFormat);
// delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
for( unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineThickness; y++ )
{
- if( ( y < 0 ) || ( y > bufferHeight - 1 ) )
+ if( y > bufferHeight - 1 )
{
// Do not write out of bounds.
break;
for( unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++ )
{
- if( ( x < 0 ) || ( x > bufferWidth - 1 ) )
+ if( x > bufferWidth - 1 )
{
// Do not write out of bounds.
break;
uint8_t* underlinePixelBuffer = reinterpret_cast<uint8_t*>( &underlinePixel );
// Write the underline color to the pixel buffer
- *( underlinePixelBuffer ) = static_cast<uint8_t>( underlineColor.r * 255.f );
- *( underlinePixelBuffer + 1u ) = static_cast<uint8_t>( underlineColor.g * 255.f );
- *( underlinePixelBuffer + 2u ) = static_cast<uint8_t>( underlineColor.b * 255.f );
- *( underlinePixelBuffer + 3u ) = static_cast<uint8_t>( underlineColor.a * 255.f );
+ uint8_t colorAlpha = static_cast< uint8_t >( underlineColor.a * 255.f );
+ *( underlinePixelBuffer + 3u ) = colorAlpha;
+ *( underlinePixelBuffer + 2u ) = static_cast< uint8_t >( underlineColor.b * colorAlpha );
+ *( underlinePixelBuffer + 1u ) = static_cast< uint8_t >( underlineColor.g * colorAlpha );
+ *( underlinePixelBuffer ) = static_cast< uint8_t >( underlineColor.r * colorAlpha );
*( bitmapBuffer + y * glyphData.width + x ) = underlinePixel;
}
}
}
+ // Draw the background color from the leftmost glyph to the rightmost glyph
+ if ( style == Typesetter::STYLE_BACKGROUND )
+ {
+ Vector4 backgroundColor = mModel->GetBackgroundColor();
+
+ for( int y = glyphData.verticalOffset + baseline - line.ascender; y < glyphData.verticalOffset + baseline - line.descender; y++ )
+ {
+ if( ( y < 0 ) || ( y > static_cast<int>(bufferHeight - 1) ) )
+ {
+ // Do not write out of bounds.
+ continue;
+ }
+
+ for( int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++ )
+ {
+ if( ( x < 0 ) || ( x > static_cast<int>(bufferWidth - 1) ) )
+ {
+ // Do not write out of bounds.
+ continue;
+ }
+
+ // Always RGBA image for text with styles
+ uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( glyphData.bitmapBuffer.GetBuffer() );
+ uint32_t backgroundPixel = *( bitmapBuffer + y * glyphData.width + x );
+ uint8_t* backgroundPixelBuffer = reinterpret_cast<uint8_t*>( &backgroundPixel );
+
+ // Write the background color to the pixel buffer
+ uint8_t colorAlpha = static_cast< uint8_t >( backgroundColor.a * 255.f );
+ *( backgroundPixelBuffer + 3u ) = colorAlpha;
+ *( backgroundPixelBuffer + 2u ) = static_cast< uint8_t >( backgroundColor.b * colorAlpha );
+ *( backgroundPixelBuffer + 1u ) = static_cast< uint8_t >( backgroundColor.g * colorAlpha );
+ *( backgroundPixelBuffer ) = static_cast< uint8_t >( backgroundColor.r * colorAlpha );
+
+ *( bitmapBuffer + y * glyphData.width + x ) = backgroundPixel;
+ }
+ }
+ }
+
// Increases the vertical offset with the line's descender.
glyphData.verticalOffset += static_cast<int>( -line.descender );
}
// Otherwise, copy pixel from topBuffer to combinedBuffer.
unsigned int alphaBuffer1 = topBuffer[pixelIndex*4+3];
- unsigned int alphaBuffer2 = bottomBuffer[pixelIndex*4+3];
- if ( alphaBuffer1 != 255 || alphaBuffer2 != 255 )
+ if ( alphaBuffer1 != 255 )
{
// At least one pixel is not fully opaque
// "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
- combinedBuffer[pixelIndex*4] = ( topBuffer[pixelIndex*4] * topBuffer[pixelIndex*4+3] / 255 ) + ( bottomBuffer[pixelIndex*4] * bottomBuffer[pixelIndex*4+3] * ( 255 - topBuffer[pixelIndex*4+3] ) / ( 255*255 ) );
- combinedBuffer[pixelIndex*4+1] = ( topBuffer[pixelIndex*4+1] * topBuffer[pixelIndex*4+3] / 255 ) + ( bottomBuffer[pixelIndex*4+1] * bottomBuffer[pixelIndex*4+3] * ( 255 - topBuffer[pixelIndex*4+3] ) / ( 255*255 ) );
- combinedBuffer[pixelIndex*4+2] = ( topBuffer[pixelIndex*4+2] * topBuffer[pixelIndex*4+3] / 255 ) + ( bottomBuffer[pixelIndex*4+2] * bottomBuffer[pixelIndex*4+3] * ( 255 - topBuffer[pixelIndex*4+3] ) / ( 255*255 ) );
+ combinedBuffer[pixelIndex*4] = topBuffer[pixelIndex*4] + ( bottomBuffer[pixelIndex*4] * ( 255 - topBuffer[pixelIndex*4+3] ) / 255 );
+ combinedBuffer[pixelIndex*4+1] = topBuffer[pixelIndex*4+1] + ( bottomBuffer[pixelIndex*4+1] * ( 255 - topBuffer[pixelIndex*4+3] ) / 255 );
+ combinedBuffer[pixelIndex*4+2] = topBuffer[pixelIndex*4+2] + ( bottomBuffer[pixelIndex*4+2] * ( 255 - topBuffer[pixelIndex*4+3] ) / 255 );
combinedBuffer[pixelIndex*4+3] = topBuffer[pixelIndex*4+3] + ( bottomBuffer[pixelIndex*4+3] * ( 255 - topBuffer[pixelIndex*4+3] ) / 255 );
}
else