Merge "Extending Style - Adding Strikethrough" 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 {
385   int       underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
386   uint32_t* bitmapBuffer     = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
387
388   for(unsigned int y = underlineYOffset; y < underlineYOffset + maxUnderlineThickness; y++)
389   {
390     if(y > bufferHeight - 1)
391     {
392       // Do not write out of bounds.
393       break;
394     }
395
396     for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
397     {
398       if(x > bufferWidth - 1)
399       {
400         // Do not write out of bounds.
401         break;
402       }
403
404       WriteColorToPixelBuffer(glyphData, bitmapBuffer, underlineColor, x, y);
405     }
406   }
407 }
408
409 /// Draws the background color to the buffer
410 void DrawBackgroundColor(
411   Vector4            backgroundColor,
412   const unsigned int bufferWidth,
413   const unsigned int bufferHeight,
414   GlyphData&         glyphData,
415   const float        baseline,
416   const LineRun&     line,
417   const float        lineExtentLeft,
418   const float        lineExtentRight)
419 {
420   uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
421
422   for(int y = glyphData.verticalOffset + baseline - line.ascender; y < glyphData.verticalOffset + baseline - line.descender; y++)
423   {
424     if((y < 0) || (y > static_cast<int>(bufferHeight - 1)))
425     {
426       // Do not write out of bounds.
427       continue;
428     }
429
430     for(int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
431     {
432       if((x < 0) || (x > static_cast<int>(bufferWidth - 1)))
433       {
434         // Do not write out of bounds.
435         continue;
436       }
437
438       WriteColorToPixelBuffer(glyphData, bitmapBuffer, backgroundColor, x, y);
439     }
440   }
441 }
442
443 Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuffer& buffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, int horizontalOffset, int verticalOffset)
444 {
445   // Retrieve lines, glyphs, positions and colors from the view model.
446   const Length            modelNumberOfLines           = model->GetNumberOfLines();
447   const LineRun* const    modelLinesBuffer             = model->GetLines();
448   const Length            numberOfGlyphs               = model->GetNumberOfGlyphs();
449   const GlyphInfo* const  glyphsBuffer                 = model->GetGlyphs();
450   const Vector2* const    positionBuffer               = model->GetLayout();
451   const Vector4* const    backgroundColorsBuffer       = model->GetBackgroundColors();
452   const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
453
454   // Create and initialize the pixel buffer.
455   GlyphData glyphData;
456   glyphData.verticalOffset   = verticalOffset;
457   glyphData.width            = bufferWidth;
458   glyphData.height           = bufferHeight;
459   glyphData.bitmapBuffer     = buffer;
460   glyphData.horizontalOffset = 0;
461
462   ColorIndex prevBackgroundColorIndex = 0;
463   ColorIndex backgroundColorIndex     = 0;
464
465   // Traverses the lines of the text.
466   for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
467   {
468     const LineRun& line = *(modelLinesBuffer + lineIndex);
469
470     // Sets the horizontal offset of the line.
471     glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
472     glyphData.horizontalOffset += horizontalOffset;
473
474     // Increases the vertical offset with the line's ascender.
475     glyphData.verticalOffset += static_cast<int>(line.ascender);
476
477     // Include line spacing after first line
478     if(lineIndex > 0u)
479     {
480       glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
481     }
482
483     float left     = bufferWidth;
484     float right    = 0.0f;
485     float baseline = 0.0f;
486
487     // Traverses the glyphs of the line.
488     const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
489     for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
490     {
491       // Retrieve the glyph's info.
492       const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
493
494       if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
495          (glyphInfo->height < Math::MACHINE_EPSILON_1000))
496       {
497         // Nothing to do if default background color, the glyph's width or height is zero.
498         continue;
499       }
500
501       backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
502
503       if((backgroundColorIndex != prevBackgroundColorIndex) &&
504          (prevBackgroundColorIndex != 0u))
505       {
506         const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
507         DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
508       }
509
510       if(backgroundColorIndex == 0u)
511       {
512         prevBackgroundColorIndex = backgroundColorIndex;
513         //if background color is the default do nothing
514         continue;
515       }
516
517       // Retrieves the glyph's position.
518       const Vector2* const position = positionBuffer + glyphIndex;
519
520       if(baseline < position->y + glyphInfo->yBearing)
521       {
522         baseline = position->y + glyphInfo->yBearing;
523       }
524
525       // Calculate the positions of leftmost and rightmost glyphs in the current line
526       if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
527       {
528         left = position->x - glyphInfo->xBearing;
529       }
530
531       if(position->x + glyphInfo->width > right)
532       {
533         right = position->x - glyphInfo->xBearing + glyphInfo->advance;
534       }
535
536       prevBackgroundColorIndex = backgroundColorIndex;
537     }
538
539     //draw last background at line end if not default
540     if(backgroundColorIndex != 0u)
541     {
542       const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
543       DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
544     }
545
546     // Increases the vertical offset with the line's descender.
547     glyphData.verticalOffset += static_cast<int>(-line.descender);
548   }
549
550   return glyphData.bitmapBuffer;
551 }
552
553 /// Draws the specified strikethrough color to the buffer
554 void DrawStrikethrough(
555   const Vector4&     strikethroughColor,
556   const unsigned int bufferWidth,
557   const unsigned int bufferHeight,
558   GlyphData&         glyphData,
559   const float        baseline,
560   const LineRun&     line,
561   const float        maxStrikethroughThickness,
562   const float        lineExtentLeft,
563   const float        lineExtentRight,
564   float              strikethroughStartingYPosition)
565 {
566   uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
567
568   for(unsigned int y = strikethroughStartingYPosition; y < strikethroughStartingYPosition + maxStrikethroughThickness; y++)
569   {
570     if(y > bufferHeight - 1)
571     {
572       // Do not write out of bounds.
573       break;
574     }
575
576     for(unsigned int x = glyphData.horizontalOffset + lineExtentLeft; x <= glyphData.horizontalOffset + lineExtentRight; x++)
577     {
578       if(x > bufferWidth - 1)
579       {
580         // Do not write out of bounds.
581         break;
582       }
583
584       WriteColorToPixelBuffer(glyphData, bitmapBuffer, strikethroughColor, x, y);
585     }
586   }
587 }
588
589 } // namespace
590
591 TypesetterPtr Typesetter::New(const ModelInterface* const model)
592 {
593   return TypesetterPtr(new Typesetter(model));
594 }
595
596 ViewModel* Typesetter::GetViewModel()
597 {
598   return mModel;
599 }
600
601 Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, const unsigned int bufferHeight, Pixel::Format pixelFormat)
602 {
603   Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
604
605   if(Pixel::RGBA8888 == pixelFormat)
606   {
607     const unsigned int bufferSizeInt  = bufferWidth * bufferHeight;
608     const unsigned int bufferSizeChar = 4u * bufferSizeInt;
609     memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
610   }
611   else
612   {
613     memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
614   }
615
616   return imageBuffer;
617 }
618
619 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
620 {
621   // @todo. This initial implementation for a TextLabel has only one visible page.
622
623   // Elides the text if needed.
624   mModel->ElideGlyphs();
625
626   // Retrieves the layout size.
627   const Size& layoutSize = mModel->GetLayoutSize();
628
629   const int outlineWidth = static_cast<int>(mModel->GetOutlineWidth());
630
631   // Set the offset for the horizontal alignment according to the text direction and outline width.
632   int penX = 0;
633
634   switch(mModel->GetHorizontalAlignment())
635   {
636     case HorizontalAlignment::BEGIN:
637     {
638       // No offset to add.
639       break;
640     }
641     case HorizontalAlignment::CENTER:
642     {
643       penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
644       break;
645     }
646     case HorizontalAlignment::END:
647     {
648       penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
649       break;
650     }
651   }
652
653   // Set the offset for the vertical alignment.
654   int penY = 0u;
655
656   switch(mModel->GetVerticalAlignment())
657   {
658     case VerticalAlignment::TOP:
659     {
660       // No offset to add.
661       break;
662     }
663     case VerticalAlignment::CENTER:
664     {
665       penY = static_cast<int>(0.5f * (size.height - layoutSize.height));
666       penY = penY < 0.f ? 0.f : penY;
667       break;
668     }
669     case VerticalAlignment::BOTTOM:
670     {
671       penY = static_cast<int>(size.height - layoutSize.height);
672       break;
673     }
674   }
675
676   // Calculate vertical line alignment
677   switch(mModel->GetVerticalLineAlignment())
678   {
679     case DevelText::VerticalLineAlignment::TOP:
680     {
681       break;
682     }
683     case DevelText::VerticalLineAlignment::MIDDLE:
684     {
685       const auto& line = *mModel->GetLines();
686       penY -= line.descender;
687       penY += static_cast<int>(line.lineSpacing * 0.5f + line.descender);
688       break;
689     }
690     case DevelText::VerticalLineAlignment::BOTTOM:
691     {
692       const auto& line       = *mModel->GetLines();
693       const auto  lineHeight = line.ascender + (-line.descender) + line.lineSpacing;
694       penY += static_cast<int>(lineHeight - (line.ascender - line.descender));
695       break;
696     }
697   }
698
699   // Generate the image buffers of the text for each different style first,
700   // then combine all of them together as one final image buffer. We try to
701   // do all of these in CPU only, so that once the final texture is generated,
702   // no calculation is needed in GPU during each frame.
703
704   const unsigned int bufferWidth  = static_cast<unsigned int>(size.width);
705   const unsigned int bufferHeight = static_cast<unsigned int>(size.height);
706
707   const unsigned int bufferSizeInt  = bufferWidth * bufferHeight;
708   const unsigned int bufferSizeChar = 4u * bufferSizeInt;
709
710   //Elided text in ellipsis at START could start on index greater than 0
711   auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
712   auto endIndexOfGlyphs   = mModel->GetEndIndexOfElidedGlyphs();
713
714   Devel::PixelBuffer imageBuffer;
715
716   if(RENDER_MASK == behaviour)
717   {
718     // Generate the image buffer as an alpha mask for color glyphs.
719     imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
720   }
721   else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
722   {
723     // Generate an empty image buffer so that it can been combined with the image buffers for styles
724     imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
725     memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
726   }
727   else
728   {
729     // Generate the image buffer for the text with no style.
730     imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
731   }
732
733   if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
734   {
735     // Generate the outline if enabled
736     const uint16_t outlineWidth = mModel->GetOutlineWidth();
737     if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
738     {
739       // Create the image buffer for outline
740       Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
741
742       // Combine the two buffers
743       imageBuffer = CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight);
744     }
745
746     // @todo. Support shadow and underline for partial text later on.
747
748     // Generate the shadow if enabled
749     const Vector2& shadowOffset = mModel->GetShadowOffset();
750     if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
751     {
752       // Create the image buffer for shadow
753       Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
754
755       // Check whether it will be a soft shadow
756       const float& blurRadius = mModel->GetShadowBlurRadius();
757
758       if(blurRadius > Math::MACHINE_EPSILON_1)
759       {
760         shadowImageBuffer.ApplyGaussianBlur(blurRadius);
761       }
762
763       // Combine the two buffers
764       imageBuffer = CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight);
765     }
766
767     // Generate the underline if enabled
768     const bool underlineEnabled = mModel->IsUnderlineEnabled();
769     if(underlineEnabled && RENDER_OVERLAY_STYLE == behaviour)
770     {
771       // Create the image buffer for underline
772       Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
773
774       // Combine the two buffers
775       imageBuffer = CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
776     }
777
778     // Generate the background if enabled
779     const bool backgroundEnabled   = mModel->IsBackgroundEnabled();
780     const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
781     if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
782     {
783       Devel::PixelBuffer backgroundImageBuffer;
784
785       if(backgroundEnabled)
786       {
787         backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
788       }
789       else
790       {
791         backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
792       }
793
794       if(backgroundMarkupSet)
795       {
796         DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
797       }
798
799       // Combine the two buffers
800       imageBuffer = CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight);
801     }
802
803     // Generate the strikethrough if enabled
804     const bool strikethroughEnabled = mModel->IsStrikethroughEnabled();
805     if(strikethroughEnabled && RENDER_OVERLAY_STYLE == behaviour)
806     {
807       // Create the image buffer for strikethrough
808       Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
809
810       // Combine the two buffers
811       imageBuffer = CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight);
812     }
813
814     // Markup-Processor
815
816     imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
817   }
818
819   // Create the final PixelData for the combined image buffer
820   PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
821
822   return pixelData;
823 }
824
825 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)
826 {
827   // Retrieve lines, glyphs, positions and colors from the view model.
828   const Length            modelNumberOfLines = mModel->GetNumberOfLines();
829   const LineRun* const    modelLinesBuffer   = mModel->GetLines();
830   const GlyphInfo* const  glyphsBuffer       = mModel->GetGlyphs();
831   const Vector2* const    positionBuffer     = mModel->GetLayout();
832   const Vector4* const    colorsBuffer       = mModel->GetColors();
833   const ColorIndex* const colorIndexBuffer   = mModel->GetColorIndices();
834   const GlyphInfo*        hyphens            = mModel->GetHyphens();
835   const Length*           hyphenIndices      = mModel->GetHyphenIndices();
836   const Length            hyphensCount       = mModel->GetHyphensCount();
837
838   // Elided text info. Indices according to elided text and Ellipsis position.
839   const auto startIndexOfGlyphs              = mModel->GetStartIndexOfElidedGlyphs();
840   const auto endIndexOfGlyphs                = mModel->GetEndIndexOfElidedGlyphs();
841   const auto firstMiddleIndexOfElidedGlyphs  = mModel->GetFirstMiddleIndexOfElidedGlyphs();
842   const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
843   const auto ellipsisPosition                = mModel->GetEllipsisPosition();
844
845   // Whether to use the default color.
846   const bool     useDefaultColor = (NULL == colorsBuffer);
847   const Vector4& defaultColor    = mModel->GetDefaultColor();
848
849   // Create and initialize the pixel buffer.
850   GlyphData glyphData;
851   glyphData.verticalOffset   = verticalOffset;
852   glyphData.width            = bufferWidth;
853   glyphData.height           = bufferHeight;
854   glyphData.bitmapBuffer     = CreateImageBuffer(bufferWidth, bufferHeight, pixelFormat);
855   glyphData.horizontalOffset = 0;
856
857   // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
858   TextAbstraction::FontClient fontClient  = TextAbstraction::FontClient::Get();
859   Length                      hyphenIndex = 0;
860
861   // Traverses the lines of the text.
862   for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
863   {
864     const LineRun& line = *(modelLinesBuffer + lineIndex);
865
866     // Sets the horizontal offset of the line.
867     glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int>(line.alignmentOffset);
868     glyphData.horizontalOffset += horizontalOffset;
869
870     // Increases the vertical offset with the line's ascender.
871     glyphData.verticalOffset += static_cast<int>(line.ascender);
872
873     // Include line spacing after first line
874     if(lineIndex > 0u)
875     {
876       glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
877     }
878
879     // Retrieves the glyph's outline width
880     float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
881
882     if(style == Typesetter::STYLE_OUTLINE)
883     {
884       glyphData.horizontalOffset -= outlineWidth;
885       if(lineIndex == 0u)
886       {
887         // Only need to add the vertical outline offset for the first line
888         glyphData.verticalOffset -= outlineWidth;
889       }
890     }
891     else if(style == Typesetter::STYLE_SHADOW)
892     {
893       const Vector2& shadowOffset = mModel->GetShadowOffset();
894       glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
895
896       if(lineIndex == 0u)
897       {
898         // Only need to add the vertical shadow offset for first line
899         glyphData.verticalOffset += shadowOffset.y - outlineWidth;
900       }
901     }
902
903     const bool     underlineEnabled = mModel->IsUnderlineEnabled();
904     const Vector4& underlineColor   = mModel->GetUnderlineColor();
905     const float    underlineHeight  = mModel->GetUnderlineHeight();
906
907     const bool     strikethroughEnabled = mModel->IsStrikethroughEnabled();
908     const Vector4& strikethroughColor   = mModel->GetStrikethroughColor();
909     const float    strikethroughHeight  = mModel->GetStrikethroughHeight();
910
911     // Get the underline runs.
912     const Length     numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
913     Vector<GlyphRun> underlineRuns;
914     underlineRuns.Resize(numberOfUnderlineRuns);
915     mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
916
917     bool thereAreUnderlinedGlyphs = false;
918     bool strikethroughGlyphsExist = false;
919
920     float currentUnderlinePosition       = 0.0f;
921     float currentUnderlineThickness      = underlineHeight;
922     float maxUnderlineThickness          = currentUnderlineThickness;
923     float currentStrikethroughThickness  = strikethroughHeight;
924     float maxStrikethroughThickness      = currentStrikethroughThickness;
925     float strikethroughStartingYPosition = 0.0f;
926
927     FontId lastUnderlinedFontId = 0;
928
929     float lineExtentLeft  = bufferWidth;
930     float lineExtentRight = 0.0f;
931     float baseline        = 0.0f;
932     bool  addHyphen       = false;
933
934     // Traverses the glyphs of the line.
935     const GlyphIndex startGlyphIndex = std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs);
936     GlyphIndex       endGlyphIndex   = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
937     endGlyphIndex                    = std::min(endGlyphIndex, endIndexOfGlyphs);
938
939     for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
940     {
941       if(glyphIndex < fromGlyphIndex || glyphIndex > toGlyphIndex)
942       {
943         // Ignore any glyph that out of the specified range
944         continue;
945       }
946
947       //To handle START case of ellipsis, the first glyph has been shifted
948       //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
949       GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
950
951       //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.
952       if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
953       {
954         if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
955            glyphIndex < secondMiddleIndexOfElidedGlyphs)
956         {
957           // Ignore any glyph that removed for MIDDLE ellipsis
958           continue;
959         }
960         if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
961         {
962           elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
963         }
964       }
965
966       // Retrieve the glyph's info.
967       const GlyphInfo* glyphInfo;
968
969       if(addHyphen && hyphens)
970       {
971         glyphInfo = hyphens + hyphenIndex;
972         hyphenIndex++;
973       }
974       else
975       {
976         glyphInfo = glyphsBuffer + elidedGlyphIndex;
977       }
978
979       if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
980          (glyphInfo->height < Math::MACHINE_EPSILON_1000))
981       {
982         // Nothing to do if the glyph's width or height is zero.
983         continue;
984       }
985
986       const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns);
987       thereAreUnderlinedGlyphs  = thereAreUnderlinedGlyphs || underlineGlyph;
988
989       strikethroughGlyphsExist = strikethroughGlyphsExist || strikethroughEnabled;
990
991       // Are we still using the same fontId as previous
992       if((strikethroughEnabled || underlineGlyph) && (glyphInfo->fontId != lastUnderlinedFontId))
993       {
994         // We need to fetch fresh font underline metrics
995         FetchFontDecorationlinesMetrics(fontClient, glyphInfo, currentUnderlinePosition, underlineHeight, currentUnderlineThickness, maxUnderlineThickness, lastUnderlinedFontId, strikethroughHeight, currentStrikethroughThickness, maxStrikethroughThickness);
996       } // underline
997
998       // Retrieves the glyph's position.
999       Vector2 position = *(positionBuffer + elidedGlyphIndex);
1000
1001       if(addHyphen)
1002       {
1003         GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1004         position.x         = position.x + tempInfo.advance - tempInfo.xBearing + glyphInfo->xBearing;
1005         position.y         = -glyphInfo->yBearing;
1006       }
1007
1008       if(baseline < position.y + glyphInfo->yBearing)
1009       {
1010         baseline = position.y + glyphInfo->yBearing;
1011       }
1012
1013       // Calculate the positions of leftmost and rightmost glyphs in the current line
1014       if(position.x < lineExtentLeft)
1015       {
1016         lineExtentLeft = position.x;
1017       }
1018
1019       if(position.x + glyphInfo->width > lineExtentRight)
1020       {
1021         lineExtentRight = position.x + glyphInfo->width;
1022       }
1023
1024       // Retrieves the glyph's color.
1025       const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1026
1027       Vector4 color;
1028       if(style == Typesetter::STYLE_SHADOW)
1029       {
1030         color = mModel->GetShadowColor();
1031       }
1032       else if(style == Typesetter::STYLE_OUTLINE)
1033       {
1034         color = mModel->GetOutlineColor();
1035       }
1036       else
1037       {
1038         color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1039       }
1040
1041       // Premultiply alpha
1042       color.r *= color.a;
1043       color.g *= color.a;
1044       color.b *= color.a;
1045
1046       // Retrieves the glyph's bitmap.
1047       glyphData.glyphBitmap.buffer = NULL;
1048       glyphData.glyphBitmap.width  = glyphInfo->width; // Desired width and height.
1049       glyphData.glyphBitmap.height = glyphInfo->height;
1050
1051       if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1052       {
1053         // Don't render outline for other styles
1054         outlineWidth = 0.0f;
1055       }
1056
1057       if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1058       {
1059         fontClient.CreateBitmap(glyphInfo->fontId,
1060                                 glyphInfo->index,
1061                                 glyphInfo->isItalicRequired,
1062                                 glyphInfo->isBoldRequired,
1063                                 glyphData.glyphBitmap,
1064                                 static_cast<int>(outlineWidth));
1065       }
1066
1067       // Sets the glyph's bitmap into the bitmap of the whole text.
1068       if(NULL != glyphData.glyphBitmap.buffer)
1069       {
1070         if(style == Typesetter::STYLE_OUTLINE)
1071         {
1072           // Set the position offset for the current glyph
1073           glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1074           glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1075         }
1076
1077         // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1078         TypesetGlyph(glyphData,
1079                      &position,
1080                      &color,
1081                      style,
1082                      pixelFormat);
1083
1084         if(style == Typesetter::STYLE_OUTLINE)
1085         {
1086           // Reset the position offset for the next glyph
1087           glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1088           glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1089         }
1090
1091         // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1092         delete[] glyphData.glyphBitmap.buffer;
1093         glyphData.glyphBitmap.buffer = NULL;
1094       }
1095
1096       if(hyphenIndices)
1097       {
1098         while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1099         {
1100           hyphenIndex++;
1101         }
1102
1103         addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1104         if(addHyphen)
1105         {
1106           glyphIndex--;
1107         }
1108       }
1109     }
1110
1111     // Draw the underline from the leftmost glyph to the rightmost glyph
1112     if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1113     {
1114       DrawUnderline(underlineColor, bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineThickness, lineExtentLeft, lineExtentRight);
1115     }
1116
1117     // Draw the background color from the leftmost glyph to the rightmost glyph
1118     if(style == Typesetter::STYLE_BACKGROUND)
1119     {
1120       DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1121     }
1122
1123     // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1124     if(strikethroughGlyphsExist && style == Typesetter::STYLE_STRIKETHROUGH)
1125     {
1126       //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.
1127       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.
1128       DrawStrikethrough(strikethroughColor, bufferWidth, bufferHeight, glyphData, baseline, line, maxStrikethroughThickness, lineExtentLeft, lineExtentRight, strikethroughStartingYPosition);
1129     }
1130
1131     // Increases the vertical offset with the line's descender.
1132     glyphData.verticalOffset += static_cast<int>(-line.descender);
1133   }
1134
1135   return glyphData.bitmapBuffer;
1136 }
1137
1138 Devel::PixelBuffer Typesetter::CombineImageBuffer(Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight)
1139 {
1140   unsigned char* topBuffer    = topPixelBuffer.GetBuffer();
1141   unsigned char* bottomBuffer = bottomPixelBuffer.GetBuffer();
1142
1143   Devel::PixelBuffer combinedPixelBuffer;
1144
1145   if(topBuffer == NULL && bottomBuffer == NULL)
1146   {
1147     // Nothing to do if both buffers are empty.
1148     return combinedPixelBuffer;
1149   }
1150
1151   if(topBuffer == NULL)
1152   {
1153     // Nothing to do if topBuffer is empty.
1154     return bottomPixelBuffer;
1155   }
1156
1157   if(bottomBuffer == NULL)
1158   {
1159     // Nothing to do if bottomBuffer is empty.
1160     return topPixelBuffer;
1161   }
1162
1163   // Always combine two RGBA images
1164   const unsigned int bufferSizeInt  = bufferWidth * bufferHeight;
1165   const unsigned int bufferSizeChar = 4u * bufferSizeInt;
1166
1167   combinedPixelBuffer     = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1168   uint8_t* combinedBuffer = reinterpret_cast<uint8_t*>(combinedPixelBuffer.GetBuffer());
1169   memset(combinedBuffer, 0u, bufferSizeChar);
1170
1171   for(unsigned int pixelIndex = 0; pixelIndex < bufferSizeInt; pixelIndex++)
1172   {
1173     // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
1174     // Otherwise, copy pixel from topBuffer to combinedBuffer.
1175
1176     unsigned int alphaBuffer1 = topBuffer[pixelIndex * 4 + 3];
1177
1178     if(alphaBuffer1 != 255)
1179     {
1180       // At least one pixel is not fully opaque
1181       // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
1182       combinedBuffer[pixelIndex * 4]     = topBuffer[pixelIndex * 4] + (bottomBuffer[pixelIndex * 4] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1183       combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1] + (bottomBuffer[pixelIndex * 4 + 1] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1184       combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2] + (bottomBuffer[pixelIndex * 4 + 2] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1185       combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3] + (bottomBuffer[pixelIndex * 4 + 3] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1186     }
1187     else
1188     {
1189       // Copy the pixel from topBuffer to combinedBuffer
1190       combinedBuffer[pixelIndex * 4]     = topBuffer[pixelIndex * 4];
1191       combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1];
1192       combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2];
1193       combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3];
1194     }
1195   }
1196
1197   return combinedPixelBuffer;
1198 }
1199
1200 Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1201 {
1202   // Apply the markup-Processor if enabled
1203   const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1204   if(markupProcessorEnabled)
1205   {
1206     // Underline-tags (this is for Markup case)
1207     // Get the underline runs.
1208     const Length     numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1209     Vector<GlyphRun> underlineRuns;
1210     underlineRuns.Resize(numberOfUnderlineRuns);
1211     mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1212
1213     // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1214     Vector<GlyphRun>::ConstIterator itGlyphRun    = underlineRuns.Begin();
1215     Vector<GlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1216     GlyphIndex                      startGlyphIndex, endGlyphIndex;
1217
1218     //The outer loop to iterate on the separated chunks of underlined glyph runs
1219     while(itGlyphRun != endItGlyphRun)
1220     {
1221       startGlyphIndex = itGlyphRun->glyphIndex;
1222       endGlyphIndex   = startGlyphIndex;
1223       //The inner loop to make a connected underline for the consecutive characters
1224       do
1225       {
1226         endGlyphIndex += itGlyphRun->numberOfGlyphs;
1227         itGlyphRun++;
1228       } while(itGlyphRun != endItGlyphRun && itGlyphRun->glyphIndex == endGlyphIndex);
1229
1230       endGlyphIndex--;
1231
1232       // Create the image buffer for underline
1233       Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1234       // Combine the two buffers
1235       topPixelBuffer = CombineImageBuffer(topPixelBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
1236     }
1237   }
1238
1239   return topPixelBuffer;
1240 }
1241
1242 Typesetter::Typesetter(const ModelInterface* const model)
1243 : mModel(new ViewModel(model))
1244 {
1245 }
1246
1247 Typesetter::~Typesetter()
1248 {
1249   delete mModel;
1250 }
1251
1252 } // namespace Text
1253
1254 } // namespace Toolkit
1255
1256 } // namespace Dali