Text background support for TextLabel
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / rendering / text-typesetter.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/rendering/text-typesetter.h>
20
21 // EXTERNAL INCLUDES
22 #include <memory.h>
23 #include <dali/devel-api/text-abstraction/font-client.h>
24 #include <dali/public-api/common/constants.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/text/rendering/view-model.h>
28 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
29
30 namespace Dali
31 {
32
33 namespace Toolkit
34 {
35
36 namespace Text
37 {
38
39 namespace
40 {
41
42 /**
43  * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
44  */
45 struct GlyphData
46 {
47   Devel::PixelBuffer                           bitmapBuffer;     ///< The buffer of the whole bitmap. The format is RGBA8888.
48   Vector2*                                     position;         ///< The position of the glyph.
49   TextAbstraction::FontClient::GlyphBufferData glyphBitmap;      ///< The glyph's bitmap.
50   unsigned int                                 width;            ///< The bitmap's width.
51   unsigned int                                 height;           ///< The bitmap's height.
52   int                                          horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
53   int                                          verticalOffset;   ///< The vertical offset to be added to the 'y' glyph's position.
54 };
55
56 /**
57  * @brief Sets the glyph's buffer into the bitmap's buffer.
58  *
59  * @param[in] data Struct which contains the glyph's data and the bitmap's data.
60  * @param[in] position The position of the glyph.
61  * @param[in] color The color of the glyph.
62  * @param[in] style The style of the text.
63  * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
64  */
65 void TypesetGlyph( GlyphData& data,
66                    const Vector2* const position,
67                    const Vector4* const color,
68                    Typesetter::Style style,
69                    Pixel::Format pixelFormat )
70 {
71   if( ( 0u == data.glyphBitmap.width ) || ( 0u == data.glyphBitmap.height ) )
72   {
73     // Nothing to do if the width or height of the buffer is zero.
74     return;
75   }
76
77   const int widthMinusOne = static_cast<int>( data.width - 1u );
78   const int heightMinusOne = static_cast<int>( data.height - 1u );
79
80   if ( Pixel::RGBA8888 == pixelFormat )
81   {
82     // Whether the given glyph is a color one.
83     const bool isColorGlyph = Pixel::BGRA8888 == data.glyphBitmap.format;
84
85     // Pointer to the color glyph if there is one.
86     const uint32_t* const colorGlyphBuffer = isColorGlyph ? reinterpret_cast<uint32_t*>( data.glyphBitmap.buffer ) : NULL;
87
88     // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
89     // The format is RGBA8888.
90     uint32_t packedColor = 0u;
91     uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>( &packedColor );
92     *( packedColorBuffer + 2 ) = static_cast<uint8_t>( color->b * 255.f );
93     *( packedColorBuffer + 1 ) = static_cast<uint8_t>( color->g * 255.f );
94       *packedColorBuffer       = static_cast<uint8_t>( color->r * 255.f );
95
96     // Initial vertical offset.
97     const int yOffset = data.verticalOffset + position->y;
98
99     // Traverse the pixels of the glyph line per line.
100     for( int lineIndex = 0, glyphHeight = static_cast<int>( data.glyphBitmap.height ); lineIndex < glyphHeight; ++lineIndex )
101     {
102       const int yOffsetIndex = yOffset + lineIndex;
103       if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
104       {
105         // Do not write out of bounds.
106         continue;
107       }
108
109       const int verticalOffset = yOffsetIndex * data.width;
110       const int xOffset = data.horizontalOffset + position->x;
111       const int glyphBufferOffset = lineIndex * static_cast<int>( data.glyphBitmap.width );
112       for( int index = 0, glyphWidth = static_cast<int>( data.glyphBitmap.width ); index < glyphWidth; ++index )
113       {
114         const int xOffsetIndex = xOffset + index;
115         if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
116         {
117           // Don't write out of bounds.
118           continue;
119         }
120
121         uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( data.bitmapBuffer.GetBuffer() );
122
123         if( isColorGlyph )
124         {
125           // Retrieves the color from the color glyph. The format is BGRA8888.
126           uint32_t packedColorGlyph = *( colorGlyphBuffer + glyphBufferOffset + index );
127           uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>( &packedColorGlyph );
128
129           if( Typesetter::STYLE_SHADOW == style )
130           {
131             // The shadow of color glyph needs to have the shadow color.
132             *( packedColorGlyphBuffer + 2 ) = static_cast<uint8_t>( color->b * 255.f );
133             *( packedColorGlyphBuffer + 1 ) = static_cast<uint8_t>( color->g * 255.f );
134               *packedColorGlyphBuffer       = static_cast<uint8_t>( color->r * 255.f );
135           }
136           else
137           {
138             std::swap( *packedColorGlyphBuffer, *( packedColorGlyphBuffer + 2u ) ); // Swap B and R.
139           }
140
141           // Update the alpha channel.
142           if( Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style ) // Outline not shown for color glyph
143           {
144             // Create an alpha mask for color glyph.
145             *( packedColorGlyphBuffer + 3u ) = 0u;
146           }
147           else
148           {
149             *( packedColorGlyphBuffer + 3u ) = static_cast<uint8_t>( color->a * static_cast<float>( *( packedColorGlyphBuffer + 3u ) ) );
150           }
151
152           // Set the color into the final pixel buffer.
153           *( bitmapBuffer + verticalOffset + xOffsetIndex ) = packedColorGlyph;
154         }
155         else
156         {
157           // Update the alpha channel.
158           const uint8_t alpha = *( data.glyphBitmap.buffer + glyphBufferOffset + index );
159
160           // Copy non-transparent pixels only
161           if ( alpha > 0u )
162           {
163             // Check alpha of overlapped pixels
164             uint32_t& currentColor = *( bitmapBuffer + verticalOffset + xOffsetIndex );
165             uint8_t* packedCurrentColorBuffer = reinterpret_cast<uint8_t*>( &currentColor );
166
167             uint8_t currentAlpha = *( packedCurrentColorBuffer + 3u );
168             uint8_t newAlpha = static_cast<uint8_t>( color->a * static_cast<float>( alpha ) );
169
170             // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
171             // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
172             // semi-transparent gaps between joint glyphs with overlapped pixels, which could
173             // happen, for example, in the RTL text when we copy glyphs from right to left).
174             *( packedColorBuffer + 3u ) = std::max( currentAlpha, newAlpha );
175
176             // Set the color into the final pixel buffer.
177             currentColor = packedColor;
178           }
179         }
180       }
181     }
182   }
183   else
184   {
185     // Whether the given glyph is a color one.
186     const bool isColorGlyph = Pixel::BGRA8888 == data.glyphBitmap.format;
187
188     // Initial vertical offset.
189     const int yOffset = data.verticalOffset + position->y;
190
191     // Traverse the pixels of the glyph line per line.
192     for( int lineIndex = 0, glyphHeight = static_cast<int>( data.glyphBitmap.height ); lineIndex < glyphHeight; ++lineIndex )
193     {
194       const int yOffsetIndex = yOffset + lineIndex;
195       if( ( 0 > yOffsetIndex ) || ( yOffsetIndex > heightMinusOne ) )
196       {
197         // Do not write out of bounds.
198         continue;
199       }
200
201       const int verticalOffset = yOffsetIndex * data.width;
202       const int xOffset = data.horizontalOffset + position->x;
203       const int glyphBufferOffset = lineIndex * static_cast<int>( data.glyphBitmap.width );
204       for( int index = 0, glyphWidth = static_cast<int>( data.glyphBitmap.width ); index < glyphWidth; ++index )
205       {
206         const int xOffsetIndex = xOffset + index;
207         if( ( 0 > xOffsetIndex ) || ( xOffsetIndex > widthMinusOne ) )
208         {
209           // Don't write out of bounds.
210           continue;
211         }
212
213         uint8_t* bitmapBuffer = reinterpret_cast< uint8_t* >( data.bitmapBuffer.GetBuffer() );
214
215         if ( !isColorGlyph )
216         {
217           // Update the alpha channel.
218           const uint8_t alpha = *( data.glyphBitmap.buffer + glyphBufferOffset + index );
219
220           // Copy non-transparent pixels only
221           if ( alpha > 0u )
222           {
223             // Check alpha of overlapped pixels
224             uint8_t& currentAlpha = *( bitmapBuffer + verticalOffset + xOffsetIndex );
225             uint8_t newAlpha = static_cast<uint8_t>( color->a * static_cast<float>( alpha ) );
226
227             // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
228             // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
229             // semi-transparent gaps between joint glyphs with overlapped pixels, which could
230             // happen, for example, in the RTL text when we copy glyphs from right to left).
231             *( bitmapBuffer + verticalOffset + xOffsetIndex ) = std::max( currentAlpha, newAlpha );
232           }
233         }
234       }
235     }
236   }
237 }
238
239 bool IsGlyphUnderlined( GlyphIndex index,
240                          const Vector<GlyphRun>& underlineRuns )
241 {
242   for( Vector<GlyphRun>::ConstIterator it = underlineRuns.Begin(),
243          endIt = underlineRuns.End();
244          it != endIt;
245        ++it )
246   {
247     const GlyphRun& run = *it;
248
249     if( ( run.glyphIndex <= index ) && ( index < run.glyphIndex + run.numberOfGlyphs ) )
250     {
251       return true;
252     }
253   }
254
255   return false;
256 }
257
258 } // namespace
259
260 TypesetterPtr Typesetter::New( const ModelInterface* const model )
261 {
262   return TypesetterPtr( new Typesetter( model ) );
263 }
264
265 ViewModel* Typesetter::GetViewModel()
266 {
267   return mModel;
268 }
269
270 PixelData Typesetter::Render( const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat )
271 {
272   // @todo. This initial implementation for a TextLabel has only one visible page.
273
274   // Elides the text if needed.
275   mModel->ElideGlyphs();
276
277   // Retrieves the layout size.
278   const Size& layoutSize = mModel->GetLayoutSize();
279
280   const float outlineWidth = mModel->GetOutlineWidth();
281
282   // Set the offset for the horizontal alignment according to the text direction and outline width.
283   int penX = 0;
284
285   switch( mModel->GetHorizontalAlignment() )
286   {
287     case HorizontalAlignment::BEGIN:
288     {
289       // No offset to add.
290       break;
291     }
292     case HorizontalAlignment::CENTER:
293     {
294       penX += ( textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT ) ? -outlineWidth : outlineWidth;
295       break;
296     }
297     case HorizontalAlignment::END:
298     {
299       penX += ( textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT ) ? -outlineWidth * 2.0f : outlineWidth * 2.0f;
300       break;
301     }
302   }
303
304   // Set the offset for the vertical alignment.
305   int penY = 0;
306
307   switch( mModel->GetVerticalAlignment() )
308   {
309     case VerticalAlignment::TOP:
310     {
311       // No offset to add.
312       break;
313     }
314     case VerticalAlignment::CENTER:
315     {
316       penY = static_cast<int>( 0.5f * ( size.height - layoutSize.height ) );
317       break;
318     }
319     case VerticalAlignment::BOTTOM:
320     {
321       penY = static_cast<int>( size.height - layoutSize.height );
322       break;
323     }
324   }
325
326   // Calculate vertical line alignment
327   switch( mModel->GetVerticalLineAlignment() )
328   {
329     case DevelText::VerticalLineAlignment::TOP:
330     {
331       break;
332     }
333     case DevelText::VerticalLineAlignment::MIDDLE:
334     {
335       const auto& line = *mModel->GetLines();
336       penY -= line.descender;
337       penY += static_cast<int>(line.lineSpacing*0.5f + line.descender);
338       break;
339     }
340     case DevelText::VerticalLineAlignment::BOTTOM:
341     {
342       const auto& line = *mModel->GetLines();
343       const auto lineHeight = line.ascender + (-line.descender) + line.lineSpacing;
344       penY += static_cast<int>(lineHeight - (line.ascender - line.descender));
345       break;
346     }
347   }
348
349   // Generate the image buffers of the text for each different style first,
350   // then combine all of them together as one final image buffer. We try to
351   // do all of these in CPU only, so that once the final texture is generated,
352   // no calculation is needed in GPU during each frame.
353
354   const unsigned int bufferWidth = static_cast<unsigned int>( size.width );
355   const unsigned int bufferHeight = static_cast<unsigned int>( size.height );
356
357   const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
358   const unsigned int bufferSizeChar = 4u * bufferSizeInt;
359
360   Length numberOfGlyphs = mModel->GetNumberOfGlyphs();
361
362   Devel::PixelBuffer imageBuffer;
363
364   if( RENDER_MASK == behaviour )
365   {
366     // Generate the image buffer as an alpha mask for color glyphs.
367     imageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, numberOfGlyphs - 1 );
368   }
369   else if( RENDER_NO_TEXT == behaviour )
370   {
371     // Generate an empty image buffer so that it can been combined with the image buffers for styles
372     imageBuffer = Devel::PixelBuffer::New( bufferWidth, bufferHeight, Pixel::RGBA8888 );
373     memset( imageBuffer.GetBuffer(), 0u, bufferSizeChar );
374   }
375   else
376   {
377     // Generate the image buffer for the text with no style.
378     imageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, numberOfGlyphs -1 );
379   }
380
381   if ( ( RENDER_NO_STYLES != behaviour ) && ( RENDER_MASK != behaviour ) )
382   {
383
384     // Generate the outline if enabled
385     const float outlineWidth = mModel->GetOutlineWidth();
386     if ( outlineWidth > Math::MACHINE_EPSILON_1 )
387     {
388       // Create the image buffer for outline
389       Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, numberOfGlyphs -1 );
390
391       // Combine the two buffers
392       imageBuffer = CombineImageBuffer( imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight );
393     }
394
395     // @todo. Support shadow and underline for partial text later on.
396
397     // Generate the shadow if enabled
398     const Vector2& shadowOffset = mModel->GetShadowOffset();
399     if ( fabsf( shadowOffset.x ) > Math::MACHINE_EPSILON_1 || fabsf( shadowOffset.y ) > Math::MACHINE_EPSILON_1 )
400     {
401       // Create the image buffer for shadow
402       Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, numberOfGlyphs - 1 );
403
404       // Check whether it will be a soft shadow
405       const float& blurRadius = mModel->GetShadowBlurRadius();
406
407       if ( blurRadius > Math::MACHINE_EPSILON_1 )
408       {
409         shadowImageBuffer.ApplyGaussianBlur( blurRadius );
410       }
411
412       // Combine the two buffers
413       imageBuffer = CombineImageBuffer( imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight );
414     }
415
416     // Generate the underline if enabled
417     const bool underlineEnabled = mModel->IsUnderlineEnabled();
418     if ( underlineEnabled )
419     {
420       // Create the image buffer for underline
421       Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, numberOfGlyphs - 1 );
422
423       // Combine the two buffers
424       imageBuffer = CombineImageBuffer( imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight );
425     }
426
427     // Generate the background if enabled
428     const bool backgroundEnabled = mModel->IsBackgroundEnabled();
429     if ( backgroundEnabled )
430     {
431       Devel::PixelBuffer backgroundImageBuffer = CreateImageBuffer( bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, numberOfGlyphs -1 );
432
433       // Combine the two buffers
434       imageBuffer = CombineImageBuffer( imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight );
435     }
436   }
437
438   // Create the final PixelData for the combined image buffer
439   PixelData pixelData = Devel::PixelBuffer::Convert( imageBuffer );
440
441   return pixelData;
442 }
443
444 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 )
445 {
446   // Retrieve lines, glyphs, positions and colors from the view model.
447   const Length modelNumberOfLines = mModel->GetNumberOfLines();
448   const LineRun* const modelLinesBuffer = mModel->GetLines();
449   const Length numberOfGlyphs = mModel->GetNumberOfGlyphs();
450   const GlyphInfo* const glyphsBuffer = mModel->GetGlyphs();
451   const Vector2* const positionBuffer = mModel->GetLayout();
452   const Vector4* const colorsBuffer = mModel->GetColors();
453   const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices();
454
455   // Whether to use the default color.
456   const bool useDefaultColor = ( NULL == colorsBuffer );
457   const Vector4& defaultColor = mModel->GetDefaultColor();
458
459   // Create and initialize the pixel buffer.
460   GlyphData glyphData;
461   glyphData.verticalOffset = verticalOffset;
462   glyphData.width = bufferWidth;
463   glyphData.height = bufferHeight;
464   glyphData.bitmapBuffer = Devel::PixelBuffer::New( bufferWidth, bufferHeight, pixelFormat );
465   glyphData.horizontalOffset = 0;
466
467   if ( Pixel::RGBA8888 == pixelFormat )
468   {
469     const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
470     const unsigned int bufferSizeChar = 4u * bufferSizeInt;
471     memset( glyphData.bitmapBuffer.GetBuffer(), 0u, bufferSizeChar );
472   }
473   else
474   {
475     memset( glyphData.bitmapBuffer.GetBuffer(), 0, bufferWidth * bufferHeight );
476   }
477
478   // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
479   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
480
481   // Traverses the lines of the text.
482   for( LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex )
483   {
484     const LineRun& line = *( modelLinesBuffer + lineIndex );
485
486     // Sets the horizontal offset of the line.
487     glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>( line.alignmentOffset );
488     glyphData.horizontalOffset += horizontalOffset;
489
490     // Increases the vertical offset with the line's ascender.
491     glyphData.verticalOffset += static_cast<int>( line.ascender );
492
493     // Include line spacing after first line
494     if( lineIndex > 0u )
495     {
496       glyphData.verticalOffset += static_cast<int>( line.lineSpacing );
497     }
498
499     // Retrieves the glyph's outline width
500     float outlineWidth = mModel->GetOutlineWidth();
501
502     if( style == Typesetter::STYLE_OUTLINE )
503     {
504       glyphData.horizontalOffset -= outlineWidth;
505       if( lineIndex == 0u )
506       {
507         // Only need to add the vertical outline offset for the first line
508         glyphData.verticalOffset -= outlineWidth;
509       }
510     }
511     else if ( style == Typesetter::STYLE_SHADOW )
512     {
513       const Vector2& shadowOffset = mModel->GetShadowOffset();
514       glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
515
516       if ( lineIndex == 0u )
517       {
518         // Only need to add the vertical shadow offset for first line
519         glyphData.verticalOffset += shadowOffset.y - outlineWidth;
520       }
521     }
522
523     const bool underlineEnabled = mModel->IsUnderlineEnabled();
524     const Vector4& underlineColor = mModel->GetUnderlineColor();
525     const float underlineHeight = mModel->GetUnderlineHeight();
526
527     // Get the underline runs.
528     const Length numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
529     Vector<GlyphRun> underlineRuns;
530     underlineRuns.Resize( numberOfUnderlineRuns );
531     mModel->GetUnderlineRuns( underlineRuns.Begin(), 0u, numberOfUnderlineRuns );
532
533     bool thereAreUnderlinedGlyphs = false;
534
535     float currentUnderlinePosition = 0.0f;
536     float currentUnderlineThickness = underlineHeight;
537     float maxUnderlineThickness = currentUnderlineThickness;
538
539     FontId lastUnderlinedFontId = 0;
540
541     float lineExtentLeft = bufferWidth;
542     float lineExtentRight = 0.0f;
543     float baseline = 0.0f;
544
545     // Traverses the glyphs of the line.
546     const GlyphIndex endGlyphIndex = std::min( numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs );
547     for( GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex )
548     {
549       if ( glyphIndex < fromGlyphIndex || glyphIndex > toGlyphIndex )
550       {
551         // Ignore any glyph that out of the specified range
552         continue;
553       }
554
555       // Retrieve the glyph's info.
556       const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
557
558       if( ( glyphInfo->width < Math::MACHINE_EPSILON_1000 ) ||
559           ( glyphInfo->height < Math::MACHINE_EPSILON_1000 ) )
560       {
561         // Nothing to do if the glyph's width or height is zero.
562         continue;
563       }
564
565       const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined( glyphIndex, underlineRuns );
566       thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
567
568       // Are we still using the same fontId as previous
569       if( underlineGlyph && ( glyphInfo->fontId != lastUnderlinedFontId ) )
570       {
571         // We need to fetch fresh font underline metrics
572         FontMetrics fontMetrics;
573         fontClient.GetFontMetrics( glyphInfo->fontId, fontMetrics );
574         currentUnderlinePosition = ceil( fabsf( fontMetrics.underlinePosition ) );
575         const float descender = ceil( fabsf( fontMetrics.descender ) );
576
577         if( fabsf( underlineHeight ) < Math::MACHINE_EPSILON_1000 )
578         {
579           currentUnderlineThickness = fontMetrics.underlineThickness;
580
581           // Ensure underline will be at least a pixel high
582           if ( currentUnderlineThickness < 1.0f )
583           {
584             currentUnderlineThickness = 1.0f;
585           }
586           else
587           {
588             currentUnderlineThickness = ceil( currentUnderlineThickness );
589           }
590         }
591
592         // The underline thickness should be the max underline thickness of all glyphs of the line.
593         if ( currentUnderlineThickness > maxUnderlineThickness )
594         {
595           maxUnderlineThickness = currentUnderlineThickness;
596         }
597
598         // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
599         if( currentUnderlinePosition > descender )
600         {
601           currentUnderlinePosition = descender;
602         }
603
604         if( fabsf( currentUnderlinePosition ) < Math::MACHINE_EPSILON_1000 )
605         {
606           // Move offset down by one ( EFL behavior )
607           currentUnderlinePosition = 1.0f;
608         }
609
610         lastUnderlinedFontId = glyphInfo->fontId;
611       } // underline
612
613       // Retrieves the glyph's position.
614       const Vector2* const position = positionBuffer + glyphIndex;
615       if ( baseline < position->y + glyphInfo->yBearing )
616       {
617         baseline = position->y + glyphInfo->yBearing;
618       }
619
620       // Calculate the positions of leftmost and rightmost glyphs in the current line
621       if ( position->x < lineExtentLeft)
622       {
623         lineExtentLeft = position->x;
624       }
625
626       if ( position->x + glyphInfo->width > lineExtentRight)
627       {
628         lineExtentRight = position->x + glyphInfo->width;
629       }
630
631       // Retrieves the glyph's color.
632       const ColorIndex colorIndex = *( colorIndexBuffer + glyphIndex );
633
634       const Vector4* color;
635       if ( style == Typesetter::STYLE_SHADOW )
636       {
637         color = &( mModel->GetShadowColor() );
638       }
639       else if ( style == Typesetter::STYLE_OUTLINE )
640       {
641         color = &( mModel->GetOutlineColor() );
642       }
643       else
644       {
645         color = ( useDefaultColor || ( 0u == colorIndex ) ) ? &defaultColor : colorsBuffer + ( colorIndex - 1u );
646       }
647
648       // Retrieves the glyph's bitmap.
649       glyphData.glyphBitmap.buffer = NULL;
650       glyphData.glyphBitmap.width = glyphInfo->width;   // Desired width and height.
651       glyphData.glyphBitmap.height = glyphInfo->height;
652
653       if( style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW )
654       {
655         // Don't render outline for other styles
656         outlineWidth = 0.0f;
657       }
658       if( style != Typesetter::STYLE_UNDERLINE )
659       {
660         fontClient.CreateBitmap( glyphInfo->fontId,
661                                  glyphInfo->index,
662                                  glyphData.glyphBitmap,
663                                  outlineWidth );
664       }
665
666
667       // Sets the glyph's bitmap into the bitmap of the whole text.
668       if( NULL != glyphData.glyphBitmap.buffer )
669       {
670         TypesetGlyph( glyphData,
671                       position,
672                       color,
673                       style,
674                       pixelFormat);
675         // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
676         delete []glyphData.glyphBitmap.buffer;
677         glyphData.glyphBitmap.buffer = NULL;
678       }
679     }
680
681     // Draw the underline from the leftmost glyph to the rightmost glyph
682     if ( thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE )
683     {
684       int underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
685
686       for( unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineThickness; y++ )
687       {
688         if( y > bufferHeight - 1 )
689         {
690           // Do not write out of bounds.
691           break;
692         }
693
694         for( unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++ )
695         {
696           if( x > bufferWidth - 1 )
697           {
698             // Do not write out of bounds.
699             break;
700           }
701
702           // Always RGBA image for text with styles
703           uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( glyphData.bitmapBuffer.GetBuffer() );
704           uint32_t underlinePixel = *( bitmapBuffer + y * glyphData.width + x );
705           uint8_t* underlinePixelBuffer = reinterpret_cast<uint8_t*>( &underlinePixel );
706
707           // Write the underline color to the pixel buffer
708           *( underlinePixelBuffer ) = static_cast<uint8_t>( underlineColor.r * 255.f );
709           *( underlinePixelBuffer + 1u ) = static_cast<uint8_t>( underlineColor.g * 255.f );
710           *( underlinePixelBuffer + 2u ) = static_cast<uint8_t>( underlineColor.b * 255.f );
711           *( underlinePixelBuffer + 3u ) = static_cast<uint8_t>( underlineColor.a * 255.f );
712
713           *( bitmapBuffer + y * glyphData.width + x ) = underlinePixel;
714         }
715       }
716     }
717
718     // Draw the background color from the leftmost glyph to the rightmost glyph
719     if ( style == Typesetter::STYLE_BACKGROUND )
720     {
721       Vector4 backgroundColor = mModel->GetBackgroundColor();
722
723       for( int y = glyphData.verticalOffset + baseline - line.ascender; y < glyphData.verticalOffset + baseline - line.descender; y++ )
724       {
725         if( ( y < 0 ) || ( y > static_cast<int>(bufferHeight - 1) ) )
726         {
727           // Do not write out of bounds.
728           continue;
729         }
730
731         for( int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++ )
732         {
733           if( ( x < 0 ) || ( x > static_cast<int>(bufferWidth - 1) ) )
734           {
735             // Do not write out of bounds.
736             continue;
737           }
738
739           // Always RGBA image for text with styles
740           uint32_t* bitmapBuffer = reinterpret_cast< uint32_t* >( glyphData.bitmapBuffer.GetBuffer() );
741           uint32_t backgroundPixel = *( bitmapBuffer + y * glyphData.width + x );
742           uint8_t* backgroundPixelBuffer = reinterpret_cast<uint8_t*>( &backgroundPixel );
743
744           // Write the background color to the pixel buffer
745           *( backgroundPixelBuffer ) = static_cast<uint8_t>( backgroundColor.r * 255.f );
746           *( backgroundPixelBuffer + 1u ) = static_cast<uint8_t>( backgroundColor.g * 255.f );
747           *( backgroundPixelBuffer + 2u ) = static_cast<uint8_t>( backgroundColor.b * 255.f );
748           *( backgroundPixelBuffer + 3u ) = static_cast<uint8_t>( backgroundColor.a * 255.f );
749
750           *( bitmapBuffer + y * glyphData.width + x ) = backgroundPixel;
751         }
752       }
753     }
754
755     // Increases the vertical offset with the line's descender.
756     glyphData.verticalOffset += static_cast<int>( -line.descender );
757   }
758
759   return glyphData.bitmapBuffer;
760 }
761
762 Devel::PixelBuffer Typesetter::CombineImageBuffer( Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight )
763 {
764   unsigned char* topBuffer = topPixelBuffer.GetBuffer();
765   unsigned char* bottomBuffer = bottomPixelBuffer.GetBuffer();
766
767   Devel::PixelBuffer combinedPixelBuffer;
768
769   if ( topBuffer == NULL && bottomBuffer == NULL )
770   {
771     // Nothing to do if both buffers are empty.
772     return combinedPixelBuffer;
773   }
774
775   if ( topBuffer == NULL )
776   {
777     // Nothing to do if topBuffer is empty.
778     return bottomPixelBuffer;
779   }
780
781   if ( bottomBuffer == NULL )
782   {
783     // Nothing to do if bottomBuffer is empty.
784     return topPixelBuffer;
785   }
786
787   // Always combine two RGBA images
788   const unsigned int bufferSizeInt = bufferWidth * bufferHeight;
789   const unsigned int bufferSizeChar = 4u * bufferSizeInt;
790
791   combinedPixelBuffer = Devel::PixelBuffer::New( bufferWidth, bufferHeight, Pixel::RGBA8888 );
792   uint8_t* combinedBuffer = reinterpret_cast< uint8_t* >( combinedPixelBuffer.GetBuffer() );
793   memset( combinedBuffer, 0u, bufferSizeChar );
794
795   for (unsigned int pixelIndex = 0; pixelIndex < bufferSizeInt; pixelIndex++)
796   {
797     // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
798     // Otherwise, copy pixel from topBuffer to combinedBuffer.
799
800     unsigned int alphaBuffer1 = topBuffer[pixelIndex*4+3];
801     unsigned int alphaBuffer2 = bottomBuffer[pixelIndex*4+3];
802
803     if ( alphaBuffer1 != 255 || alphaBuffer2 != 255 )
804     {
805       // At least one pixel is not fully opaque
806       // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
807       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 ) );
808       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 ) );
809       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 ) );
810       combinedBuffer[pixelIndex*4+3] = topBuffer[pixelIndex*4+3] + ( bottomBuffer[pixelIndex*4+3] * ( 255 - topBuffer[pixelIndex*4+3] ) / 255 );
811     }
812     else
813     {
814       // Copy the pixel from topBuffer to combinedBuffer
815       combinedBuffer[pixelIndex*4] = topBuffer[pixelIndex*4];
816       combinedBuffer[pixelIndex*4+1] = topBuffer[pixelIndex*4+1];
817       combinedBuffer[pixelIndex*4+2] = topBuffer[pixelIndex*4+2];
818       combinedBuffer[pixelIndex*4+3] = topBuffer[pixelIndex*4+3];
819     }
820   }
821
822   return combinedPixelBuffer;
823 }
824
825 Typesetter::Typesetter( const ModelInterface* const model )
826 : mModel( new ViewModel( model ) )
827 {
828 }
829
830 Typesetter::~Typesetter()
831 {
832   delete mModel;
833 }
834
835 } // namespace Text
836
837 } // namespace Toolkit
838
839 } // namespace Dali