/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 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.
if ( Pixel::RGBA8888 == pixelFormat )
{
// Whether the given glyph is a color one.
- const bool isColorGlyph = Pixel::BGRA8888 == data.glyphBitmap.format;
+ const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
+ const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel( data.glyphBitmap.format );
+ const uint32_t alphaIndex = glyphPixelSize - 1u;
+ const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
// Pointer to the color glyph if there is one.
const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>( data.glyphBitmap.buffer ) : NULL;
// Initial vertical offset.
const int yOffset = data.verticalOffset + position->y;
+ uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( data.bitmapBuffer.GetBuffer() );
+
// Traverse the pixels of the glyph line per line.
for( int lineIndex = 0, glyphHeight = static_cast<int>( data.glyphBitmap.height ); lineIndex < glyphHeight; ++lineIndex )
{
continue;
}
- uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( data.bitmapBuffer.GetBuffer() );
-
if( isColorGlyph )
{
- // Retrieves the color from the color glyph. The format is BGRA8888.
+ // Retrieves the color from the color glyph.
uint32_t packedColorGlyph = *( colorGlyphBuffer + glyphBufferOffset + index );
uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>( &packedColorGlyph );
}
else
{
- uint8_t colorAlpha = static_cast<uint8_t>( color->a * static_cast<float>( *( packedColorGlyphBuffer + 3u ) ) );
+ const uint8_t colorAlpha = static_cast<uint8_t>( color->a * static_cast<float>( *( packedColorGlyphBuffer + 3u ) ) );
*( packedColorGlyphBuffer + 3u ) = colorAlpha;
if( Typesetter::STYLE_SHADOW == style )
}
else
{
- std::swap( *packedColorGlyphBuffer, *( packedColorGlyphBuffer + 2u ) ); // Swap B and R.
+ if( swapChannelsBR )
+ {
+ 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 );
+
+ if( data.glyphBitmap.isColorBitmap )
+ {
+ *( packedColorGlyphBuffer + 2u ) = static_cast<uint8_t>( *( packedColorGlyphBuffer + 2u ) * color->b );
+ *( packedColorGlyphBuffer + 1u ) = static_cast<uint8_t>( *( packedColorGlyphBuffer + 1u ) * color->g );
+ *packedColorGlyphBuffer = static_cast<uint8_t>( *packedColorGlyphBuffer * color->r );
+ }
}
}
uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>( &packedColor );
// Update the alpha channel.
- const uint8_t alpha = *( data.glyphBitmap.buffer + glyphBufferOffset + index );
+ const uint8_t alpha = *( data.glyphBitmap.buffer + glyphPixelSize * ( glyphBufferOffset + index ) + alphaIndex );
// Copy non-transparent pixels only
if ( alpha > 0u )
else
{
// Whether the given glyph is a color one.
- const bool isColorGlyph = Pixel::BGRA8888 == data.glyphBitmap.format;
+ const bool isColorGlyph = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
+ const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel( data.glyphBitmap.format );
+ const uint32_t alphaIndex = glyphPixelSize - 1u;
// Initial vertical offset.
const int yOffset = data.verticalOffset + position->y;
+ uint8_t* bitmapBuffer = reinterpret_cast< uint8_t* >( data.bitmapBuffer.GetBuffer() );
+
// Traverse the pixels of the glyph line per line.
for( int lineIndex = 0, glyphHeight = static_cast<int>( data.glyphBitmap.height ); lineIndex < glyphHeight; ++lineIndex )
{
continue;
}
- uint8_t* bitmapBuffer = reinterpret_cast< uint8_t* >( data.bitmapBuffer.GetBuffer() );
-
if ( !isColorGlyph )
{
// Update the alpha channel.
- const uint8_t alpha = *( data.glyphBitmap.buffer + glyphBufferOffset + index );
+ const uint8_t alpha = *( data.glyphBitmap.buffer + glyphPixelSize * ( glyphBufferOffset + index ) + alphaIndex );
// Copy non-transparent pixels only
if ( alpha > 0u )
// 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 );
+ currentAlpha = std::max( currentAlpha, alpha );
}
}
}
return false;
}
+/// Helper method to fetch the underline metrics for the specified font glyph
+void FetchFontUnderlineMetrics(
+ TextAbstraction::FontClient& fontClient,
+ const GlyphInfo* const glyphInfo,
+ float& currentUnderlinePosition,
+ const float underlineHeight,
+ float& currentUnderlineThickness,
+ float& maxUnderlineThickness,
+ FontId& lastUnderlinedFontId)
+{
+ FontMetrics fontMetrics;
+ fontClient.GetFontMetrics( glyphInfo->fontId, fontMetrics );
+ currentUnderlinePosition = ceil( fabsf( fontMetrics.underlinePosition ) );
+ const float descender = ceil( fabsf( fontMetrics.descender ) );
+
+ if( fabsf( underlineHeight ) < Math::MACHINE_EPSILON_1000 )
+ {
+ currentUnderlineThickness = fontMetrics.underlineThickness;
+
+ // Ensure underline will be at least a pixel high
+ if ( currentUnderlineThickness < 1.0f )
+ {
+ currentUnderlineThickness = 1.0f;
+ }
+ else
+ {
+ currentUnderlineThickness = ceil( currentUnderlineThickness );
+ }
+ }
+
+ // The underline thickness should be the max underline thickness of all glyphs of the line.
+ if ( currentUnderlineThickness > maxUnderlineThickness )
+ {
+ maxUnderlineThickness = currentUnderlineThickness;
+ }
+
+ // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
+ if( currentUnderlinePosition > descender )
+ {
+ currentUnderlinePosition = descender;
+ }
+
+ if( fabsf( currentUnderlinePosition ) < Math::MACHINE_EPSILON_1000 )
+ {
+ // Move offset down by one ( EFL behavior )
+ currentUnderlinePosition = 1.0f;
+ }
+
+ lastUnderlinedFontId = glyphInfo->fontId;
+}
+
+/// Draws the specified color to the pixel buffer
+void WriteColorToPixelBuffer(
+ GlyphData& glyphData,
+ uint32_t* bitmapBuffer,
+ const Vector4& color,
+ const unsigned int x,
+ const unsigned int y)
+{
+ // Always RGBA image for text with styles
+ uint32_t pixel = *( bitmapBuffer + y * glyphData.width + x );
+ uint8_t* pixelBuffer = reinterpret_cast<uint8_t*>( &pixel );
+
+ // Write the color to the pixel buffer
+ uint8_t colorAlpha = static_cast< uint8_t >( color.a * 255.f );
+ *( pixelBuffer + 3u ) = colorAlpha;
+ *( pixelBuffer + 2u ) = static_cast< uint8_t >( color.b * colorAlpha );
+ *( pixelBuffer + 1u ) = static_cast< uint8_t >( color.g * colorAlpha );
+ *( pixelBuffer ) = static_cast< uint8_t >( color.r * colorAlpha );
+
+ *( bitmapBuffer + y * glyphData.width + x ) = pixel;
+}
+
+/// Draws the specified underline color to the buffer
+void DrawUnderline(
+ const Vector4& underlineColor,
+ const unsigned int bufferWidth,
+ const unsigned int bufferHeight,
+ GlyphData& glyphData,
+ const float baseline,
+ const float currentUnderlinePosition,
+ const float maxUnderlineThickness,
+ const float lineExtentLeft,
+ const float lineExtentRight)
+{
+ int underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
+ uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( glyphData.bitmapBuffer.GetBuffer() );
+
+ for( unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineThickness; y++ )
+ {
+ 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 > bufferWidth - 1 )
+ {
+ // Do not write out of bounds.
+ break;
+ }
+
+ WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
+ }
+ }
+}
+
+/// Draws the background color to the buffer
+void DrawBackgroundColor(
+ Vector4 backgroundColor,
+ const unsigned int bufferWidth,
+ const unsigned int bufferHeight,
+ GlyphData& glyphData,
+ const float baseline,
+ const LineRun& line,
+ const float lineExtentLeft,
+ const float lineExtentRight)
+{
+ uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( glyphData.bitmapBuffer.GetBuffer() );
+
+ 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;
+ }
+
+ WriteColorToPixelBuffer(glyphData, bitmapBuffer, backgroundColor, x, y);
+ }
+ }
+}
+
} // namespace
TypesetterPtr Typesetter::New( const ModelInterface* const model )
// Retrieves the layout size.
const Size& layoutSize = mModel->GetLayoutSize();
- const float outlineWidth = mModel->GetOutlineWidth();
+ const int outlineWidth = static_cast<int>( mModel->GetOutlineWidth() );
// Set the offset for the horizontal alignment according to the text direction and outline width.
int penX = 0;
}
case HorizontalAlignment::END:
{
- penX += ( textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT ) ? -outlineWidth * 2.0f : outlineWidth * 2.0f;
+ penX += ( textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT ) ? -outlineWidth * 2 : outlineWidth * 2;
break;
}
}
// Set the offset for the vertical alignment.
- int penY = 0;
+ int penY = 0u;
switch( mModel->GetVerticalAlignment() )
{
case VerticalAlignment::CENTER:
{
penY = static_cast<int>( 0.5f * ( size.height - layoutSize.height ) );
+ penY = penY < 0.f ? 0.f : penY;
break;
}
case VerticalAlignment::BOTTOM:
{
// Generate the outline if enabled
- const float outlineWidth = mModel->GetOutlineWidth();
- if ( outlineWidth > Math::MACHINE_EPSILON_1 )
+ const uint16_t outlineWidth = mModel->GetOutlineWidth();
+ if ( outlineWidth != 0u )
{
// Create the image buffer for outline
Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, numberOfGlyphs -1 );
}
// Retrieves the glyph's outline width
- float outlineWidth = mModel->GetOutlineWidth();
+ float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
if( style == Typesetter::STYLE_OUTLINE )
{
if( underlineGlyph && ( glyphInfo->fontId != lastUnderlinedFontId ) )
{
// We need to fetch fresh font underline metrics
- FontMetrics fontMetrics;
- fontClient.GetFontMetrics( glyphInfo->fontId, fontMetrics );
- currentUnderlinePosition = ceil( fabsf( fontMetrics.underlinePosition ) );
- const float descender = ceil( fabsf( fontMetrics.descender ) );
-
- if( fabsf( underlineHeight ) < Math::MACHINE_EPSILON_1000 )
- {
- currentUnderlineThickness = fontMetrics.underlineThickness;
-
- // Ensure underline will be at least a pixel high
- if ( currentUnderlineThickness < 1.0f )
- {
- currentUnderlineThickness = 1.0f;
- }
- else
- {
- currentUnderlineThickness = ceil( currentUnderlineThickness );
- }
- }
-
- // The underline thickness should be the max underline thickness of all glyphs of the line.
- if ( currentUnderlineThickness > maxUnderlineThickness )
- {
- maxUnderlineThickness = currentUnderlineThickness;
- }
-
- // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
- if( currentUnderlinePosition > descender )
- {
- currentUnderlinePosition = descender;
- }
-
- if( fabsf( currentUnderlinePosition ) < Math::MACHINE_EPSILON_1000 )
- {
- // Move offset down by one ( EFL behavior )
- currentUnderlinePosition = 1.0f;
- }
-
- lastUnderlinedFontId = glyphInfo->fontId;
+ FetchFontUnderlineMetrics(fontClient, glyphInfo, currentUnderlinePosition, underlineHeight, currentUnderlineThickness, maxUnderlineThickness, lastUnderlinedFontId);
} // underline
// Retrieves the glyph's position.
// Don't render outline for other styles
outlineWidth = 0.0f;
}
+
if( style != Typesetter::STYLE_UNDERLINE )
{
fontClient.CreateBitmap( glyphInfo->fontId,
glyphInfo->index,
- glyphInfo->softwareItalic,
- glyphInfo->softwareBold,
+ glyphInfo->isItalicRequired,
+ glyphInfo->isBoldRequired,
glyphData.glyphBitmap,
- outlineWidth );
+ static_cast<int>( outlineWidth ) );
}
-
// Sets the glyph's bitmap into the bitmap of the whole text.
if( NULL != glyphData.glyphBitmap.buffer )
{
+ if ( style == Typesetter::STYLE_OUTLINE )
+ {
+ // Set the position offset for the current glyph
+ glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
+ glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
+ }
+
+ // Set the buffer of the glyph's bitmap into the final bitmap's buffer
TypesetGlyph( glyphData,
position,
&color,
style,
pixelFormat);
+
+ if ( style == Typesetter::STYLE_OUTLINE )
+ {
+ // Reset the position offset for the next glyph
+ glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
+ glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
+ }
+
// delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
delete []glyphData.glyphBitmap.buffer;
glyphData.glyphBitmap.buffer = NULL;
// Draw the underline from the leftmost glyph to the rightmost glyph
if ( thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE )
{
- int underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
-
- for( unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineThickness; y++ )
- {
- 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 > bufferWidth - 1 )
- {
- // Do not write out of bounds.
- break;
- }
-
- // Always RGBA image for text with styles
- uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( glyphData.bitmapBuffer.GetBuffer() );
- uint32_t underlinePixel = *( bitmapBuffer + y * glyphData.width + x );
- uint8_t* underlinePixelBuffer = reinterpret_cast<uint8_t*>( &underlinePixel );
-
- // Write the underline color to the pixel buffer
- 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;
- }
- }
+ DrawUnderline(underlineColor, bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineThickness, lineExtentLeft, lineExtentRight);
}
// 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;
- }
- }
+ DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
}
// Increases the vertical offset with the line's descender.