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