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