d58c3f10d50d08b59856c15ebf799758f826f969
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / cairo-renderer.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 // FILE HEADER
19 #include <dali/internal/text/text-abstraction/cairo-renderer.h>
20
21 // EXTERNAL INCLUDES
22 #include <cairo-ft.h>
23 #include <cairo.h>
24 #include <dali/public-api/common/constants.h>
25 #include <cstring>
26 #include <memory>
27
28 #include <ft2build.h>
29 #include FT_FREETYPE_H
30 #include FT_GLYPH_H
31 #include FT_OUTLINE_H
32 #include FT_STROKER_H
33
34 // INTERNAL INCLUDES
35 #include <dali/devel-api/text-abstraction/font-client.h>
36 #include <dali/devel-api/text-abstraction/text-renderer-layout-helper.h>
37 #include <dali/integration-api/debug.h>
38 #include <dali/internal/imaging/common/image-operations.h>
39 #include <dali/internal/text/text-abstraction/font-client-impl.h>
40
41 using namespace std;
42
43 namespace
44 {
45 const float TO_FLOAT = 1.f / 255.f;
46 const float TO_UCHAR = 255.f;
47 const float TWO_PI   = 2.f * Dali::Math::PI; ///< 360 degrees in radians
48
49 /**
50  * @brief Run of glyphs that have the same style.
51  */
52 struct GlyphRun
53 {
54   GlyphRun()
55   : fontFace{nullptr},
56     fontSize{0.0},
57     glyphIndex{0u},
58     numberOfGlyphs{0u},
59     fontId{0u},
60     colorIndex{0u},
61     isItalicRequired{false},
62     isBoldRequired{false}
63   {
64   }
65
66   FT_Face      fontFace;             ///< The font face used by the glyphs in the run.
67   double       fontSize;             ///< The font size used by the glyphs in the run. According the Cairo's documentation this is in user space units. It works if I set the size in pixels.
68   unsigned int glyphIndex;           ///< Index to the first glyph of the run.
69   unsigned int numberOfGlyphs;       ///< Number of glyphs in the run.
70   unsigned int fontId;               ///< The id of the font.
71   unsigned int colorIndex;           ///< The index to the color of the glyphs.
72   bool         isItalicRequired : 1; ///< Whether the italic style is required.
73   bool         isBoldRequired : 1;   ///< Whether the bold style is required.
74 };
75
76 /**
77  * @brief Helper struct used to destroy a bitmap buffer.
78  *
79  * The font client allocates a bitmap's buffer with the new operator.
80  * However, the PixelBuffer class allocates the buffer with the
81  * malloc() function and the Rotate() function which is intended
82  * for the PixelBuffer as well allocates memory with malloc().
83  *
84  * This struct keeps the type of allocation and uses the delete[]
85  * operator or the free() function to deallocate resources.
86  */
87 struct GlyphBuffer
88 {
89   enum DestructorType
90   {
91     FREE,
92     DELETE
93   };
94
95   GlyphBuffer(Dali::TextAbstraction::FontClient::GlyphBufferData& data, DestructorType type)
96   : data(data),
97     type(type)
98   {
99   }
100
101   ~GlyphBuffer()
102   {
103     switch(type)
104     {
105       case FREE:
106       {
107         free(data.buffer);
108         break;
109       }
110       case DELETE:
111       {
112         delete[] data.buffer;
113       }
114     }
115   }
116
117   Dali::TextAbstraction::FontClient::GlyphBufferData& data;
118   DestructorType                                      type;
119 };
120
121 /**
122  * @brief Creates a pixel buffer with all pixels set to transparent.
123  *
124  * @param[in] parameters Contains the width and height of the pixel buffer.
125  *
126  * @return The pixel buffer.
127  */
128 Dali::Devel::PixelBuffer CreateVoidPixelBuffer(const Dali::TextAbstraction::TextRenderer::Parameters& parameters)
129 {
130   Dali::Pixel::Format      pixelFormat = parameters.pixelFormat == Dali::TextAbstraction::TextRenderer::Parameters::A8 ? Dali::Pixel::A8 : Dali::Pixel::RGBA8888;
131   Dali::Devel::PixelBuffer pixelBuffer = Dali::Devel::PixelBuffer::New(parameters.width,
132                                                                        parameters.height,
133                                                                        pixelFormat);
134
135   const unsigned int bufferSize = parameters.width * parameters.height * Dali::Pixel::GetBytesPerPixel(pixelFormat);
136   unsigned char*     buffer     = pixelBuffer.GetBuffer();
137   memset(buffer, 0, bufferSize);
138
139   return pixelBuffer;
140 }
141
142 /**
143  * @brief Wraps the vertices of glyphs laid out on a horizontal strainght line on a circular path.
144  *
145  * It copies the vertices from the extra cairo context created to lay out the text
146  * on a horizontal straight line to the cairo context used to render it.
147  *
148  * @param[in,out] cr The cairo context used to render the text.
149  * @param[in] circularCr The extra cairo context created to layout horizontal text.
150  * @param[in] parameters The parameters of the circular path.
151  */
152 void WrapToCircularPath(cairo_t* cr, cairo_t* circularCr, const Dali::TextAbstraction::CircularTextParameters& parameters)
153 {
154   bool first = true;
155
156   // Copy the path to get a cairo_path_t pointer used to iterate through all its items.
157   std::unique_ptr<cairo_path_t, void (*)(cairo_path_t*)> path(cairo_copy_path(circularCr), cairo_path_destroy);
158
159   // Iterates through all the path items and transform each vertex to follow the circle.
160   // Transformed vertices are added to a new path in the 'cr' context (the one used to render the circular text)
161   for(int i = 0; i < path->num_data; i += path->data[i].header.length)
162   {
163     cairo_path_data_t* data = &path->data[i];
164
165     switch(data->header.type)
166     {
167       case CAIRO_PATH_MOVE_TO:
168       {
169         if(first)
170         {
171           cairo_new_path(cr);
172         }
173
174         first    = false;
175         double x = data[1].point.x;
176         double y = data[1].point.y;
177         Dali::TextAbstraction::TransformToArc(parameters, x, y);
178         cairo_move_to(cr, x, y);
179         break;
180       }
181       case CAIRO_PATH_LINE_TO:
182       {
183         double x = data[1].point.x;
184         double y = data[1].point.y;
185         Dali::TextAbstraction::TransformToArc(parameters, x, y);
186         cairo_line_to(cr, x, y);
187         break;
188       }
189       case CAIRO_PATH_CURVE_TO:
190       {
191         double x1 = data[1].point.x;
192         double y1 = data[1].point.y;
193         double x2 = data[2].point.x;
194         double y2 = data[2].point.y;
195         double x3 = data[3].point.x;
196         double y3 = data[3].point.y;
197         Dali::TextAbstraction::TransformToArc(parameters, x1, y1);
198         Dali::TextAbstraction::TransformToArc(parameters, x2, y2);
199         Dali::TextAbstraction::TransformToArc(parameters, x3, y3);
200         cairo_curve_to(cr, x1, y1, x2, y2, x3, y3);
201         break;
202       }
203       case CAIRO_PATH_CLOSE_PATH:
204       {
205         cairo_close_path(cr);
206         break;
207       }
208       default:
209       {
210         DALI_LOG_WARNING("Type of path not handled.\n");
211         // Nothing else to do.
212         break;
213       }
214     }
215   }
216 }
217
218 } // namespace
219
220 namespace Dali
221 {
222 namespace TextAbstraction
223 {
224 namespace Internal
225 {
226 namespace
227 {
228 /**
229  * @brief Converts the size so that it can be used by Cairo
230  * @param[in] fontClient A reference to the font client
231  * @param[in] ftLibrary A reference to the Freetype library instance
232  * @param[in] numberOfGlyphs The total number of glyphs
233  * @param[in] daliGlyphsBuffer A pointer to the glyphs buffer
234  * @param[in] colorIndicesBuffer A pointer to the color indices buffer
235  * @param[in/out] glyphRuns A vector of glyph-runs
236  */
237 bool ConvertSizeForCairo(
238   TextAbstraction::FontClient& fontClient,
239   FT_Library&                  ftLibrary,
240   const unsigned int           numberOfGlyphs,
241   const GlyphInfo* const       daliGlyphsBuffer,
242   const ColorIndex* const      colorIndicesBuffer,
243   std::vector<GlyphRun>&       glyphRuns)
244 {
245   // The size set in Cairo and FreeType has different units.
246   // Before the size is set in Cairo it needs to be converted according the formula
247   // 'pixel_size = point_size * resolution / 72' got from the FreeType doc.
248   // https://www.freetype.org/freetype2/docs/glyphs/glyphs-2.html
249
250   unsigned int horizontalDpi = 0u;
251   unsigned int verticalDpi   = 0u;
252   fontClient.GetDpi(horizontalDpi, verticalDpi);
253   const double dVerticalDpi = static_cast<double>(verticalDpi);
254
255   const double FROM_26_DOT_6_TO_PIXELS = dVerticalDpi / (64.0 * 72.0);
256
257   GlyphRun currentGlyphRun;
258   currentGlyphRun.fontId           = 0u;
259   currentGlyphRun.colorIndex       = 0u;
260   currentGlyphRun.isItalicRequired = false;
261   currentGlyphRun.isBoldRequired   = false;
262   for(unsigned index = 0u; index < numberOfGlyphs; ++index)
263   {
264     const GlyphInfo& daliGlyph        = *(daliGlyphsBuffer + index);
265     const FontId     fontId           = daliGlyph.fontId;
266     const ColorIndex colorIndex       = (nullptr == colorIndicesBuffer) ? 0u : *(colorIndicesBuffer + index);
267     const bool       isItalicRequired = daliGlyph.isItalicRequired;
268     const bool       isBoldRequired   = daliGlyph.isBoldRequired;
269
270     if((fontId != currentGlyphRun.fontId) ||
271        ((0u == fontId) && (0u != daliGlyph.index)) ||
272        (colorIndex != currentGlyphRun.colorIndex) ||
273        (isItalicRequired != currentGlyphRun.isItalicRequired) ||
274        (isBoldRequired != currentGlyphRun.isBoldRequired))
275     {
276       // There is a new run. First set the number of glyphs of the previous run and store it.
277       currentGlyphRun.numberOfGlyphs = index - currentGlyphRun.glyphIndex;
278       if(0u != currentGlyphRun.numberOfGlyphs)
279       {
280         glyphRuns.push_back(currentGlyphRun);
281       }
282
283       currentGlyphRun.fontFace         = nullptr;
284       currentGlyphRun.fontSize         = 0.0;
285       currentGlyphRun.glyphIndex       = index;
286       currentGlyphRun.numberOfGlyphs   = 0u;
287       currentGlyphRun.fontId           = 0u;
288       currentGlyphRun.colorIndex       = 0u;
289       currentGlyphRun.isItalicRequired = false;
290       currentGlyphRun.isBoldRequired   = false;
291
292       if(0u != fontId)
293       {
294         // Get the font's path file name from the font Id.
295         FontDescription fontDescription;
296         fontClient.GetDescription(fontId, fontDescription);
297
298         switch(fontDescription.type)
299         {
300           case FontDescription::FACE_FONT:
301           {
302             // Create a FreeType font's face.
303             auto error = FT_New_Face(ftLibrary, fontDescription.path.c_str(), 0u, &currentGlyphRun.fontFace);
304             if(error)
305             {
306               DALI_LOG_ERROR("Error in FT while creating a new face\n");
307
308               // Error so just return false
309               return false;
310             }
311
312             // Set the font's size. It needs to be set in the Freetype font and in the Cairo's context.
313             unsigned int fontSize = fontClient.GetPointSize(fontId);
314
315             // Font's size to be set in the Cairo's context.
316             currentGlyphRun.fontSize = FROM_26_DOT_6_TO_PIXELS * static_cast<double>(fontSize);
317             break;
318           }
319           case FontDescription::BITMAP_FONT:
320           {
321             //Nothing to do.
322             break;
323           }
324           default:
325           {
326             //Nothing to do.
327             break;
328           }
329         }
330       }
331       currentGlyphRun.fontId           = fontId;
332       currentGlyphRun.colorIndex       = colorIndex;
333       currentGlyphRun.isItalicRequired = isItalicRequired;
334       currentGlyphRun.isBoldRequired   = isBoldRequired;
335     }
336   }
337
338   // Calculate the number of glyphs of the last run and store it.
339   currentGlyphRun.numberOfGlyphs = numberOfGlyphs - currentGlyphRun.glyphIndex;
340   if(0u != currentGlyphRun.numberOfGlyphs)
341   {
342     glyphRuns.push_back(currentGlyphRun);
343   }
344
345   return true; // Successfully converted
346 }
347
348 /**
349  * @brief Copies the image to the cairo surface
350  * @param[in] parameters The text renderer parameters
351  * @param[in] pixelFormat The pixel format
352  * @param[in] data The glyph buffer data
353  * @param[in] buffer The output buffer
354  * @param[in] rgbaCase 0 -> image and cairo buffer are A8
355  *                     1 -> image is A8, cairo buffer is ARGB
356  *                     2 -> image is RGBA and cairo buffer is ARGB
357  *                     3 -> image is BGRA and cairo buffer is ARGB
358  * @param[in] glyphX The x-position of the glyph
359  * @param[in] glyphY The y-position of the glyph
360  * @param[in] strideWidth The stride width
361  * @param[in] color The color of the text
362  * @param[in] doBlendWithTextColor Whether to blend with the text color or not)
363  */
364 void CopyImageToSurface(
365   const TextAbstraction::TextRenderer::Parameters& parameters,
366   const Pixel::Format                              pixelFormat,
367   TextAbstraction::FontClient::GlyphBufferData&    data,
368   unsigned char*                                   buffer,
369   const int                                        rgbaCase,
370   const double                                     glyphX,
371   const double                                     glyphY,
372   const int                                        strideWidth,
373   const Vector4&                                   color,
374   const bool                                       doBlendWithTextColor)
375 {
376   // Select the cropped source image area to copy into the surface buffer
377   unsigned int glyphUintX = 0u;
378   unsigned int glyphUintY = 0u;
379   unsigned int srcWidth   = data.width;
380   unsigned int srcHeight  = data.height;
381   unsigned int xSrcIndex  = 0u;
382   unsigned int ySrcIndex  = 0u;
383   if(glyphX < 0.f)
384   {
385     xSrcIndex = static_cast<unsigned int>(abs(glyphX));
386     srcWidth -= xSrcIndex;
387   }
388   else
389   {
390     glyphUintX = static_cast<unsigned int>(glyphX);
391   }
392
393   if(glyphUintX + srcWidth > static_cast<unsigned int>(strideWidth))
394   {
395     srcWidth -= ((glyphUintX + srcWidth) - strideWidth);
396   }
397
398   if(glyphY - static_cast<float>(srcHeight) < 0.f)
399   {
400     ySrcIndex = static_cast<unsigned int>(abs(glyphY - static_cast<float>(srcHeight)));
401     srcHeight -= ySrcIndex;
402   }
403   else
404   {
405     glyphUintY = static_cast<unsigned int>(glyphY - static_cast<float>(srcHeight));
406   }
407
408   if(glyphUintY + srcHeight > parameters.height)
409   {
410     srcHeight -= ((glyphUintY + srcHeight) - parameters.height);
411   }
412
413   // Calculate the source and destination indices.
414   const unsigned int srcPixelSize = Pixel::GetBytesPerPixel(data.format);
415   const unsigned int dstPixelSize = Pixel::GetBytesPerPixel(pixelFormat);
416
417   unsigned int srcIndex = srcPixelSize * (ySrcIndex * srcWidth + xSrcIndex);
418   unsigned int dstIndex = dstPixelSize * (glyphUintY * strideWidth + glyphUintX);
419
420   const unsigned int srcWidthOffset = srcPixelSize * (data.width - srcWidth);
421   const unsigned int dstWidthOffset = dstPixelSize * (strideWidth - srcWidth);
422
423   // Copy the image to the surface
424   for(unsigned int j = 0; j < srcHeight; ++j)
425   {
426     for(unsigned int i = 0; i < srcWidth; ++i)
427     {
428       switch(rgbaCase)
429       {
430         case 0: // Both the image's buffer and cairo's buffer are A8
431         {
432           const unsigned char alpha = *(data.buffer + srcIndex);
433           if(alpha != 0u)
434           {
435             // @todo needs a proper blending!
436             *(buffer + dstIndex) = alpha;
437           }
438           break;
439         }
440         case 1: // The image's buffer is A8 and the cairo's buffer is ARGB
441         {
442           const unsigned char alpha = *(data.buffer + srcIndex);
443           if(alpha != 0u)
444           {
445             // @todo needs a proper blending!
446             const float srcAlpha = TO_FLOAT * static_cast<float>(alpha);
447
448             // Write the RGBA modulated with the given default color.
449             const float* const colorPtr = color.AsFloat();
450             *(buffer + dstIndex + 0u)   = static_cast<unsigned char>(TO_UCHAR * colorPtr[0u] * srcAlpha);
451             *(buffer + dstIndex + 1u)   = static_cast<unsigned char>(TO_UCHAR * colorPtr[1u] * srcAlpha);
452             *(buffer + dstIndex + 2u)   = static_cast<unsigned char>(TO_UCHAR * colorPtr[2u] * srcAlpha);
453             *(buffer + dstIndex + 3u)   = static_cast<unsigned char>(TO_UCHAR * colorPtr[3u] * srcAlpha);
454           }
455           break;
456         }
457         case 2: // The image's buffer is RGBA and the cairo's buffer is ARGB
458         {
459           const unsigned char alpha = *(data.buffer + srcIndex + 3u);
460           if(alpha != 0u)
461           {
462             if(doBlendWithTextColor)
463             {
464               const float* const colorPtr = color.AsFloat();
465
466               const float srcAlpha = TO_FLOAT * static_cast<float>(alpha) * colorPtr[3u];
467
468               *(buffer + dstIndex + 0u) = static_cast<unsigned char>(static_cast<float>(*(data.buffer + srcIndex + 0u)) * colorPtr[0u]);
469               *(buffer + dstIndex + 1u) = static_cast<unsigned char>(static_cast<float>(*(data.buffer + srcIndex + 1u)) * colorPtr[1u]);
470               *(buffer + dstIndex + 2u) = static_cast<unsigned char>(static_cast<float>(*(data.buffer + srcIndex + 2u)) * colorPtr[2u]);
471
472               // Write the alpha.
473               *(buffer + dstIndex + 3u) = static_cast<unsigned char>(TO_UCHAR * srcAlpha);
474             }
475             else
476             {
477               // @todo needs a proper blending!
478               // Write the RGB
479               *(buffer + dstIndex + 0u) = *(data.buffer + srcIndex + 0u);
480               *(buffer + dstIndex + 1u) = *(data.buffer + srcIndex + 1u);
481               *(buffer + dstIndex + 2u) = *(data.buffer + srcIndex + 2u);
482
483               // Write the alpha.
484               *(buffer + dstIndex + 3u) = *(data.buffer + srcIndex + 3u);
485             }
486           }
487           break;
488         }
489         case 3: // The image's buffer is BGRA and the cairo's buffer is ARGB
490         {
491           const unsigned char alpha = *(data.buffer + srcIndex + 3u);
492           if(alpha != 0u)
493           {
494             if(doBlendWithTextColor)
495             {
496               const float* const colorPtr = color.AsFloat();
497
498               const float srcAlpha = TO_FLOAT * static_cast<float>(alpha) * colorPtr[3u];
499
500               *(buffer + dstIndex + 0u) = static_cast<unsigned char>(static_cast<float>(*(data.buffer + srcIndex + 2u)) * colorPtr[0u]);
501               *(buffer + dstIndex + 1u) = static_cast<unsigned char>(static_cast<float>(*(data.buffer + srcIndex + 1u)) * colorPtr[1u]);
502               *(buffer + dstIndex + 2u) = static_cast<unsigned char>(static_cast<float>(*(data.buffer + srcIndex + 0u)) * colorPtr[2u]);
503
504               // Write the alpha.
505               *(buffer + dstIndex + 3u) = static_cast<unsigned char>(TO_UCHAR * srcAlpha);
506             }
507             else
508             {
509               // @todo needs a proper blending!
510               // Write the RGBA
511               *(buffer + dstIndex + 0u) = *(data.buffer + srcIndex + 2u);
512               *(buffer + dstIndex + 1u) = *(data.buffer + srcIndex + 1u);
513               *(buffer + dstIndex + 2u) = *(data.buffer + srcIndex + 0u);
514               *(buffer + dstIndex + 3u) = *(data.buffer + srcIndex + 3u);
515             }
516           }
517           break;
518         }
519         default:
520         {
521           DALI_ASSERT_ALWAYS(!"Cairo Renderer: The accepted values for this switch case are: 0, 1, 2!");
522         }
523       } // switch
524       srcIndex += srcPixelSize;
525       dstIndex += dstPixelSize;
526     } // for width
527     srcIndex += srcWidthOffset;
528     dstIndex += dstWidthOffset;
529   } // for height
530 }
531
532 /**
533  * @brief Renders the glyph
534  * @param[in] parameters The text renderer parameters
535  * @param[in] run The current glyph-run
536  * @param[in] cairoGlyphsBuffer The cairo glyphs buffer
537  * @param[in/out] cr The cairo surface
538  * @param[in/out] circularCr The cairo surface if using circular text
539  * @param[in] isCircularText Whether we're using circular text or not
540  * @param[in/out] circularTextParameters The circular text parameters
541  */
542 void RenderGlyphs(
543   const TextAbstraction::TextRenderer::Parameters& parameters,
544   const GlyphRun&                                  run,
545   cairo_glyph_t*&                                  cairoGlyphsBuffer,
546   cairo_t*&                                        cr,
547   cairo_t*&                                        circularCr,
548   const bool                                       isCircularText,
549   CircularTextParameters&                          circularTextParameters)
550 {
551   // Sets the color. The color is actually BGRA
552   const Vector4& color = parameters.colors[run.colorIndex];
553
554   cairo_set_source_rgba(cr,
555                         static_cast<double>(color.b),
556                         static_cast<double>(color.g),
557                         static_cast<double>(color.r),
558                         static_cast<double>(color.a));
559
560   // Create the Cairo's font from the FreeType font.
561   int options = 0;
562   options     = CAIRO_HINT_STYLE_SLIGHT;
563   std::unique_ptr<cairo_font_face_t, void (*)(cairo_font_face_t*)> fontFacePtr(cairo_ft_font_face_create_for_ft_face(run.fontFace, options), cairo_font_face_destroy);
564   cairo_font_face_t*                                               fontFace = fontFacePtr.get();
565
566   static const cairo_user_data_key_t key    = {0};
567   cairo_status_t                     status = cairo_font_face_set_user_data(fontFace, &key, run.fontFace, reinterpret_cast<cairo_destroy_func_t>(FT_Done_Face));
568   if(status)
569   {
570     cairo_font_face_destroy(fontFace);
571   }
572
573   unsigned int ftSynthesizeFlag = 0u;
574   if(run.isBoldRequired && !(run.fontFace->style_flags & FT_STYLE_FLAG_BOLD))
575   {
576     ftSynthesizeFlag |= CAIRO_FT_SYNTHESIZE_BOLD;
577   }
578
579   cairo_ft_font_face_set_synthesize(fontFace, ftSynthesizeFlag);
580
581   cairo_font_face_reference(fontFace);
582
583   const bool synthesizeItalic = (run.isItalicRequired && !(run.fontFace->style_flags & FT_STYLE_FLAG_ITALIC));
584
585   if(CAIRO_STATUS_SUCCESS != cairo_font_face_status(fontFace))
586   {
587     DALI_LOG_ERROR("Failed to load the Freetype Font\n");
588   }
589
590   // Sets the font.
591   cairo_set_font_face(isCircularText ? circularCr : cr, fontFace);
592
593   // Sets the size
594   cairo_set_font_size(isCircularText ? circularCr : cr, run.fontSize);
595
596   // Render the glyphs.
597   if(isCircularText)
598   {
599     circularTextParameters.synthesizeItalic = synthesizeItalic;
600
601     const unsigned int glyphJump = circularTextParameters.synthesizeItalic ? 1u : run.numberOfGlyphs;
602
603     for(unsigned int index = 0u; index < run.numberOfGlyphs; index += glyphJump)
604     {
605       // Clears the current path where the text is laid out on a horizontal straight line.
606       cairo_new_path(circularCr);
607       cairo_move_to(circularCr, 0.0, 0.0);
608
609       cairo_glyph_path(circularCr, (cairoGlyphsBuffer + run.glyphIndex + index), glyphJump);
610
611       WrapToCircularPath(cr, circularCr, circularTextParameters);
612       cairo_fill(cr);
613     }
614   }
615   else
616   {
617     if(synthesizeItalic)
618     {
619       // Apply a shear transform to synthesize the italics.
620       // For a reason Cairo may trim some glyphs if the CAIRO_FT_SYNTHESIZE_OBLIQUE flag is used.
621
622       // This is to calculate an offset used to compensate the 'translation' done by the shear transform
623       // as it's done for the whole render buffer.
624       double maxY = 0.0;
625       for(unsigned int index = run.glyphIndex, endIndex = run.glyphIndex + run.numberOfGlyphs; index < endIndex; ++index)
626       {
627         maxY = std::max(maxY, (*(cairoGlyphsBuffer + index)).y);
628       }
629
630       cairo_matrix_t matrix;
631       cairo_matrix_init(&matrix,
632                         1.0,
633                         0.0,
634                         -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
635                         1.0,
636                         maxY * TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
637                         0.0);
638
639       cairo_transform(cr, &matrix);
640     }
641
642     cairo_show_glyphs(cr, (cairoGlyphsBuffer + run.glyphIndex), run.numberOfGlyphs);
643
644     if(synthesizeItalic)
645     {
646       // Restore the transform matrix to the identity.
647       cairo_matrix_t matrix;
648       cairo_matrix_init_identity(&matrix);
649       cairo_set_matrix(cr, &matrix);
650     }
651
652     cairo_fill(cr);
653   }
654 }
655
656 } // unnamed namespace
657
658 Devel::PixelBuffer RenderTextCairo(const TextAbstraction::TextRenderer::Parameters& parameters)
659 {
660   const unsigned int numberOfGlyphs = parameters.glyphs.Count();
661
662   if(0u == numberOfGlyphs)
663   {
664     // return a pixel buffer with all pixels set to transparent.
665     return CreateVoidPixelBuffer(parameters);
666   }
667
668   // Choose the pixel format to be used.
669   //
670   // @note Behdad wrote "Upper 8 bits maps to the fourth byte in a little-endian machine like the intels."
671   //       https://lists.cairographics.org/archives/cairo/2006-March/006563.html
672   //
673   //       Here in practice Cairo's ARGB32 is like DALi's RGBA8888.
674   //
675   const bool           isDstRgba   = TextAbstraction::TextRenderer::Parameters::RGBA8888 == parameters.pixelFormat;
676   const Pixel::Format  pixelFormat = isDstRgba ? Pixel::Format::RGBA8888 : Pixel::Format::A8;
677   const cairo_format_t cairoFormat = isDstRgba ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_A8;
678
679   const int bpp = Pixel::GetBytesPerPixel(pixelFormat);
680   if(0u == bpp)
681   {
682     // return a pixel buffer with all pixels set to transparent.
683     return CreateVoidPixelBuffer(parameters);
684   }
685
686   // This function provides a stride value that will respect all alignment requirements of the
687   // accelerated image-rendering code within cairo.
688   const int stride      = cairo_format_stride_for_width(cairoFormat,
689                                                    static_cast<int>(parameters.width));
690   const int strideWidth = stride / bpp;
691
692   // Convert from DALi glyphs to Cairo glyphs.
693   std::vector<cairo_glyph_t> cairoGlyphs;
694   cairoGlyphs.resize(numberOfGlyphs);
695   cairo_glyph_t* cairoGlyphsBuffer = &cairoGlyphs[0u];
696
697   const GlyphInfo* const  daliGlyphsBuffer   = parameters.glyphs.Begin();
698   const Vector2* const    positionsBuffer    = parameters.positions.Begin();
699   const ColorIndex* const colorIndicesBuffer = (0u == parameters.colorIndices.Count()) ? nullptr : parameters.colorIndices.Begin();
700
701   for(unsigned index = 0u; index < numberOfGlyphs; ++index)
702   {
703     const GlyphInfo& daliGlyph  = *(daliGlyphsBuffer + index);
704     const Vector2&   position   = *(positionsBuffer + index);
705     cairo_glyph_t&   cairoGlyph = *(cairoGlyphsBuffer + index);
706
707     cairoGlyph.index = daliGlyph.index;
708     cairoGlyph.x     = round(position.x);
709     cairoGlyph.y     = round(position.y);
710   }
711
712   // Retrieve the FreeType fonts needed by Cairo from the font-client.
713   Dali::TextAbstraction::FontClient fontClient = Dali::TextAbstraction::FontClient::Get();
714
715   FT_Library ftLibrary;
716   auto       error = FT_Init_FreeType(&ftLibrary);
717   if(error)
718   {
719     DALI_LOG_ERROR("Error initializing FT library\n");
720
721     // return a pixel buffer with all pixels set to transparent.
722     return CreateVoidPixelBuffer(parameters);
723   }
724
725   // Vector used to store the FreeType font faces, its size and the run of glyphs that use the font.
726   std::vector<GlyphRun> glyphRuns;
727   glyphRuns.reserve(8u);
728
729   if(!ConvertSizeForCairo(fontClient, ftLibrary, numberOfGlyphs, daliGlyphsBuffer, colorIndicesBuffer, glyphRuns))
730   {
731     // return a pixel buffer with all pixels set to transparent.
732     return CreateVoidPixelBuffer(parameters);
733   }
734
735   // Creates the pixel buffer and retrieves the buffer pointer used to create the Cairo's surface.
736   Devel::PixelBuffer pixelBuffer = Devel::PixelBuffer::New(strideWidth, parameters.height, pixelFormat);
737
738   unsigned char*     buffer     = pixelBuffer.GetBuffer();
739   const unsigned int bufferSize = stride * parameters.height;
740   memset(buffer, 0, bufferSize);
741
742   std::unique_ptr<cairo_surface_t, void (*)(cairo_surface_t*)> surfacePtr(cairo_image_surface_create_for_data(buffer,
743                                                                                                               cairoFormat,
744                                                                                                               parameters.width,
745                                                                                                               parameters.height,
746                                                                                                               stride),
747                                                                           cairo_surface_destroy);
748   cairo_surface_t*                                             surface = surfacePtr.get();
749
750   if((nullptr == surface) || (CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)))
751   {
752     DALI_LOG_ERROR("Failed to create a cairo's surface\n");
753
754     return CreateVoidPixelBuffer(parameters);
755   }
756
757   // Whether the text is circular.
758   const bool isCircularText = 0u != parameters.radius;
759
760   // Creates a surface for circular text.
761   //
762   // The reason to create a surface for circular text is that the strategy
763   // followed is to layout the text in a straight horizontal line and apply a
764   // transform to each vertex that forms the geometry of the glyphs to place
765   // and bend the glyphs accordingly to the circular path.
766   //
767   // As the glyphs are laid out first in a straight line they may exceed the
768   // boundaries of the surface in that case cairo ignores them.
769   std::unique_ptr<cairo_surface_t, void (*)(cairo_surface_t*)> circularSurfacePtr(nullptr, cairo_surface_destroy);
770   cairo_surface_t*                                             circularSurface = nullptr;
771   if(isCircularText)
772   {
773     circularSurfacePtr.reset(cairo_surface_create_similar(surface,
774                                                           CAIRO_CONTENT_ALPHA,
775                                                           parameters.circularWidth,
776                                                           parameters.circularHeight));
777     circularSurface = circularSurfacePtr.get();
778
779     if((nullptr == circularSurface) || (CAIRO_STATUS_SUCCESS != cairo_surface_status(circularSurface)))
780     {
781       DALI_LOG_ERROR("Failed to create a cairo's circular surface\n");
782
783       return CreateVoidPixelBuffer(parameters);
784     }
785   }
786
787   std::unique_ptr<cairo_t, void (*)(cairo_t*)> crPtr(cairo_create(surface), cairo_destroy);
788   cairo_t*                                     cr = crPtr.get();
789
790   if(CAIRO_STATUS_SUCCESS != cairo_status(cr))
791   {
792     DALI_LOG_ERROR("Failed to create a cairo context\n");
793
794     return CreateVoidPixelBuffer(parameters);
795   }
796
797   std::unique_ptr<cairo_t, void (*)(cairo_t*)> circularCrPtr(nullptr, cairo_destroy);
798   cairo_t*                                     circularCr = nullptr;
799
800   if(isCircularText)
801   {
802     circularCrPtr.reset(cairo_create(circularSurface));
803     circularCr = circularCrPtr.get();
804
805     if(CAIRO_STATUS_SUCCESS != cairo_status(circularCr))
806     {
807       DALI_LOG_ERROR("Failed to create a cairo context\n");
808
809       return CreateVoidPixelBuffer(parameters);
810     }
811   }
812
813   CircularTextParameters circularTextParameters;
814
815   // Render the glyphs.
816   if(isCircularText)
817   {
818     // Set the parameters.
819     circularTextParameters.isClockwise = (TextAbstraction::TextRenderer::Parameters::CLOCKWISE == parameters.circularLayout);
820
821     circularTextParameters.centerX    = static_cast<double>(parameters.centerX);
822     circularTextParameters.centerY    = static_cast<double>(parameters.centerY);
823     circularTextParameters.radius     = static_cast<double>(parameters.radius);
824     circularTextParameters.invRadius  = 1.0 / circularTextParameters.radius;
825     circularTextParameters.beginAngle = -parameters.beginAngle + Dali::Math::PI_2;
826   }
827
828   cairo_move_to(cr, 0.0, 0.0);
829
830   for(const auto& run : glyphRuns)
831   {
832     const bool isEmoji = parameters.isEmoji[run.glyphIndex];
833     if(isEmoji || (nullptr == run.fontFace))
834     {
835       // Retrieve the color for the glyph.
836       const Vector4& color = parameters.colors[run.colorIndex];
837
838       const unsigned int lastGlyphIndex = run.glyphIndex + run.numberOfGlyphs;
839       for(unsigned int index = run.glyphIndex; index < lastGlyphIndex; ++index)
840       {
841         // Whether it's a bitmap font.
842         const bool doBlendWithTextColor = !isEmoji && (ColorBlendingMode::MULTIPLY == parameters.blendingMode[index]);
843
844         // Check if there is an embedded image or a bitmap font image.
845         const GlyphIndex glyphFontIndex = daliGlyphsBuffer[index].index;
846         if(0u != glyphFontIndex)
847         {
848           // The embedded image could be A8, RGBA8888 or BGRA8888.
849           //
850           // If the embedded image is RGBA8888 or BGRA8888 then the cairo's buffer is ARGB32. It's needed to convert from RGBA or BGRA to ARGB.
851           // If the embedded image is A8 it's needed to check if the cairo's buffer is A8 or ARGB32 and do the conversion if needed.
852
853           const cairo_glyph_t& glyph = *(cairoGlyphsBuffer + index);
854
855           // Retrieve the image
856           TextAbstraction::FontClient::GlyphBufferData data;
857           if(isEmoji)
858           {
859             data.width  = parameters.glyphs[run.glyphIndex].width;
860             data.height = parameters.glyphs[run.glyphIndex].height;
861           }
862
863           fontClient.CreateBitmap(run.fontId, glyphFontIndex, false, false, data, 0u);
864
865           if(nullptr == data.buffer)
866           {
867             // nothing else to do if there is no image.
868             continue;
869           }
870
871           // Calculate the position for the circular text.
872           double glyphX = glyph.x;
873           double glyphY = glyph.y;
874
875           if(isCircularText)
876           {
877             // Center of the bitmap.
878             const double halfWidth  = 0.5 * static_cast<double>(data.width);
879             const double halfHeight = 0.5 * static_cast<double>(data.height);
880
881             double centerX = glyph.x + halfWidth;
882             double centerY = glyph.y - halfHeight;
883
884             float radians = circularTextParameters.beginAngle + (circularTextParameters.isClockwise ? -1.f : 1.f) * (Dali::Math::PI_2 + circularTextParameters.invRadius * centerX);
885             radians       = fmod(radians, TWO_PI);
886             radians += (radians < 0.f) ? TWO_PI : 0.f;
887
888             TransformToArc(circularTextParameters, centerX, centerY);
889
890             uint8_t*           pixelsOut = nullptr;
891             unsigned int       widthOut  = data.width;
892             unsigned int       heightOut = data.height;
893             const unsigned int pixelSize = Pixel::GetBytesPerPixel(data.format);
894
895             // If we need to decompress, create new memory and replace ownership.
896             if(data.compressionType != TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION)
897             {
898               uint8_t* newBuffer = (uint8_t*)malloc(widthOut * heightOut * pixelSize);
899               if(DALI_LIKELY(newBuffer != nullptr))
900               {
901                 TextAbstraction::FontClient::GlyphBufferData::Decompress(data, newBuffer);
902                 if(data.isBufferOwned)
903                 {
904                   // Release previous buffer
905                   free(data.buffer);
906                 }
907                 data.isBufferOwned   = true;
908                 data.buffer          = newBuffer;
909                 data.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
910               }
911             }
912
913             Dali::Internal::Platform::RotateByShear(data.buffer,
914                                                     data.width,
915                                                     data.height,
916                                                     data.width,
917                                                     pixelSize,
918                                                     radians,
919                                                     pixelsOut,
920                                                     widthOut,
921                                                     heightOut);
922             if(nullptr != pixelsOut)
923             {
924               if(data.isBufferOwned)
925               {
926                 free(data.buffer);
927               }
928               data.isBufferOwned   = true;
929               data.compressionType = Dali::TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
930               data.buffer          = pixelsOut;
931               data.width           = widthOut;
932               data.height          = heightOut;
933             }
934
935             glyphX = centerX - 0.5 * static_cast<double>(data.width);
936             glyphY = centerY + 0.5 * static_cast<double>(data.height);
937           }
938
939           if((Pixel::A8 != data.format) &&
940              (Pixel::L8 != data.format) &&
941              (Pixel::RGBA8888 != data.format) &&
942              (Pixel::BGRA8888 != data.format))
943           {
944             DALI_LOG_ERROR(" Cairo Renderer: The valid pixel format for embedded items are A8 or RGBA8888\n");
945             continue;
946           }
947
948           // Check if the item is out of the buffer.
949           if((glyphX + static_cast<float>(data.width) < 0.f) ||
950              (glyphX > static_cast<float>(strideWidth)) ||
951              (glyphY < 0.f) ||
952              (glyphY - static_cast<float>(data.height) > static_cast<float>(parameters.height)))
953           {
954             // The embedded item is completely out of the buffer.
955             continue;
956           }
957
958           const bool isSrcA    = (Pixel::A8 == data.format) || (Pixel::L8 == data.format);
959           const bool isSrcRgba = Pixel::RGBA8888 == data.format;
960           const bool isSrcBgra = Pixel::BGRA8888 == data.format;
961
962           // 0 -> image and cairo buffer are A8
963           // 1 -> image is A8, cairo buffer is ARGB
964           // 2 -> image is RGBA and cairo buffer is ARGB
965           // 3 -> image is BGRA and cairo buffer is ARGB
966           int rgbaCase = 0;
967           if(isSrcA && isDstRgba)
968           {
969             rgbaCase = 1;
970           }
971           else if(isSrcRgba && isDstRgba)
972           {
973             rgbaCase = 2;
974           }
975           else if(isSrcBgra && isDstRgba)
976           {
977             rgbaCase = 3;
978           }
979           else if((isSrcRgba || isSrcBgra) && !isDstRgba)
980           {
981             DALI_LOG_ERROR("Cairo Renderer: The embedded image is RGBA or BGRA and the Cairo's buffer has been creates with A8 format!\n");
982             continue;
983           }
984
985           CopyImageToSurface(parameters, pixelFormat, data, buffer, rgbaCase, glyphX, glyphY, strideWidth, color, doBlendWithTextColor);
986         }
987       }
988     }
989     else
990     {
991       RenderGlyphs(parameters, run, cairoGlyphsBuffer, cr, circularCr, isCircularText, circularTextParameters);
992     }
993   }
994
995   return pixelBuffer;
996 }
997
998 } // namespace Internal
999
1000 } // namespace TextAbstraction
1001
1002 } // namespace Dali