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