2 * Copyright (c) 2021 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>
24 #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>
45 const float TO_FLOAT = 1.f / 255.f;
46 const float TO_UCHAR = 255.f;
47 const float TWO_PI = 2.f * Dali::Math::PI; ///< 360 degrees in radians
50 * @brief Run of glyphs that have the same style.
61 isItalicRequired{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:
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.
222 namespace TextAbstraction
229 * @brief Converts the size so that it can be used by Cairo
230 * @param[in] fontClient A reference to the font client
231 * @param[in] ftLibrary A reference to the Freetype library instance
232 * @param[in] numberOfGlyphs The total number of glyphs
233 * @param[in] daliGlyphsBuffer A pointer to the glyphs buffer
234 * @param[in] colorIndicesBuffer A pointer to the color indices buffer
235 * @param[in/out] glyphRuns A vector of glyph-runs
237 bool ConvertSizeForCairo(
238 TextAbstraction::FontClient& fontClient,
239 FT_Library& ftLibrary,
240 const unsigned int numberOfGlyphs,
241 const GlyphInfo* const daliGlyphsBuffer,
242 const ColorIndex* const colorIndicesBuffer,
243 std::vector<GlyphRun>& glyphRuns)
245 // The size set in Cairo and FreeType has different units.
246 // Before the size is set in Cairo it needs to be converted according the formula
247 // 'pixel_size = point_size * resolution / 72' got from the FreeType doc.
248 // https://www.freetype.org/freetype2/docs/glyphs/glyphs-2.html
250 unsigned int horizontalDpi = 0u;
251 unsigned int verticalDpi = 0u;
252 fontClient.GetDpi(horizontalDpi, verticalDpi);
253 const double dVerticalDpi = static_cast<double>(verticalDpi);
255 const double FROM_26_DOT_6_TO_PIXELS = dVerticalDpi / (64.0 * 72.0);
257 GlyphRun currentGlyphRun;
258 currentGlyphRun.fontId = 0u;
259 currentGlyphRun.colorIndex = 0u;
260 currentGlyphRun.isItalicRequired = false;
261 currentGlyphRun.isBoldRequired = false;
262 for(unsigned index = 0u; index < numberOfGlyphs; ++index)
264 const GlyphInfo& daliGlyph = *(daliGlyphsBuffer + index);
265 const FontId fontId = daliGlyph.fontId;
266 const ColorIndex colorIndex = (nullptr == colorIndicesBuffer) ? 0u : *(colorIndicesBuffer + index);
267 const bool isItalicRequired = daliGlyph.isItalicRequired;
268 const bool isBoldRequired = daliGlyph.isBoldRequired;
270 if((fontId != currentGlyphRun.fontId) ||
271 ((0u == fontId) && (0u != daliGlyph.index)) ||
272 (colorIndex != currentGlyphRun.colorIndex) ||
273 (isItalicRequired != currentGlyphRun.isItalicRequired) ||
274 (isBoldRequired != currentGlyphRun.isBoldRequired))
276 // There is a new run. First set the number of glyphs of the previous run and store it.
277 currentGlyphRun.numberOfGlyphs = index - currentGlyphRun.glyphIndex;
278 if(0u != currentGlyphRun.numberOfGlyphs)
280 glyphRuns.push_back(currentGlyphRun);
283 currentGlyphRun.fontFace = nullptr;
284 currentGlyphRun.fontSize = 0.0;
285 currentGlyphRun.glyphIndex = index;
286 currentGlyphRun.numberOfGlyphs = 0u;
287 currentGlyphRun.fontId = 0u;
288 currentGlyphRun.colorIndex = 0u;
289 currentGlyphRun.isItalicRequired = false;
290 currentGlyphRun.isBoldRequired = false;
294 // Get the font's path file name from the font Id.
295 FontDescription fontDescription;
296 fontClient.GetDescription(fontId, fontDescription);
298 switch(fontDescription.type)
300 case FontDescription::FACE_FONT:
302 // Create a FreeType font's face.
303 auto error = FT_New_Face(ftLibrary, fontDescription.path.c_str(), 0u, ¤tGlyphRun.fontFace);
306 DALI_LOG_ERROR("Error in FT while creating a new face\n");
308 // Error so just return false
312 // Set the font's size. It needs to be set in the Freetype font and in the Cairo's context.
313 unsigned int fontSize = fontClient.GetPointSize(fontId);
315 // Font's size to be set in the Cairo's context.
316 currentGlyphRun.fontSize = FROM_26_DOT_6_TO_PIXELS * static_cast<double>(fontSize);
319 case FontDescription::BITMAP_FONT:
331 currentGlyphRun.fontId = fontId;
332 currentGlyphRun.colorIndex = colorIndex;
333 currentGlyphRun.isItalicRequired = isItalicRequired;
334 currentGlyphRun.isBoldRequired = isBoldRequired;
338 // Calculate the number of glyphs of the last run and store it.
339 currentGlyphRun.numberOfGlyphs = numberOfGlyphs - currentGlyphRun.glyphIndex;
340 if(0u != currentGlyphRun.numberOfGlyphs)
342 glyphRuns.push_back(currentGlyphRun);
345 return true; // Successfully converted
349 * @brief Copies the image to the cairo surface
350 * @param[in] parameters The text renderer parameters
351 * @param[in] pixelFormat The pixel format
352 * @param[in] data The glyph buffer data
353 * @param[in] buffer The output buffer
354 * @param[in] rgbaCase 0 -> image and cairo buffer are A8
355 * 1 -> image is A8, cairo buffer is ARGB
356 * 2 -> image is RGBA and cairo buffer is ARGB
357 * 3 -> image is BGRA and cairo buffer is ARGB
358 * @param[in] glyphX The x-position of the glyph
359 * @param[in] glyphY The y-position of the glyph
360 * @param[in] strideWidth The stride width
361 * @param[in] color The color of the text
362 * @param[in] doBlendWithTextColor Whether to blend with the text color or not)
364 void CopyImageToSurface(
365 const TextAbstraction::TextRenderer::Parameters& parameters,
366 const Pixel::Format pixelFormat,
367 TextAbstraction::FontClient::GlyphBufferData& data,
368 unsigned char* buffer,
372 const int strideWidth,
373 const Vector4& color,
374 const bool doBlendWithTextColor)
376 // Select the cropped source image area to copy into the surface buffer
377 unsigned int glyphUintX = 0u;
378 unsigned int glyphUintY = 0u;
379 unsigned int srcWidth = data.width;
380 unsigned int srcHeight = data.height;
381 unsigned int xSrcIndex = 0u;
382 unsigned int ySrcIndex = 0u;
385 xSrcIndex = static_cast<unsigned int>(abs(glyphX));
386 srcWidth -= xSrcIndex;
390 glyphUintX = static_cast<unsigned int>(glyphX);
393 if(glyphUintX + srcWidth > static_cast<unsigned int>(strideWidth))
395 srcWidth -= ((glyphUintX + srcWidth) - strideWidth);
398 if(glyphY - static_cast<float>(srcHeight) < 0.f)
400 ySrcIndex = static_cast<unsigned int>(abs(glyphY - static_cast<float>(srcHeight)));
401 srcHeight -= ySrcIndex;
405 glyphUintY = static_cast<unsigned int>(glyphY - static_cast<float>(srcHeight));
408 if(glyphUintY + srcHeight > parameters.height)
410 srcHeight -= ((glyphUintY + srcHeight) - parameters.height);
413 // Calculate the source and destination indices.
414 const unsigned int srcPixelSize = Pixel::GetBytesPerPixel(data.format);
415 const unsigned int dstPixelSize = Pixel::GetBytesPerPixel(pixelFormat);
417 unsigned int srcIndex = srcPixelSize * (ySrcIndex * srcWidth + xSrcIndex);
418 unsigned int dstIndex = dstPixelSize * (glyphUintY * strideWidth + glyphUintX);
420 const unsigned int srcWidthOffset = srcPixelSize * (data.width - srcWidth);
421 const unsigned int dstWidthOffset = dstPixelSize * (strideWidth - srcWidth);
423 // Copy the image to the surface
424 for(unsigned int j = 0; j < srcHeight; ++j)
426 for(unsigned int i = 0; i < srcWidth; ++i)
430 case 0: // Both the image's buffer and cairo's buffer are A8
432 const unsigned char alpha = *(data.buffer + srcIndex);
435 // @todo needs a proper blending!
436 *(buffer + dstIndex) = alpha;
440 case 1: // The image's buffer is A8 and the cairo's buffer is ARGB
442 const unsigned char alpha = *(data.buffer + srcIndex);
445 // @todo needs a proper blending!
446 const float srcAlpha = TO_FLOAT * static_cast<float>(alpha);
448 // Write the RGBA modulated with the given default color.
449 const float* const colorPtr = color.AsFloat();
450 *(buffer + dstIndex + 0u) = static_cast<unsigned char>(TO_UCHAR * colorPtr[0u] * srcAlpha);
451 *(buffer + dstIndex + 1u) = static_cast<unsigned char>(TO_UCHAR * colorPtr[1u] * srcAlpha);
452 *(buffer + dstIndex + 2u) = static_cast<unsigned char>(TO_UCHAR * colorPtr[2u] * srcAlpha);
453 *(buffer + dstIndex + 3u) = static_cast<unsigned char>(TO_UCHAR * colorPtr[3u] * srcAlpha);
457 case 2: // The image's buffer is RGBA and the cairo's buffer is ARGB
459 const unsigned char alpha = *(data.buffer + srcIndex + 3u);
462 if(doBlendWithTextColor)
464 const float* const colorPtr = color.AsFloat();
466 const float srcAlpha = TO_FLOAT * static_cast<float>(alpha) * colorPtr[3u];
468 *(buffer + dstIndex + 0u) = static_cast<unsigned char>(static_cast<float>(*(data.buffer + srcIndex + 0u)) * colorPtr[0u]);
469 *(buffer + dstIndex + 1u) = static_cast<unsigned char>(static_cast<float>(*(data.buffer + srcIndex + 1u)) * colorPtr[1u]);
470 *(buffer + dstIndex + 2u) = static_cast<unsigned char>(static_cast<float>(*(data.buffer + srcIndex + 2u)) * colorPtr[2u]);
473 *(buffer + dstIndex + 3u) = static_cast<unsigned char>(TO_UCHAR * srcAlpha);
477 // @todo needs a proper blending!
479 *(buffer + dstIndex + 0u) = *(data.buffer + srcIndex + 0u);
480 *(buffer + dstIndex + 1u) = *(data.buffer + srcIndex + 1u);
481 *(buffer + dstIndex + 2u) = *(data.buffer + srcIndex + 2u);
484 *(buffer + dstIndex + 3u) = *(data.buffer + srcIndex + 3u);
489 case 3: // The image's buffer is BGRA and the cairo's buffer is ARGB
491 const unsigned char alpha = *(data.buffer + srcIndex + 3u);
494 if(doBlendWithTextColor)
496 const float* const colorPtr = color.AsFloat();
498 const float srcAlpha = TO_FLOAT * static_cast<float>(alpha) * colorPtr[3u];
500 *(buffer + dstIndex + 0u) = static_cast<unsigned char>(static_cast<float>(*(data.buffer + srcIndex + 2u)) * colorPtr[0u]);
501 *(buffer + dstIndex + 1u) = static_cast<unsigned char>(static_cast<float>(*(data.buffer + srcIndex + 1u)) * colorPtr[1u]);
502 *(buffer + dstIndex + 2u) = static_cast<unsigned char>(static_cast<float>(*(data.buffer + srcIndex + 0u)) * colorPtr[2u]);
505 *(buffer + dstIndex + 3u) = static_cast<unsigned char>(TO_UCHAR * srcAlpha);
509 // @todo needs a proper blending!
511 *(buffer + dstIndex + 0u) = *(data.buffer + srcIndex + 2u);
512 *(buffer + dstIndex + 1u) = *(data.buffer + srcIndex + 1u);
513 *(buffer + dstIndex + 2u) = *(data.buffer + srcIndex + 0u);
514 *(buffer + dstIndex + 3u) = *(data.buffer + srcIndex + 3u);
521 DALI_ASSERT_ALWAYS(!"Cairo Renderer: The accepted values for this switch case are: 0, 1, 2!");
524 srcIndex += srcPixelSize;
525 dstIndex += dstPixelSize;
527 srcIndex += srcWidthOffset;
528 dstIndex += dstWidthOffset;
533 * @brief Renders the glyph
534 * @param[in] parameters The text renderer parameters
535 * @param[in] run The current glyph-run
536 * @param[in] cairoGlyphsBuffer The cairo glyphs buffer
537 * @param[in/out] cr The cairo surface
538 * @param[in/out] circularCr The cairo surface if using circular text
539 * @param[in] isCircularText Whether we're using circular text or not
540 * @param[in/out] circularTextParameters The circular text parameters
543 const TextAbstraction::TextRenderer::Parameters& parameters,
545 cairo_glyph_t*& cairoGlyphsBuffer,
547 cairo_t*& circularCr,
548 const bool isCircularText,
549 CircularTextParameters& circularTextParameters)
551 // Sets the color. The color is actually BGRA
552 const Vector4& color = parameters.colors[run.colorIndex];
554 cairo_set_source_rgba(cr,
555 static_cast<double>(color.b),
556 static_cast<double>(color.g),
557 static_cast<double>(color.r),
558 static_cast<double>(color.a));
560 // Create the Cairo's font from the FreeType font.
562 options = CAIRO_HINT_STYLE_SLIGHT;
563 std::unique_ptr<cairo_font_face_t, void (*)(cairo_font_face_t*)> fontFacePtr(cairo_ft_font_face_create_for_ft_face(run.fontFace, options), cairo_font_face_destroy);
564 cairo_font_face_t* fontFace = fontFacePtr.get();
566 static const cairo_user_data_key_t key = {0};
567 cairo_status_t status = cairo_font_face_set_user_data(fontFace, &key, run.fontFace, reinterpret_cast<cairo_destroy_func_t>(FT_Done_Face));
570 cairo_font_face_destroy(fontFace);
573 unsigned int ftSynthesizeFlag = 0u;
574 if(run.isBoldRequired && !(run.fontFace->style_flags & FT_STYLE_FLAG_BOLD))
576 ftSynthesizeFlag |= CAIRO_FT_SYNTHESIZE_BOLD;
579 cairo_ft_font_face_set_synthesize(fontFace, ftSynthesizeFlag);
581 cairo_font_face_reference(fontFace);
583 const bool synthesizeItalic = (run.isItalicRequired && !(run.fontFace->style_flags & FT_STYLE_FLAG_ITALIC));
585 if(CAIRO_STATUS_SUCCESS != cairo_font_face_status(fontFace))
587 DALI_LOG_ERROR("Failed to load the Freetype Font\n");
591 cairo_set_font_face(isCircularText ? circularCr : cr, fontFace);
594 cairo_set_font_size(isCircularText ? circularCr : cr, run.fontSize);
596 // Render the glyphs.
599 circularTextParameters.synthesizeItalic = synthesizeItalic;
601 const unsigned int glyphJump = circularTextParameters.synthesizeItalic ? 1u : run.numberOfGlyphs;
603 for(unsigned int index = 0u; index < run.numberOfGlyphs; index += glyphJump)
605 // Clears the current path where the text is laid out on a horizontal straight line.
606 cairo_new_path(circularCr);
607 cairo_move_to(circularCr, 0.0, 0.0);
609 cairo_glyph_path(circularCr, (cairoGlyphsBuffer + run.glyphIndex + index), glyphJump);
611 WrapToCircularPath(cr, circularCr, circularTextParameters);
619 // Apply a shear transform to synthesize the italics.
620 // For a reason Cairo may trim some glyphs if the CAIRO_FT_SYNTHESIZE_OBLIQUE flag is used.
622 // This is to calculate an offset used to compensate the 'translation' done by the shear transform
623 // as it's done for the whole render buffer.
625 for(unsigned int index = run.glyphIndex, endIndex = run.glyphIndex + run.numberOfGlyphs; index < endIndex; ++index)
627 maxY = std::max(maxY, (*(cairoGlyphsBuffer + index)).y);
630 cairo_matrix_t matrix;
631 cairo_matrix_init(&matrix,
634 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
636 maxY * TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
639 cairo_transform(cr, &matrix);
642 cairo_show_glyphs(cr, (cairoGlyphsBuffer + run.glyphIndex), run.numberOfGlyphs);
646 // Restore the transform matrix to the identity.
647 cairo_matrix_t matrix;
648 cairo_matrix_init_identity(&matrix);
649 cairo_set_matrix(cr, &matrix);
656 } // unnamed namespace
658 Devel::PixelBuffer RenderTextCairo(const TextAbstraction::TextRenderer::Parameters& parameters)
660 const unsigned int numberOfGlyphs = parameters.glyphs.Count();
662 if(0u == numberOfGlyphs)
664 // return a pixel buffer with all pixels set to transparent.
665 return CreateVoidPixelBuffer(parameters);
668 // Choose the pixel format to be used.
670 // @note Behdad wrote "Upper 8 bits maps to the fourth byte in a little-endian machine like the intels."
671 // https://lists.cairographics.org/archives/cairo/2006-March/006563.html
673 // Here in practice Cairo's ARGB32 is like DALi's RGBA8888.
675 const bool isDstRgba = TextAbstraction::TextRenderer::Parameters::RGBA8888 == parameters.pixelFormat;
676 const Pixel::Format pixelFormat = isDstRgba ? Pixel::Format::RGBA8888 : Pixel::Format::A8;
677 const cairo_format_t cairoFormat = isDstRgba ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_A8;
679 const int bpp = Pixel::GetBytesPerPixel(pixelFormat);
682 // return a pixel buffer with all pixels set to transparent.
683 return CreateVoidPixelBuffer(parameters);
686 // This function provides a stride value that will respect all alignment requirements of the
687 // accelerated image-rendering code within cairo.
688 const int stride = cairo_format_stride_for_width(cairoFormat,
689 static_cast<int>(parameters.width));
690 const int strideWidth = stride / bpp;
692 // Convert from DALi glyphs to Cairo glyphs.
693 std::vector<cairo_glyph_t> cairoGlyphs;
694 cairoGlyphs.resize(numberOfGlyphs);
695 cairo_glyph_t* cairoGlyphsBuffer = &cairoGlyphs[0u];
697 const GlyphInfo* const daliGlyphsBuffer = parameters.glyphs.Begin();
698 const Vector2* const positionsBuffer = parameters.positions.Begin();
699 const ColorIndex* const colorIndicesBuffer = (0u == parameters.colorIndices.Count()) ? nullptr : parameters.colorIndices.Begin();
701 for(unsigned index = 0u; index < numberOfGlyphs; ++index)
703 const GlyphInfo& daliGlyph = *(daliGlyphsBuffer + index);
704 const Vector2& position = *(positionsBuffer + index);
705 cairo_glyph_t& cairoGlyph = *(cairoGlyphsBuffer + index);
707 cairoGlyph.index = daliGlyph.index;
708 cairoGlyph.x = round(position.x);
709 cairoGlyph.y = round(position.y);
712 // Retrieve the FreeType fonts needed by Cairo from the font-client.
713 Dali::TextAbstraction::FontClient fontClient = Dali::TextAbstraction::FontClient::Get();
715 FT_Library ftLibrary;
716 auto error = FT_Init_FreeType(&ftLibrary);
719 DALI_LOG_ERROR("Error initializing FT library\n");
721 // return a pixel buffer with all pixels set to transparent.
722 return CreateVoidPixelBuffer(parameters);
725 // Vector used to store the FreeType font faces, its size and the run of glyphs that use the font.
726 std::vector<GlyphRun> glyphRuns;
727 glyphRuns.reserve(8u);
729 if(!ConvertSizeForCairo(fontClient, ftLibrary, numberOfGlyphs, daliGlyphsBuffer, colorIndicesBuffer, glyphRuns))
731 // return a pixel buffer with all pixels set to transparent.
732 return CreateVoidPixelBuffer(parameters);
735 // Creates the pixel buffer and retrieves the buffer pointer used to create the Cairo's surface.
736 Devel::PixelBuffer pixelBuffer = Devel::PixelBuffer::New(strideWidth, parameters.height, pixelFormat);
738 unsigned char* buffer = pixelBuffer.GetBuffer();
739 const unsigned int bufferSize = stride * parameters.height;
740 memset(buffer, 0, bufferSize);
742 std::unique_ptr<cairo_surface_t, void (*)(cairo_surface_t*)> surfacePtr(cairo_image_surface_create_for_data(buffer,
747 cairo_surface_destroy);
748 cairo_surface_t* surface = surfacePtr.get();
750 if((nullptr == surface) || (CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)))
752 DALI_LOG_ERROR("Failed to create a cairo's surface\n");
754 return CreateVoidPixelBuffer(parameters);
757 // Whether the text is circular.
758 const bool isCircularText = 0u != parameters.radius;
760 // Creates a surface for circular text.
762 // The reason to create a surface for circular text is that the strategy
763 // followed is to layout the text in a straight horizontal line and apply a
764 // transform to each vertex that forms the geometry of the glyphs to place
765 // and bend the glyphs accordingly to the circular path.
767 // As the glyphs are laid out first in a straight line they may exceed the
768 // boundaries of the surface in that case cairo ignores them.
769 std::unique_ptr<cairo_surface_t, void (*)(cairo_surface_t*)> circularSurfacePtr(nullptr, cairo_surface_destroy);
770 cairo_surface_t* circularSurface = nullptr;
773 circularSurfacePtr.reset(cairo_surface_create_similar(surface,
775 parameters.circularWidth,
776 parameters.circularHeight));
777 circularSurface = circularSurfacePtr.get();
779 if((nullptr == circularSurface) || (CAIRO_STATUS_SUCCESS != cairo_surface_status(circularSurface)))
781 DALI_LOG_ERROR("Failed to create a cairo's circular surface\n");
783 return CreateVoidPixelBuffer(parameters);
787 std::unique_ptr<cairo_t, void (*)(cairo_t*)> crPtr(cairo_create(surface), cairo_destroy);
788 cairo_t* cr = crPtr.get();
790 if(CAIRO_STATUS_SUCCESS != cairo_status(cr))
792 DALI_LOG_ERROR("Failed to create a cairo context\n");
794 return CreateVoidPixelBuffer(parameters);
797 std::unique_ptr<cairo_t, void (*)(cairo_t*)> circularCrPtr(nullptr, cairo_destroy);
798 cairo_t* circularCr = nullptr;
802 circularCrPtr.reset(cairo_create(circularSurface));
803 circularCr = circularCrPtr.get();
805 if(CAIRO_STATUS_SUCCESS != cairo_status(circularCr))
807 DALI_LOG_ERROR("Failed to create a cairo context\n");
809 return CreateVoidPixelBuffer(parameters);
813 CircularTextParameters circularTextParameters;
815 // Render the glyphs.
818 // Set the parameters.
819 circularTextParameters.isClockwise = (TextAbstraction::TextRenderer::Parameters::CLOCKWISE == parameters.circularLayout);
821 circularTextParameters.centerX = static_cast<double>(parameters.centerX);
822 circularTextParameters.centerY = static_cast<double>(parameters.centerY);
823 circularTextParameters.radius = static_cast<double>(parameters.radius);
824 circularTextParameters.invRadius = 1.0 / circularTextParameters.radius;
825 circularTextParameters.beginAngle = -parameters.beginAngle + Dali::Math::PI_2;
828 cairo_move_to(cr, 0.0, 0.0);
830 for(const auto& run : glyphRuns)
832 const bool isEmoji = parameters.isEmoji[run.glyphIndex];
833 if(isEmoji || (nullptr == run.fontFace))
835 // Retrieve the color for the glyph.
836 const Vector4& color = parameters.colors[run.colorIndex];
838 const unsigned int lastGlyphIndex = run.glyphIndex + run.numberOfGlyphs;
839 for(unsigned int index = run.glyphIndex; index < lastGlyphIndex; ++index)
841 // Whether it's a bitmap font.
842 const bool doBlendWithTextColor = !isEmoji && (ColorBlendingMode::MULTIPLY == parameters.blendingMode[index]);
844 // Check if there is an embedded image or a bitmap font image.
845 const GlyphIndex glyphFontIndex = daliGlyphsBuffer[index].index;
846 if(0u != glyphFontIndex)
848 // The embedded image could be A8, RGBA8888 or BGRA8888.
850 // If the embedded image is RGBA8888 or BGRA8888 then the cairo's buffer is ARGB32. It's needed to convert from RGBA or BGRA to ARGB.
851 // If the embedded image is A8 it's needed to check if the cairo's buffer is A8 or ARGB32 and do the conversion if needed.
853 const cairo_glyph_t& glyph = *(cairoGlyphsBuffer + index);
855 // Retrieve the image
856 TextAbstraction::FontClient::GlyphBufferData data;
857 std::unique_ptr<GlyphBuffer> glyphBufferPtr(new GlyphBuffer(data, GlyphBuffer::DELETE));
860 data.width = parameters.glyphs[run.glyphIndex].width;
861 data.height = parameters.glyphs[run.glyphIndex].height;
864 fontClient.CreateBitmap(run.fontId, glyphFontIndex, false, false, data, 0u);
866 if(nullptr == data.buffer)
868 // nothing else to do if there is no image.
872 // Calculate the position for the circular text.
873 double glyphX = glyph.x;
874 double glyphY = glyph.y;
878 // Center of the bitmap.
879 const double halfWidth = 0.5 * static_cast<double>(data.width);
880 const double halfHeight = 0.5 * static_cast<double>(data.height);
882 double centerX = glyph.x + halfWidth;
883 double centerY = glyph.y - halfHeight;
885 float radians = circularTextParameters.beginAngle + (circularTextParameters.isClockwise ? -1.f : 1.f) * (Dali::Math::PI_2 + circularTextParameters.invRadius * centerX);
886 radians = fmod(radians, TWO_PI);
887 radians += (radians < 0.f) ? TWO_PI : 0.f;
889 TransformToArc(circularTextParameters, centerX, centerY);
891 uint8_t* pixelsOut = nullptr;
892 unsigned int widthOut = data.width;
893 unsigned int heightOut = data.height;
894 const unsigned int pixelSize = Pixel::GetBytesPerPixel(data.format);
896 Dali::Internal::Platform::RotateByShear(data.buffer,
904 if(nullptr != pixelsOut)
906 delete[] data.buffer;
907 data.buffer = pixelsOut;
908 glyphBufferPtr.get()->type = GlyphBuffer::FREE;
909 data.width = widthOut;
910 data.height = heightOut;
913 glyphX = centerX - 0.5 * static_cast<double>(data.width);
914 glyphY = centerY + 0.5 * static_cast<double>(data.height);
917 if((Pixel::A8 != data.format) &&
918 (Pixel::L8 != data.format) &&
919 (Pixel::RGBA8888 != data.format) &&
920 (Pixel::BGRA8888 != data.format))
922 DALI_LOG_ERROR(" Cairo Renderer: The valid pixel format for embedded items are A8 or RGBA8888\n");
926 // Check if the item is out of the buffer.
927 if((glyphX + static_cast<float>(data.width) < 0.f) ||
928 (glyphX > static_cast<float>(strideWidth)) ||
930 (glyphY - static_cast<float>(data.height) > static_cast<float>(parameters.height)))
932 // The embedded item is completely out of the buffer.
936 const bool isSrcA = (Pixel::A8 == data.format) || (Pixel::L8 == data.format);
937 const bool isSrcRgba = Pixel::RGBA8888 == data.format;
938 const bool isSrcBgra = Pixel::BGRA8888 == data.format;
940 // 0 -> image and cairo buffer are A8
941 // 1 -> image is A8, cairo buffer is ARGB
942 // 2 -> image is RGBA and cairo buffer is ARGB
943 // 3 -> image is BGRA and cairo buffer is ARGB
945 if(isSrcA && isDstRgba)
949 else if(isSrcRgba && isDstRgba)
953 else if(isSrcBgra && isDstRgba)
957 else if((isSrcRgba || isSrcBgra) && !isDstRgba)
959 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 CopyImageToSurface(parameters, pixelFormat, data, buffer, rgbaCase, glyphX, glyphY, strideWidth, color, doBlendWithTextColor);
969 RenderGlyphs(parameters, run, cairoGlyphsBuffer, cr, circularCr, isCircularText, circularTextParameters);
976 } // namespace Internal
978 } // namespace TextAbstraction