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