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