Fix markup underline, strikethrough in overlay style
[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     if(outlineWidth != 0u && RENDER_OVERLAY_STYLE != behaviour)
978     {
979       // Create the image buffer for outline
980       Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
981
982       // Combine the two buffers
983       CombineImageBuffer(imageBuffer, outlineImageBuffer, bufferWidth, bufferHeight, true);
984     }
985
986     // @todo. Support shadow for partial text later on.
987
988     // Generate the shadow if enabled
989     const Vector2& shadowOffset = mModel->GetShadowOffset();
990     if(RENDER_OVERLAY_STYLE != behaviour && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
991     {
992       // Create the image buffer for shadow
993       Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
994
995       // Check whether it will be a soft shadow
996       const float& blurRadius = mModel->GetShadowBlurRadius();
997
998       if(blurRadius > Math::MACHINE_EPSILON_1)
999       {
1000         shadowImageBuffer.ApplyGaussianBlur(blurRadius);
1001       }
1002
1003       // Combine the two buffers
1004       CombineImageBuffer(imageBuffer, shadowImageBuffer, bufferWidth, bufferHeight, true);
1005     }
1006
1007     // Generate the background if enabled
1008     const bool backgroundEnabled   = mModel->IsBackgroundEnabled();
1009     const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
1010     if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
1011     {
1012       Devel::PixelBuffer backgroundImageBuffer;
1013
1014       if(backgroundEnabled)
1015       {
1016         backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1017       }
1018       else
1019       {
1020         backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1021       }
1022
1023       if(backgroundMarkupSet)
1024       {
1025         DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
1026       }
1027
1028       // Combine the two buffers
1029       CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
1030     }
1031
1032     if(RENDER_OVERLAY_STYLE == behaviour)
1033     {
1034       if(mModel->IsUnderlineEnabled())
1035       {
1036         // Create the image buffer for underline
1037         Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
1038
1039         // Combine the two buffers
1040         CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
1041       }
1042
1043       if(mModel->IsStrikethroughEnabled())
1044       {
1045         // Create the image buffer for strikethrough
1046         Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
1047
1048         // Combine the two buffers
1049         CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
1050       }
1051
1052       // Markup-Processor for overlay styles
1053       if(mModel->IsMarkupProcessorEnabled())
1054       {
1055         if(mModel->IsMarkupUnderlineSet())
1056         {
1057           imageBuffer = ApplyUnderlineMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1058         }
1059
1060         if(mModel->IsMarkupStrikethroughSet())
1061         {
1062           imageBuffer = ApplyStrikethroughMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
1063         }
1064       }
1065     }
1066   }
1067
1068   // Create the final PixelData for the combined image buffer
1069   PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
1070
1071   return pixelData;
1072 }
1073
1074 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)
1075 {
1076   // Retrieve lines, glyphs, positions and colors from the view model.
1077   const Length modelNumberOfLines                       = mModel->GetNumberOfLines();
1078   const LineRun* const __restrict__ modelLinesBuffer    = mModel->GetLines();
1079   const GlyphInfo* const __restrict__ glyphsBuffer      = mModel->GetGlyphs();
1080   const Vector2* const __restrict__ positionBuffer      = mModel->GetLayout();
1081   const Vector4* const __restrict__ colorsBuffer        = mModel->GetColors();
1082   const ColorIndex* const __restrict__ colorIndexBuffer = mModel->GetColorIndices();
1083   const GlyphInfo* __restrict__ hyphens                 = mModel->GetHyphens();
1084   const Length* __restrict__ hyphenIndices              = mModel->GetHyphenIndices();
1085   const Length hyphensCount                             = mModel->GetHyphensCount();
1086
1087   // Elided text info. Indices according to elided text and Ellipsis position.
1088   const auto startIndexOfGlyphs              = mModel->GetStartIndexOfElidedGlyphs();
1089   const auto endIndexOfGlyphs                = mModel->GetEndIndexOfElidedGlyphs();
1090   const auto firstMiddleIndexOfElidedGlyphs  = mModel->GetFirstMiddleIndexOfElidedGlyphs();
1091   const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
1092   const auto ellipsisPosition                = mModel->GetEllipsisPosition();
1093
1094   // Whether to use the default color.
1095   const bool     useDefaultColor = (NULL == colorsBuffer);
1096   const Vector4& defaultColor    = mModel->GetDefaultColor();
1097
1098   // Create and initialize the pixel buffer.
1099   GlyphData glyphData;
1100   glyphData.verticalOffset   = verticalOffset;
1101   glyphData.width            = bufferWidth;
1102   glyphData.height           = bufferHeight;
1103   glyphData.bitmapBuffer     = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
1104   glyphData.horizontalOffset = 0;
1105
1106   // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
1107   TextAbstraction::FontClient fontClient  = TextAbstraction::FontClient::Get();
1108   Length                      hyphenIndex = 0;
1109
1110   const Character* __restrict__ textBuffer                       = mModel->GetTextBuffer();
1111   float calculatedAdvance                                        = 0.f;
1112   const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = mModel->GetGlyphsToCharacters();
1113   const CharacterIndex* __restrict__ glyphToCharacterMapBuffer   = glyphToCharacterMap.Begin();
1114
1115   const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
1116
1117   // Traverses the lines of the text.
1118   for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
1119   {
1120     const LineRun& line = *(modelLinesBuffer + lineIndex);
1121
1122     // Sets the horizontal offset of the line.
1123     glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
1124     glyphData.horizontalOffset += horizontalOffset;
1125
1126     // Increases the vertical offset with the line's ascender.
1127     glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
1128
1129     // Retrieves the glyph's outline width
1130     float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1131
1132     if(style == Typesetter::STYLE_OUTLINE)
1133     {
1134       glyphData.horizontalOffset -= outlineWidth;
1135       if(lineIndex == 0u)
1136       {
1137         // Only need to add the vertical outline offset for the first line
1138         glyphData.verticalOffset -= outlineWidth;
1139       }
1140     }
1141     else if(style == Typesetter::STYLE_SHADOW)
1142     {
1143       const Vector2& shadowOffset = mModel->GetShadowOffset();
1144       glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
1145
1146       if(lineIndex == 0u)
1147       {
1148         // Only need to add the vertical shadow offset for first line
1149         glyphData.verticalOffset += shadowOffset.y - outlineWidth;
1150       }
1151     }
1152
1153     const bool  underlineEnabled      = mModel->IsUnderlineEnabled();
1154     const bool  strikethroughEnabled  = mModel->IsStrikethroughEnabled();
1155     const float modelCharacterSpacing = mModel->GetCharacterSpacing();
1156
1157     // Get the character-spacing runs.
1158     const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
1159
1160     // Aggregate underline-style-properties from mModel
1161     const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
1162                                                             mModel->GetUnderlineColor(),
1163                                                             mModel->GetUnderlineHeight(),
1164                                                             mModel->GetDashedUnderlineGap(),
1165                                                             mModel->GetDashedUnderlineWidth(),
1166                                                             true,
1167                                                             true,
1168                                                             true,
1169                                                             true,
1170                                                             true};
1171
1172     // Aggregate strikethrough-style-properties from mModel
1173     const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
1174                                                                     mModel->GetStrikethroughHeight(),
1175                                                                     true,
1176                                                                     true};
1177
1178     // Get the underline runs.
1179     const Length               numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1180     Vector<UnderlinedGlyphRun> underlineRuns;
1181     underlineRuns.Resize(numberOfUnderlineRuns);
1182     mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1183
1184     // Get the strikethrough runs.
1185     const Length                  numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1186     Vector<StrikethroughGlyphRun> strikethroughRuns;
1187     strikethroughRuns.Resize(numberOfStrikethroughRuns);
1188     mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1189
1190     bool thereAreUnderlinedGlyphs    = false;
1191     bool thereAreStrikethroughGlyphs = false;
1192
1193     float currentUnderlinePosition   = 0.0f;
1194     float currentUnderlineHeight     = modelUnderlineProperties.height;
1195     float maxUnderlineHeight         = currentUnderlineHeight;
1196     auto  currentUnderlineProperties = modelUnderlineProperties;
1197
1198     float currentStrikethroughHeight     = modelStrikethroughProperties.height;
1199     float maxStrikethroughHeight         = currentStrikethroughHeight;
1200     auto  currentStrikethroughProperties = modelStrikethroughProperties;
1201     float strikethroughStartingYPosition = 0.0f;
1202
1203     FontId lastFontId = 0;
1204
1205     float lineExtentLeft  = bufferWidth;
1206     float lineExtentRight = 0.0f;
1207     float baseline        = 0.0f;
1208     bool  addHyphen       = false;
1209
1210     // Traverses the glyphs of the line.
1211     const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
1212     GlyphIndex       endGlyphIndex   = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
1213     endGlyphIndex                    = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
1214
1215     for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
1216     {
1217       //To handle START case of ellipsis, the first glyph has been shifted
1218       //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
1219       GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
1220
1221       //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.
1222       if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1223       {
1224         if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
1225            glyphIndex < secondMiddleIndexOfElidedGlyphs)
1226         {
1227           // Ignore any glyph that removed for MIDDLE ellipsis
1228           continue;
1229         }
1230         if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
1231         {
1232           elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
1233         }
1234       }
1235
1236       // Retrieve the glyph's info.
1237       const GlyphInfo* glyphInfo;
1238
1239       if(addHyphen && hyphens)
1240       {
1241         glyphInfo = hyphens + hyphenIndex;
1242         hyphenIndex++;
1243       }
1244       else
1245       {
1246         glyphInfo = glyphsBuffer + elidedGlyphIndex;
1247       }
1248
1249       if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
1250          (glyphInfo->height < Math::MACHINE_EPSILON_1000))
1251       {
1252         // Nothing to do if the glyph's width or height is zero.
1253         continue;
1254       }
1255
1256       Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
1257       const bool                                underlineGlyph              = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
1258       currentUnderlineProperties                                            = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
1259       currentUnderlineHeight                                                = currentUnderlineProperties.height;
1260       thereAreUnderlinedGlyphs                                              = thereAreUnderlinedGlyphs || underlineGlyph;
1261
1262       Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
1263       const bool                                   strikethroughGlyph             = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
1264       currentStrikethroughProperties                                              = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
1265       currentStrikethroughHeight                                                  = currentStrikethroughProperties.height;
1266       thereAreStrikethroughGlyphs                                                 = thereAreStrikethroughGlyphs || strikethroughGlyph;
1267
1268       // Are we still using the same fontId as previous
1269       if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
1270       {
1271         // We need to fetch fresh font underline metrics
1272         FontMetrics fontMetrics;
1273         fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
1274
1275         //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1276         currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1277
1278         if(underlineGlyph)
1279         {
1280           CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1281         }
1282
1283         if(strikethroughGlyph)
1284         {
1285           CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1286         }
1287
1288         // Update lastFontId because fontId is changed
1289         lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1290       }
1291
1292       // Retrieves the glyph's position.
1293       Vector2 position = *(positionBuffer + elidedGlyphIndex);
1294
1295       if(addHyphen)
1296       {
1297         GlyphInfo   tempInfo         = *(glyphsBuffer + elidedGlyphIndex);
1298         const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
1299         calculatedAdvance            = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1300         position.x                   = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1301         position.y                   = -glyphInfo->yBearing;
1302       }
1303
1304       if(baseline < position.y + glyphInfo->yBearing)
1305       {
1306         baseline = position.y + glyphInfo->yBearing;
1307       }
1308
1309       // Calculate the positions of leftmost and rightmost glyphs in the current line
1310       if(position.x < lineExtentLeft)
1311       {
1312         lineExtentLeft = position.x;
1313       }
1314
1315       if(position.x + glyphInfo->width > lineExtentRight)
1316       {
1317         lineExtentRight = position.x + glyphInfo->width;
1318       }
1319
1320       // Retrieves the glyph's color.
1321       const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1322
1323       Vector4 color;
1324       if(style == Typesetter::STYLE_SHADOW)
1325       {
1326         color = mModel->GetShadowColor();
1327       }
1328       else if(style == Typesetter::STYLE_OUTLINE)
1329       {
1330         color = mModel->GetOutlineColor();
1331       }
1332       else
1333       {
1334         color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1335       }
1336
1337       // Premultiply alpha
1338       color.r *= color.a;
1339       color.g *= color.a;
1340       color.b *= color.a;
1341
1342       // Retrieves the glyph's bitmap.
1343       glyphData.glyphBitmap.buffer = NULL;
1344       glyphData.glyphBitmap.width  = glyphInfo->width; // Desired width and height.
1345       glyphData.glyphBitmap.height = glyphInfo->height;
1346
1347       if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1348       {
1349         // Don't render outline for other styles
1350         outlineWidth = 0.0f;
1351       }
1352
1353       if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1354       {
1355         fontClient.CreateBitmap(glyphInfo->fontId,
1356                                 glyphInfo->index,
1357                                 glyphInfo->isItalicRequired,
1358                                 glyphInfo->isBoldRequired,
1359                                 glyphData.glyphBitmap,
1360                                 static_cast<int32_t>(outlineWidth));
1361       }
1362
1363       // Sets the glyph's bitmap into the bitmap of the whole text.
1364       if(NULL != glyphData.glyphBitmap.buffer)
1365       {
1366         if(style == Typesetter::STYLE_OUTLINE)
1367         {
1368           // Set the position offset for the current glyph
1369           glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1370           glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1371         }
1372
1373         // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1374         TypesetGlyph(glyphData,
1375                      &position,
1376                      &color,
1377                      style,
1378                      pixelFormat);
1379
1380         if(style == Typesetter::STYLE_OUTLINE)
1381         {
1382           // Reset the position offset for the next glyph
1383           glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1384           glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1385         }
1386
1387         // free the glyphBitmap.buffer if it is owner of buffer
1388         if(glyphData.glyphBitmap.isBufferOwned)
1389         {
1390           free(glyphData.glyphBitmap.buffer);
1391           glyphData.glyphBitmap.isBufferOwned = false;
1392         }
1393         glyphData.glyphBitmap.buffer = NULL;
1394       }
1395
1396       if(hyphenIndices)
1397       {
1398         while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1399         {
1400           hyphenIndex++;
1401         }
1402
1403         addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1404         if(addHyphen)
1405         {
1406           glyphIndex--;
1407         }
1408       }
1409     }
1410
1411     // Draw the underline from the leftmost glyph to the rightmost glyph
1412     if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1413     {
1414       DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1415     }
1416
1417     // Draw the background color from the leftmost glyph to the rightmost glyph
1418     if(style == Typesetter::STYLE_BACKGROUND)
1419     {
1420       DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1421     }
1422
1423     // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1424     if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1425     {
1426       //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.
1427       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.
1428       DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1429     }
1430
1431     // Increases the vertical offset with the line's descender & line spacing.
1432     glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
1433   }
1434
1435   return glyphData.bitmapBuffer;
1436 }
1437
1438 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)
1439 {
1440   // Underline-tags (this is for Markup case)
1441   // Get the underline runs.
1442   const Length               numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1443   Vector<UnderlinedGlyphRun> underlineRuns;
1444   underlineRuns.Resize(numberOfUnderlineRuns);
1445   mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1446
1447   // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1448   Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun    = underlineRuns.Begin();
1449   Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1450   GlyphIndex                                startGlyphIndex, endGlyphIndex;
1451
1452   //The outer loop to iterate on the separated chunks of underlined glyph runs
1453   while(itGlyphRun != endItGlyphRun)
1454   {
1455     startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1456     endGlyphIndex   = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1457
1458     // Create the image buffer for underline
1459     Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1460     // Combine the two buffers
1461     // Result pixel buffer will be stored into topPixelBuffer.
1462     CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1463
1464     itGlyphRun++;
1465   }
1466
1467   return topPixelBuffer;
1468 }
1469
1470 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)
1471 {
1472   // strikethrough-tags (this is for Markup case)
1473   // Get the strikethrough runs.
1474   const Length                  numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1475   Vector<StrikethroughGlyphRun> strikethroughRuns;
1476   strikethroughRuns.Resize(numberOfStrikethroughRuns);
1477   mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1478
1479   // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1480   Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun    = strikethroughRuns.Begin();
1481   Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1482   GlyphIndex                                   startGlyphIndex, endGlyphIndex;
1483
1484   //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1485   while(itGlyphRun != endItGlyphRun)
1486   {
1487     startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1488     endGlyphIndex   = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1489
1490     // Create the image buffer for strikethrough
1491     Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1492     // Combine the two buffers
1493     // Result pixel buffer will be stored into topPixelBuffer.
1494     CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
1495
1496     itGlyphRun++;
1497   }
1498
1499   return topPixelBuffer;
1500 }
1501
1502 Typesetter::Typesetter(const ModelInterface* const model)
1503 : mModel(new ViewModel(model))
1504 {
1505 }
1506
1507 Typesetter::~Typesetter()
1508 {
1509   delete mModel;
1510 }
1511
1512 } // namespace Text
1513
1514 } // namespace Toolkit
1515
1516 } // namespace Dali