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