+ if ( Pixel::RGBA8888 == pixelFormat )
+ {
+ // Whether the given glyph is a color one.
+ 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 )
+ {
+ const int yOffsetIndex = yOffset + lineIndex;
+ if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
+ {
+ // Do not write out of bounds.
+ continue;
+ }
+
+ const int verticalOffset = yOffsetIndex * data.width;
+ const int xOffset = data.horizontalOffset + position->x;
+ const int glyphBufferOffset = lineIndex * static_cast<int>( data.glyphBitmap.width );
+ for( int index = 0, glyphWidth = static_cast<int>( data.glyphBitmap.width ); index < glyphWidth; ++index )
+ {
+ const int xOffsetIndex = xOffset + index;
+ if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
+ {
+ // Don't write out of bounds.
+ continue;
+ }
+
+ if( isColorGlyph )
+ {
+ // Retrieves the color from the color glyph.
+ uint32_t packedColorGlyph = *( colorGlyphBuffer + glyphBufferOffset + index );
+ uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>( &packedColorGlyph );
+
+ // Update the alpha channel.
+ 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
+ {
+ const 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
+ {
+ 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 );
+ }
+ }
+ }
+
+ // Set the color into the final pixel buffer.
+ *( bitmapBuffer + verticalOffset + xOffsetIndex ) = packedColorGlyph;
+ }
+ 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 + glyphPixelSize * ( glyphBufferOffset + index ) + alphaIndex );
+
+ // Copy non-transparent pixels only
+ if ( alpha > 0u )
+ {
+ // Check alpha of overlapped pixels
+ uint32_t& currentColor = *( bitmapBuffer + verticalOffset + xOffsetIndex );
+ uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>( ¤tColor );
+
+ // 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).
+ 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 = 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 )
+ {
+ const int yOffsetIndex = yOffset + lineIndex;
+ if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
+ {
+ // Do not write out of bounds.
+ continue;
+ }
+
+ const int verticalOffset = yOffsetIndex * data.width;
+ const int xOffset = data.horizontalOffset + position->x;
+ const int glyphBufferOffset = lineIndex * static_cast<int>( data.glyphBitmap.width );
+ for( int index = 0, glyphWidth = static_cast<int>( data.glyphBitmap.width ); index < glyphWidth; ++index )
+ {
+ const int xOffsetIndex = xOffset + index;
+ if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
+ {
+ // Don't write out of bounds.
+ continue;
+ }
+
+ if ( !isColorGlyph )
+ {
+ // Update the alpha channel.
+ const uint8_t alpha = *( data.glyphBitmap.buffer + glyphPixelSize * ( glyphBufferOffset + index ) + alphaIndex );
+
+ // 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).
+ currentAlpha = std::max( currentAlpha, alpha );
+ }
+ }
+ }
+ }
+ }
+}
+
+bool IsGlyphUnderlined( GlyphIndex index,
+ const Vector<GlyphRun>& underlineRuns )
+{
+ for( Vector<GlyphRun>::ConstIterator it = underlineRuns.Begin(),
+ endIt = underlineRuns.End();
+ it != endIt;
+ ++it )
+ {
+ const GlyphRun& run = *it;
+
+ if( ( run.glyphIndex <= index ) && ( index < run.glyphIndex + run.numberOfGlyphs ) )
+ {
+ return true;
+ }
+ }
+
+ 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;
+ }