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