Merge "Extending Text Styles - Adding Dashed/Double Underline" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / rendering / text-typesetter.cpp
1 /*
2  * Copyright (c) 2021 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 <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/public-api/common/constants.h>
24 #include <memory.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
28 #include <dali-toolkit/internal/text/rendering/view-model.h>
29
30 namespace Dali
31 {
32 namespace Toolkit
33 {
34 namespace Text
35 {
36 namespace
37 {
38 const float HALF(0.5f);
39 /**
40  * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
41  */
42 struct GlyphData
43 {
44   Devel::PixelBuffer                           bitmapBuffer;     ///< The buffer of the whole bitmap. The format is RGBA8888.
45   Vector2*                                     position;         ///< The position of the glyph.
46   TextAbstraction::FontClient::GlyphBufferData glyphBitmap;      ///< The glyph's bitmap.
47   unsigned int                                 width;            ///< The bitmap's width.
48   unsigned int                                 height;           ///< The bitmap's height.
49   int                                          horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
50   int                                          verticalOffset;   ///< The vertical offset to be added to the 'y' glyph's position.
51 };
52
53 /**
54  * @brief Sets the glyph's buffer into the bitmap's buffer.
55  *
56  * @param[in] data Struct which contains the glyph's data and the bitmap's data.
57  * @param[in] position The position of the glyph.
58  * @param[in] color The color of the glyph.
59  * @param[in] style The style of the text.
60  * @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).
61  */
62 void TypesetGlyph(GlyphData&           data,
63                   const Vector2* const position,
64                   const Vector4* const color,
65                   Typesetter::Style    style,
66                   Pixel::Format        pixelFormat)
67 {
68   if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
69   {
70     // Nothing to do if the width or height of the buffer is zero.
71     return;
72   }
73
74   const int widthMinusOne  = static_cast<int>(data.width - 1u);
75   const int heightMinusOne = static_cast<int>(data.height - 1u);
76
77   if(Pixel::RGBA8888 == pixelFormat)
78   {
79     // Whether the given glyph is a color one.
80     const bool     isColorGlyph   = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
81     const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
82     const uint32_t alphaIndex     = glyphPixelSize - 1u;
83     const bool     swapChannelsBR = 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     // Initial vertical offset.
89     const int yOffset = data.verticalOffset + position->y;
90
91     uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
92
93     // Traverse the pixels of the glyph line per line.
94     for(int lineIndex = 0, glyphHeight = static_cast<int>(data.glyphBitmap.height); lineIndex < glyphHeight; ++lineIndex)
95     {
96       const int yOffsetIndex = yOffset + lineIndex;
97       if((0 > yOffsetIndex) || (yOffsetIndex > heightMinusOne))
98       {
99         // Do not write out of bounds.
100         continue;
101       }
102
103       const int verticalOffset    = yOffsetIndex * data.width;
104       const int xOffset           = data.horizontalOffset + position->x;
105       const int glyphBufferOffset = lineIndex * static_cast<int>(data.glyphBitmap.width);
106       for(int index = 0, glyphWidth = static_cast<int>(data.glyphBitmap.width); index < glyphWidth; ++index)
107       {
108         const int xOffsetIndex = xOffset + index;
109         if((0 > xOffsetIndex) || (xOffsetIndex > widthMinusOne))
110         {
111           // Don't write out of bounds.
112           continue;
113         }
114
115         if(isColorGlyph)
116         {
117           // Retrieves the color from the color glyph.
118           uint32_t packedColorGlyph       = *(colorGlyphBuffer + glyphBufferOffset + index);
119           uint8_t* packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
120
121           // Update the alpha channel.
122           if(Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style) // Outline not shown for color glyph
123           {
124             // Create an alpha mask for color glyph.
125             *(packedColorGlyphBuffer + 3u) = 0u;
126             *(packedColorGlyphBuffer + 2u) = 0u;
127             *(packedColorGlyphBuffer + 1u) = 0u;
128             *packedColorGlyphBuffer        = 0u;
129           }
130           else
131           {
132             const uint8_t colorAlpha       = static_cast<uint8_t>(color->a * static_cast<float>(*(packedColorGlyphBuffer + 3u)));
133             *(packedColorGlyphBuffer + 3u) = colorAlpha;
134
135             if(Typesetter::STYLE_SHADOW == style)
136             {
137               // The shadow of color glyph needs to have the shadow color.
138               *(packedColorGlyphBuffer + 2u) = static_cast<uint8_t>(color->b * colorAlpha);
139               *(packedColorGlyphBuffer + 1u) = static_cast<uint8_t>(color->g * colorAlpha);
140               *packedColorGlyphBuffer        = static_cast<uint8_t>(color->r * colorAlpha);
141             }
142             else
143             {
144               if(swapChannelsBR)
145               {
146                 std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
147               }
148
149               *(packedColorGlyphBuffer + 2u) = (*(packedColorGlyphBuffer + 2u) * colorAlpha / 255);
150               *(packedColorGlyphBuffer + 1u) = (*(packedColorGlyphBuffer + 1u) * colorAlpha / 255);
151               *packedColorGlyphBuffer        = (*(packedColorGlyphBuffer)*colorAlpha / 255);
152
153               if(data.glyphBitmap.isColorBitmap)
154               {
155                 *(packedColorGlyphBuffer + 2u) = static_cast<uint8_t>(*(packedColorGlyphBuffer + 2u) * color->b);
156                 *(packedColorGlyphBuffer + 1u) = static_cast<uint8_t>(*(packedColorGlyphBuffer + 1u) * color->g);
157                 *packedColorGlyphBuffer        = static_cast<uint8_t>(*packedColorGlyphBuffer * color->r);
158               }
159             }
160           }
161
162           // Set the color into the final pixel buffer.
163           *(bitmapBuffer + verticalOffset + xOffsetIndex) = packedColorGlyph;
164         }
165         else
166         {
167           // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
168           // The format is RGBA8888.
169           uint32_t packedColor       = 0u;
170           uint8_t* packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
171
172           // Update the alpha channel.
173           const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
174
175           // Copy non-transparent pixels only
176           if(alpha > 0u)
177           {
178             // Check alpha of overlapped pixels
179             uint32_t& currentColor             = *(bitmapBuffer + verticalOffset + xOffsetIndex);
180             uint8_t*  packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(&currentColor);
181
182             // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
183             // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
184             // semi-transparent gaps between joint glyphs with overlapped pixels, which could
185             // happen, for example, in the RTL text when we copy glyphs from right to left).
186             uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
187             currentAlpha         = std::max(currentAlpha, alpha);
188
189             // Color is pre-muliplied with its alpha.
190             *(packedColorBuffer + 3u) = static_cast<uint8_t>(color->a * currentAlpha);
191             *(packedColorBuffer + 2u) = static_cast<uint8_t>(color->b * currentAlpha);
192             *(packedColorBuffer + 1u) = static_cast<uint8_t>(color->g * currentAlpha);
193             *(packedColorBuffer)      = static_cast<uint8_t>(color->r * currentAlpha);
194
195             // Set the color into the final pixel buffer.
196             currentColor = packedColor;
197           }
198         }
199       }
200     }
201   }
202   else
203   {
204     // Whether the given glyph is a color one.
205     const bool     isColorGlyph   = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
206     const uint32_t glyphPixelSize = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
207     const uint32_t alphaIndex     = glyphPixelSize - 1u;
208
209     // Initial vertical offset.
210     const int yOffset = data.verticalOffset + position->y;
211
212     uint8_t* bitmapBuffer = reinterpret_cast<uint8_t*>(data.bitmapBuffer.GetBuffer());
213
214     // Traverse the pixels of the glyph line per line.
215     for(int lineIndex = 0, glyphHeight = static_cast<int>(data.glyphBitmap.height); lineIndex < glyphHeight; ++lineIndex)
216     {
217       const int yOffsetIndex = yOffset + lineIndex;
218       if((0 > yOffsetIndex) || (yOffsetIndex > heightMinusOne))
219       {
220         // Do not write out of bounds.
221         continue;
222       }
223
224       const int verticalOffset    = yOffsetIndex * data.width;
225       const int xOffset           = data.horizontalOffset + position->x;
226       const int glyphBufferOffset = lineIndex * static_cast<int>(data.glyphBitmap.width);
227       for(int index = 0, glyphWidth = static_cast<int>(data.glyphBitmap.width); index < glyphWidth; ++index)
228       {
229         const int xOffsetIndex = xOffset + index;
230         if((0 > xOffsetIndex) || (xOffsetIndex > widthMinusOne))
231         {
232           // Don't write out of bounds.
233           continue;
234         }
235
236         if(!isColorGlyph)
237         {
238           // Update the alpha channel.
239           const uint8_t alpha = *(data.glyphBitmap.buffer + glyphPixelSize * (glyphBufferOffset + index) + alphaIndex);
240
241           // Copy non-transparent pixels only
242           if(alpha > 0u)
243           {
244             // Check alpha of overlapped pixels
245             uint8_t& currentAlpha = *(bitmapBuffer + verticalOffset + xOffsetIndex);
246
247             // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
248             // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
249             // semi-transparent gaps between joint glyphs with overlapped pixels, which could
250             // happen, for example, in the RTL text when we copy glyphs from right to left).
251             currentAlpha = std::max(currentAlpha, alpha);
252           }
253         }
254       }
255     }
256   }
257 }
258
259 bool IsGlyphUnderlined(GlyphIndex              index,
260                        const Vector<GlyphRun>& underlineRuns)
261 {
262   for(Vector<GlyphRun>::ConstIterator it    = underlineRuns.Begin(),
263                                       endIt = underlineRuns.End();
264       it != endIt;
265       ++it)
266   {
267     const GlyphRun& run = *it;
268
269     if((run.glyphIndex <= index) && (index < run.glyphIndex + run.numberOfGlyphs))
270     {
271       return true;
272     }
273   }
274
275   return false;
276 }
277
278 /// Helper method to fetch the underline metrics for the specified font glyph
279 void FetchFontDecorationlinesMetrics(
280   TextAbstraction::FontClient& fontClient,
281   const GlyphInfo* const       glyphInfo,
282   float&                       currentUnderlinePosition,
283   const float                  underlineHeight,
284   float&                       currentUnderlineThickness,
285   float&                       maxUnderlineThickness,
286   FontId&                      lastlinedFontId,
287   const float                  strikethroughHeight,
288   float&                       currentStrikethroughThickness,
289   float&                       maxStrikethroughThickness)
290 {
291   FontMetrics fontMetrics;
292   fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
293   currentUnderlinePosition = ceil(fabsf(fontMetrics.underlinePosition));
294   const float descender    = ceil(fabsf(fontMetrics.descender));
295
296   if(fabsf(underlineHeight) < Math::MACHINE_EPSILON_1000)
297   {
298     currentUnderlineThickness = fontMetrics.underlineThickness;
299
300     // Ensure underline will be at least a pixel high
301     if(currentUnderlineThickness < 1.0f)
302     {
303       currentUnderlineThickness = 1.0f;
304     }
305     else
306     {
307       currentUnderlineThickness = ceil(currentUnderlineThickness);
308     }
309   }
310
311   if(fabsf(strikethroughHeight) < Math::MACHINE_EPSILON_1000)
312   {
313     // Ensure strikethrough will be at least a pixel high
314     if(currentStrikethroughThickness < 1.0f)
315     {
316       currentStrikethroughThickness = 1.0f;
317     }
318     else
319     {
320       currentStrikethroughThickness = ceil(currentStrikethroughThickness);
321     }
322   }
323
324   // The underline thickness should be the max underline thickness of all glyphs of the line.
325   if(currentUnderlineThickness > maxUnderlineThickness)
326   {
327     maxUnderlineThickness = currentUnderlineThickness;
328   }
329
330   // The strikethrough thickness should be the max strikethrough thickness of all glyphs of the line.
331   if(currentStrikethroughThickness > maxStrikethroughThickness)
332   {
333     maxStrikethroughThickness = currentStrikethroughThickness;
334   }
335
336   // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
337   if(currentUnderlinePosition > descender)
338   {
339     currentUnderlinePosition = descender;
340   }
341
342   if(fabsf(currentUnderlinePosition) < Math::MACHINE_EPSILON_1000)
343   {
344     // Move offset down by one ( EFL behavior )
345     currentUnderlinePosition = 1.0f;
346   }
347
348   lastlinedFontId = glyphInfo->fontId;
349 }
350
351 /// Draws the specified color to the pixel buffer
352 void WriteColorToPixelBuffer(
353   GlyphData&         glyphData,
354   uint32_t*          bitmapBuffer,
355   const Vector4&     color,
356   const unsigned int x,
357   const unsigned int y)
358 {
359   // Always RGBA image for text with styles
360   uint32_t pixel       = *(bitmapBuffer + y * glyphData.width + x);
361   uint8_t* pixelBuffer = reinterpret_cast<uint8_t*>(&pixel);
362
363   // Write the color to the pixel buffer
364   uint8_t colorAlpha  = static_cast<uint8_t>(color.a * 255.f);
365   *(pixelBuffer + 3u) = colorAlpha;
366   *(pixelBuffer + 2u) = static_cast<uint8_t>(color.b * colorAlpha);
367   *(pixelBuffer + 1u) = static_cast<uint8_t>(color.g * colorAlpha);
368   *(pixelBuffer)      = static_cast<uint8_t>(color.r * colorAlpha);
369
370   *(bitmapBuffer + y * glyphData.width + x) = pixel;
371 }
372
373 /// Draws the specified underline color to the buffer
374 void DrawUnderline(
375   const Vector4&              underlineColor,
376   const unsigned int          bufferWidth,
377   const unsigned int          bufferHeight,
378   GlyphData&                  glyphData,
379   const float                 baseline,
380   const float                 currentUnderlinePosition,
381   const float                 maxUnderlineThickness,
382   const float                 lineExtentLeft,
383   const float                 lineExtentRight,
384   const Text::Underline::Type underlineType,
385   const float                 dashedUnderlineWidth,
386   const float                 dashedUnderlineGap,
387   const LineRun&              line)
388 {
389   int       underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
390   uint32_t* bitmapBuffer     = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
391
392   for(unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineThickness; y++)
393   {
394     if(y > bufferHeight - 1)
395     {
396       // Do not write out of bounds.
397       break;
398     }
399     if(underlineType == Text::Underline::DASHED)
400     {
401       float dashWidth = dashedUnderlineWidth;
402       float dashGap   = 0;
403
404       for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
405       {
406         if(x > bufferWidth - 1)
407         {
408           // Do not write out of bounds.
409           break;
410         }
411         if(dashGap == 0 && dashWidth > 0)
412         {
413           WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
414           dashWidth--;
415         }
416         else if(dashGap < dashedUnderlineGap)
417         {
418           dashGap++;
419         }
420         else
421         {
422           //reset
423           dashWidth = dashedUnderlineWidth;
424           dashGap   = 0;
425         }
426       }
427     }
428     else
429     {
430       for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
431       {
432         if(x > bufferWidth - 1)
433         {
434           // Do not write out of bounds.
435           break;
436         }
437         WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
438       }
439     }
440   }
441   if(underlineType == Text::Underline::DOUBLE)
442   {
443     int secondUnderlineYOffset = glyphData.verticalOffset - line.descender - maxUnderlineThickness;
444     for(unsigned int y = secondUnderlineYOffset; y < secondUnderlineYOffset + maxUnderlineThickness; y++)
445     {
446       if(y > bufferHeight - 1)
447       {
448         // Do not write out of bounds.
449         break;
450       }
451       for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
452       {
453         if(x > bufferWidth - 1)
454         {
455           // Do not write out of bounds.
456           break;
457         }
458         WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
459       }
460     }
461   }
462 }
463
464 /// Draws the background color to the buffer
465 void DrawBackgroundColor(
466   Vector4            backgroundColor,
467   const unsigned int bufferWidth,
468   const unsigned int bufferHeight,
469   GlyphData&         glyphData,
470   const float        baseline,
471   const LineRun&     line,
472   const float        lineExtentLeft,
473   const float        lineExtentRight)
474 {
475   uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
476
477   for(int y = glyphData.verticalOffset + baseline - line.ascender; y < glyphData.verticalOffset + baseline - line.descender; y++)
478   {
479     if((y < 0) || (y > static_cast<int>(bufferHeight - 1)))
480     {
481       // Do not write out of bounds.
482       continue;
483     }
484
485     for(int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
486     {
487       if((x < 0) || (x > static_cast<int>(bufferWidth - 1)))
488       {
489         // Do not write out of bounds.
490         continue;
491       }
492
493       WriteColorToPixelBuffer(glyphData, bitmapBuffer, backgroundColor, x, y);
494     }
495   }
496 }
497
498 Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuffer& buffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, int horizontalOffset, int verticalOffset)
499 {
500   // Retrieve lines, glyphs, positions and colors from the view model.
501   const Length            modelNumberOfLines           = model->GetNumberOfLines();
502   const LineRun* const    modelLinesBuffer             = model->GetLines();
503   const Length            numberOfGlyphs               = model->GetNumberOfGlyphs();
504   const GlyphInfo* const  glyphsBuffer                 = model->GetGlyphs();
505   const Vector2* const    positionBuffer               = model->GetLayout();
506   const Vector4* const    backgroundColorsBuffer       = model->GetBackgroundColors();
507   const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
508
509   // Create and initialize the pixel buffer.
510   GlyphData glyphData;
511   glyphData.verticalOffset   = verticalOffset;
512   glyphData.width            = bufferWidth;
513   glyphData.height           = bufferHeight;
514   glyphData.bitmapBuffer     = buffer;
515   glyphData.horizontalOffset = 0;
516
517   ColorIndex prevBackgroundColorIndex = 0;
518   ColorIndex backgroundColorIndex     = 0;
519
520   // Traverses the lines of the text.
521   for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
522   {
523     const LineRun& line = *(modelLinesBuffer + lineIndex);
524
525     // Sets the horizontal offset of the line.
526     glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
527     glyphData.horizontalOffset += horizontalOffset;
528
529     // Increases the vertical offset with the line's ascender.
530     glyphData.verticalOffset += static_cast<int>(line.ascender);
531
532     // Include line spacing after first line
533     if(lineIndex > 0u)
534     {
535       glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
536     }
537
538     float left     = bufferWidth;
539     float right    = 0.0f;
540     float baseline = 0.0f;
541
542     // Traverses the glyphs of the line.
543     const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
544     for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
545     {
546       // Retrieve the glyph's info.
547       const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
548
549       if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
550          (glyphInfo->height < Math::MACHINE_EPSILON_1000))
551       {
552         // Nothing to do if default background color, the glyph's width or height is zero.
553         continue;
554       }
555
556       backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
557
558       if((backgroundColorIndex != prevBackgroundColorIndex) &&
559          (prevBackgroundColorIndex != 0u))
560       {
561         const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
562         DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
563       }
564
565       if(backgroundColorIndex == 0u)
566       {
567         prevBackgroundColorIndex = backgroundColorIndex;
568         //if background color is the default do nothing
569         continue;
570       }
571
572       // Retrieves the glyph's position.
573       const Vector2* const position = positionBuffer + glyphIndex;
574
575       if(baseline < position->y + glyphInfo->yBearing)
576       {
577         baseline = position->y + glyphInfo->yBearing;
578       }
579
580       // Calculate the positions of leftmost and rightmost glyphs in the current line
581       if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
582       {
583         left = position->x - glyphInfo->xBearing;
584       }
585
586       if(position->x + glyphInfo->width > right)
587       {
588         right = position->x - glyphInfo->xBearing + glyphInfo->advance;
589       }
590
591       prevBackgroundColorIndex = backgroundColorIndex;
592     }
593
594     //draw last background at line end if not default
595     if(backgroundColorIndex != 0u)
596     {
597       const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
598       DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
599     }
600
601     // Increases the vertical offset with the line's descender.
602     glyphData.verticalOffset += static_cast<int>(-line.descender);
603   }
604
605   return glyphData.bitmapBuffer;
606 }
607
608 /// Draws the specified strikethrough color to the buffer
609 void DrawStrikethrough(
610   const Vector4&     strikethroughColor,
611   const unsigned int bufferWidth,
612   const unsigned int bufferHeight,
613   GlyphData&         glyphData,
614   const float        baseline,
615   const LineRun&     line,
616   const float        maxStrikethroughThickness,
617   const float        lineExtentLeft,
618   const float        lineExtentRight,
619   float              strikethroughStartingYPosition)
620 {
621   uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
622
623   for(unsigned int y = strikethroughStartingYPosition; y < strikethroughStartingYPosition + maxStrikethroughThickness; y++)
624   {
625     if(y > bufferHeight - 1)
626     {
627       // Do not write out of bounds.
628       break;
629     }
630
631     for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
632     {
633       if(x > bufferWidth - 1)
634       {
635         // Do not write out of bounds.
636         break;
637       }
638
639       WriteColorToPixelBuffer(glyphData, bitmapBuffer, strikethroughColor, x, y);
640     }
641   }
642 }
643
644 } // namespace
645
646 TypesetterPtr Typesetter::New(const ModelInterface* const model)
647 {
648   return TypesetterPtr(new Typesetter(model));
649 }
650
651 ViewModel* Typesetter::GetViewModel()
652 {
653   return mModel;
654 }
655
656 Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, const unsigned int bufferHeight, Pixel::Format pixelFormat)
657 {
658   Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
659
660   if(Pixel::RGBA8888 == pixelFormat)
661   {
662     const unsigned int bufferSizeInt  = bufferWidth * bufferHeight;
663     const unsigned int bufferSizeChar = 4u * bufferSizeInt;
664     memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
665   }
666   else
667   {
668     memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
669   }
670
671   return imageBuffer;
672 }
673
674 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
675 {
676   // @todo. This initial implementation for a TextLabel has only one visible page.
677
678   // Elides the text if needed.
679   mModel->ElideGlyphs();
680
681   // Retrieves the layout size.
682   const Size& layoutSize = mModel->GetLayoutSize();
683
684   const int outlineWidth = static_cast<int>(mModel->GetOutlineWidth());
685
686   // Set the offset for the horizontal alignment according to the text direction and outline width.
687   int penX = 0;
688
689   switch(mModel->GetHorizontalAlignment())
690   {
691     case HorizontalAlignment::BEGIN:
692     {
693       // No offset to add.
694       break;
695     }
696     case HorizontalAlignment::CENTER:
697     {
698       penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
699       break;
700     }
701     case HorizontalAlignment::END:
702     {
703       penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
704       break;
705     }
706   }
707
708   // Set the offset for the vertical alignment.
709   int penY = 0u;
710
711   switch(mModel->GetVerticalAlignment())
712   {
713     case VerticalAlignment::TOP:
714     {
715       // No offset to add.
716       break;
717     }
718     case VerticalAlignment::CENTER:
719     {
720       penY = static_cast<int>(0.5f * (size.height - layoutSize.height));
721       penY = penY < 0.f ? 0.f : penY;
722       break;
723     }
724     case VerticalAlignment::BOTTOM:
725     {
726       penY = static_cast<int>(size.height - layoutSize.height);
727       break;
728     }
729   }
730
731   // Calculate vertical line alignment
732   switch(mModel->GetVerticalLineAlignment())
733   {
734     case DevelText::VerticalLineAlignment::TOP:
735     {
736       break;
737     }
738     case DevelText::VerticalLineAlignment::MIDDLE:
739     {
740       const auto& line = *mModel->GetLines();
741       penY -= line.descender;
742       penY += static_cast<int>(line.lineSpacing * 0.5f + line.descender);
743       break;
744     }
745     case DevelText::VerticalLineAlignment::BOTTOM:
746     {
747       const auto& line       = *mModel->GetLines();
748       const auto  lineHeight = line.ascender + (-line.descender) + line.lineSpacing;
749       penY += static_cast<int>(lineHeight - (line.ascender - line.descender));
750       break;
751     }
752   }
753
754   // Generate the image buffers of the text for each different style first,
755   // then combine all of them together as one final image buffer. We try to
756   // do all of these in CPU only, so that once the final texture is generated,
757   // no calculation is needed in GPU during each frame.
758
759   const unsigned int bufferWidth  = static_cast<unsigned int>(size.width);
760   const unsigned int bufferHeight = static_cast<unsigned int>(size.height);
761
762   const unsigned int bufferSizeInt  = bufferWidth * bufferHeight;
763   const unsigned int bufferSizeChar = 4u * bufferSizeInt;
764
765   //Elided text in ellipsis at START could start on index greater than 0
766   auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
767   auto endIndexOfGlyphs   = mModel->GetEndIndexOfElidedGlyphs();
768
769   Devel::PixelBuffer imageBuffer;
770
771   if(RENDER_MASK == behaviour)
772   {
773     // Generate the image buffer as an alpha mask for color glyphs.
774     imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
775   }
776   else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
777   {
778     // Generate an empty image buffer so that it can been combined with the image buffers for styles
779     imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
780     memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
781   }
782   else
783   {
784     // Generate the image buffer for the text with no style.
785     imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
786   }
787
788   if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
789   {
790     // Generate the outline if enabled
791     const uint16_t outlineWidth = mModel->GetOutlineWidth();
792     if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
793     {
794       // Create the image buffer for outline
795       Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
796
797       // Combine the two buffers
798       imageBuffer = CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight);
799     }
800
801     // @todo. Support shadow and underline for partial text later on.
802
803     // Generate the shadow if enabled
804     const Vector2& shadowOffset = mModel->GetShadowOffset();
805     if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
806     {
807       // Create the image buffer for shadow
808       Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
809
810       // Check whether it will be a soft shadow
811       const float& blurRadius = mModel->GetShadowBlurRadius();
812
813       if(blurRadius > Math::MACHINE_EPSILON_1)
814       {
815         shadowImageBuffer.ApplyGaussianBlur(blurRadius);
816       }
817
818       // Combine the two buffers
819       imageBuffer = CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight);
820     }
821
822     // Generate the underline if enabled
823     const bool underlineEnabled = mModel->IsUnderlineEnabled();
824     if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
825     {
826       // Create the image buffer for underline
827       Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
828
829       // Combine the two buffers
830       imageBuffer = CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
831     }
832
833     // Generate the background if enabled
834     const bool backgroundEnabled   = mModel->IsBackgroundEnabled();
835     const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
836     if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
837     {
838       Devel::PixelBuffer backgroundImageBuffer;
839
840       if(backgroundEnabled)
841       {
842         backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
843       }
844       else
845       {
846         backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
847       }
848
849       if(backgroundMarkupSet)
850       {
851         DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
852       }
853
854       // Combine the two buffers
855       imageBuffer = CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight);
856     }
857
858     // Generate the strikethrough if enabled
859     const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
860     if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
861     {
862       // Create the image buffer for strikethrough
863       Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
864
865       // Combine the two buffers
866       imageBuffer = CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight);
867     }
868
869     // Markup-Processor
870
871     imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
872   }
873
874   // Create the final PixelData for the combined image buffer
875   PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
876
877   return pixelData;
878 }
879
880 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)
881 {
882   // Retrieve lines, glyphs, positions and colors from the view model.
883   const Length            modelNumberOfLines = mModel->GetNumberOfLines();
884   const LineRun* const    modelLinesBuffer   = mModel->GetLines();
885   const GlyphInfo* const  glyphsBuffer       = mModel->GetGlyphs();
886   const Vector2* const    positionBuffer     = mModel->GetLayout();
887   const Vector4* const    colorsBuffer       = mModel->GetColors();
888   const ColorIndex* const colorIndexBuffer   = mModel->GetColorIndices();
889   const GlyphInfo*        hyphens            = mModel->GetHyphens();
890   const Length*           hyphenIndices      = mModel->GetHyphenIndices();
891   const Length            hyphensCount       = mModel->GetHyphensCount();
892
893   // Elided text info. Indices according to elided text and Ellipsis position.
894   const auto startIndexOfGlyphs              = mModel->GetStartIndexOfElidedGlyphs();
895   const auto endIndexOfGlyphs                = mModel->GetEndIndexOfElidedGlyphs();
896   const auto firstMiddleIndexOfElidedGlyphs  = mModel->GetFirstMiddleIndexOfElidedGlyphs();
897   const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
898   const auto ellipsisPosition                = mModel->GetEllipsisPosition();
899
900   // Whether to use the default color.
901   const bool     useDefaultColor = (NULL == colorsBuffer);
902   const Vector4& defaultColor    = mModel->GetDefaultColor();
903
904   // Create and initialize the pixel buffer.
905   GlyphData glyphData;
906   glyphData.verticalOffset   = verticalOffset;
907   glyphData.width            = bufferWidth;
908   glyphData.height           = bufferHeight;
909   glyphData.bitmapBuffer     = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
910   glyphData.horizontalOffset = 0;
911
912   // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
913   TextAbstraction::FontClient fontClient  = TextAbstraction::FontClient::Get();
914   Length                      hyphenIndex = 0;
915
916   // Traverses the lines of the text.
917   for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
918   {
919     const LineRun& line = *(modelLinesBuffer + lineIndex);
920
921     // Sets the horizontal offset of the line.
922     glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
923     glyphData.horizontalOffset += horizontalOffset;
924
925     // Increases the vertical offset with the line's ascender.
926     glyphData.verticalOffset += static_cast<int>(line.ascender);
927
928     // Include line spacing after first line
929     if(lineIndex > 0u)
930     {
931       glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
932     }
933
934     // Retrieves the glyph's outline width
935     float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
936
937     if(style == Typesetter::STYLE_OUTLINE)
938     {
939       glyphData.horizontalOffset -= outlineWidth;
940       if(lineIndex == 0u)
941       {
942         // Only need to add the vertical outline offset for the first line
943         glyphData.verticalOffset -= outlineWidth;
944       }
945     }
946     else if(style == Typesetter::STYLE_SHADOW)
947     {
948       const Vector2& shadowOffset = mModel->GetShadowOffset();
949       glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
950
951       if(lineIndex == 0u)
952       {
953         // Only need to add the vertical shadow offset for first line
954         glyphData.verticalOffset += shadowOffset.y - outlineWidth;
955       }
956     }
957
958     const bool                  underlineEnabled     = mModel->IsUnderlineEnabled();
959     const Vector4&              underlineColor       = mModel->GetUnderlineColor();
960     const float                 underlineHeight      = mModel->GetUnderlineHeight();
961     const Text::Underline::Type underlineType        = mModel->GetUnderlineType();
962     const float                 dashedUnderlineWidth = mModel->GetDashedUnderlineWidth();
963     const float                 dashedUnderlineGap   = mModel->GetDashedUnderlineGap();
964
965     const bool     strikethroughEnabled = mModel->IsStrikethroughEnabled();
966     const Vector4& strikethroughColor   = mModel->GetStrikethroughColor();
967     const float    strikethroughHeight  = mModel->GetStrikethroughHeight();
968
969     // Get the underline runs.
970     const Length     numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
971     Vector<GlyphRun> underlineRuns;
972     underlineRuns.Resize(numberOfUnderlineRuns);
973     mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
974
975     bool thereAreUnderlinedGlyphs = false;
976     bool strikethroughGlyphsExist = false;
977
978     float currentUnderlinePosition       = 0.0f;
979     float currentUnderlineThickness      = underlineHeight;
980     float maxUnderlineThickness          = currentUnderlineThickness;
981     float currentStrikethroughThickness  = strikethroughHeight;
982     float maxStrikethroughThickness      = currentStrikethroughThickness;
983     float strikethroughStartingYPosition = 0.0f;
984
985     FontId lastUnderlinedFontId = 0;
986
987     float lineExtentLeft  = bufferWidth;
988     float lineExtentRight = 0.0f;
989     float baseline        = 0.0f;
990     bool  addHyphen       = false;
991
992     // Traverses the glyphs of the line.
993     const GlyphIndex startGlyphIndex = std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs);
994     GlyphIndex       endGlyphIndex   = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
995     endGlyphIndex                    = std::min(endGlyphIndex, endIndexOfGlyphs);
996
997     for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
998     {
999       if(glyphIndex < fromGlyphIndex || glyphIndex > toGlyphIndex)
1000       {
1001         // Ignore any glyph that out of the specified range
1002         continue;
1003       }
1004
1005       //To handle START case of ellipsis, the first glyph has been shifted
1006       //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1007       GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1008
1009       //To handle MIDDLE case of ellipsis, the first glyph in the second half of line has been shifted and skip the removed glyph from middle.
1010       if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1011       {
1012         if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1013            glyphIndex < secondMiddleIndexOfElidedGlyphs)
1014         {
1015           // Ignore any glyph that removed for MIDDLE ellipsis
1016           continue;
1017         }
1018         if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1019         {
1020           elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1021         }
1022       }
1023
1024       // Retrieve the glyph's info.
1025       const GlyphInfo* glyphInfo;
1026
1027       if(addHyphen && hyphens)
1028       {
1029         glyphInfo = hyphens + hyphenIndex;
1030         hyphenIndex++;
1031       }
1032       else
1033       {
1034         glyphInfo = glyphsBuffer + elidedGlyphIndex;
1035       }
1036
1037       if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1038          (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1039       {
1040         // Nothing to do if the glyph's width or height is zero.
1041         continue;
1042       }
1043
1044       const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns);
1045       thereAreUnderlinedGlyphs  = thereAreUnderlinedGlyphs || underlineGlyph;
1046
1047       strikethroughGlyphsExist = strikethroughGlyphsExist || strikethroughEnabled;
1048
1049       // Are we still using the same fontId as previous
1050       if((strikethroughEnabled || underlineGlyph) && (glyphInfo->fontId != lastUnderlinedFontId))
1051       {
1052         // We need to fetch fresh font underline metrics
1053         FetchFontDecorationlinesMetrics(fontClient, glyphInfo, currentUnderlinePosition, underlineHeight, currentUnderlineThickness, maxUnderlineThickness, lastUnderlinedFontId, strikethroughHeight, currentStrikethroughThickness, maxStrikethroughThickness);
1054       } // underline
1055
1056       // Retrieves the glyph's position.
1057       Vector2 position = *(positionBuffer + elidedGlyphIndex);
1058
1059       if(addHyphen)
1060       {
1061         GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1062         position.x         = position.x + tempInfo.advance - tempInfo.xBearing + glyphInfo->xBearing;
1063         position.y         = -glyphInfo->yBearing;
1064       }
1065
1066       if(baseline < position.y + glyphInfo->yBearing)
1067       {
1068         baseline = position.y + glyphInfo->yBearing;
1069       }
1070
1071       // Calculate the positions of leftmost and rightmost glyphs in the current line
1072       if(position.x < lineExtentLeft)
1073       {
1074         lineExtentLeft = position.x;
1075       }
1076
1077       if(position.x + glyphInfo->width > lineExtentRight)
1078       {
1079         lineExtentRight = position.x + glyphInfo->width;
1080       }
1081
1082       // Retrieves the glyph's color.
1083       const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1084
1085       Vector4 color;
1086       if(style == Typesetter::STYLE_SHADOW)
1087       {
1088         color = mModel->GetShadowColor();
1089       }
1090       else if(style == Typesetter::STYLE_OUTLINE)
1091       {
1092         color = mModel->GetOutlineColor();
1093       }
1094       else
1095       {
1096         color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1097       }
1098
1099       // Premultiply alpha
1100       color.r *= color.a;
1101       color.g *= color.a;
1102       color.b *= color.a;
1103
1104       // Retrieves the glyph's bitmap.
1105       glyphData.glyphBitmap.buffer = NULL;
1106       glyphData.glyphBitmap.width  = glyphInfo->width; // Desired width and height.
1107       glyphData.glyphBitmap.height = glyphInfo->height;
1108
1109       if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1110       {
1111         // Don't render outline for other styles
1112         outlineWidth = 0.0f;
1113       }
1114
1115       if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1116       {
1117         fontClient.CreateBitmap(glyphInfo->fontId,
1118                                 glyphInfo->index,
1119                                 glyphInfo->isItalicRequired,
1120                                 glyphInfo->isBoldRequired,
1121                                 glyphData.glyphBitmap,
1122                                 static_cast<int>(outlineWidth));
1123       }
1124
1125       // Sets the glyph's bitmap into the bitmap of the whole text.
1126       if(NULL != glyphData.glyphBitmap.buffer)
1127       {
1128         if(style == Typesetter::STYLE_OUTLINE)
1129         {
1130           // Set the position offset for the current glyph
1131           glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1132           glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1133         }
1134
1135         // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1136         TypesetGlyph(glyphData,
1137                      &position,
1138                      &color,
1139                      style,
1140                      pixelFormat);
1141
1142         if(style == Typesetter::STYLE_OUTLINE)
1143         {
1144           // Reset the position offset for the next glyph
1145           glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1146           glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1147         }
1148
1149         // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1150         delete[] glyphData.glyphBitmap.buffer;
1151         glyphData.glyphBitmap.buffer = NULL;
1152       }
1153
1154       if(hyphenIndices)
1155       {
1156         while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1157         {
1158           hyphenIndex++;
1159         }
1160
1161         addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1162         if(addHyphen)
1163         {
1164           glyphIndex--;
1165         }
1166       }
1167     }
1168
1169     // Draw the underline from the leftmost glyph to the rightmost glyph
1170     if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1171     {
1172       DrawUnderline(underlineColor, bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineThickness, lineExtentLeft, lineExtentRight, underlineType, dashedUnderlineWidth, dashedUnderlineGap, line);
1173     }
1174
1175     // Draw the background color from the leftmost glyph to the rightmost glyph
1176     if(style == Typesetter::STYLE_BACKGROUND)
1177     {
1178       DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1179     }
1180
1181     // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1182     if(strikethroughGlyphsExist && style == Typesetter::STYLE_STRIKETHROUGH)
1183     {
1184       //TODO : The currently implemented strikethrough creates a strikethrough on the line level. We need to create different strikethroughs the case of glyphs with different sizes.
1185       strikethroughStartingYPosition = (glyphData.verticalOffset + baseline + currentUnderlinePosition) - ((line.ascender) * HALF); // Since Free Type font doesn't contain the strikethrough-position property, strikethrough position will be calculated by moving the underline position upwards by half the value of the line height.
1186       DrawStrikethrough(strikethroughColor, bufferWidth, bufferHeight, glyphData, baseline, line, maxStrikethroughThickness, lineExtentLeft, lineExtentRight, strikethroughStartingYPosition);
1187     }
1188
1189     // Increases the vertical offset with the line's descender.
1190     glyphData.verticalOffset += static_cast<int>(-line.descender);
1191   }
1192
1193   return glyphData.bitmapBuffer;
1194 }
1195
1196 Devel::PixelBuffer Typesetter::CombineImageBuffer(Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight)
1197 {
1198   unsigned char* topBuffer    = topPixelBuffer.GetBuffer();
1199   unsigned char* bottomBuffer = bottomPixelBuffer.GetBuffer();
1200
1201   Devel::PixelBuffer combinedPixelBuffer;
1202
1203   if(topBuffer == NULL && bottomBuffer == NULL)
1204   {
1205     // Nothing to do if both buffers are empty.
1206     return combinedPixelBuffer;
1207   }
1208
1209   if(topBuffer == NULL)
1210   {
1211     // Nothing to do if topBuffer is empty.
1212     return bottomPixelBuffer;
1213   }
1214
1215   if(bottomBuffer == NULL)
1216   {
1217     // Nothing to do if bottomBuffer is empty.
1218     return topPixelBuffer;
1219   }
1220
1221   // Always combine two RGBA images
1222   const unsigned int bufferSizeInt  = bufferWidth * bufferHeight;
1223   const unsigned int bufferSizeChar = 4u * bufferSizeInt;
1224
1225   combinedPixelBuffer     = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1226   uint8_t* combinedBuffer = reinterpret_cast<uint8_t*>(combinedPixelBuffer.GetBuffer());
1227   memset(combinedBuffer, 0u, bufferSizeChar);
1228
1229   for(unsigned int pixelIndex = 0; pixelIndex < bufferSizeInt; pixelIndex++)
1230   {
1231     // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
1232     // Otherwise, copy pixel from topBuffer to combinedBuffer.
1233
1234     unsigned int alphaBuffer1 = topBuffer[pixelIndex * 4 + 3];
1235
1236     if(alphaBuffer1 != 255)
1237     {
1238       // At least one pixel is not fully opaque
1239       // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
1240       combinedBuffer[pixelIndex * 4]     = topBuffer[pixelIndex * 4] + (bottomBuffer[pixelIndex * 4] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1241       combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1] + (bottomBuffer[pixelIndex * 4 + 1] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1242       combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2] + (bottomBuffer[pixelIndex * 4 + 2] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1243       combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3] + (bottomBuffer[pixelIndex * 4 + 3] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1244     }
1245     else
1246     {
1247       // Copy the pixel from topBuffer to combinedBuffer
1248       combinedBuffer[pixelIndex * 4]     = topBuffer[pixelIndex * 4];
1249       combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1];
1250       combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2];
1251       combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3];
1252     }
1253   }
1254
1255   return combinedPixelBuffer;
1256 }
1257
1258 Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1259 {
1260   // Apply the markup-Processor if enabled
1261   const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1262   if(markupProcessorEnabled)
1263   {
1264     // Underline-tags (this is for Markup case)
1265     // Get the underline runs.
1266     const Length     numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1267     Vector<GlyphRun> underlineRuns;
1268     underlineRuns.Resize(numberOfUnderlineRuns);
1269     mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1270
1271     // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1272     Vector<GlyphRun>::ConstIterator itGlyphRun    = underlineRuns.Begin();
1273     Vector<GlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1274     GlyphIndex                      startGlyphIndex, endGlyphIndex;
1275
1276     //The outer loop to iterate on the separated chunks of underlined glyph runs
1277     while(itGlyphRun != endItGlyphRun)
1278     {
1279       startGlyphIndex = itGlyphRun->glyphIndex;
1280       endGlyphIndex   = startGlyphIndex;
1281       //The inner loop to make a connected underline for the consecutive characters
1282       do
1283       {
1284         endGlyphIndex += itGlyphRun->numberOfGlyphs;
1285         itGlyphRun++;
1286       } while(itGlyphRun != endItGlyphRun && itGlyphRun->glyphIndex == endGlyphIndex);
1287
1288       endGlyphIndex--;
1289
1290       // Create the image buffer for underline
1291       Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1292       // Combine the two buffers
1293       topPixelBuffer = CombineImageBuffer(topPixelBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
1294     }
1295   }
1296
1297   return topPixelBuffer;
1298 }
1299
1300 Typesetter::Typesetter(const ModelInterface* const model)
1301 : mModel(new ViewModel(model))
1302 {
1303 }
1304
1305 Typesetter::~Typesetter()
1306 {
1307   delete mModel;
1308 }
1309
1310 } // namespace Text
1311
1312 } // namespace Toolkit
1313
1314 } // namespace Dali