2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/text/text-abstraction/cairo-renderer.h>
22 #include <dali/public-api/common/constants.h>
29 #include FT_FREETYPE_H
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>
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
51 * @brief Run of glyphs that have the same style.
56 : fontFace{ nullptr },
62 isItalicRequired{ false },
63 isBoldRequired{ false }
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.
77 * @brief Helper struct used to destroy a bitmap buffer.
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().
84 * This struct keeps the type of allocation and uses the delete[]
85 * operator or the free() function to deallocate resources.
95 GlyphBuffer( Dali::TextAbstraction::FontClient::GlyphBufferData& data, DestructorType type )
112 delete[] data.buffer;
117 Dali::TextAbstraction::FontClient::GlyphBufferData& data;
122 * @brief Creates a pixel buffer with all pixels set to transparent.
124 * @param[in] parameters Contains the width and height of the pixel buffer.
126 * @return The pixel buffer.
128 Dali::Devel::PixelBuffer CreateVoidPixelBuffer( const Dali::TextAbstraction::TextRenderer::Parameters& parameters )
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,
135 const unsigned int bufferSize = parameters.width * parameters.height * Dali::Pixel::GetBytesPerPixel( pixelFormat );
136 unsigned char* buffer = pixelBuffer.GetBuffer();
137 memset( buffer, 0, bufferSize );
143 * @brief Wraps the vertices of glyphs laid out on a horizontal strainght line on a circular path.
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.
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.
152 void WrapToCircularPath( cairo_t* cr, cairo_t* circularCr, const Dali::TextAbstraction::CircularTextParameters& parameters )
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 );
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 )
163 cairo_path_data_t* data = &path->data[i];
165 switch( data->header.type )
167 case CAIRO_PATH_MOVE_TO:
171 cairo_new_path( cr );
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 );
181 case CAIRO_PATH_LINE_TO:
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 );
189 case CAIRO_PATH_CURVE_TO:
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 );
203 case CAIRO_PATH_CLOSE_PATH:
205 cairo_close_path( cr );
210 DALI_LOG_WARNING( "Type of path not handled.\n" );
211 // Nothing else to do.
223 namespace TextAbstraction
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
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)
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
254 unsigned int horizontalDpi = 0u;
255 unsigned int verticalDpi = 0u;
256 fontClient.GetDpi( horizontalDpi, verticalDpi );
257 const double dVerticalDpi = static_cast<double>( verticalDpi );
259 const double FROM_26_DOT_6_TO_PIXELS = dVerticalDpi / ( 64.0 * 72.0 );
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 )
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;
275 if( ( fontId != currentGlyphRun.fontId ) ||
276 ( ( 0u == fontId ) && ( 0u != daliGlyph.index ) ) ||
277 ( colorIndex != currentGlyphRun.colorIndex ) ||
278 ( isItalicRequired != currentGlyphRun.isItalicRequired ) ||
279 ( isBoldRequired != currentGlyphRun.isBoldRequired ) )
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 )
285 glyphRuns.push_back( currentGlyphRun );
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;
299 // Get the font's path file name from the font Id.
300 FontDescription fontDescription;
301 fontClient.GetDescription( fontId, fontDescription );
303 switch( fontDescription.type )
305 case FontDescription::FACE_FONT:
307 // Create a FreeType font's face.
308 auto error = FT_New_Face( ftLibrary, fontDescription.path.c_str(), 0u, ¤tGlyphRun.fontFace );
311 DALI_LOG_ERROR( "Error in FT while creating a new face\n" );
313 // Error so just return false
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 );
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 );
324 case FontDescription::BITMAP_FONT:
336 currentGlyphRun.fontId = fontId;
337 currentGlyphRun.colorIndex = colorIndex;
338 currentGlyphRun.isItalicRequired = isItalicRequired;
339 currentGlyphRun.isBoldRequired = isBoldRequired;
343 // Calculate the number of glyphs of the last run and store it.
344 currentGlyphRun.numberOfGlyphs = numberOfGlyphs - currentGlyphRun.glyphIndex;
345 if( 0u != currentGlyphRun.numberOfGlyphs )
347 glyphRuns.push_back( currentGlyphRun );
350 return true; // Successfully converted
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)
369 void CopyImageToSurface(
370 const TextAbstraction::TextRenderer::Parameters& parameters,
371 const Pixel::Format pixelFormat,
372 TextAbstraction::FontClient::GlyphBufferData& data,
373 unsigned char * buffer,
377 const int strideWidth,
378 const Vector4& color,
379 const bool doBlendWithTextColor)
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;
390 xSrcIndex = static_cast<unsigned int>( abs( glyphX ) );
391 srcWidth -= xSrcIndex;
395 glyphUintX = static_cast<unsigned int>( glyphX );
398 if( glyphUintX + srcWidth > static_cast<unsigned int>( strideWidth ) )
400 srcWidth -= ( ( glyphUintX + srcWidth ) - strideWidth );
403 if( glyphY - static_cast<float>( srcHeight ) < 0.f )
405 ySrcIndex = static_cast<unsigned int>( abs( glyphY - static_cast<float>( srcHeight ) ) );
406 srcHeight -= ySrcIndex;
410 glyphUintY = static_cast<unsigned int>( glyphY - static_cast<float>( srcHeight ) );
413 if( glyphUintY + srcHeight > parameters.height )
415 srcHeight -= ( ( glyphUintY + srcHeight ) - parameters.height );
418 // Calculate the source and destination indices.
419 const unsigned int srcPixelSize = Pixel::GetBytesPerPixel( data.format );
420 const unsigned int dstPixelSize = Pixel::GetBytesPerPixel( pixelFormat );
422 unsigned int srcIndex = srcPixelSize * ( ySrcIndex * srcWidth + xSrcIndex );
423 unsigned int dstIndex = dstPixelSize * ( glyphUintY * strideWidth + glyphUintX );
425 const unsigned int srcWidthOffset = srcPixelSize * ( data.width - srcWidth );
426 const unsigned int dstWidthOffset = dstPixelSize * ( strideWidth - srcWidth );
428 // Copy the image to the surface
429 for( unsigned int j = 0; j < srcHeight; ++j )
431 for( unsigned int i = 0; i < srcWidth; ++i )
435 case 0: // Both the image's buffer and cairo's buffer are A8
437 const unsigned char alpha = *( data.buffer + srcIndex );
440 // @todo needs a proper blending!
441 *( buffer + dstIndex ) = alpha;
445 case 1: // The image's buffer is A8 and the cairo's buffer is ARGB
447 const unsigned char alpha = *( data.buffer + srcIndex );
450 // @todo needs a proper blending!
451 const float srcAlpha = TO_FLOAT * static_cast<float>( alpha );
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 );
462 case 2: // The image's buffer is RGBA and the cairo's buffer is ARGB
464 const unsigned char alpha = *(data.buffer + srcIndex + 3u);
467 if( doBlendWithTextColor )
469 const float* const colorPtr = color.AsFloat();
471 const float srcAlpha = TO_FLOAT * static_cast<float>(alpha) * colorPtr[3u];
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] );
478 *(buffer + dstIndex + 3u) = static_cast<unsigned char>( TO_UCHAR * srcAlpha );
482 // @todo needs a proper blending!
484 *(buffer + dstIndex + 0u) = *(data.buffer + srcIndex + 0u);
485 *(buffer + dstIndex + 1u) = *(data.buffer + srcIndex + 1u);
486 *(buffer + dstIndex + 2u) = *(data.buffer + srcIndex + 2u);
489 *(buffer + dstIndex + 3u) = *(data.buffer + srcIndex + 3u);
494 case 3: // The image's buffer is BGRA and the cairo's buffer is ARGB
496 const unsigned char alpha = *(data.buffer + srcIndex + 3u);
499 if( doBlendWithTextColor )
501 const float* const colorPtr = color.AsFloat();
503 const float srcAlpha = TO_FLOAT * static_cast<float>(alpha) * colorPtr[3u];
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] );
510 *(buffer + dstIndex + 3u) = static_cast<unsigned char>( TO_UCHAR * srcAlpha );
514 // @todo needs a proper blending!
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);
526 DALI_ASSERT_ALWAYS( !"Cairo Renderer: The accepted values for this switch case are: 0, 1, 2!" );
529 srcIndex += srcPixelSize;
530 dstIndex += dstPixelSize;
532 srcIndex += srcWidthOffset;
533 dstIndex += dstWidthOffset;
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
548 const TextAbstraction::TextRenderer::Parameters& parameters,
550 cairo_glyph_t*& cairoGlyphsBuffer,
552 cairo_t*& circularCr,
553 const bool isCircularText,
554 CircularTextParameters& circularTextParameters)
556 // Sets the color. The color is actually BGRA
557 const Vector4& color = parameters.colors[run.colorIndex];
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 ) );
565 // Create the Cairo's font from the FreeType font.
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();
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 ) );
575 cairo_font_face_destroy( fontFace );
578 unsigned int ftSynthesizeFlag = 0u;
579 if( run.isBoldRequired && !( run.fontFace->style_flags & FT_STYLE_FLAG_BOLD ) )
581 ftSynthesizeFlag |= CAIRO_FT_SYNTHESIZE_BOLD;
584 cairo_ft_font_face_set_synthesize( fontFace, ftSynthesizeFlag );
586 cairo_font_face_reference( fontFace );
588 const bool synthesizeItalic = ( run.isItalicRequired && !( run.fontFace->style_flags & FT_STYLE_FLAG_ITALIC ) );
590 if( CAIRO_STATUS_SUCCESS != cairo_font_face_status( fontFace ) )
592 DALI_LOG_ERROR( "Failed to load the Freetype Font\n" );
596 cairo_set_font_face( isCircularText ? circularCr : cr, fontFace );
599 cairo_set_font_size( isCircularText ? circularCr : cr, run.fontSize );
601 // Render the glyphs.
604 circularTextParameters.synthesizeItalic = synthesizeItalic;
606 const unsigned int glyphJump = circularTextParameters.synthesizeItalic ? 1u : run.numberOfGlyphs;
608 for( unsigned int index = 0u; index < run.numberOfGlyphs; index += glyphJump )
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 );
614 cairo_glyph_path( circularCr, ( cairoGlyphsBuffer + run.glyphIndex + index ), glyphJump );
616 WrapToCircularPath( cr, circularCr, circularTextParameters );
622 if( synthesizeItalic )
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.
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.
630 for( unsigned int index = run.glyphIndex, endIndex = run.glyphIndex + run.numberOfGlyphs; index < endIndex; ++index )
632 maxY = std::max( maxY, (*( cairoGlyphsBuffer + index )).y );
635 cairo_matrix_t matrix;
636 cairo_matrix_init( &matrix,
638 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE, 1.0,
639 maxY * TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE, 0.0 );
641 cairo_transform( cr, &matrix );
644 cairo_show_glyphs( cr, ( cairoGlyphsBuffer + run.glyphIndex ), run.numberOfGlyphs );
646 if( synthesizeItalic )
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 );
658 } // unnamed namespace
660 Devel::PixelBuffer RenderTextCairo( const TextAbstraction::TextRenderer::Parameters& parameters )
662 const unsigned int numberOfGlyphs = parameters.glyphs.Count();
664 if( 0u == numberOfGlyphs )
666 // return a pixel buffer with all pixels set to transparent.
667 return CreateVoidPixelBuffer( parameters );
670 // Choose the pixel format to be used.
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
675 // Here in practice Cairo's ARGB32 is like DALi's RGBA8888.
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;
681 const int bpp = Pixel::GetBytesPerPixel( pixelFormat );
684 // return a pixel buffer with all pixels set to transparent.
685 return CreateVoidPixelBuffer( parameters );
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;
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];
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();
703 for( unsigned index = 0u; index < numberOfGlyphs; ++index )
705 const GlyphInfo& daliGlyph = *( daliGlyphsBuffer + index );
706 const Vector2& position = *( positionsBuffer + index );
707 cairo_glyph_t& cairoGlyph = *( cairoGlyphsBuffer + index );
709 cairoGlyph.index = daliGlyph.index;
710 cairoGlyph.x = round( position.x );
711 cairoGlyph.y = round( position.y );
714 // Retrieve the FreeType fonts needed by Cairo from the font-client.
715 Dali::TextAbstraction::FontClient fontClient = Dali::TextAbstraction::FontClient::Get();
717 FT_Library ftLibrary;
718 auto error = FT_Init_FreeType( &ftLibrary );
721 DALI_LOG_ERROR( "Error initializing FT library\n" );
723 // return a pixel buffer with all pixels set to transparent.
724 return CreateVoidPixelBuffer( parameters );
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 );
731 if( ! ConvertSizeForCairo(fontClient, ftLibrary, numberOfGlyphs, daliGlyphsBuffer, colorIndicesBuffer, glyphRuns) )
733 // return a pixel buffer with all pixels set to transparent.
734 return CreateVoidPixelBuffer( parameters );
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 );
740 unsigned char* buffer = pixelBuffer.GetBuffer();
741 const unsigned int bufferSize = stride * parameters.height;
742 memset( buffer, 0, bufferSize );
744 std::unique_ptr<cairo_surface_t, void(*)(cairo_surface_t*)> surfacePtr( cairo_image_surface_create_for_data( buffer,
749 cairo_surface_destroy );
750 cairo_surface_t* surface = surfacePtr.get();
752 if( ( nullptr == surface ) || ( CAIRO_STATUS_SUCCESS != cairo_surface_status( surface ) ) )
754 DALI_LOG_ERROR( "Failed to create a cairo's surface\n" );
756 return CreateVoidPixelBuffer( parameters );
759 // Whether the text is circular.
760 const bool isCircularText = 0u != parameters.radius;
762 // Creates a surface for circular text.
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.
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;
775 circularSurfacePtr.reset( cairo_surface_create_similar( surface,
777 parameters.circularWidth,
778 parameters.circularHeight ) );
779 circularSurface = circularSurfacePtr.get();
781 if( ( nullptr == circularSurface ) || ( CAIRO_STATUS_SUCCESS != cairo_surface_status( circularSurface ) ) )
783 DALI_LOG_ERROR( "Failed to create a cairo's circular surface\n" );
785 return CreateVoidPixelBuffer( parameters );
789 std::unique_ptr<cairo_t, void(*)(cairo_t*)> crPtr( cairo_create( surface ), cairo_destroy );
790 cairo_t* cr = crPtr.get();
792 if( CAIRO_STATUS_SUCCESS != cairo_status( cr ) )
794 DALI_LOG_ERROR( "Failed to create a cairo context\n" );
796 return CreateVoidPixelBuffer( parameters );
799 std::unique_ptr<cairo_t, void(*)(cairo_t*)> circularCrPtr( nullptr, cairo_destroy );
800 cairo_t* circularCr = nullptr;
804 circularCrPtr.reset( cairo_create( circularSurface ) );
805 circularCr = circularCrPtr.get();
807 if( CAIRO_STATUS_SUCCESS != cairo_status( circularCr ) )
809 DALI_LOG_ERROR( "Failed to create a cairo context\n" );
811 return CreateVoidPixelBuffer( parameters );
815 CircularTextParameters circularTextParameters;
817 // Render the glyphs.
820 // Set the parameters.
821 circularTextParameters.isClockwise = ( TextAbstraction::TextRenderer::Parameters::CLOCKWISE == parameters.circularLayout );
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;
830 cairo_move_to( cr, 0.0, 0.0 );
832 for( const auto& run: glyphRuns )
834 const bool isEmoji = parameters.isEmoji[run.glyphIndex];
835 if( isEmoji || ( nullptr == run.fontFace ) )
837 // Retrieve the color for the glyph.
838 const Vector4& color = parameters.colors[run.colorIndex];
840 const unsigned int lastGlyphIndex = run.glyphIndex + run.numberOfGlyphs;
841 for( unsigned int index = run.glyphIndex; index < lastGlyphIndex; ++index )
843 // Whether it's a bitmap font.
844 const bool doBlendWithTextColor = !isEmoji && ( ColorBlendingMode::MULTIPLY == parameters.blendingMode[index] );
846 // Check if there is an embedded image or a bitmap font image.
847 const GlyphIndex glyphFontIndex = daliGlyphsBuffer[index].index;
848 if( 0u != glyphFontIndex )
850 // The embedded image could be A8, RGBA8888 or BGRA8888.
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.
855 const cairo_glyph_t& glyph = *( cairoGlyphsBuffer + index );
857 // Retrieve the image
858 TextAbstraction::FontClient::GlyphBufferData data;
859 std::unique_ptr<GlyphBuffer> glyphBufferPtr( new GlyphBuffer( data, GlyphBuffer::DELETE ) );
862 data.width = parameters.glyphs[run.glyphIndex].width;
863 data.height = parameters.glyphs[run.glyphIndex].height;
866 fontClient.CreateBitmap( run.fontId, glyphFontIndex, false, false, data, 0u );
868 if( nullptr == data.buffer )
870 // nothing else to do if there is no image.
874 // Calculate the position for the circular text.
875 double glyphX = glyph.x;
876 double glyphY = glyph.y;
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 );
884 double centerX = glyph.x + halfWidth;
885 double centerY = glyph.y - halfHeight;
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;
891 TransformToArc( circularTextParameters, centerX, centerY );
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 );
898 Dali::Internal::Platform::RotateByShear( data.buffer,
906 if( nullptr != pixelsOut )
908 delete[] data.buffer;
909 data.buffer = pixelsOut;
910 glyphBufferPtr.get()->type = GlyphBuffer::FREE;
911 data.width = widthOut;
912 data.height = heightOut;
915 glyphX = centerX - 0.5 * static_cast<double>( data.width );
916 glyphY = centerY + 0.5 * static_cast<double>( data.height );
920 if( ( Pixel::A8 != data.format ) &&
921 ( Pixel::L8 != data.format ) &&
922 ( Pixel::RGBA8888 != data.format ) &&
923 ( Pixel::BGRA8888 != data.format ) )
925 DALI_LOG_ERROR( " Cairo Renderer: The valid pixel format for embedded items are A8 or RGBA8888\n" );
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 ) ) ||
933 ( glyphY - static_cast<float>( data.height ) > static_cast<float>( parameters.height ) ) )
935 // The embedded item is completely out of the buffer.
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;
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
948 if( isSrcA && isDstRgba )
952 else if( isSrcRgba && isDstRgba )
956 else if( isSrcBgra && isDstRgba )
960 else if( ( isSrcRgba || isSrcBgra ) && !isDstRgba )
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" );
966 CopyImageToSurface(parameters, pixelFormat, data, buffer, rgbaCase, glyphX, glyphY, strideWidth, color, doBlendWithTextColor);
972 RenderGlyphs(parameters, run, cairoGlyphsBuffer, cr, circularCr, isCircularText, circularTextParameters);
979 } // namespace Internal
981 } // namespace TextAbstraction