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