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