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