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