Prevent render of outline, shadow when transparent
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / rendering / text-typesetter.cpp
1 /*
2  * Copyright (c) 2022 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/glyph-metrics-helper.h>
29 #include <dali-toolkit/internal/text/line-helper-functions.h>
30 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
31 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
32 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
33 #include <dali-toolkit/internal/text/rendering/view-model.h>
34
35 namespace Dali
36 {
37 namespace Toolkit
38 {
39 namespace Text
40 {
41 namespace
42 {
43 const float HALF(0.5f);
44 const float ONE_AND_A_HALF(1.5f);
45
46 /**
47  * @brief Fast multiply & divide by 255. It wiil be useful when we applying alpha value in color
48  *
49  * @param x The value between [0..255]
50  * @param y The value between [0..255]
51  * @return (x*y)/255
52  */
53 inline uint8_t MultiplyAndNormalizeColor(const uint8_t x, const uint8_t y) noexcept
54 {
55   const uint32_t xy = static_cast<const uint32_t>(x) * y;
56   return ((xy << 15) + (xy << 7) + xy) >> 23;
57 }
58
59 /**
60  * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
61  */
62 struct GlyphData
63 {
64   Devel::PixelBuffer                           bitmapBuffer;     ///< The buffer of the whole bitmap. The format is RGBA8888.
65   Vector2*                                     position;         ///< The position of the glyph.
66   TextAbstraction::FontClient::GlyphBufferData glyphBitmap;      ///< The glyph's bitmap.
67   uint32_t                                     width;            ///< The bitmap's width.
68   uint32_t                                     height;           ///< The bitmap's height.
69   int32_t                                      horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
70   int32_t                                      verticalOffset;   ///< The vertical offset to be added to the 'y' glyph's position.
71 };
72
73 /**
74  * @brief Sets the glyph's buffer into the bitmap's buffer.
75  *
76  * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
77  * @param[in] position The position of the glyph.
78  * @param[in] color The color of the glyph.
79  * @param[in] style The style of the text.
80  * @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).
81  */
82 void TypesetGlyph(GlyphData& __restrict__ data,
83                   const Vector2* const __restrict__ position,
84                   const Vector4* const __restrict__ color,
85                   const Typesetter::Style style,
86                   const Pixel::Format     pixelFormat)
87 {
88   if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
89   {
90     // Nothing to do if the width or height of the buffer is zero.
91     return;
92   }
93
94   // Initial vertical / horizontal offset.
95   const int32_t yOffset = data.verticalOffset + position->y;
96   const int32_t xOffset = data.horizontalOffset + position->x;
97
98   // Whether the given glyph is a color one.
99   const bool     isColorGlyph    = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
100   const uint32_t glyphPixelSize  = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
101   const uint32_t glyphAlphaIndex = glyphPixelSize - 1u;
102
103   // Determinate iterator range.
104   const int32_t lineIndexRangeMin = std::max(0, -yOffset);
105   const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
106   const int32_t indexRangeMin     = std::max(0, -xOffset);
107   const int32_t indexRangeMax     = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
108
109   // If current glyph don't need to be rendered, just ignore.
110   if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
111   {
112     return;
113   }
114
115   if(Pixel::RGBA8888 == pixelFormat)
116   {
117     uint32_t* __restrict__ bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
118     // Skip basic line.
119     bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
120
121     // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
122     // Just overwrite transparent color and return.
123     if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
124     {
125       for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
126       {
127         // We can use memset here.
128         memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
129         bitmapBuffer += data.width;
130       }
131       return;
132     }
133
134     const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
135
136     // Offset byte value of glyph bitmap.
137     uint32_t glyphOffet = 0u;
138
139     // Allocate scanline memory for glyph bitmap if we need.
140     const bool useLocalScanline         = data.glyphBitmap.compressionType != TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
141     uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer;
142
143     // Precalculate input color's packed result.
144     uint32_t packedInputColor                    = 0u;
145     uint8_t* __restrict__ packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
146
147     *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
148     *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
149     *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
150     *(packedInputColorBuffer)      = static_cast<uint8_t>(color->r * 255);
151
152     // Skip basic line of glyph.
153     if(useLocalScanline)
154     {
155       for(int32_t lineIndex = 0; lineIndex < lineIndexRangeMin; ++lineIndex)
156       {
157         TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
158       }
159     }
160     else
161     {
162       glyphScanline += lineIndexRangeMin * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize);
163     }
164
165     // Traverse the pixels of the glyph line per line.
166     if(isColorGlyph)
167     {
168       for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
169       {
170         if(useLocalScanline)
171         {
172           TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
173         }
174
175         for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
176         {
177           const int32_t xOffsetIndex = xOffset + index;
178
179           // Retrieves the color from the color glyph.
180           uint32_t packedColorGlyph                    = *(reinterpret_cast<const uint32_t*>(glyphScanline + (index << 2)));
181           uint8_t* __restrict__ packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
182
183           // Update the alpha channel.
184           const uint8_t colorAlpha       = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
185           *(packedColorGlyphBuffer + 3u) = colorAlpha;
186
187           if(Typesetter::STYLE_SHADOW == style)
188           {
189             // The shadow of color glyph needs to have the shadow color.
190             *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
191             *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
192             *packedColorGlyphBuffer        = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
193           }
194           else
195           {
196             if(swapChannelsBR)
197             {
198               std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
199             }
200
201             *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
202             *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
203             *packedColorGlyphBuffer        = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
204
205             if(data.glyphBitmap.isColorBitmap)
206             {
207               *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
208               *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
209               *packedColorGlyphBuffer        = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
210             }
211           }
212
213           // Set the color into the final pixel buffer.
214           *(bitmapBuffer + xOffsetIndex) = packedColorGlyph;
215         }
216
217         bitmapBuffer += data.width;
218         if(!useLocalScanline)
219         {
220           glyphScanline += data.glyphBitmap.width * glyphPixelSize;
221         }
222       }
223     }
224     else
225     {
226       for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
227       {
228         if(useLocalScanline)
229         {
230           TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
231         }
232
233         for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
234         {
235           // Update the alpha channel.
236           const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
237
238           // Copy non-transparent pixels only
239           if(alpha > 0u)
240           {
241             const int32_t xOffsetIndex = xOffset + index;
242
243             // Check alpha of overlapped pixels
244             uint32_t& currentColor             = *(bitmapBuffer + xOffsetIndex);
245             uint8_t*  packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(&currentColor);
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             uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
252             currentAlpha         = std::max(currentAlpha, alpha);
253             if(currentAlpha == 255)
254             {
255               // Fast-cut to avoid float type operation.
256               currentColor = packedInputColor;
257             }
258             else
259             {
260               // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
261               // The format is RGBA8888.
262               uint32_t packedColor                    = 0u;
263               uint8_t* __restrict__ packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
264
265               // Color is pre-muliplied with its alpha.
266               *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
267               *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
268               *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
269               *(packedColorBuffer)      = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
270
271               // Set the color into the final pixel buffer.
272               currentColor = packedColor;
273             }
274           }
275         }
276
277         bitmapBuffer += data.width;
278         if(!useLocalScanline)
279         {
280           glyphScanline += data.glyphBitmap.width * glyphPixelSize;
281         }
282       }
283     }
284
285     if(useLocalScanline)
286     {
287       free(glyphScanline);
288     }
289   }
290   else // Pixel::L8
291   {
292     // Below codes required only if not color glyph.
293     if(!isColorGlyph)
294     {
295       uint8_t* __restrict__ bitmapBuffer = data.bitmapBuffer.GetBuffer();
296
297       // Offset byte value of glyph bitmap.
298       uint32_t glyphOffet = 0u;
299
300       // Allocate scanline memory for glyph bitmap if we need.
301       const bool useLocalScanline         = data.glyphBitmap.compressionType != TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
302       uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer;
303
304       // Skip basic line.
305       bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
306
307       // Skip basic line of glyph.
308       if(useLocalScanline)
309       {
310         for(int32_t lineIndex = 0; lineIndex < lineIndexRangeMin; ++lineIndex)
311         {
312           TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
313         }
314       }
315       else
316       {
317         glyphScanline += lineIndexRangeMin * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize);
318       }
319
320       // Traverse the pixels of the glyph line per line.
321       for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
322       {
323         if(useLocalScanline)
324         {
325           TextAbstraction::FontClient::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet);
326         }
327
328         for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
329         {
330           const int32_t xOffsetIndex = xOffset + index;
331
332           // Update the alpha channel.
333           const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
334
335           // Copy non-transparent pixels only
336           if(alpha > 0u)
337           {
338             // Check alpha of overlapped pixels
339             uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
340
341             // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
342             // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
343             // semi-transparent gaps between joint glyphs with overlapped pixels, which could
344             // happen, for example, in the RTL text when we copy glyphs from right to left).
345             currentAlpha = std::max(currentAlpha, alpha);
346           }
347         }
348
349         bitmapBuffer += data.width;
350         if(!useLocalScanline)
351         {
352           glyphScanline += data.glyphBitmap.width * glyphPixelSize;
353         }
354       }
355
356       if(useLocalScanline)
357       {
358         free(glyphScanline);
359       }
360     }
361   }
362 }
363
364 /// Draws the specified underline color to the buffer
365 void DrawUnderline(
366   const uint32_t                  bufferWidth,
367   const uint32_t                  bufferHeight,
368   GlyphData&                      glyphData,
369   const float                     baseline,
370   const float                     currentUnderlinePosition,
371   const float                     maxUnderlineHeight,
372   const float                     lineExtentLeft,
373   const float                     lineExtentRight,
374   const UnderlineStyleProperties& commonUnderlineProperties,
375   const UnderlineStyleProperties& currentUnderlineProperties,
376   const LineRun&                  line)
377 {
378   const Vector4&              underlineColor       = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
379   const Text::Underline::Type underlineType        = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
380   const float                 dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
381   const float                 dashedUnderlineGap   = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
382
383   int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
384
385   const uint32_t yRangeMin = underlineYOffset;
386   const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
387   const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
388   const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
389
390   // If current glyph don't need to be rendered, just ignore.
391   if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
392   {
393     return;
394   }
395
396   // We can optimize by memset when underlineColor.a is near zero
397   uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
398
399   uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
400
401   // Skip yRangeMin line.
402   bitmapBuffer += yRangeMin * glyphData.width;
403
404   // Note if underlineType is DASHED, we cannot setup color by memset.
405   if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
406   {
407     for(uint32_t y = yRangeMin; y < yRangeMax; y++)
408     {
409       // We can use memset.
410       memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
411       bitmapBuffer += glyphData.width;
412     }
413     if(underlineType == Text::Underline::DOUBLE)
414     {
415       int32_t        secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
416       const uint32_t secondYRangeMin        = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
417       const uint32_t secondYRangeMax        = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
418
419       // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
420       bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
421
422       for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
423       {
424         // We can use memset.
425         memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
426         bitmapBuffer += glyphData.width;
427       }
428     }
429   }
430   else
431   {
432     uint32_t packedUnderlineColor       = 0u;
433     uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
434
435     // Write the color to the pixel buffer
436     *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
437     *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
438     *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
439     *(packedUnderlineColorBuffer)      = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
440
441     for(uint32_t y = yRangeMin; y < yRangeMax; y++)
442     {
443       if(underlineType == Text::Underline::DASHED)
444       {
445         float dashWidth = dashedUnderlineWidth;
446         float dashGap   = 0;
447
448         for(uint32_t x = xRangeMin; x < xRangeMax; x++)
449         {
450           if(dashGap == 0 && dashWidth > 0)
451           {
452             // Note : this is same logic as bitmap[y][x] = underlineColor;
453             *(bitmapBuffer + x) = packedUnderlineColor;
454             dashWidth--;
455           }
456           else if(dashGap < dashedUnderlineGap)
457           {
458             dashGap++;
459           }
460           else
461           {
462             //reset
463             dashWidth = dashedUnderlineWidth;
464             dashGap   = 0;
465           }
466         }
467       }
468       else
469       {
470         for(uint32_t x = xRangeMin; x < xRangeMax; x++)
471         {
472           // Note : this is same logic as bitmap[y][x] = underlineColor;
473           *(bitmapBuffer + x) = packedUnderlineColor;
474         }
475       }
476       bitmapBuffer += glyphData.width;
477     }
478     if(underlineType == Text::Underline::DOUBLE)
479     {
480       int32_t        secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
481       const uint32_t secondYRangeMin        = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
482       const uint32_t secondYRangeMax        = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
483
484       // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
485       bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
486
487       for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
488       {
489         for(uint32_t x = xRangeMin; x < xRangeMax; x++)
490         {
491           // Note : this is same logic as bitmap[y][x] = underlineColor;
492           *(bitmapBuffer + x) = packedUnderlineColor;
493         }
494         bitmapBuffer += glyphData.width;
495       }
496     }
497   }
498 }
499
500 /// Draws the background color to the buffer
501 void DrawBackgroundColor(
502   Vector4        backgroundColor,
503   const uint32_t bufferWidth,
504   const uint32_t bufferHeight,
505   GlyphData&     glyphData,
506   const float    baseline,
507   const LineRun& line,
508   const float    lineExtentLeft,
509   const float    lineExtentRight)
510 {
511   const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
512   const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
513   const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
514   const int32_t xRangeMax = std::min(static_cast<int32_t>(bufferWidth), static_cast<int32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
515
516   // If current glyph don't need to be rendered, just ignore.
517   if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
518   {
519     return;
520   }
521
522   // We can optimize by memset when backgroundColor.a is near zero
523   uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
524
525   uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
526
527   // Skip yRangeMin line.
528   bitmapBuffer += yRangeMin * glyphData.width;
529
530   if(backgroundColorAlpha == 0)
531   {
532     for(int32_t y = yRangeMin; y < yRangeMax; y++)
533     {
534       // We can use memset.
535       memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
536       bitmapBuffer += glyphData.width;
537     }
538   }
539   else
540   {
541     uint32_t packedBackgroundColor       = 0u;
542     uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
543
544     // Write the color to the pixel buffer
545     *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
546     *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
547     *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
548     *(packedBackgroundColorBuffer)      = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
549
550     for(int32_t y = yRangeMin; y < yRangeMax; y++)
551     {
552       for(int32_t x = xRangeMin; x < xRangeMax; x++)
553       {
554         // Note : this is same logic as bitmap[y][x] = backgroundColor;
555         *(bitmapBuffer + x) = packedBackgroundColor;
556       }
557       bitmapBuffer += glyphData.width;
558     }
559   }
560 }
561
562 Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuffer& buffer, const uint32_t bufferWidth, const uint32_t bufferHeight, const bool ignoreHorizontalAlignment, const int32_t horizontalOffset, const int32_t verticalOffset)
563 {
564   // Retrieve lines, glyphs, positions and colors from the view model.
565   const Length            modelNumberOfLines           = model->GetNumberOfLines();
566   const LineRun* const    modelLinesBuffer             = model->GetLines();
567   const Length            numberOfGlyphs               = model->GetNumberOfGlyphs();
568   const GlyphInfo* const  glyphsBuffer                 = model->GetGlyphs();
569   const Vector2* const    positionBuffer               = model->GetLayout();
570   const Vector4* const    backgroundColorsBuffer       = model->GetBackgroundColors();
571   const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
572
573   const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
574
575   // Create and initialize the pixel buffer.
576   GlyphData glyphData;
577   glyphData.verticalOffset   = verticalOffset;
578   glyphData.width            = bufferWidth;
579   glyphData.height           = bufferHeight;
580   glyphData.bitmapBuffer     = buffer;
581   glyphData.horizontalOffset = 0;
582
583   ColorIndex prevBackgroundColorIndex = 0;
584   ColorIndex backgroundColorIndex     = 0;
585
586   // Traverses the lines of the text.
587   for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
588   {
589     const LineRun& line = *(modelLinesBuffer + lineIndex);
590
591     // Sets the horizontal offset of the line.
592     glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
593     glyphData.horizontalOffset += horizontalOffset;
594
595     // Increases the vertical offset with the line's ascender.
596     glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
597
598     float left     = bufferWidth;
599     float right    = 0.0f;
600     float baseline = 0.0f;
601
602     // Traverses the glyphs of the line.
603     const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
604     for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
605     {
606       // Retrieve the glyph's info.
607       const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
608
609       if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
610          (glyphInfo->height < Math::MACHINE_EPSILON_1000))
611       {
612         // Nothing to do if default background color, the glyph's width or height is zero.
613         continue;
614       }
615
616       backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
617
618       if((backgroundColorIndex != prevBackgroundColorIndex) &&
619          (prevBackgroundColorIndex != 0u))
620       {
621         const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
622         DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
623       }
624
625       if(backgroundColorIndex == 0u)
626       {
627         prevBackgroundColorIndex = backgroundColorIndex;
628         //if background color is the default do nothing
629         continue;
630       }
631
632       // Retrieves the glyph's position.
633       const Vector2* const position = positionBuffer + glyphIndex;
634
635       if(baseline < position->y + glyphInfo->yBearing)
636       {
637         baseline = position->y + glyphInfo->yBearing;
638       }
639
640       // Calculate the positions of leftmost and rightmost glyphs in the current line
641       if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
642       {
643         left = position->x - glyphInfo->xBearing;
644       }
645
646       if(position->x + glyphInfo->width > right)
647       {
648         right = position->x - glyphInfo->xBearing + glyphInfo->advance;
649       }
650
651       prevBackgroundColorIndex = backgroundColorIndex;
652     }
653
654     //draw last background at line end if not default
655     if(backgroundColorIndex != 0u)
656     {
657       const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
658       DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
659     }
660
661     // Increases the vertical offset with the line's descender.
662     glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
663   }
664
665   return glyphData.bitmapBuffer;
666 }
667
668 /// Draws the specified strikethrough color to the buffer
669 void DrawStrikethrough(const uint32_t                      bufferWidth,
670                        const uint32_t                      bufferHeight,
671                        GlyphData&                          glyphData,
672                        const float                         baseline,
673                        const float                         strikethroughStartingYPosition,
674                        const float                         maxStrikethroughHeight,
675                        const float                         lineExtentLeft,
676                        const float                         lineExtentRight,
677                        const StrikethroughStyleProperties& commonStrikethroughProperties,
678                        const StrikethroughStyleProperties& currentStrikethroughProperties,
679                        const LineRun&                      line)
680 {
681   const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
682
683   const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
684   const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
685   const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
686   const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
687
688   // If current glyph don't need to be rendered, just ignore.
689   if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
690   {
691     return;
692   }
693
694   // We can optimize by memset when strikethroughColor.a is near zero
695   uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
696
697   uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
698
699   // Skip yRangeMin line.
700   bitmapBuffer += yRangeMin * glyphData.width;
701
702   if(strikethroughColorAlpha == 0)
703   {
704     for(uint32_t y = yRangeMin; y < yRangeMax; y++)
705     {
706       // We can use memset.
707       memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
708       bitmapBuffer += glyphData.width;
709     }
710   }
711   else
712   {
713     uint32_t packedStrikethroughColor       = 0u;
714     uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
715
716     // Write the color to the pixel buffer
717     *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
718     *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
719     *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
720     *(packedStrikethroughColorBuffer)      = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
721
722     for(uint32_t y = yRangeMin; y < yRangeMax; y++)
723     {
724       for(uint32_t x = xRangeMin; x < xRangeMax; x++)
725       {
726         // Note : this is same logic as bitmap[y][x] = strikethroughColor;
727         *(bitmapBuffer + x) = packedStrikethroughColor;
728       }
729       bitmapBuffer += glyphData.width;
730     }
731   }
732 }
733
734 /**
735  * @brief Create an initialized image buffer filled with transparent color.
736  *
737  * Creates the pixel data used to generate the final image with the given size.
738  *
739  * @param[in] bufferWidth The width of the image buffer.
740  * @param[in] bufferHeight The height of the image buffer.
741  * @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).
742  *
743  * @return An image buffer.
744  */
745 inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat)
746 {
747   Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
748
749   if(Pixel::RGBA8888 == pixelFormat)
750   {
751     const uint32_t bufferSizeInt  = bufferWidth * bufferHeight;
752     const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
753     memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
754   }
755   else
756   {
757     memset(imageBuffer.GetBuffer(), 0, bufferWidth * bufferHeight);
758   }
759
760   return imageBuffer;
761 }
762
763 /**
764  * @brief Combine the two RGBA image buffers together.
765  *
766  * The top layer buffer will blend over the bottom layer buffer:
767  * - If the pixel is not fully opaque from either buffer, it will be blended with
768  *   the pixel from the other buffer and copied to the combined buffer.
769  * - If the pixels from both buffers are fully opaque, the pixels from the top layer
770  *   buffer will be copied to the combined buffer.
771  *
772  * Due to the performance issue, We need to re-use input'ed pixelBuffer memory.
773  * We can determine which pixelBuffer's memory is destination
774  *
775  * @param[in, out] topPixelBuffer The top layer buffer.
776  * @param[in, out] bottomPixelBuffer The bottom layer buffer.
777  * @param[in] bufferWidth The width of the image buffer.
778  * @param[in] bufferHeight The height of the image buffer.
779  * @param[in] storeResultIntoTop True if we store the combined image buffer result into topPixelBuffer.
780  * False if we store the combined image buffer result into bottomPixelBuffer.
781  *
782  */
783 void CombineImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, bool storeResultIntoTop)
784 {
785   // Assume that we always combine two RGBA images
786   // Jump with 4bytes for optimize runtime.
787   uint32_t* topBuffer    = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
788   uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
789
790   if(topBuffer == NULL && bottomBuffer == NULL)
791   {
792     // Nothing to do if both buffers are empty.
793     return;
794   }
795
796   if(topBuffer == NULL)
797   {
798     // Nothing to do if topBuffer is empty.
799     // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
800     if(storeResultIntoTop)
801     {
802       topPixelBuffer = bottomPixelBuffer;
803     }
804     return;
805   }
806
807   if(bottomBuffer == NULL)
808   {
809     // Nothing to do if bottomBuffer is empty.
810     // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
811     if(!storeResultIntoTop)
812     {
813       bottomPixelBuffer = topPixelBuffer;
814     }
815     return;
816   }
817
818   const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
819
820   uint32_t* __restrict__ combinedBuffer       = storeResultIntoTop ? topBuffer : bottomBuffer;
821   uint8_t* __restrict__ topAlphaBufferPointer = reinterpret_cast<uint8_t*>(topBuffer) + 3;
822
823   for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
824   {
825     // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
826     // Otherwise, copy pixel from topBuffer to combinedBuffer.
827     // Note : Be careful when we read & write into combinedBuffer. It can be write into same pointer.
828
829     uint8_t topAlpha = *topAlphaBufferPointer;
830
831     if(topAlpha == 0)
832     {
833       // Copy the pixel from bottomBuffer to combinedBuffer
834       if(storeResultIntoTop)
835       {
836         *(combinedBuffer) = *(bottomBuffer);
837       }
838     }
839     else if(topAlpha == 255)
840     {
841       // Copy the pixel from topBuffer to combinedBuffer
842       if(!storeResultIntoTop)
843       {
844         *(combinedBuffer) = *(topBuffer);
845       }
846     }
847     else
848     {
849       // At least one pixel is not fully opaque
850       // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
851       uint32_t blendedBottomBufferColor                    = *(bottomBuffer);
852       uint8_t* __restrict__ blendedBottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&blendedBottomBufferColor);
853
854       blendedBottomBufferColorBuffer[0] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[0], 255 - topAlpha);
855       blendedBottomBufferColorBuffer[1] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[1], 255 - topAlpha);
856       blendedBottomBufferColorBuffer[2] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[2], 255 - topAlpha);
857       blendedBottomBufferColorBuffer[3] = MultiplyAndNormalizeColor(blendedBottomBufferColorBuffer[3], 255 - topAlpha);
858
859       *(combinedBuffer) = *(topBuffer) + blendedBottomBufferColor;
860     }
861
862     // Increase each buffer's pointer.
863     ++combinedBuffer;
864     ++topBuffer;
865     ++bottomBuffer;
866     topAlphaBufferPointer += sizeof(uint32_t) / sizeof(uint8_t);
867   }
868 }
869
870 } // namespace
871
872 TypesetterPtr Typesetter::New(const ModelInterface* const model)
873 {
874   return TypesetterPtr(new Typesetter(model));
875 }
876
877 ViewModel* Typesetter::GetViewModel()
878 {
879   return mModel;
880 }
881
882 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
883 {
884   // @todo. This initial implementation for a TextLabel has only one visible page.
885
886   // Elides the text if needed.
887   mModel->ElideGlyphs();
888
889   // Retrieves the layout size.
890   const Size& layoutSize = mModel->GetLayoutSize();
891
892   const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
893
894   // Set the offset for the horizontal alignment according to the text direction and outline width.
895   int32_t penX = 0;
896
897   switch(mModel->GetHorizontalAlignment())
898   {
899     case HorizontalAlignment::BEGIN:
900     {
901       // No offset to add.
902       break;
903     }
904     case HorizontalAlignment::CENTER:
905     {
906       penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth : outlineWidth;
907       break;
908     }
909     case HorizontalAlignment::END:
910     {
911       penX += (textDirection == Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT) ? -outlineWidth * 2 : outlineWidth * 2;
912       break;
913     }
914   }
915
916   // Set the offset for the vertical alignment.
917   int32_t penY = 0u;
918
919   switch(mModel->GetVerticalAlignment())
920   {
921     case VerticalAlignment::TOP:
922     {
923       // No offset to add.
924       break;
925     }
926     case VerticalAlignment::CENTER:
927     {
928       penY = static_cast<int32_t>(0.5f * (size.height - layoutSize.height));
929       penY = penY < 0.f ? 0.f : penY;
930       break;
931     }
932     case VerticalAlignment::BOTTOM:
933     {
934       penY = static_cast<int32_t>(size.height - layoutSize.height);
935       break;
936     }
937   }
938
939   // Generate the image buffers of the text for each different style first,
940   // then combine all of them together as one final image buffer. We try to
941   // do all of these in CPU only, so that once the final texture is generated,
942   // no calculation is needed in GPU during each frame.
943
944   const uint32_t bufferWidth  = static_cast<uint32_t>(size.width);
945   const uint32_t bufferHeight = static_cast<uint32_t>(size.height);
946
947   const uint32_t bufferSizeInt  = bufferWidth * bufferHeight;
948   const uint32_t bufferSizeChar = sizeof(uint32_t) * bufferSizeInt;
949
950   //Elided text in ellipsis at START could start on index greater than 0
951   auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
952   auto endIndexOfGlyphs   = mModel->GetEndIndexOfElidedGlyphs();
953
954   Devel::PixelBuffer imageBuffer;
955
956   if(RENDER_MASK == behaviour)
957   {
958     // Generate the image buffer as an alpha mask for color glyphs.
959     imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
960   }
961   else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
962   {
963     // Generate an empty image buffer so that it can been combined with the image buffers for styles
964     imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
965     memset(imageBuffer.GetBuffer(), 0u, bufferSizeChar);
966   }
967   else
968   {
969     // Generate the image buffer for the text with no style.
970     imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
971   }
972
973   if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
974   {
975     // Generate the outline if enabled
976     const uint16_t outlineWidth = mModel->GetOutlineWidth();
977     const float    outlineAlpha = mModel->GetOutlineColor().a;
978     if(outlineWidth != 0u && fabsf(outlineAlpha) > Math::MACHINE_EPSILON_1 && RENDER_OVERLAY_STYLE != behaviour)
979     {
980       // Create the image buffer for outline
981       Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
982
983       // Combine the two buffers
984       CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
985     }
986
987     // @todo. Support shadow for partial text later on.
988
989     // Generate the shadow if enabled
990     const Vector2& shadowOffset = mModel->GetShadowOffset();
991     const float    shadowAlpha  = mModel->GetShadowColor().a;
992     if(RENDER_OVERLAY_STYLE != behaviour && fabsf(shadowAlpha) > Math::MACHINE_EPSILON_1 && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
993     {
994       // Create the image buffer for shadow
995       Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
996
997       // Check whether it will be a soft shadow
998       const float& blurRadius = mModel->GetShadowBlurRadius();
999
1000       if(blurRadius > Math::MACHINE_EPSILON_1)
1001       {
1002         shadowImageBuffer.ApplyGaussianBlur(blurRadius);
1003       }
1004
1005       // Combine the two buffers
1006       CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
1007     }
1008
1009     // Generate the background if enabled
1010     const bool backgroundEnabled   = mModel->IsBackgroundEnabled();
1011     const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
1012     if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
1013     {
1014       Devel::PixelBuffer backgroundImageBuffer;
1015
1016       if(backgroundEnabled)
1017       {
1018         backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1019       }
1020       else
1021       {
1022         backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1023       }
1024
1025       if(backgroundMarkupSet)
1026       {
1027         DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
1028       }
1029
1030       // Combine the two buffers
1031       CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1032     }
1033
1034     if(RENDER_OVERLAY_STYLE == behaviour)
1035     {
1036       if(mModel->IsUnderlineEnabled())
1037       {
1038         // Create the image buffer for underline
1039         Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1040
1041         // Combine the two buffers
1042         CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
1043       }
1044
1045       if(mModel->IsStrikethroughEnabled())
1046       {
1047         // Create the image buffer for strikethrough
1048         Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
1049
1050         // Combine the two buffers
1051         CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
1052       }
1053
1054       // Markup-Processor for overlay styles
1055       if(mModel->IsMarkupProcessorEnabled())
1056       {
1057         if(mModel->IsMarkupUnderlineSet())
1058         {
1059           imageBuffer = ApplyUnderlineMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1060         }
1061
1062         if(mModel->IsMarkupStrikethroughSet())
1063         {
1064           imageBuffer = ApplyStrikethroughMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1065         }
1066       }
1067     }
1068   }
1069
1070   // Create the final PixelData for the combined image buffer
1071   PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
1072
1073   return pixelData;
1074 }
1075
1076 Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Typesetter::Style style, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset, const GlyphIndex fromGlyphIndex, const GlyphIndex toGlyphIndex)
1077 {
1078   // Retrieve lines, glyphs, positions and colors from the view model.
1079   const Length modelNumberOfLines                       = mModel->GetNumberOfLines();
1080   const LineRun* const __restrict__ modelLinesBuffer    = mModel->GetLines();
1081   const GlyphInfo* const __restrict__ glyphsBuffer      = mModel->GetGlyphs();
1082   const Vector2* const __restrict__ positionBuffer      = mModel->GetLayout();
1083   const Vector4* const __restrict__ colorsBuffer        = mModel->GetColors();
1084   const ColorIndex* const __restrict__ colorIndexBuffer = mModel->GetColorIndices();
1085   const GlyphInfo* __restrict__ hyphens                 = mModel->GetHyphens();
1086   const Length* __restrict__ hyphenIndices              = mModel->GetHyphenIndices();
1087   const Length hyphensCount                             = mModel->GetHyphensCount();
1088
1089   // Elided text info. Indices according to elided text and Ellipsis position.
1090   const auto startIndexOfGlyphs              = mModel->GetStartIndexOfElidedGlyphs();
1091   const auto endIndexOfGlyphs                = mModel->GetEndIndexOfElidedGlyphs();
1092   const auto firstMiddleIndexOfElidedGlyphs  = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1093   const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1094   const auto ellipsisPosition                = mModel->GetEllipsisPosition();
1095
1096   // Whether to use the default color.
1097   const bool     useDefaultColor = (NULL == colorsBuffer);
1098   const Vector4& defaultColor    = mModel->GetDefaultColor();
1099
1100   // Create and initialize the pixel buffer.
1101   GlyphData glyphData;
1102   glyphData.verticalOffset   = verticalOffset;
1103   glyphData.width            = bufferWidth;
1104   glyphData.height           = bufferHeight;
1105   glyphData.bitmapBuffer     = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1106   glyphData.horizontalOffset = 0;
1107
1108   // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1109   TextAbstraction::FontClient fontClient  = TextAbstraction::FontClient::Get();
1110   Length                      hyphenIndex = 0;
1111
1112   const Character* __restrict__ textBuffer                       = mModel->GetTextBuffer();
1113   float calculatedAdvance                                        = 0.f;
1114   const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1115   const CharacterIndex* __restrict__ glyphToCharacterMapBuffer   = glyphToCharacterMap.Begin();
1116
1117   const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1118
1119   // Traverses the lines of the text.
1120   for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1121   {
1122     const LineRun& line = *(modelLinesBuffer + lineIndex);
1123
1124     // Sets the horizontal offset of the line.
1125     glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1126     glyphData.horizontalOffset += horizontalOffset;
1127
1128     // Increases the vertical offset with the line's ascender.
1129     glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1130
1131     // Retrieves the glyph's outline width
1132     float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1133
1134     if(style == Typesetter::STYLE_OUTLINE)
1135     {
1136       glyphData.horizontalOffset -= outlineWidth;
1137       if(lineIndex == 0u)
1138       {
1139         // Only need to add the vertical outline offset for the first line
1140         glyphData.verticalOffset -= outlineWidth;
1141       }
1142     }
1143     else if(style == Typesetter::STYLE_SHADOW)
1144     {
1145       const Vector2& shadowOffset = mModel->GetShadowOffset();
1146       glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1147
1148       if(lineIndex == 0u)
1149       {
1150         // Only need to add the vertical shadow offset for first line
1151         glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1152       }
1153     }
1154
1155     const bool  underlineEnabled      = mModel->IsUnderlineEnabled();
1156     const bool  strikethroughEnabled  = mModel->IsStrikethroughEnabled();
1157     const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1158
1159     // Get the character-spacing runs.
1160     const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1161
1162     // Aggregate underline-style-properties from mModel
1163     const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1164                                                             mModel->GetUnderlineColor(),
1165                                                             mModel->GetUnderlineHeight(),
1166                                                             mModel->GetDashedUnderlineGap(),
1167                                                             mModel->GetDashedUnderlineWidth(),
1168                                                             true,
1169                                                             true,
1170                                                             true,
1171                                                             true,
1172                                                             true};
1173
1174     // Aggregate strikethrough-style-properties from mModel
1175     const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1176                                                                     mModel->GetStrikethroughHeight(),
1177                                                                     true,
1178                                                                     true};
1179
1180     // Get the underline runs.
1181     const Length               numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1182     Vector<UnderlinedGlyphRun> underlineRuns;
1183     underlineRuns.Resize(numberOfUnderlineRuns);
1184     mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1185
1186     // Get the strikethrough runs.
1187     const Length                  numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1188     Vector<StrikethroughGlyphRun> strikethroughRuns;
1189     strikethroughRuns.Resize(numberOfStrikethroughRuns);
1190     mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1191
1192     bool thereAreUnderlinedGlyphs    = false;
1193     bool thereAreStrikethroughGlyphs = false;
1194
1195     float currentUnderlinePosition   = 0.0f;
1196     float currentUnderlineHeight     = modelUnderlineProperties.height;
1197     float maxUnderlineHeight         = currentUnderlineHeight;
1198     auto  currentUnderlineProperties = modelUnderlineProperties;
1199
1200     float currentStrikethroughHeight     = modelStrikethroughProperties.height;
1201     float maxStrikethroughHeight         = currentStrikethroughHeight;
1202     auto  currentStrikethroughProperties = modelStrikethroughProperties;
1203     float strikethroughStartingYPosition = 0.0f;
1204
1205     FontId lastFontId = 0;
1206
1207     float lineExtentLeft  = bufferWidth;
1208     float lineExtentRight = 0.0f;
1209     float baseline        = 0.0f;
1210     bool  addHyphen       = false;
1211
1212     // Traverses the glyphs of the line.
1213     const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1214     GlyphIndex       endGlyphIndex   = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1215     endGlyphIndex                    = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1216
1217     for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1218     {
1219       //To handle START case of ellipsis, the first glyph has been shifted
1220       //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1221       GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1222
1223       //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.
1224       if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1225       {
1226         if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1227            glyphIndex < secondMiddleIndexOfElidedGlyphs)
1228         {
1229           // Ignore any glyph that removed for MIDDLE ellipsis
1230           continue;
1231         }
1232         if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1233         {
1234           elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1235         }
1236       }
1237
1238       // Retrieve the glyph's info.
1239       const GlyphInfo* glyphInfo;
1240
1241       if(addHyphen && hyphens)
1242       {
1243         glyphInfo = hyphens + hyphenIndex;
1244         hyphenIndex++;
1245       }
1246       else
1247       {
1248         glyphInfo = glyphsBuffer + elidedGlyphIndex;
1249       }
1250
1251       if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1252          (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1253       {
1254         // Nothing to do if the glyph's width or height is zero.
1255         continue;
1256       }
1257
1258       Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1259       const bool                                underlineGlyph              = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1260       currentUnderlineProperties                                            = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1261       currentUnderlineHeight                                                = currentUnderlineProperties.height;
1262       thereAreUnderlinedGlyphs                                              = thereAreUnderlinedGlyphs || underlineGlyph;
1263
1264       Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1265       const bool                                   strikethroughGlyph             = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1266       currentStrikethroughProperties                                              = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1267       currentStrikethroughHeight                                                  = currentStrikethroughProperties.height;
1268       thereAreStrikethroughGlyphs                                                 = thereAreStrikethroughGlyphs || strikethroughGlyph;
1269
1270       // Are we still using the same fontId as previous
1271       if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1272       {
1273         // We need to fetch fresh font underline metrics
1274         FontMetrics fontMetrics;
1275         fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1276
1277         //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1278         currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1279
1280         if(underlineGlyph)
1281         {
1282           CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1283         }
1284
1285         if(strikethroughGlyph)
1286         {
1287           CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1288         }
1289
1290         // Update lastFontId because fontId is changed
1291         lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1292       }
1293
1294       // Retrieves the glyph's position.
1295       Vector2 position = *(positionBuffer + elidedGlyphIndex);
1296
1297       if(addHyphen)
1298       {
1299         GlyphInfo   tempInfo         = *(glyphsBuffer + elidedGlyphIndex);
1300         const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1301         calculatedAdvance            = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1302         position.x                   = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1303         position.y                   = -glyphInfo->yBearing;
1304       }
1305
1306       if(baseline < position.y + glyphInfo->yBearing)
1307       {
1308         baseline = position.y + glyphInfo->yBearing;
1309       }
1310
1311       // Calculate the positions of leftmost and rightmost glyphs in the current line
1312       if(position.x < lineExtentLeft)
1313       {
1314         lineExtentLeft = position.x;
1315       }
1316
1317       if(position.x + glyphInfo->width > lineExtentRight)
1318       {
1319         lineExtentRight = position.x + glyphInfo->width;
1320       }
1321
1322       // Retrieves the glyph's color.
1323       const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1324
1325       Vector4 color;
1326       if(style == Typesetter::STYLE_SHADOW)
1327       {
1328         color = mModel->GetShadowColor();
1329       }
1330       else if(style == Typesetter::STYLE_OUTLINE)
1331       {
1332         color = mModel->GetOutlineColor();
1333       }
1334       else
1335       {
1336         color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1337       }
1338
1339       // Premultiply alpha
1340       color.r *= color.a;
1341       color.g *= color.a;
1342       color.b *= color.a;
1343
1344       // Retrieves the glyph's bitmap.
1345       glyphData.glyphBitmap.buffer = NULL;
1346       glyphData.glyphBitmap.width  = glyphInfo->width; // Desired width and height.
1347       glyphData.glyphBitmap.height = glyphInfo->height;
1348
1349       if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1350       {
1351         // Don't render outline for other styles
1352         outlineWidth = 0.0f;
1353       }
1354
1355       if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1356       {
1357         fontClient.CreateBitmap(glyphInfo->fontId,
1358                                 glyphInfo->index,
1359                                 glyphInfo->isItalicRequired,
1360                                 glyphInfo->isBoldRequired,
1361                                 glyphData.glyphBitmap,
1362                                 static_cast<int32_t>(outlineWidth));
1363       }
1364
1365       // Sets the glyph's bitmap into the bitmap of the whole text.
1366       if(NULL != glyphData.glyphBitmap.buffer)
1367       {
1368         if(style == Typesetter::STYLE_OUTLINE)
1369         {
1370           // Set the position offset for the current glyph
1371           glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1372           glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1373         }
1374
1375         // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1376         TypesetGlyph(glyphData,
1377                      &position,
1378                      &color,
1379                      style,
1380                      pixelFormat);
1381
1382         if(style == Typesetter::STYLE_OUTLINE)
1383         {
1384           // Reset the position offset for the next glyph
1385           glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1386           glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1387         }
1388
1389         // free the glyphBitmap.buffer if it is owner of buffer
1390         if(glyphData.glyphBitmap.isBufferOwned)
1391         {
1392           free(glyphData.glyphBitmap.buffer);
1393           glyphData.glyphBitmap.isBufferOwned = false;
1394         }
1395         glyphData.glyphBitmap.buffer = NULL;
1396       }
1397
1398       if(hyphenIndices)
1399       {
1400         while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1401         {
1402           hyphenIndex++;
1403         }
1404
1405         addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1406         if(addHyphen)
1407         {
1408           glyphIndex--;
1409         }
1410       }
1411     }
1412
1413     // Draw the underline from the leftmost glyph to the rightmost glyph
1414     if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1415     {
1416       DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1417     }
1418
1419     // Draw the background color from the leftmost glyph to the rightmost glyph
1420     if(style == Typesetter::STYLE_BACKGROUND)
1421     {
1422       DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1423     }
1424
1425     // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1426     if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1427     {
1428       //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.
1429       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.
1430       DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1431     }
1432
1433     // Increases the vertical offset with the line's descender & line spacing.
1434     glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1435   }
1436
1437   return glyphData.bitmapBuffer;
1438 }
1439
1440 Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset)
1441 {
1442   // Underline-tags (this is for Markup case)
1443   // Get the underline runs.
1444   const Length               numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1445   Vector<UnderlinedGlyphRun> underlineRuns;
1446   underlineRuns.Resize(numberOfUnderlineRuns);
1447   mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1448
1449   // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1450   Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun    = underlineRuns.Begin();
1451   Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1452   GlyphIndex                                startGlyphIndex, endGlyphIndex;
1453
1454   //The outer loop to iterate on the separated chunks of underlined glyph runs
1455   while(itGlyphRun != endItGlyphRun)
1456   {
1457     startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1458     endGlyphIndex   = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1459
1460     // Create the image buffer for underline
1461     Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1462     // Combine the two buffers
1463     // Result pixel buffer will be stored into topPixelBuffer.
1464     CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1465
1466     itGlyphRun++;
1467   }
1468
1469   return topPixelBuffer;
1470 }
1471
1472 Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset)
1473 {
1474   // strikethrough-tags (this is for Markup case)
1475   // Get the strikethrough runs.
1476   const Length                  numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1477   Vector<StrikethroughGlyphRun> strikethroughRuns;
1478   strikethroughRuns.Resize(numberOfStrikethroughRuns);
1479   mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1480
1481   // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1482   Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun    = strikethroughRuns.Begin();
1483   Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1484   GlyphIndex                                   startGlyphIndex, endGlyphIndex;
1485
1486   //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1487   while(itGlyphRun != endItGlyphRun)
1488   {
1489     startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1490     endGlyphIndex   = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1491
1492     // Create the image buffer for strikethrough
1493     Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1494     // Combine the two buffers
1495     // Result pixel buffer will be stored into topPixelBuffer.
1496     CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1497
1498     itGlyphRun++;
1499   }
1500
1501   return topPixelBuffer;
1502 }
1503
1504 Typesetter::Typesetter(const ModelInterface* const model)
1505 : mModel(new ViewModel(model))
1506 {
1507 }
1508
1509 Typesetter::~Typesetter()
1510 {
1511   delete mModel;
1512 }
1513
1514 } // namespace Text
1515
1516 } // namespace Toolkit
1517
1518 } // namespace Dali