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