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