fix linespacing calculation in TextLabel
[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     // Retrieves the glyph's outline width
851     float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
852
853     if(style == Typesetter::STYLE_OUTLINE)
854     {
855       glyphData.horizontalOffset -= outlineWidth;
856       if(lineIndex == 0u)
857       {
858         // Only need to add the vertical outline offset for the first line
859         glyphData.verticalOffset -= outlineWidth;
860       }
861     }
862     else if(style == Typesetter::STYLE_SHADOW)
863     {
864       const Vector2& shadowOffset = mModel->GetShadowOffset();
865       glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
866
867       if(lineIndex == 0u)
868       {
869         // Only need to add the vertical shadow offset for first line
870         glyphData.verticalOffset += shadowOffset.y - outlineWidth;
871       }
872     }
873
874     const bool  underlineEnabled     = mModel->IsUnderlineEnabled();
875     const bool  strikethroughEnabled = mModel->IsStrikethroughEnabled();
876     const float characterSpacing     = mModel->GetCharacterSpacing();
877
878     // Aggregate underline-style-properties from mModel
879     const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
880                                                             mModel->GetUnderlineColor(),
881                                                             mModel->GetUnderlineHeight(),
882                                                             mModel->GetDashedUnderlineGap(),
883                                                             mModel->GetDashedUnderlineWidth(),
884                                                             true,
885                                                             true,
886                                                             true,
887                                                             true,
888                                                             true};
889
890     // Aggregate strikethrough-style-properties from mModel
891     const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
892                                                                     mModel->GetStrikethroughHeight(),
893                                                                     true,
894                                                                     true};
895
896     // Get the underline runs.
897     const Length               numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
898     Vector<UnderlinedGlyphRun> underlineRuns;
899     underlineRuns.Resize(numberOfUnderlineRuns);
900     mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
901
902     // Get the strikethrough runs.
903     const Length                  numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
904     Vector<StrikethroughGlyphRun> strikethroughRuns;
905     strikethroughRuns.Resize(numberOfStrikethroughRuns);
906     mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
907
908     bool thereAreUnderlinedGlyphs    = false;
909     bool thereAreStrikethroughGlyphs = false;
910
911     float currentUnderlinePosition   = 0.0f;
912     float currentUnderlineHeight     = modelUnderlineProperties.height;
913     float maxUnderlineHeight         = currentUnderlineHeight;
914     auto  currentUnderlineProperties = modelUnderlineProperties;
915
916     float currentStrikethroughHeight     = modelStrikethroughProperties.height;
917     float maxStrikethroughHeight         = currentStrikethroughHeight;
918     auto  currentStrikethroughProperties = modelStrikethroughProperties;
919     float strikethroughStartingYPosition = 0.0f;
920
921     FontId lastFontId = 0;
922
923     float lineExtentLeft  = bufferWidth;
924     float lineExtentRight = 0.0f;
925     float baseline        = 0.0f;
926     bool  addHyphen       = false;
927
928     // Traverses the glyphs of the line.
929     const GlyphIndex startGlyphIndex = std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs);
930     GlyphIndex       endGlyphIndex   = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
931     endGlyphIndex                    = std::min(endGlyphIndex, endIndexOfGlyphs);
932
933     for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
934     {
935       if(glyphIndex < fromGlyphIndex || glyphIndex > toGlyphIndex)
936       {
937         // Ignore any glyph that out of the specified range
938         continue;
939       }
940
941       //To handle START case of ellipsis, the first glyph has been shifted
942       //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
943       GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
944
945       //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.
946       if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
947       {
948         if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
949            glyphIndex < secondMiddleIndexOfElidedGlyphs)
950         {
951           // Ignore any glyph that removed for MIDDLE ellipsis
952           continue;
953         }
954         if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
955         {
956           elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
957         }
958       }
959
960       // Retrieve the glyph's info.
961       const GlyphInfo* glyphInfo;
962
963       if(addHyphen && hyphens)
964       {
965         glyphInfo = hyphens + hyphenIndex;
966         hyphenIndex++;
967       }
968       else
969       {
970         glyphInfo = glyphsBuffer + elidedGlyphIndex;
971       }
972
973       if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
974          (glyphInfo->height < Math::MACHINE_EPSILON_1000))
975       {
976         // Nothing to do if the glyph's width or height is zero.
977         continue;
978       }
979
980       Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
981       const bool                                underlineGlyph              = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
982       currentUnderlineProperties                                            = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
983       currentUnderlineHeight                                                = currentUnderlineProperties.height;
984       thereAreUnderlinedGlyphs                                              = thereAreUnderlinedGlyphs || underlineGlyph;
985
986       Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
987       const bool                                   strikethroughGlyph             = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
988       currentStrikethroughProperties                                              = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
989       currentStrikethroughHeight                                                  = currentStrikethroughProperties.height;
990       thereAreStrikethroughGlyphs                                                 = thereAreStrikethroughGlyphs || strikethroughGlyph;
991
992       // Are we still using the same fontId as previous
993       if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
994       {
995         // We need to fetch fresh font underline metrics
996         FontMetrics fontMetrics;
997         fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
998
999         //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
1000         currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
1001
1002         if(underlineGlyph)
1003         {
1004           CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
1005         }
1006
1007         if(strikethroughGlyph)
1008         {
1009           CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
1010         }
1011
1012         // Update lastFontId because fontId is changed
1013         lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
1014       }
1015
1016       // Retrieves the glyph's position.
1017       Vector2 position = *(positionBuffer + elidedGlyphIndex);
1018
1019       if(addHyphen)
1020       {
1021         GlyphInfo tempInfo = *(glyphsBuffer + elidedGlyphIndex);
1022         calculatedAdvance  = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
1023         position.x         = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
1024         position.y         = -glyphInfo->yBearing;
1025       }
1026
1027       if(baseline < position.y + glyphInfo->yBearing)
1028       {
1029         baseline = position.y + glyphInfo->yBearing;
1030       }
1031
1032       // Calculate the positions of leftmost and rightmost glyphs in the current line
1033       if(position.x < lineExtentLeft)
1034       {
1035         lineExtentLeft = position.x;
1036       }
1037
1038       if(position.x + glyphInfo->width > lineExtentRight)
1039       {
1040         lineExtentRight = position.x + glyphInfo->width;
1041       }
1042
1043       // Retrieves the glyph's color.
1044       const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
1045
1046       Vector4 color;
1047       if(style == Typesetter::STYLE_SHADOW)
1048       {
1049         color = mModel->GetShadowColor();
1050       }
1051       else if(style == Typesetter::STYLE_OUTLINE)
1052       {
1053         color = mModel->GetOutlineColor();
1054       }
1055       else
1056       {
1057         color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
1058       }
1059
1060       // Premultiply alpha
1061       color.r *= color.a;
1062       color.g *= color.a;
1063       color.b *= color.a;
1064
1065       // Retrieves the glyph's bitmap.
1066       glyphData.glyphBitmap.buffer = NULL;
1067       glyphData.glyphBitmap.width  = glyphInfo->width; // Desired width and height.
1068       glyphData.glyphBitmap.height = glyphInfo->height;
1069
1070       if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
1071       {
1072         // Don't render outline for other styles
1073         outlineWidth = 0.0f;
1074       }
1075
1076       if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
1077       {
1078         fontClient.CreateBitmap(glyphInfo->fontId,
1079                                 glyphInfo->index,
1080                                 glyphInfo->isItalicRequired,
1081                                 glyphInfo->isBoldRequired,
1082                                 glyphData.glyphBitmap,
1083                                 static_cast<int>(outlineWidth));
1084       }
1085
1086       // Sets the glyph's bitmap into the bitmap of the whole text.
1087       if(NULL != glyphData.glyphBitmap.buffer)
1088       {
1089         if(style == Typesetter::STYLE_OUTLINE)
1090         {
1091           // Set the position offset for the current glyph
1092           glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
1093           glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
1094         }
1095
1096         // Set the buffer of the glyph's bitmap into the final bitmap's buffer
1097         TypesetGlyph(glyphData,
1098                      &position,
1099                      &color,
1100                      style,
1101                      pixelFormat);
1102
1103         if(style == Typesetter::STYLE_OUTLINE)
1104         {
1105           // Reset the position offset for the next glyph
1106           glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
1107           glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
1108         }
1109
1110         // delete the glyphBitmap.buffer as it is now copied into glyphData.bitmapBuffer
1111         delete[] glyphData.glyphBitmap.buffer;
1112         glyphData.glyphBitmap.buffer = NULL;
1113       }
1114
1115       if(hyphenIndices)
1116       {
1117         while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
1118         {
1119           hyphenIndex++;
1120         }
1121
1122         addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
1123         if(addHyphen)
1124         {
1125           glyphIndex--;
1126         }
1127       }
1128     }
1129
1130     // Draw the underline from the leftmost glyph to the rightmost glyph
1131     if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
1132     {
1133       DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
1134     }
1135
1136     // Draw the background color from the leftmost glyph to the rightmost glyph
1137     if(style == Typesetter::STYLE_BACKGROUND)
1138     {
1139       DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
1140     }
1141
1142     // Draw the strikethrough from the leftmost glyph to the rightmost glyph
1143     if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
1144     {
1145       //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.
1146       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.
1147       DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
1148     }
1149
1150     // Increases the vertical offset with the line's descender & line spacing.
1151     glyphData.verticalOffset += static_cast<int>(-line.descender+line.lineSpacing);
1152   }
1153
1154   return glyphData.bitmapBuffer;
1155 }
1156
1157 Devel::PixelBuffer Typesetter::CombineImageBuffer(Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight)
1158 {
1159   unsigned char* topBuffer    = topPixelBuffer.GetBuffer();
1160   unsigned char* bottomBuffer = bottomPixelBuffer.GetBuffer();
1161
1162   Devel::PixelBuffer combinedPixelBuffer;
1163
1164   if(topBuffer == NULL && bottomBuffer == NULL)
1165   {
1166     // Nothing to do if both buffers are empty.
1167     return combinedPixelBuffer;
1168   }
1169
1170   if(topBuffer == NULL)
1171   {
1172     // Nothing to do if topBuffer is empty.
1173     return bottomPixelBuffer;
1174   }
1175
1176   if(bottomBuffer == NULL)
1177   {
1178     // Nothing to do if bottomBuffer is empty.
1179     return topPixelBuffer;
1180   }
1181
1182   // Always combine two RGBA images
1183   const unsigned int bufferSizeInt  = bufferWidth * bufferHeight;
1184   const unsigned int bufferSizeChar = 4u * bufferSizeInt;
1185
1186   combinedPixelBuffer     = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
1187   uint8_t* combinedBuffer = reinterpret_cast<uint8_t*>(combinedPixelBuffer.GetBuffer());
1188   memset(combinedBuffer, 0u, bufferSizeChar);
1189
1190   for(unsigned int pixelIndex = 0; pixelIndex < bufferSizeInt; pixelIndex++)
1191   {
1192     // If the alpha of the pixel in either buffer is not fully opaque, blend the two pixels.
1193     // Otherwise, copy pixel from topBuffer to combinedBuffer.
1194
1195     unsigned int alphaBuffer1 = topBuffer[pixelIndex * 4 + 3];
1196
1197     if(alphaBuffer1 != 255)
1198     {
1199       // At least one pixel is not fully opaque
1200       // "Over" blend the the pixel from topBuffer with the pixel in bottomBuffer
1201       combinedBuffer[pixelIndex * 4]     = topBuffer[pixelIndex * 4] + (bottomBuffer[pixelIndex * 4] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1202       combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1] + (bottomBuffer[pixelIndex * 4 + 1] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1203       combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2] + (bottomBuffer[pixelIndex * 4 + 2] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1204       combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3] + (bottomBuffer[pixelIndex * 4 + 3] * (255 - topBuffer[pixelIndex * 4 + 3]) / 255);
1205     }
1206     else
1207     {
1208       // Copy the pixel from topBuffer to combinedBuffer
1209       combinedBuffer[pixelIndex * 4]     = topBuffer[pixelIndex * 4];
1210       combinedBuffer[pixelIndex * 4 + 1] = topBuffer[pixelIndex * 4 + 1];
1211       combinedBuffer[pixelIndex * 4 + 2] = topBuffer[pixelIndex * 4 + 2];
1212       combinedBuffer[pixelIndex * 4 + 3] = topBuffer[pixelIndex * 4 + 3];
1213     }
1214   }
1215
1216   return combinedPixelBuffer;
1217 }
1218
1219 Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1220 {
1221   // Underline-tags (this is for Markup case)
1222   // Get the underline runs.
1223   const Length               numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
1224   Vector<UnderlinedGlyphRun> underlineRuns;
1225   underlineRuns.Resize(numberOfUnderlineRuns);
1226   mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
1227
1228   // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
1229   Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun    = underlineRuns.Begin();
1230   Vector<UnderlinedGlyphRun>::ConstIterator endItGlyphRun = underlineRuns.End();
1231   GlyphIndex                                startGlyphIndex, endGlyphIndex;
1232
1233   //The outer loop to iterate on the separated chunks of underlined glyph runs
1234   while(itGlyphRun != endItGlyphRun)
1235   {
1236     startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1237     endGlyphIndex   = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1238
1239     // Create the image buffer for underline
1240     Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1241     // Combine the two buffers
1242     topPixelBuffer = CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight);
1243
1244     itGlyphRun++;
1245   }
1246
1247   return topPixelBuffer;
1248 }
1249
1250 Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1251 {
1252   // strikethrough-tags (this is for Markup case)
1253   // Get the strikethrough runs.
1254   const Length                  numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
1255   Vector<StrikethroughGlyphRun> strikethroughRuns;
1256   strikethroughRuns.Resize(numberOfStrikethroughRuns);
1257   mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
1258
1259   // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
1260   Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun    = strikethroughRuns.Begin();
1261   Vector<StrikethroughGlyphRun>::ConstIterator endItGlyphRun = strikethroughRuns.End();
1262   GlyphIndex                                   startGlyphIndex, endGlyphIndex;
1263
1264   //The outer loop to iterate on the separated chunks of strikethrough glyph runs
1265   while(itGlyphRun != endItGlyphRun)
1266   {
1267     startGlyphIndex = itGlyphRun->glyphRun.glyphIndex;
1268     endGlyphIndex   = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
1269
1270     // Create the image buffer for strikethrough
1271     Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
1272     // Combine the two buffers
1273     topPixelBuffer = CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight);
1274
1275     itGlyphRun++;
1276   }
1277
1278   return topPixelBuffer;
1279 }
1280
1281 Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
1282 {
1283   // Apply the markup-Processor if enabled
1284   const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
1285   if(markupProcessorEnabled)
1286   {
1287     topPixelBuffer = ApplyUnderlineMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1288
1289     topPixelBuffer = ApplyStrikethroughMarkupImageBuffer(topPixelBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset);
1290   }
1291
1292   return topPixelBuffer;
1293 }
1294
1295 Typesetter::Typesetter(const ModelInterface* const model)
1296 : mModel(new ViewModel(model))
1297 {
1298 }
1299
1300 Typesetter::~Typesetter()
1301 {
1302   delete mModel;
1303 }
1304
1305 } // namespace Text
1306
1307 } // namespace Toolkit
1308
1309 } // namespace Dali