From: Victor Cebollada Date: Mon, 16 Apr 2018 15:34:47 +0000 (+0100) Subject: Text improvement X-Git-Tag: dali_1.4.8~1^2 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git;a=commitdiff_plain;h=0cef5f33606916b0ccbfada25f8b99d21f018e47 Text improvement 1. Circular text implementation. * Adds to dali-adaptor a back-end to render text with Cairo. * Add support for bitmap fonts. * Add support embedded Item. 2. Software italic/bold issues fixed. Change-Id: I6491bdc19db789385f1928a99d63eaecb1c12959 --- diff --git a/build/tizen/adaptor/Makefile.am b/build/tizen/adaptor/Makefile.am index 24570db..8a79d7d 100644 --- a/build/tizen/adaptor/Makefile.am +++ b/build/tizen/adaptor/Makefile.am @@ -436,6 +436,7 @@ LIBDALI_ADAPTOR_LA_CXXFLAGS = \ $(OPENGLES20_CFLAGS) \ $(FREETYPE_CFLAGS) \ $(FONTCONFIG_CFLAGS) \ + $(CAIRO_CFLAGS) \ $(PNG_CFLAGS) \ $(DLOG_CFLAGS) \ $(VCONF_CFLAGS) \ @@ -465,6 +466,7 @@ LIBDALI_ADAPTOR_LA_LIBADD = \ $(OPENGLES20_LIBS) \ $(FREETYPE_LIBS) \ $(FONTCONFIG_LIBS) \ + $(CAIRO_LIBS) \ $(PNG_LIBS) \ $(DLOG_LIBS) \ $(VCONF_LIBS) \ diff --git a/build/tizen/adaptor/configure.ac b/build/tizen/adaptor/configure.ac index ea05c8b..dba2891 100644 --- a/build/tizen/adaptor/configure.ac +++ b/build/tizen/adaptor/configure.ac @@ -47,6 +47,7 @@ PKG_CHECK_MODULES(LIBCURL, libcurl) PKG_CHECK_MODULES(LIBCRYPTO, libcrypto) PKG_CHECK_MODULES(HARFBUZZ, harfbuzz) PKG_CHECK_MODULES(FRIBIDI, fribidi) +PKG_CHECK_MODULES(CAIRO, cairo) PKG_CHECK_MODULES(EVAS, evas) PKG_CHECK_MODULES(TTRACE, ttrace, AC_DEFINE(ENABLE_TTRACE, 1, [ttrace available]), [ AC_MSG_NOTICE([Tizen Trace not avaiable]) ] diff --git a/dali/devel-api/adaptor-framework/pixel-buffer.cpp b/dali/devel-api/adaptor-framework/pixel-buffer.cpp old mode 100644 new mode 100755 index f8d12b2..8ba24a0 --- a/dali/devel-api/adaptor-framework/pixel-buffer.cpp +++ b/dali/devel-api/adaptor-framework/pixel-buffer.cpp @@ -96,6 +96,11 @@ unsigned char* PixelBuffer::GetBuffer() return GetImplementation(*this).GetBuffer(); } +const unsigned char* const PixelBuffer::GetBuffer() const +{ + return GetImplementation(*this).GetConstBuffer(); +} + void PixelBuffer::ApplyMask( PixelBuffer mask, float contentScale, bool cropToMask ) { GetImplementation(*this).ApplyMask( GetImplementation( mask ), contentScale, cropToMask ); diff --git a/dali/devel-api/adaptor-framework/pixel-buffer.h b/dali/devel-api/adaptor-framework/pixel-buffer.h index a63a623..449bc02 100755 --- a/dali/devel-api/adaptor-framework/pixel-buffer.h +++ b/dali/devel-api/adaptor-framework/pixel-buffer.h @@ -134,6 +134,17 @@ public: unsigned char* GetBuffer(); /** + * @brief Gets the pixel buffer. This is a pointer to the internal + * pixel buffer. + * + * @warning If there is no pixel buffer (e.g. this object has been + * converted to a PixelData), this method will return NULL. + * + * @return The pixel buffer, or NULL. + */ + const unsigned char* const GetBuffer() const; + + /** * @brief Gets the width of the buffer in pixels. * * @SINCE_1_2.46 diff --git a/dali/devel-api/file.list b/dali/devel-api/file.list index 9e05c21..f787144 100755 --- a/dali/devel-api/file.list +++ b/dali/devel-api/file.list @@ -84,16 +84,20 @@ devel_api_adaptor_framework_header_files = \ devel_api_text_abstraction_src_files = \ $(adaptor_devel_api_dir)/text-abstraction/bidirectional-support.cpp \ + $(adaptor_devel_api_dir)/text-abstraction/bitmap-font.cpp \ $(adaptor_devel_api_dir)/text-abstraction/font-client.cpp \ $(adaptor_devel_api_dir)/text-abstraction/font-list.cpp \ $(adaptor_devel_api_dir)/text-abstraction/font-metrics.cpp \ $(adaptor_devel_api_dir)/text-abstraction/glyph-info.cpp \ $(adaptor_devel_api_dir)/text-abstraction/script.cpp \ $(adaptor_devel_api_dir)/text-abstraction/segmentation.cpp \ - $(adaptor_devel_api_dir)/text-abstraction/shaping.cpp + $(adaptor_devel_api_dir)/text-abstraction/shaping.cpp \ + $(adaptor_devel_api_dir)/text-abstraction/text-renderer.cpp \ + $(adaptor_devel_api_dir)/text-abstraction/text-renderer-layout-helper.cpp text_abstraction_header_files = \ $(adaptor_devel_api_dir)/text-abstraction/bidirectional-support.h \ + $(adaptor_devel_api_dir)/text-abstraction/bitmap-font.h \ $(adaptor_devel_api_dir)/text-abstraction/font-client.h \ $(adaptor_devel_api_dir)/text-abstraction/font-list.h \ $(adaptor_devel_api_dir)/text-abstraction/font-metrics.h \ @@ -102,5 +106,7 @@ text_abstraction_header_files = \ $(adaptor_devel_api_dir)/text-abstraction/segmentation.h \ $(adaptor_devel_api_dir)/text-abstraction/shaping.h \ $(adaptor_devel_api_dir)/text-abstraction/text-abstraction.h \ - $(adaptor_devel_api_dir)/text-abstraction/text-abstraction-definitions.h + $(adaptor_devel_api_dir)/text-abstraction/text-abstraction-definitions.h \ + $(adaptor_devel_api_dir)/text-abstraction/text-renderer.h \ + $(adaptor_devel_api_dir)/text-abstraction/text-renderer-layout-helper.h diff --git a/dali/devel-api/text-abstraction/bitmap-font.cpp b/dali/devel-api/text-abstraction/bitmap-font.cpp new file mode 100755 index 0000000..4e17df5 --- /dev/null +++ b/dali/devel-api/text-abstraction/bitmap-font.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// FILE HEADER +#include + +namespace Dali +{ + +namespace TextAbstraction +{ + +BitmapGlyph::BitmapGlyph() +: url{}, + utf32{ 0u }, + ascender{ 0.f }, + descender{ 0.f } +{} + +BitmapGlyph::BitmapGlyph( const std::string& url, GlyphIndex utf32, float ascender, float descender ) +: url{ url }, + utf32{ utf32 }, + ascender{ ascender }, + descender{ descender } +{} + +BitmapGlyph::~BitmapGlyph() +{} + +BitmapFont::BitmapFont() +: glyphs{}, + name{}, + ascender{ 0.f }, + descender{ 0.f }, + underlinePosition{ 0.f }, + underlineThickness{ 1.f } +{} + +BitmapFont::~BitmapFont() +{} + +} // namespace TextAbstraction + +} // namespace Dali + diff --git a/dali/devel-api/text-abstraction/bitmap-font.h b/dali/devel-api/text-abstraction/bitmap-font.h new file mode 100755 index 0000000..53bbec2 --- /dev/null +++ b/dali/devel-api/text-abstraction/bitmap-font.h @@ -0,0 +1,106 @@ +#ifndef DALI_TEXT_ABSTRACTION_BITMAP_FONT_H +#define DALI_TEXT_ABSTRACTION_BITMAP_FONT_H + +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include + + + +namespace Dali +{ + +namespace TextAbstraction +{ + +/** + * @brief Struct that stores the needed info to create a bitmap glyph. + * + * BitmapGlyph objects need to be added to a BitmapFont. + */ +struct DALI_ADAPTOR_API BitmapGlyph +{ + /** + * @brief Default constructor. + * + * Initialize the members to its defaults. + */ + BitmapGlyph(); + + /** + * @brief Constructor. + * + * Initialize the members with the given values. + * + * @param[in] url The url of the bitmap for that glyph. + * @param[in] utf32 The utf32 codification of the glyph. + * @param[in] ascender The ascender of the glyph. + * @param[in] descender The descender of the glyph. + */ + BitmapGlyph( const std::string& url, GlyphIndex utf32, float ascender, float descender ); + + /** + * @brief Default destructor. + */ + ~BitmapGlyph(); + + std::string url; ///< The url of the glyph's bitmap. + GlyphIndex utf32; ///< The id of the glyph encoded in utf32. + float ascender; ///< The ascender in pixels. The distance from the base line to the top of the glyph. + float descender; ///< The descender in pixels. The distance from the base line to the bottom of the glyph. +}; + +/** + * @brief Struct that stores the needed info to create a bitmap font. + * + * A bitmap font can be created by calling FontClient::GetFontId( const BitmapFont& ). + */ +struct DALI_ADAPTOR_API BitmapFont +{ + /** + * @brief Default constructor. + * + * Initialize the members to its defaults but the @e underlineThickness which is initilized to 1 pixel. + */ + BitmapFont(); + + /** + * @brief Default destructor. + */ + ~BitmapFont(); + + std::vector glyphs; ///< The glyphs of the font. + std::string name; ///< The name of the font. + float ascender; ///< The ascender in pixels. Maximum ascender of all the glyphs. + float descender; ///< The descender in pixels. Minimum descender of all the glyphs. + float underlinePosition; ///< The position in pixels of the underline from the base line. + float underlineThickness; ///< The thickness in pixels of the underline. +}; + +} // namespace TextAbstraction + +} // namespace Dali + +#endif // DALI_TEXT_ABSTRACTION_BITMAP_FONT_H diff --git a/dali/devel-api/text-abstraction/font-client.cpp b/dali/devel-api/text-abstraction/font-client.cpp old mode 100644 new mode 100755 index 00b7528..653d11c --- a/dali/devel-api/text-abstraction/font-client.cpp +++ b/dali/devel-api/text-abstraction/font-client.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ namespace TextAbstraction { const PointSize26Dot6 FontClient::DEFAULT_POINT_SIZE = 768u; // 12*64 +const float FontClient::DEFAULT_ITALIC_ANGLE = 12.f * Dali::Math::PI_OVER_180; // FreeType documentation states the software italic is done by doing a horizontal shear of 12 degrees (file ftsynth.h). FontClient::GlyphBufferData::GlyphBufferData() : buffer( nullptr ), @@ -146,6 +147,11 @@ FontId FontClient::GetFontId( const FontDescription& fontDescription, faceIndex ); } +FontId FontClient::GetFontId( const BitmapFont& bitmapFont ) +{ + return GetImplementation(*this).GetFontId( bitmapFont ); +} + bool FontClient::IsScalable( const FontPath& path ) { return GetImplementation(*this).IsScalable( path ); @@ -167,6 +173,11 @@ void FontClient::GetFixedSizes( const FontDescription& fontDescription, GetImplementation(*this).GetFixedSizes( fontDescription, sizes ); } +bool FontClient::HasItalicStyle( FontId fontId ) const +{ + return GetImplementation(*this).HasItalicStyle( fontId ); +} + void FontClient::GetFontMetrics( FontId fontId, FontMetrics& metrics ) { GetImplementation(*this).GetFontMetrics( fontId, metrics ); @@ -182,9 +193,9 @@ bool FontClient::GetGlyphMetrics( GlyphInfo* array, uint32_t size, GlyphType typ return GetImplementation(*this).GetGlyphMetrics( array, size, type, horizontal ); } -void FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool softwareItalic, bool softwareBold, GlyphBufferData& data, int outlineWidth ) +void FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, GlyphBufferData& data, int outlineWidth ) { - GetImplementation(*this).CreateBitmap( fontId, glyphIndex, softwareItalic, softwareBold, data, outlineWidth ); + GetImplementation(*this).CreateBitmap( fontId, glyphIndex, isItalicRequired, isBoldRequired, data, outlineWidth ); } PixelData FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth ) @@ -212,6 +223,11 @@ bool FontClient::AddCustomFontDirectory( const FontPath& path ) return GetImplementation(*this).AddCustomFontDirectory( path ); } +GlyphIndex FontClient::CreateEmbeddedItem(const EmbeddedItemDescription& description, Pixel::Format& pixelFormat) +{ + return GetImplementation(*this).CreateEmbeddedItem( description, pixelFormat); +} + FontClient::FontClient( Internal::FontClient* internal ) : BaseHandle( internal ) { diff --git a/dali/devel-api/text-abstraction/font-client.h b/dali/devel-api/text-abstraction/font-client.h index c444eb8..4dbb5f5 100755 --- a/dali/devel-api/text-abstraction/font-client.h +++ b/dali/devel-api/text-abstraction/font-client.h @@ -2,7 +2,7 @@ #define DALI_PLATFORM_TEXT_ABSTRACTION_FONT_CLIENT_H /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ namespace TextAbstraction struct FontMetrics; struct GlyphInfo; +struct BitmapFont; namespace Internal DALI_INTERNAL { @@ -64,6 +65,7 @@ class DALI_ADAPTOR_API FontClient : public BaseHandle { public: static const PointSize26Dot6 DEFAULT_POINT_SIZE; ///< The default point size. + static const float DEFAULT_ITALIC_ANGLE; ///< The default software italic angle in radians. /** * @brief Struct used to retrieve the glyph's bitmap. @@ -88,6 +90,17 @@ public: Pixel::Format format; ///< The pixel's format of the bitmap. }; + /** + * @brief Used to load an embedded item into the font client. + */ + struct EmbeddedItemDescription + { + std::string url; ///< The url path of the image. + unsigned int width; ///< The width of the item. + unsigned int height; ///< The height of the item. + ColorBlendingMode colorblendingMode; ///< Whether the color of the image is multiplied by the color of the text. + }; + public: /** @@ -273,6 +286,15 @@ public: FaceIndex faceIndex = 0 ); /** + * @brief Retrieves a unique font identifier for a given bitmap font. + * + * @param[in] bitmapFont A bitmap font. + * + * @return A valid font identifier, or zero if no bitmap font is created. + */ + FontId GetFontId( const BitmapFont& bitmapFont ); + + /** * @brief Check to see if a font is scalable. * * @param[in] path The path to a font file. @@ -310,6 +332,15 @@ public: void GetFixedSizes( const FontDescription& fontDescription, Dali::Vector< PointSize26Dot6 >& sizes ); + /** + * @brief Whether the font has Italic style. + * + * @param[in] fontId The font identifier. + * + * @return true if the font has italic style. + */ + bool HasItalicStyle( FontId fontId ) const; + //////////////////////////////////////// // Font metrics, glyphs and bitmaps. //////////////////////////////////////// @@ -351,14 +382,14 @@ public: * * @note The caller is responsible for deallocating the bitmap data @p data.buffer using delete[]. * - * @param[in] fontId The identifier of the font. - * @param[in] glyphIndex The index of a glyph within the specified font. - * @param[in] softwareItalic Whether glyph needs software support to draw italic style. - * @param[in] softwareBold Whether glyph needs software support to draw bold style. - * @param[out] data The bitmap data. - * @param[in] outlineWidth The width of the glyph outline in pixels. + * @param[in] fontId The identifier of the font. + * @param[in] glyphIndex The index of a glyph within the specified font. + * @param[in] isItalicRequired Whether the glyph requires italic style. + * @param[in] isBoldRequired Whether the glyph requires bold style. + * @param[out] data The bitmap data. + * @param[in] outlineWidth The width of the glyph outline in pixels. */ - void CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool softwareItalic, bool softwareBold, GlyphBufferData& data, int outlineWidth ); + void CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, GlyphBufferData& data, int outlineWidth ); /** * @brief Create a bitmap representation of a glyph. @@ -417,6 +448,21 @@ public: */ bool AddCustomFontDirectory( const FontPath& path ); + /** + * @brief Creates and stores an embedded item and it's metrics. + * + * If in the @p description there is a non empty url, it calls Dali::LoadImageFromFile() internally. + * If in the @p description there is a url and @e width or @e height are zero it stores the default size. Otherwise the image is resized. + * If the url in the @p description is empty it stores the size. + * + * @param[in] description The description of the embedded item. + * @param[out] pixelFormat The pixel format of the image. + * + * return The index within the vector of embedded items. + */ + GlyphIndex CreateEmbeddedItem( const EmbeddedItemDescription& description, Pixel::Format& pixelFormat); + + public: // Not intended for application developers /** * @brief This constructor is used by FontClient::Get(). diff --git a/dali/devel-api/text-abstraction/font-list.h b/dali/devel-api/text-abstraction/font-list.h index 2e76ad1..65c8a5a 100755 --- a/dali/devel-api/text-abstraction/font-list.h +++ b/dali/devel-api/text-abstraction/font-list.h @@ -1,8 +1,8 @@ -#ifndef __DALI_TEXT_ABSTRACTION_FONT_LIST_H__ -#define __DALI_TEXT_ABSTRACTION_FONT_LIST_H__ +#ifndef DALI_TEXT_ABSTRACTION_FONT_LIST_H +#define DALI_TEXT_ABSTRACTION_FONT_LIST_H /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -139,12 +139,20 @@ namespace FontSlant struct FontDescription { + enum Type + { + INVALID, ///< Not valid font. + FACE_FONT, ///< A face font. + BITMAP_FONT, ///< A bitmap font. Each glyph has a url with the bitmap. + }; + FontDescription() : path(), family(), width( FontWidth::NONE ), weight( FontWeight::NONE ), - slant( FontSlant::NONE ) + slant( FontSlant::NONE ), + type( INVALID ) {} ~FontDescription() @@ -155,6 +163,7 @@ struct FontDescription FontWidth::Type width; ///< The font's width. FontWeight::Type weight; ///< The font's weight. FontSlant::Type slant; ///< The font's slant. + Type type; ///< The type of font. }; typedef std::vector FontList; @@ -165,4 +174,4 @@ DALI_ADAPTOR_API std::ostream& operator<<( std::ostream& o, const FontList& font } // namespace Dali -#endif // __DALI_TEXT_ABSTRACTION_FONT_LIST_H__ +#endif // DALI_TEXT_ABSTRACTION_FONT_LIST_H diff --git a/dali/devel-api/text-abstraction/font-metrics.cpp b/dali/devel-api/text-abstraction/font-metrics.cpp old mode 100644 new mode 100755 index 1edff6d..e192a44 --- a/dali/devel-api/text-abstraction/font-metrics.cpp +++ b/dali/devel-api/text-abstraction/font-metrics.cpp @@ -25,11 +25,11 @@ namespace TextAbstraction { FontMetrics::FontMetrics() -: ascender( 0.f ), - descender( 0.f ), - height( 0.f ), - underlinePosition( 0.f ), - underlineThickness( 0.f ) +: ascender{ 0.f }, + descender{ 0.f }, + height{ 0.f }, + underlinePosition{ 0.f }, + underlineThickness{ 0.f } { } @@ -38,11 +38,11 @@ FontMetrics::FontMetrics( float ascenderPixels, float heightPixels, float underlinePositionPixels, float underlineThicknessPixels ) -: ascender( ascenderPixels ), - descender( descenderPixels ), - height( heightPixels ), - underlinePosition( underlinePositionPixels ), - underlineThickness( underlineThicknessPixels ) +: ascender{ ascenderPixels }, + descender{ descenderPixels }, + height{ heightPixels }, + underlinePosition{ underlinePositionPixels }, + underlineThickness{ underlineThicknessPixels } { } diff --git a/dali/devel-api/text-abstraction/font-metrics.h b/dali/devel-api/text-abstraction/font-metrics.h old mode 100644 new mode 100755 index 0edbce2..f314735 --- a/dali/devel-api/text-abstraction/font-metrics.h +++ b/dali/devel-api/text-abstraction/font-metrics.h @@ -1,8 +1,8 @@ -#ifndef __DALI_TEXT_ABSTRACTION_FONT_METRICS_H__ -#define __DALI_TEXT_ABSTRACTION_FONT_METRICS_H__ +#ifndef DALI_TEXT_ABSTRACTION_FONT_METRICS_H +#define DALI_TEXT_ABSTRACTION_FONT_METRICS_H /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,7 +44,7 @@ struct DALI_ADAPTOR_API FontMetrics float descenderPixels, float heightPixels, float underlinePositionPixels, - float underlinePositionThickness ); + float underlineThicknessPixels ); float ascender; ///< The ascender in pixels. float descender; ///< The descender in pixels. @@ -57,4 +57,4 @@ struct DALI_ADAPTOR_API FontMetrics } // TextAbstraction -#endif //__DALI_TEXT_ABSTRACTION_FONT_METRICS_H__ +#endif //DALI_TEXT_ABSTRACTION_FONT_METRICS_H diff --git a/dali/devel-api/text-abstraction/glyph-info.cpp b/dali/devel-api/text-abstraction/glyph-info.cpp old mode 100644 new mode 100755 index 898a2d5..1e87831 --- a/dali/devel-api/text-abstraction/glyph-info.cpp +++ b/dali/devel-api/text-abstraction/glyph-info.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,30 +25,30 @@ namespace TextAbstraction { GlyphInfo::GlyphInfo() -: fontId( 0 ), - index( 0 ), - width( 0 ), - height( 0 ), - xBearing( 0 ), - yBearing( 0 ), - advance( 0 ), - scaleFactor( 0 ), - softwareItalic(false), - softwareBold(false) +: fontId{ 0u }, + index{ 0u }, + width( 0.f ), + height{ 0.f }, + xBearing{ 0.f }, + yBearing{ 0.f }, + advance{ 0.f }, + scaleFactor{ 0.f }, + isItalicRequired{ false }, + isBoldRequired{ false } { } GlyphInfo::GlyphInfo( FontId font, GlyphIndex i ) -: fontId( font ), - index( i ), - width( 0 ), - height( 0 ), - xBearing( 0 ), - yBearing( 0 ), - advance( 0 ), - scaleFactor( 0 ), - softwareItalic(false), - softwareBold(false) +: fontId{ font }, + index{ i }, + width( 0.f ), + height{ 0.f }, + xBearing{ 0.f }, + yBearing{ 0.f }, + advance{ 0.f }, + scaleFactor{ 0.f }, + isItalicRequired{ false }, + isBoldRequired{ false } { } diff --git a/dali/devel-api/text-abstraction/glyph-info.h b/dali/devel-api/text-abstraction/glyph-info.h index 772c78f..fbc8fec 100755 --- a/dali/devel-api/text-abstraction/glyph-info.h +++ b/dali/devel-api/text-abstraction/glyph-info.h @@ -1,8 +1,8 @@ -#ifndef __DALI_TEXT_ABSTRACTION_GLYPH_INFO_H__ -#define __DALI_TEXT_ABSTRACTION_GLYPH_INFO_H__ +#ifndef DALI_TEXT_ABSTRACTION_GLYPH_INFO_H +#define DALI_TEXT_ABSTRACTION_GLYPH_INFO_H /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,20 +44,20 @@ struct DALI_ADAPTOR_API GlyphInfo */ GlyphInfo( FontId font, GlyphIndex i ); - FontId fontId; ///< Identifies the font containing the glyph - GlyphIndex index; ///< Uniquely identifies a glyph for a given FontId - float width; ///< The width of the glyph - float height; ///< The height of the glyph - float xBearing; ///< The distance from the cursor position to the leftmost border of the glyph - float yBearing; ///< The distance from the baseline to the topmost border of the glyph - float advance; ///< The distance to move the cursor for this glyph - float scaleFactor; ///< The scaling applied (fixed-size fonts only) - bool softwareItalic; ///< Whether glyph needs software support to draw italic style - bool softwareBold; ///< Whether glyph needs software support to draw bold style + FontId fontId; ///< Identifies the font containing the glyph + GlyphIndex index; ///< Uniquely identifies a glyph for a given FontId + float width; ///< The width of the glyph + float height; ///< The height of the glyph + float xBearing; ///< The distance from the cursor position to the leftmost border of the glyph + float yBearing; ///< The distance from the baseline to the topmost border of the glyph + float advance; ///< The distance to move the cursor for this glyph + float scaleFactor; ///< The scaling applied (fixed-size fonts only) + bool isItalicRequired:1; ///< Whether the italic style is required. + bool isBoldRequired:1; ///< Whether the bold style is required. }; } // Dali } // TextAbstraction -#endif //__DALI_TEXT_ABSTRACTION_GLYPH_INFO_H__ +#endif //DALI_TEXT_ABSTRACTION_GLYPH_INFO_H diff --git a/dali/devel-api/text-abstraction/text-abstraction-definitions.h b/dali/devel-api/text-abstraction/text-abstraction-definitions.h old mode 100644 new mode 100755 index a6be687..c8c346a --- a/dali/devel-api/text-abstraction/text-abstraction-definitions.h +++ b/dali/devel-api/text-abstraction/text-abstraction-definitions.h @@ -38,6 +38,7 @@ typedef uint32_t BidiInfoIndex; ///< Index to the bidirectional info for a typedef char LineBreakInfo; ///< Line break info (must break, allow break, no break). typedef char WordBreakInfo; ///< Word break info (break, no break). typedef bool CharacterDirection; ///< The character's direction: @e false is left to right, @e true is right to left. +typedef uint32_t ColorIndex; ///< An index into an array of colors. /** * @brief Enumerates the possible line break info values. @@ -72,6 +73,15 @@ struct VectorBlob unsigned char a; }; +/** +* @brief Defines how a color is blended. +*/ +enum class ColorBlendingMode +{ + NONE, ///< No blend. + MULTIPLY ///< The color is multiplied by another one. +}; + } // namespace TextAbstraction } // namespace Dali diff --git a/dali/devel-api/text-abstraction/text-abstraction.h b/dali/devel-api/text-abstraction/text-abstraction.h old mode 100644 new mode 100755 index adc9b2b..3a9309e --- a/dali/devel-api/text-abstraction/text-abstraction.h +++ b/dali/devel-api/text-abstraction/text-abstraction.h @@ -1,8 +1,8 @@ -#ifndef __DALI_TEXT_ABSTRACTION_H__ -#define __DALI_TEXT_ABSTRACTION_H__ +#ifndef DALI_TEXT_ABSTRACTION_H +#define DALI_TEXT_ABSTRACTION_H /* - * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -26,4 +27,4 @@ #include #include -#endif //__DALI_TEXT_ABSTRACTION_H__ +#endif // DALI_TEXT_ABSTRACTION_H diff --git a/dali/devel-api/text-abstraction/text-renderer-layout-helper.cpp b/dali/devel-api/text-abstraction/text-renderer-layout-helper.cpp new file mode 100755 index 0000000..b5a3eb4 --- /dev/null +++ b/dali/devel-api/text-abstraction/text-renderer-layout-helper.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// FILE HEADER +#include + +// EXTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace TextAbstraction +{ + +void TransformToArcClockwise( const CircularTextParameters& parameters, double& x, double& y ) +{ + double radius = parameters.radius; + double angle = parameters.beginAngle; + + angle -= parameters.invRadius * x; + + radius -= y; + x = radius * cos( angle ); + y = -radius * sin( angle ); + + x += parameters.centerX; + y += parameters.centerY; +} + +void TransformToArcAntiClockwise( const CircularTextParameters& parameters, double& x, double& y ) +{ + double radius = parameters.radius; + double angle = parameters.beginAngle; + + angle += parameters.invRadius * x; + + radius += y; + x = radius * cos( angle ); + y = radius * sin( -angle ); + + x += parameters.centerX; + y += parameters.centerY; +} + +} // namespace TextAbstraction + +} // namespace Dali diff --git a/dali/devel-api/text-abstraction/text-renderer-layout-helper.h b/dali/devel-api/text-abstraction/text-renderer-layout-helper.h new file mode 100755 index 0000000..95921a1 --- /dev/null +++ b/dali/devel-api/text-abstraction/text-renderer-layout-helper.h @@ -0,0 +1,75 @@ +#ifndef DALI_PLATFORM_TEXT_ABSTRACTION_TEXT_RENDERER_LAYOUT_HELPER_H +#define DALI_PLATFORM_TEXT_ABSTRACTION_TEXT_RENDERER_LAYOUT_HELPER_H + +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace TextAbstraction +{ + +/** + * @brief Parameters used to transform the vertices of the glyphs to wrap a circular path. + */ +struct DALI_ADAPTOR_API CircularTextParameters +{ + CircularTextParameters() + : centerX{ 0.0 }, + centerY{ 0.0 }, + radius{ 0.0 }, + invRadius{ 0.0 }, + beginAngle{ 0.0 }, + isClockwise{ true } + {} + + double centerX; ///< The 'x' center of the circular path. + double centerY; ///< The 'y' center of the circular path. + double radius; ///< The radius in pixels. + double invRadius; ///< 1.0 / radius. + double beginAngle; ///< The angle in radians where the circular text begins. + bool isClockwise:1; ///< Whether the circular text layout is clockwise. +}; + +/** + * @brief Transforms a vertex to wrap a clockwise circular path. + * + * @param[in] parameters The parameters of the circular path. + * @param[in,out] x The 'x' coordinate of the vertex. + * @param[in,out] y The 'y' coordinate of the vertex. + */ +DALI_ADAPTOR_API void TransformToArcClockwise( const CircularTextParameters& parameters, double& x, double& y ); + +/** + * @brief Transforms a vertex to wrap an anti clockwise circular path. + * + * @param[in] parameters The parameters of the circular path. + * @param[in,out] x The 'x' coordinate of the vertex. + * @param[in,out] y The 'y' coordinate of the vertex. + */ +DALI_ADAPTOR_API void TransformToArcAntiClockwise( const CircularTextParameters& parameters, double& x, double& y ); + +} // namespace TextAbstraction + +} // namespace Dali + +#endif // DALI_PLATFORM_TEXT_ABSTRACTION_TEXT_RENDERER_LAYOUT_HELPER_H diff --git a/dali/devel-api/text-abstraction/text-renderer.cpp b/dali/devel-api/text-abstraction/text-renderer.cpp new file mode 100755 index 0000000..a4a68a6 --- /dev/null +++ b/dali/devel-api/text-abstraction/text-renderer.cpp @@ -0,0 +1,55 @@ +/* +* Copyright (c) 2019 Samsung Electronics Co., Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*/ + +// CLASS HEADER +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace TextAbstraction +{ + +TextRenderer::TextRenderer() +{ +} + +TextRenderer::~TextRenderer() +{ +} + +TextRenderer TextRenderer::Get() +{ + return Internal::TextRenderer::Get(); +} + +Devel::PixelBuffer TextRenderer::Render(const Parameters& parameters) +{ + return GetImplementation(*this).Render(parameters); +} + +TextRenderer::TextRenderer(Internal::TextRenderer *impl) +: BaseHandle(impl) +{ +} + +} // namespace TextAbstraction + +} // namespace Dali diff --git a/dali/devel-api/text-abstraction/text-renderer.h b/dali/devel-api/text-abstraction/text-renderer.h new file mode 100755 index 0000000..9babd32 --- /dev/null +++ b/dali/devel-api/text-abstraction/text-renderer.h @@ -0,0 +1,169 @@ +#ifndef DALI_TOOLKIT_TEXT_ABSTRACTION_TEXT_RENDERER_H +#define DALI_TOOLKIT_TEXT_ABSTRACTION_TEXT_RENDERER_H + +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace TextAbstraction +{ + +namespace Internal +{ + + // Forward declaration + class TextRenderer; + +} // namespace Internal + +/** + * @brief Renders the given @e glyphs at the given @e positions into a pixel buffer. + * + * @note This class renders glyphs not characters. + * Font selection, RTL reordering, shaping and layout + * has to be done before calling the @e Render() + * method of this class. + */ +class DALI_ADAPTOR_API TextRenderer : public BaseHandle +{ +public: + /** + * @brief Parameters to render the text. + */ + struct Parameters + { + /** + * @brief Enum with the possible pixel formats of the output pixel buffer. + */ + enum PixelFormat + { + A8, ///< Alpha channel, 8-bit color depth. + RGBA8888 ///< Red, Green, Blue and Alpha channels, 8-bit color depth per channel. + }; + + /** + * @brief Whether the circular layout is clockwise. + */ + enum CircularLayout + { + CLOCKWISE, ///< The text is laid clockwise on a circular path. + COUNTER_CLOCKWISE ///< The text is laid counter clockwise on a circular path. + }; + + /** + * @brief Parameters for the text renderer function. + */ + Parameters( Vector& glyphs, + Vector& positions, + Vector& colors, + Vector& colorIndices, + Vector& blendingMode, + Vector& isEmoji ) + : glyphs( glyphs ), + positions( positions ), + colors( colors ), + colorIndices( colorIndices ), + blendingMode( blendingMode ), + isEmoji( isEmoji ), + width{ 0u }, + height{ 0u }, + radius{ 0u }, + circularWidth{ 0u }, + circularHeight{ 0u }, + centerX{ 0 }, + centerY{ 0 }, + beginAngle{ 0.f }, + pixelFormat{ A8 }, + circularLayout{ CLOCKWISE } + {} + + Vector& glyphs; ///< The glyphs to be rendered. + Vector& positions; ///< The position for each glyph. + Vector& colors; ///< Colors of the glyphs. + Vector& colorIndices; ///< Indices to the vector of colors for each glyphs. + Vector& blendingMode; ///< How each glyph is going to be blended with the color of the text. + Vector& isEmoji; ///< Whether each glyph is an emoji. + unsigned int width; ///< The width of the pixel buffer. @note Some implementations may change the width for performance reasons. + unsigned int height; ///< The height of the pixel buffer. + unsigned int radius; ///< The radius in pixels of the circular text. + unsigned int circularWidth; ///< The width of the text laid out on an horizontal straight line. + unsigned int circularHeight; ///< The height of the text laid out on an horizontal straight line. + int centerX; ///< The 'x' coordinate of the center. For circular layout. + int centerY; ///< The 'y' coordinate of the center. For circular layout. + float beginAngle; ///< The angle in radians where the circular text begins. + PixelFormat pixelFormat; ///< The pixel format of the pixel buffer. + CircularLayout circularLayout; ///< The direction of the text's layout. + }; + +public: + + /** + * @brief Create an uninitialized TextRenderer handle. + * + */ + TextRenderer(); + + /** + * @brief Destructor + * + * This is non-virtual since derived Handle types must not contain data or virtual methods. + */ + ~TextRenderer(); + + /** + * @brief Retrieve a handle to the TextRenderer instance. + * + * @return A handle to the TextRenderer. + */ + static TextRenderer Get(); + + /** + * @brief Renders the given @e glyphs into a pixel buffer. + * + * @param[in] parameters Struct with the glyphs, positions and the size of the pixel buffer. + * + * @return The pixel buffer with the text rendered on it. + */ + Devel::PixelBuffer Render(const Parameters& parameters); + +public: // Not intended for application developers. + + /// @cond internal + /** + * @brief This constructor is used by TextRenderer::Get(). + * + * @param[in] implementation A pointer to the internal text renderer object. + */ + explicit DALI_INTERNAL TextRenderer(Internal::TextRenderer* implementation); + /// @endcond +}; + +} // namespace TextAbstraction + +} // namespace Dali + +#endif // DALI_TOOLKIT_TEXT_ABSTRACTION_TEXT_RENDERER_H diff --git a/dali/internal/imaging/common/image-operations.cpp b/dali/internal/imaging/common/image-operations.cpp index 662d775..ae88aef 100755 --- a/dali/internal/imaging/common/image-operations.cpp +++ b/dali/internal/imaging/common/image-operations.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -2174,6 +2174,7 @@ void RotateByShear( const uint8_t* const pixelsIn, if( !fastRotationPerformed ) { + DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "fast rotation failed\n"); // The fast rotation failed. return; } @@ -2195,6 +2196,7 @@ void RotateByShear( const uint8_t* const pixelsIn, if( !fastRotationPerformed ) { + DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "fast rotation failed\n"); // The fast rotation failed. return; } @@ -2220,6 +2222,7 @@ void RotateByShear( const uint8_t* const pixelsIn, if( !fastRotationPerformed ) { + DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "fast rotation failed\n"); // The fast rotation failed. return; } @@ -2265,6 +2268,8 @@ void RotateByShear( const uint8_t* const pixelsIn, widthOut = 0u; heightOut = 0u; + DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "malloc failed to allocate memory\n"); + // The deleter of the tmpPixelsInPtr unique pointer is called freeing the memory allocated by the 'Fast rotations'. // Nothing else to do if the memory allocation fails. return; @@ -2301,6 +2306,7 @@ void RotateByShear( const uint8_t* const pixelsIn, widthOut = 0u; heightOut = 0u; + DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "malloc failed to allocate memory\n"); // The deleter of the tmpPixelsInPtr unique pointer is called freeing the memory allocated by the 'First Horizontal Skew'. // Nothing else to do if the memory allocation fails. return; @@ -2337,6 +2343,7 @@ void RotateByShear( const uint8_t* const pixelsIn, widthOut = 0u; heightOut = 0u; + DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "malloc failed to allocate memory\n"); // The deleter of the tmpPixelsInPtr unique pointer is called freeing the memory allocated by the 'Vertical Skew'. // Nothing else to do if the memory allocation fails. return; @@ -2354,6 +2361,53 @@ void RotateByShear( const uint8_t* const pixelsIn, // @note Allocated memory by the last 'Horizontal Skew' has to be freed by the caller to this function. } +void HorizontalShear( const uint8_t* const pixelsIn, + unsigned int widthIn, + unsigned int heightIn, + unsigned int pixelSize, + float radians, + uint8_t*& pixelsOut, + unsigned int& widthOut, + unsigned int& heightOut ) +{ + // Calculate the destination image dimensions. + + const float absRadians = fabs( radians ); + + if( absRadians > Math::PI_4 ) + { + // Can't shear more than 45 degrees. + widthOut = 0u; + heightOut = 0u; + + DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Can't shear more than 45 degrees (PI/4 radians). radians : %f\n", radians ); + return; + } + + widthOut = widthIn + static_cast( absRadians * static_cast( heightIn ) ); + heightOut = heightIn; + + // Allocate the buffer for the shear. + pixelsOut = static_cast( malloc( widthOut * heightOut * pixelSize ) ); + + if( nullptr == pixelsOut ) + { + widthOut = 0u; + heightOut = 0u; + + DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "malloc failed to allocate memory\n" ); + return; + } + + for( unsigned int y = 0u; y < heightOut; ++y ) + { + const float shear = radians * ( ( radians >= 0.f ) ? ( 0.5f + static_cast( y ) ) : ( 0.5f + static_cast( y ) - static_cast( heightOut ) ) ); + + const int intShear = static_cast( floor( shear ) ); + HorizontalSkew( pixelsIn, widthIn, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast( intShear ) ); + } +} + } /* namespace Platform */ } /* namespace Internal */ } /* namespace Dali */ diff --git a/dali/internal/imaging/common/image-operations.h b/dali/internal/imaging/common/image-operations.h old mode 100644 new mode 100755 index c78f8fe..6ae9ba1 --- a/dali/internal/imaging/common/image-operations.h +++ b/dali/internal/imaging/common/image-operations.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,8 @@ * */ -#ifndef DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H_ -#define DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H_ +#ifndef DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H +#define DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H // EXTERNAL INCLUDES #include @@ -407,6 +407,33 @@ void RotateByShear( const uint8_t* const pixelsIn, unsigned int& widthOut, unsigned int& heightOut ); +/** + * @brief Applies to the input image a horizontal shear transformation. + * + * @pre @p pixelsIn must not alias @p pixelsOut. The input image should be a totally + * separate buffer from the output buffer. + * @pre The maximun/minimum shear angle is +/-45 degrees (PI/4 around 0.79 radians). + * + * @note This function allocates memory in @p pixelsOut which has to be released by calling @e free() + * + * @param[in] pixelsIn The input buffer. + * @param[in] widthIn The width of the input buffer. + * @param[in] heightIn The height of the input buffer. + * @param[in] pixelSize The size of the pixel. + * @param[in] radians The shear angle in radians. + * @param[out] pixelsOut The rotated output buffer. + * @param[out] widthOut The width of the output buffer. + * @param[out] heightOut The height of the output buffer. + */ +void HorizontalShear( const uint8_t* const pixelsIn, + unsigned int widthIn, + unsigned int heightIn, + unsigned int pixelSize, + float radians, + uint8_t*& pixelsOut, + unsigned int& widthOut, + unsigned int& heightOut ); + /**@}*/ /** @@ -583,4 +610,4 @@ inline unsigned int BilinearFilter1Component(unsigned int tl, unsigned int tr, u } /* namespace Internal */ } /* namespace Dali */ -#endif /* DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H_ */ +#endif /* DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H */ diff --git a/dali/internal/imaging/common/pixel-buffer-impl.cpp b/dali/internal/imaging/common/pixel-buffer-impl.cpp old mode 100644 new mode 100755 index 3bcb4ef..85c5c8a --- a/dali/internal/imaging/common/pixel-buffer-impl.cpp +++ b/dali/internal/imaging/common/pixel-buffer-impl.cpp @@ -119,6 +119,11 @@ unsigned char* PixelBuffer::GetBuffer() const return mBuffer; } +const unsigned char* const PixelBuffer::GetConstBuffer() const +{ + return mBuffer; +} + unsigned int PixelBuffer::GetBufferSize() const { return mBufferSize; diff --git a/dali/internal/imaging/common/pixel-buffer-impl.h b/dali/internal/imaging/common/pixel-buffer-impl.h old mode 100644 new mode 100755 index b0fe5ce..5c5c045 --- a/dali/internal/imaging/common/pixel-buffer-impl.h +++ b/dali/internal/imaging/common/pixel-buffer-impl.h @@ -133,6 +133,11 @@ public: unsigned char* GetBuffer() const; /** + * @copydoc Devel::PixelBuffer::GetBuffer() + */ + const unsigned char* const GetConstBuffer() const; + + /** * Get the size of the buffer in bytes * @return The size of the buffer */ diff --git a/dali/internal/text/file.list b/dali/internal/text/file.list old mode 100644 new mode 100755 index 52a9931..324102c --- a/dali/internal/text/file.list +++ b/dali/internal/text/file.list @@ -3,9 +3,11 @@ # module: text, backend: common adaptor_text_common_src_files=\ ${adaptor_text_dir}/text-abstraction/bidirectional-support-impl.cpp \ + ${adaptor_text_dir}/text-abstraction/cairo-renderer.cpp \ ${adaptor_text_dir}/text-abstraction/font-client-helper.cpp \ ${adaptor_text_dir}/text-abstraction/font-client-impl.cpp \ ${adaptor_text_dir}/text-abstraction/font-client-plugin-impl.cpp \ ${adaptor_text_dir}/text-abstraction/segmentation-impl.cpp \ - ${adaptor_text_dir}/text-abstraction/shaping-impl.cpp + ${adaptor_text_dir}/text-abstraction/shaping-impl.cpp \ + ${adaptor_text_dir}/text-abstraction/text-renderer-impl.cpp diff --git a/dali/internal/text/text-abstraction/cairo-renderer.cpp b/dali/internal/text/text-abstraction/cairo-renderer.cpp new file mode 100755 index 0000000..ea3a013 --- /dev/null +++ b/dali/internal/text/text-abstraction/cairo-renderer.cpp @@ -0,0 +1,869 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// FILE HEADER +#include + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_OUTLINE_H +#include FT_STROKER_H + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include + +namespace +{ + +const float TO_FLOAT = 1.f / 255.f; +const float TO_UCHAR = 255.f; +const float TWO_PI = 2.f * Dali::Math::PI; ///< 360 degrees in radians + +/** + * @brief Run of glyphs that have the same style. + */ +struct GlyphRun +{ + GlyphRun() + : fontFace{ nullptr }, + fontSize{ 0.0 }, + glyphIndex{ 0u }, + numberOfGlyphs{ 0u }, + fontId{ 0u }, + colorIndex{ 0u }, + isItalicRequired{ false }, + isBoldRequired{ false } + {} + + FT_Face fontFace; ///< The font face used by the glyphs in the run. + 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. + unsigned int glyphIndex; ///< Index to the first glyph of the run. + unsigned int numberOfGlyphs; ///< Number of glyphs in the run. + unsigned int fontId; ///< The id of the font. + unsigned int colorIndex; ///< The index to the color of the glyphs. + bool isItalicRequired:1; ///< Whether the italic style is required. + bool isBoldRequired:1; ///< Whether the bold style is required. +}; + +/** + * @brief Helper struct used to destroy a bitmap buffer. + * + * The font client allocates a bitmap's buffer with the new operator. + * However, the PixelBuffer class allocates the buffer with the + * malloc() function and the Rotate() function which is intended + * for the PixelBuffer as well allocates memory with malloc(). + * + * This struct keeps the type of allocation and uses the delete[] + * operator or the free() function to deallocate resources. + */ +struct GlyphBuffer +{ + enum DestructorType + { + FREE, + DELETE + }; + + GlyphBuffer( Dali::TextAbstraction::FontClient::GlyphBufferData& data, DestructorType type ) + : data( data ), + type( type ) + { + } + + ~GlyphBuffer() + { + switch( type ) + { + case FREE: + { + free( data.buffer ); + break; + } + case DELETE: + { + delete[] data.buffer; + } + } + } + + Dali::TextAbstraction::FontClient::GlyphBufferData& data; + DestructorType type; +}; + +/** + * @brief Creates a pixel buffer with all pixels set to transparent. + * + * @param[in] parameters Contains the width and height of the pixel buffer. + * + * @return The pixel buffer. + */ +Dali::Devel::PixelBuffer CreateVoidPixelBuffer( const Dali::TextAbstraction::TextRenderer::Parameters& parameters ) +{ + Dali::Pixel::Format pixelFormat = parameters.pixelFormat == Dali::TextAbstraction::TextRenderer::Parameters::A8 ? Dali::Pixel::A8 : Dali::Pixel::RGBA8888; + Dali::Devel::PixelBuffer pixelBuffer = Dali::Devel::PixelBuffer::New( parameters.width, + parameters.height, + pixelFormat ); + + const unsigned int bufferSize = parameters.width * parameters.height * Dali::Pixel::GetBytesPerPixel( pixelFormat ); + unsigned char* buffer = pixelBuffer.GetBuffer(); + memset( buffer, 0, bufferSize ); + + return pixelBuffer; +} + +/** + * @brief Wraps the vertices of glyphs laid out on a horizontal strainght line on a circular path. + * + * It copies the vertices from the extra cairo context created to lay out the text + * on a horizontal straight line to the cairo context used to render it. + * + * @param[in,out] cr The cairo context used to render the text. + * @param[in] circularCr The extra cairo context created to layout horizontal text. + * @param[in] parameters The parameters of the circular path. + */ +void WrapToCircularPath( cairo_t* cr, cairo_t* circularCr, const Dali::TextAbstraction::CircularTextParameters& parameters ) +{ + bool first = true; + + // Copy the path to get a cairo_path_t pointer used to iterate through all its items. + std::unique_ptr path( cairo_copy_path( circularCr ), cairo_path_destroy ); + + void ( *transformToArc )( const Dali::TextAbstraction::CircularTextParameters&, double&, double& ); + transformToArc = parameters.isClockwise ? &Dali::TextAbstraction::TransformToArcClockwise : &Dali::TextAbstraction::TransformToArcAntiClockwise; + + // Iterates through all the path items and transform each vertex to follow the circle. + // Transformed vertices are added to a new path in the 'cr' context (the one used to render the circular text) + for( int i = 0; i < path->num_data; i += path->data[i].header.length ) + { + cairo_path_data_t* data = &path->data[i]; + + switch( data->header.type ) + { + case CAIRO_PATH_MOVE_TO: + { + if( first ) + { + cairo_new_path( cr ); + } + + first = false; + double x = data[1].point.x; + double y = data[1].point.y; + transformToArc( parameters, x, y ); + cairo_move_to( cr, x, y ); + break; + } + case CAIRO_PATH_LINE_TO: + { + double x = data[1].point.x; + double y = data[1].point.y; + transformToArc( parameters, x, y ); + cairo_line_to( cr, x, y ); + break; + } + case CAIRO_PATH_CURVE_TO: + { + double x1 = data[1].point.x; + double y1 = data[1].point.y; + double x2 = data[2].point.x; + double y2 = data[2].point.y; + double x3 = data[3].point.x; + double y3 = data[3].point.y; + transformToArc( parameters, x1, y1 ); + transformToArc( parameters, x2, y2 ); + transformToArc( parameters, x3, y3 ); + cairo_curve_to( cr, x1, y1, x2, y2, x3, y3 ); + break; + } + case CAIRO_PATH_CLOSE_PATH: + { + cairo_close_path( cr ); + break; + } + default: + { + DALI_LOG_WARNING( "Type of path not handled.\n" ); + // Nothing else to do. + break; + } + } + } +} + +} // namespace + +namespace Dali +{ + +namespace TextAbstraction +{ + +namespace Internal +{ + +Devel::PixelBuffer RenderTextCairo( const TextAbstraction::TextRenderer::Parameters& parameters ) +{ + const unsigned int numberOfGlyphs = parameters.glyphs.Count(); + + if( 0u == numberOfGlyphs ) + { + // return a pixel buffer with all pixels set to transparent. + return CreateVoidPixelBuffer( parameters ); + } + + // Convert from DALi glyphs to Cairo glyphs. + std::vector cairoGlyphs; + cairoGlyphs.resize( numberOfGlyphs ); + cairo_glyph_t* cairoGlyphsBuffer = &cairoGlyphs[0u]; + + const GlyphInfo* const daliGlyphsBuffer = parameters.glyphs.Begin(); + const Vector2* const positionsBuffer = parameters.positions.Begin(); + const ColorIndex* const colorIndicesBuffer = ( 0u == parameters.colorIndices.Count() ) ? nullptr : parameters.colorIndices.Begin(); + + for( unsigned index = 0u; index < numberOfGlyphs; ++index ) + { + const GlyphInfo& daliGlyph = *( daliGlyphsBuffer + index ); + const Vector2& position = *( positionsBuffer + index ); + cairo_glyph_t& cairoGlyph = *( cairoGlyphsBuffer + index ); + + cairoGlyph.index = daliGlyph.index; + cairoGlyph.x = std::round( position.x ); + cairoGlyph.y = std::round( position.y ); + } + + // Retrieve the FreeType fonts needed by Cairo from the font-client. + Dali::TextAbstraction::FontClient fontClient = Dali::TextAbstraction::FontClient::Get(); + + FT_Library ftLibrary; + FT_Init_FreeType(&ftLibrary); + + // Vector used to store the FreeType font faces, its size and the run of glyphs that use the font. + std::vector glyphRuns; + glyphRuns.reserve( 8u ); + + // The size set in Cairo and FreeType has different units. + // Before the size is set in Cairo it needs to be converted according the formula + // 'pixel_size = point_size * resolution / 72' got from the FreeType doc. + // https://www.freetype.org/freetype2/docs/glyphs/glyphs-2.html + + unsigned int horizontalDpi = 0u; + unsigned int verticalDpi = 0u; + fontClient.GetDpi( horizontalDpi, verticalDpi ); + const double dVerticalDpi = static_cast( verticalDpi ); + + const double FROM_26_DOT_6_TO_PIXELS = dVerticalDpi / ( 64.0 * 72.0 ); + + GlyphRun currentGlyphRun; + currentGlyphRun.fontId = 0u; + currentGlyphRun.colorIndex = 0u; + currentGlyphRun.isItalicRequired = false; + currentGlyphRun.isBoldRequired = false; + for( unsigned index = 0u; index < numberOfGlyphs; ++index ) + { + const GlyphInfo& daliGlyph = *( daliGlyphsBuffer + index ); + const FontId fontId = daliGlyph.fontId; + const ColorIndex colorIndex = ( nullptr == colorIndicesBuffer ) ? 0u : *( colorIndicesBuffer + index ); + const bool isItalicRequired = daliGlyph.isItalicRequired; + const bool isBoldRequired = daliGlyph.isBoldRequired; + + if( ( fontId != currentGlyphRun.fontId ) || + ( ( 0u == fontId ) && ( 0u != daliGlyph.index ) ) || + ( colorIndex != currentGlyphRun.colorIndex ) || + ( isItalicRequired != currentGlyphRun.isItalicRequired ) || + ( isBoldRequired != currentGlyphRun.isBoldRequired ) ) + { + // There is a new run. First set the number of glyphs of the previous run and store it. + currentGlyphRun.numberOfGlyphs = index - currentGlyphRun.glyphIndex; + if( 0u != currentGlyphRun.numberOfGlyphs ) + { + glyphRuns.push_back( currentGlyphRun ); + } + + currentGlyphRun.fontFace = nullptr; + currentGlyphRun.fontSize = 0.0; + currentGlyphRun.glyphIndex = index; + currentGlyphRun.numberOfGlyphs = 0u; + currentGlyphRun.fontId = 0u; + currentGlyphRun.colorIndex = 0u; + currentGlyphRun.isItalicRequired = false; + currentGlyphRun.isBoldRequired = false; + + if( 0u != fontId ) + { + // Get the font's path file name from the font Id. + FontDescription fontDescription; + fontClient.GetDescription( fontId, fontDescription ); + + switch( fontDescription.type ) + { + case FontDescription::FACE_FONT: + { + // Create a FreeType font's face. + FT_New_Face(ftLibrary, fontDescription.path.c_str(), 0u, ¤tGlyphRun.fontFace); + + // Set the font's size. It needs to be set in the Freetype font and in the Cairo's context. + unsigned int fontSize = fontClient.GetPointSize( fontId ); + + // Font's size to be set in the Cairo's context. + currentGlyphRun.fontSize = FROM_26_DOT_6_TO_PIXELS * static_cast( fontSize ); + break; + } + case FontDescription::BITMAP_FONT: + { + //Nothing to do. + break; + } + default: + { + //Nothing to do. + break; + } + } + } + currentGlyphRun.fontId = fontId; + currentGlyphRun.colorIndex = colorIndex; + currentGlyphRun.isItalicRequired = isItalicRequired; + currentGlyphRun.isBoldRequired = isBoldRequired; + } + } + + // Calculate the number of glyphs of the last run and store it. + currentGlyphRun.numberOfGlyphs = numberOfGlyphs - currentGlyphRun.glyphIndex; + if( 0u != currentGlyphRun.numberOfGlyphs ) + { + glyphRuns.push_back( currentGlyphRun ); + } + + // Choose the pixel format to be used. + // + // @note Behdad wrote "Upper 8 bits maps to the fourth byte in a little-endian machine like the intels." + // https://lists.cairographics.org/archives/cairo/2006-March/006563.html + // + // Here in practice Cairo's ARGB32 is like DALi's RGBA8888. + // + const bool isDstRgba = TextAbstraction::TextRenderer::Parameters::RGBA8888 == parameters.pixelFormat; + const Pixel::Format pixelFormat = isDstRgba ? Pixel::Format::RGBA8888 : Pixel::Format::A8; + const cairo_format_t cairoFormat = isDstRgba ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_A8; + + // This function provides a stride value that will respect all alignment requirements of the + // accelerated image-rendering code within cairo. + const int stride = cairo_format_stride_for_width( cairoFormat, + static_cast( parameters.width ) ); + const int strideWidth = stride / Pixel::GetBytesPerPixel( pixelFormat ); + + // Creates the pixel buffer and retrieves the buffer pointer used to create the Cairo's surface. + Devel::PixelBuffer pixelBuffer = Devel::PixelBuffer::New( strideWidth, parameters.height, pixelFormat ); + + unsigned char* buffer = pixelBuffer.GetBuffer(); + const unsigned int bufferSize = stride * parameters.height; + memset( buffer, 0, bufferSize ); + + std::unique_ptr surfacePtr( cairo_image_surface_create_for_data( buffer, + cairoFormat, + parameters.width, + parameters.height, + stride ), + cairo_surface_destroy ); + cairo_surface_t* surface = surfacePtr.get(); + + if( ( nullptr == surface ) || ( CAIRO_STATUS_SUCCESS != cairo_surface_status( surface ) ) ) + { + DALI_LOG_ERROR( "Failed to create a cairo's surface\n" ); + + return CreateVoidPixelBuffer( parameters ); + } + + // Whether the text is circular. + const bool isCircularText = 0u != parameters.radius; + + // Creates a surface for circular text. + // + // The reason to create a surface for circular text is that the strategy + // followed is to layout the text in a straight horizontal line and apply a + // transform to each vertex that forms the geometry of the glyphs to place + // and bend the glyphs accordingly to the circular path. + // + // As the glyphs are laid out first in a straight line they may exceed the + // boundaries of the surface in that case cairo ignores them. + std::unique_ptr circularSurfacePtr( nullptr, cairo_surface_destroy ); + cairo_surface_t* circularSurface = nullptr; + if( isCircularText ) + { + circularSurfacePtr.reset( cairo_surface_create_similar( surface, + CAIRO_CONTENT_ALPHA, + parameters.circularWidth, + parameters.circularHeight ) ); + circularSurface = circularSurfacePtr.get(); + + if( ( nullptr == circularSurface ) || ( CAIRO_STATUS_SUCCESS != cairo_surface_status( circularSurface ) ) ) + { + DALI_LOG_ERROR( "Failed to create a cairo's circular surface\n" ); + + return CreateVoidPixelBuffer( parameters ); + } + } + + std::unique_ptr crPtr( cairo_create( surface ), cairo_destroy ); + cairo_t* cr = crPtr.get(); + + if( CAIRO_STATUS_SUCCESS != cairo_status( cr ) ) + { + DALI_LOG_ERROR( "Failed to create a cairo context\n" ); + + return CreateVoidPixelBuffer( parameters ); + } + + std::unique_ptr circularCrPtr( nullptr, cairo_destroy ); + cairo_t* circularCr = nullptr; + + if( isCircularText ) + { + circularCrPtr.reset( cairo_create( circularSurface ) ); + circularCr = circularCrPtr.get(); + + if( CAIRO_STATUS_SUCCESS != cairo_status( circularCr ) ) + { + DALI_LOG_ERROR( "Failed to create a cairo context\n" ); + + return CreateVoidPixelBuffer( parameters ); + } + } + + CircularTextParameters circularTextParameters; + + // Render the glyphs. + if( isCircularText ) + { + // Set the parameters. + circularTextParameters.isClockwise = ( TextAbstraction::TextRenderer::Parameters::CLOCKWISE == parameters.circularLayout ); + + circularTextParameters.centerX = static_cast( parameters.centerX ); + circularTextParameters.centerY = static_cast( parameters.centerY ); + circularTextParameters.radius = static_cast( parameters.radius ); + circularTextParameters.invRadius = 1.0 / circularTextParameters.radius; + circularTextParameters.beginAngle = -parameters.beginAngle + Dali::Math::PI_2; + } + + cairo_move_to( cr, 0.0, 0.0 ); + + for( const auto& run: glyphRuns ) + { + const bool isEmoji = parameters.isEmoji[run.glyphIndex]; + if( isEmoji || ( nullptr == run.fontFace ) ) + { + // Retrieve the color for the glyph. + const Vector4& color = parameters.colors[run.colorIndex]; + + const unsigned int lastGlyphIndex = run.glyphIndex + run.numberOfGlyphs; + for( unsigned int index = run.glyphIndex; index < lastGlyphIndex; ++index ) + { + // Whether it's a bitmap font. + const bool doBlendWithTextColor = !isEmoji && ( ColorBlendingMode::MULTIPLY == parameters.blendingMode[index] ); + + // Check if there is an embedded image or a bitmap font image. + const GlyphIndex glyphFontIndex = daliGlyphsBuffer[index].index; + if( 0u != glyphFontIndex ) + { + // The embedded image could be A8, RGBA8888 or BGRA8888. + // + // 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. + // 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. + + const cairo_glyph_t& glyph = *( cairoGlyphsBuffer + index ); + + // Retrieve the image + TextAbstraction::FontClient::GlyphBufferData data; + std::unique_ptr glyphBufferPtr( new GlyphBuffer( data, GlyphBuffer::DELETE ) ); + if( isEmoji ) + { + data.width = parameters.glyphs[run.glyphIndex].width; + data.height = parameters.glyphs[run.glyphIndex].height; + } + + fontClient.CreateBitmap( run.fontId, glyphFontIndex, false, false, data, 0u ); + + if( nullptr == data.buffer ) + { + // nothing else to do if there is no image. + continue; + } + + // Calculate the position for the circular text. + double glyphX = glyph.x; + double glyphY = glyph.y; + + if( isCircularText ) + { + // Center of the bitmap. + const double halfWidth = 0.5 * static_cast( data.width ); + const double halfHeight = 0.5 * static_cast( data.height ); + + double centerX = glyph.x + halfWidth; + double centerY = glyph.y - halfHeight; + + float radians = circularTextParameters.beginAngle + ( circularTextParameters.isClockwise ? -1.f : 1.f ) * ( Dali::Math::PI_2 + circularTextParameters.invRadius * centerX ); + radians = fmod( radians, TWO_PI ); + radians += ( radians < 0.f ) ? TWO_PI : 0.f; + + void ( *transformToArc )( const CircularTextParameters&, double&, double& ); + transformToArc = circularTextParameters.isClockwise ? &TransformToArcClockwise : &TransformToArcAntiClockwise; + + transformToArc( circularTextParameters, centerX, centerY ); + + uint8_t* pixelsOut = nullptr; + unsigned int widthOut = data.width; + unsigned int heightOut = data.height; + const unsigned int pixelSize = Pixel::GetBytesPerPixel( data.format ); + + Dali::Internal::Platform::RotateByShear( data.buffer, + data.width, + data.height, + pixelSize, + radians, + pixelsOut, + widthOut, + heightOut ); + if( nullptr != pixelsOut ) + { + delete[] data.buffer; + data.buffer = pixelsOut; + glyphBufferPtr.get()->type = GlyphBuffer::FREE; + data.width = widthOut; + data.height = heightOut; + } + + glyphX = centerX - 0.5 * static_cast( data.width ); + glyphY = centerY + 0.5 * static_cast( data.height ); + } + + + if( ( Pixel::A8 != data.format ) && + ( Pixel::L8 != data.format ) && + ( Pixel::RGBA8888 != data.format ) && + ( Pixel::BGRA8888 != data.format ) ) + { + DALI_LOG_ERROR( " Cairo Renderer: The valid pixel format for embedded items are A8 or RGBA8888\n" ); + continue; + } + + // Check if the item is out of the buffer. + if( ( glyphX + static_cast( data.width ) < 0.f ) || + ( glyphX > static_cast( strideWidth ) ) || + ( glyphY < 0.f ) || + ( glyphY - static_cast( data.height ) > static_cast( parameters.height ) ) ) + { + // The embedded item is completely out of the buffer. + continue; + } + + const bool isSrcA = ( Pixel::A8 == data.format ) || ( Pixel::L8 == data.format ); + const bool isSrcRgba = Pixel::RGBA8888 == data.format; + const bool isSrcBgra = Pixel::BGRA8888 == data.format; + + // 0 -> image and cairo buffer are A8 + // 1 -> image is A8, cairo buffer is ARGB + // 2 -> image is RGBA and cairo buffer is ARGB + // 3 -> image is BGRA and cairo buffer is ARGB + int rgbaCase = 0; + if( isSrcA && isDstRgba ) + { + rgbaCase = 1; + } + else if( isSrcRgba && isDstRgba ) + { + rgbaCase = 2; + } + else if( isSrcBgra && isDstRgba ) + { + rgbaCase = 3; + } + else if( ( isSrcRgba || isSrcBgra ) && !isDstRgba ) + { + DALI_LOG_ERROR( "Cairo Renderer: The embedded image is RGBA or BGRA and the Cairo's buffer has been creates with A8 format!\n" ); + continue; + } + + // Select the cropped source image area to copy into the surface buffer + unsigned int glyphUintX = 0u; + unsigned int glyphUintY = 0u; + unsigned int srcWidth = data.width; + unsigned int srcHeight = data.height; + unsigned int xSrcIndex = 0u; + unsigned int ySrcIndex = 0u; + if( glyphX < 0.f ) + { + xSrcIndex = static_cast( std::abs( glyphX ) ); + srcWidth -= xSrcIndex; + } + else + { + glyphUintX = static_cast( glyphX ); + } + + if( glyphUintX + srcWidth > static_cast( strideWidth ) ) + { + srcWidth -= ( ( glyphUintX + srcWidth ) - strideWidth ); + } + + if( glyphY - static_cast( srcHeight ) < 0.f ) + { + ySrcIndex = static_cast( std::abs( glyphY - static_cast( srcHeight ) ) ); + srcHeight -= ySrcIndex; + } + else + { + glyphUintY = static_cast( glyphY - static_cast( srcHeight ) ); + } + + if( glyphUintY + srcHeight > parameters.height ) + { + srcHeight -= ( ( glyphUintY + srcHeight ) - parameters.height ); + } + + // Calculate the source and destination indices. + const unsigned int srcPixelSize = Pixel::GetBytesPerPixel( data.format ); + const unsigned int dstPixelSize = Pixel::GetBytesPerPixel( pixelFormat ); + + unsigned int srcIndex = srcPixelSize * ( ySrcIndex * srcWidth + xSrcIndex ); + unsigned int dstIndex = dstPixelSize * ( glyphUintY * strideWidth + glyphUintX ); + + const unsigned int srcWidthOffset = srcPixelSize * ( data.width - srcWidth ); + const unsigned int dstWidthOffset = dstPixelSize * ( strideWidth - srcWidth ); + + // Copy the image to the surface + for( unsigned int j = 0; j < srcHeight; ++j ) + { + for( unsigned int i = 0; i < srcWidth; ++i ) + { + switch( rgbaCase ) + { + case 0: // Both the image's buffer and cairo's buffer are A8 + { + const unsigned char alpha = *( data.buffer + srcIndex ); + if( alpha != 0u ) + { + // @todo needs a proper blending! + *( buffer + dstIndex ) = alpha; + } + break; + } + case 1: // The image's buffer is A8 and the cairo's buffer is ARGB + { + const unsigned char alpha = *( data.buffer + srcIndex ); + if( alpha != 0u ) + { + // @todo needs a proper blending! + const float srcAlpha = TO_FLOAT * static_cast( alpha ); + + // Write the RGBA modulated with the given default color. + const float* const colorPtr = color.AsFloat(); + *( buffer + dstIndex + 0u ) = static_cast( TO_UCHAR * colorPtr[0u] * srcAlpha ); + *( buffer + dstIndex + 1u ) = static_cast( TO_UCHAR * colorPtr[1u] * srcAlpha ); + *( buffer + dstIndex + 2u ) = static_cast( TO_UCHAR * colorPtr[2u] * srcAlpha ); + *( buffer + dstIndex + 3u ) = static_cast( TO_UCHAR * colorPtr[3u] * srcAlpha ); + } + break; + } + case 2: // The image's buffer is RGBA and the cairo's buffer is ARGB + { + const unsigned char alpha = *(data.buffer + srcIndex + 3u); + if( alpha != 0u ) + { + if( doBlendWithTextColor ) + { + const float* const colorPtr = color.AsFloat(); + + const float srcAlpha = TO_FLOAT * static_cast(alpha) * colorPtr[3u]; + + *(buffer + dstIndex + 0u) = static_cast( static_cast( *(data.buffer + srcIndex + 0u) ) * colorPtr[0u] ); + *(buffer + dstIndex + 1u) = static_cast( static_cast( *(data.buffer + srcIndex + 1u) ) * colorPtr[1u] ); + *(buffer + dstIndex + 2u) = static_cast( static_cast( *(data.buffer + srcIndex + 2u) ) * colorPtr[2u] ); + + // Write the alpha. + *(buffer + dstIndex + 3u) = static_cast( TO_UCHAR * srcAlpha ); + } + else + { + // @todo needs a proper blending! + // Write the RGB + *(buffer + dstIndex + 0u) = *(data.buffer + srcIndex + 0u); + *(buffer + dstIndex + 1u) = *(data.buffer + srcIndex + 1u); + *(buffer + dstIndex + 2u) = *(data.buffer + srcIndex + 2u); + + // Write the alpha. + *(buffer + dstIndex + 3u) = *(data.buffer + srcIndex + 3u); + } + } + break; + } + case 3: // The image's buffer is BGRA and the cairo's buffer is ARGB + { + const unsigned char alpha = *(data.buffer + srcIndex + 3u); + if( alpha != 0u ) + { + if( doBlendWithTextColor ) + { + const float* const colorPtr = color.AsFloat(); + + const float srcAlpha = TO_FLOAT * static_cast(alpha) * colorPtr[3u]; + + *(buffer + dstIndex + 0u) = static_cast( static_cast( *(data.buffer + srcIndex + 2u) ) * colorPtr[0u] ); + *(buffer + dstIndex + 1u) = static_cast( static_cast( *(data.buffer + srcIndex + 1u) ) * colorPtr[1u] ); + *(buffer + dstIndex + 2u) = static_cast( static_cast( *(data.buffer + srcIndex + 0u) ) * colorPtr[2u] ); + + // Write the alpha. + *(buffer + dstIndex + 3u) = static_cast( TO_UCHAR * srcAlpha ); + } + else + { + // @todo needs a proper blending! + // Write the RGBA + *(buffer + dstIndex + 0u) = *(data.buffer + srcIndex + 2u); + *(buffer + dstIndex + 1u) = *(data.buffer + srcIndex + 1u); + *(buffer + dstIndex + 2u) = *(data.buffer + srcIndex + 0u); + *(buffer + dstIndex + 3u) = *(data.buffer + srcIndex + 3u); + } + } + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"Cairo Renderer: The accepted values for this switch case are: 0, 1, 2!" ); + } + } // switch + srcIndex += srcPixelSize; + dstIndex += dstPixelSize; + } // for width + srcIndex += srcWidthOffset; + dstIndex += dstWidthOffset; + } // for height + } + } + } + else + { + // Sets the color. The color is actually BGRA + const Vector4& color = parameters.colors[run.colorIndex]; + + cairo_set_source_rgba( cr, + static_cast( color.b ), + static_cast( color.g ), + static_cast( color.r ), + static_cast( color.a ) ); + + // Create the Cairo's font from the FreeType font. + std::unique_ptr fontFacePtr( cairo_ft_font_face_create_for_ft_face( run.fontFace, 0 ), cairo_font_face_destroy ); + cairo_font_face_t* fontFace = fontFacePtr.get(); + + static const cairo_user_data_key_t key = { 0 }; + cairo_status_t status = cairo_font_face_set_user_data( fontFace, &key, run.fontFace, reinterpret_cast( FT_Done_Face ) ); + if( status ) + { + cairo_font_face_destroy(fontFace); + } + + unsigned int ftSynthesizeFlag = 0u; + if( run.isBoldRequired && !( run.fontFace->style_flags & FT_STYLE_FLAG_BOLD ) ) + { + ftSynthesizeFlag |= CAIRO_FT_SYNTHESIZE_BOLD; + } + + cairo_ft_font_face_set_synthesize( fontFace, ftSynthesizeFlag ); + + cairo_font_face_reference( fontFace ); + + const bool synthesizeItalic = ( run.isItalicRequired && !( run.fontFace->style_flags & FT_STYLE_FLAG_ITALIC ) ); + + if( CAIRO_STATUS_SUCCESS != cairo_font_face_status( fontFace ) ) + { + DALI_LOG_ERROR( "Failed to load the Freetype Font\n" ); + } + + // Sets the font. + cairo_set_font_face( isCircularText ? circularCr : cr, fontFace ); + + // Sets the size + cairo_set_font_size( isCircularText ? circularCr : cr, run.fontSize ); + + // Render the glyphs. + if( isCircularText ) + { + // Create a new path where the text is laid out on a horizontal straight line. + cairo_new_path( circularCr ); + cairo_move_to( circularCr, 0.0, 0.0 ); + + cairo_glyph_path( circularCr, ( cairoGlyphsBuffer + run.glyphIndex ), run.numberOfGlyphs ); + WrapToCircularPath( cr, circularCr, circularTextParameters ); + } + else + { + if( synthesizeItalic ) + { + // Apply a shear transform to synthesize the italics. + // For a reason Cairo may trim some glyphs if the CAIRO_FT_SYNTHESIZE_OBLIQUE flag is used. + + // This is to calculate an offset used to compensate the 'translation' done by the shear transform + // as it's done for the whole render buffer. + double maxY = 0.0; + for( unsigned int index = run.glyphIndex, endIndex = run.glyphIndex + run.numberOfGlyphs; index < endIndex; ++index ) + { + maxY = std::max( maxY, (*( cairoGlyphsBuffer + index )).y ); + } + + cairo_matrix_t matrix; + cairo_matrix_init( &matrix, + 1.0, 0.0, + -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE, 1.0, + maxY * TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE, 0.0 ); + + cairo_transform( cr, &matrix ); + } + + cairo_show_glyphs( cr, ( cairoGlyphsBuffer + run.glyphIndex ), run.numberOfGlyphs ); + + if( synthesizeItalic ) + { + // Restore the transform matrix to the identity. + cairo_matrix_t matrix; + cairo_matrix_init_identity( &matrix ); + cairo_set_matrix( cr, &matrix ); + } + } + cairo_fill( cr ); + } + } + + return pixelBuffer; +} + +} // namespace Internal + +} // namespace TextAbstraction + +} // namespace Dali diff --git a/dali/internal/text/text-abstraction/cairo-renderer.h b/dali/internal/text/text-abstraction/cairo-renderer.h new file mode 100755 index 0000000..4b6595f --- /dev/null +++ b/dali/internal/text/text-abstraction/cairo-renderer.h @@ -0,0 +1,46 @@ +#ifndef DALI_INTERNAL_TEXT_ABSTRACTION_CAIRO_RENDERER_H +#define DALI_INTERNAL_TEXT_ABSTRACTION_CAIRO_RENDERER_H + +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace TextAbstraction +{ + +namespace Internal +{ +/** + * @brief Cairo implementation of the Dali::TextAbstraction::TextRenderer interface. + * + * @see Dali::TextAbstraction::TextRenderer. + */ +Devel::PixelBuffer RenderTextCairo( const TextAbstraction::TextRenderer::Parameters& parameters ); + +} // namespace Internal + +} // namespace TextAbstraction + +} // namespace Dali + +#endif // DALI_INTERNAL_TEXT_ABSTRACTION_CAIRO_RENDERER_H + diff --git a/dali/internal/text/text-abstraction/font-client-impl.cpp b/dali/internal/text/text-abstraction/font-client-impl.cpp old mode 100644 new mode 100755 index 3d0f72b..cb9d9fc --- a/dali/internal/text/text-abstraction/font-client-impl.cpp +++ b/dali/internal/text/text-abstraction/font-client-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -204,6 +204,16 @@ void FontClient::GetFixedSizes( const FontDescription& fontDescription, mPlugin->GetFixedSizes( fontDescription, sizes ); } +bool FontClient::HasItalicStyle( FontId fontId ) const +{ + if( !mPlugin ) + { + return false; + } + + return mPlugin->HasItalicStyle( fontId ); +} + FontId FontClient::GetFontId( const FontPath& path, PointSize26Dot6 requestedPointSize, FaceIndex faceIndex ) { CreatePlugin(); @@ -225,11 +235,18 @@ FontId FontClient::GetFontId( const FontDescription& fontDescription, faceIndex ); } +FontId FontClient::GetFontId( const BitmapFont& bitmapFont ) +{ + CreatePlugin(); + + return mPlugin->GetFontId( bitmapFont ); +} + void FontClient::GetFontMetrics( FontId fontId, FontMetrics& metrics ) { CreatePlugin(); - return mPlugin->GetFontMetrics( fontId, metrics ); + mPlugin->GetFontMetrics( fontId, metrics ); } GlyphIndex FontClient::GetGlyphIndex( FontId fontId, Character charcode ) @@ -246,11 +263,11 @@ bool FontClient::GetGlyphMetrics( GlyphInfo* array, uint32_t size, GlyphType typ return mPlugin->GetGlyphMetrics( array, size, type, horizontal ); } -void FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool softwareItalic, bool softwareBold, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ) +void FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ) { CreatePlugin(); - mPlugin->CreateBitmap( fontId, glyphIndex, softwareItalic, softwareBold, data, outlineWidth ); + mPlugin->CreateBitmap( fontId, glyphIndex, isItalicRequired, isBoldRequired, data, outlineWidth ); } PixelData FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth ) @@ -264,7 +281,7 @@ void FontClient::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorB { CreatePlugin(); - return mPlugin->CreateVectorBlob( fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight ); + mPlugin->CreateVectorBlob( fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight ); } const GlyphInfo& FontClient::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize ) @@ -281,6 +298,13 @@ bool FontClient::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex ) return mPlugin->IsColorGlyph( fontId, glyphIndex ); } +GlyphIndex FontClient::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat) +{ + CreatePlugin(); + + return mPlugin->CreateEmbeddedItem( description, pixelFormat ); +} + FT_FaceRec_* FontClient::GetFreetypeFace( FontId fontId ) { CreatePlugin(); @@ -288,6 +312,13 @@ FT_FaceRec_* FontClient::GetFreetypeFace( FontId fontId ) return mPlugin->GetFreetypeFace( fontId ); } +FontDescription::Type FontClient::GetFontType( FontId fontId ) +{ + CreatePlugin(); + + return mPlugin->GetFontType( fontId ); +} + bool FontClient::AddCustomFontDirectory( const FontPath& path ) { CreatePlugin(); diff --git a/dali/internal/text/text-abstraction/font-client-impl.h b/dali/internal/text/text-abstraction/font-client-impl.h old mode 100644 new mode 100755 index 0d9cf71..37596b4 --- a/dali/internal/text/text-abstraction/font-client-impl.h +++ b/dali/internal/text/text-abstraction/font-client-impl.h @@ -2,7 +2,7 @@ #define DALI_INTERNAL_TEXT_ABSTRACTION_FONT_CLIENT_IMPL_H /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -136,6 +136,11 @@ public: FaceIndex faceIndex ); /** + * @copydoc Dali::TextAbstraction::FontClient::GetFontId( const BitmapFont& bitmapFont ) + */ + FontId GetFontId( const BitmapFont& bitmapFont ); + + /** * @copydoc Dali::TextAbstraction::FontClient::IsScalable( const FontPath& path ) */ bool IsScalable( const FontPath& path ); @@ -157,6 +162,11 @@ public: Dali::Vector< PointSize26Dot6 >& sizes ); /** + * @copydoc Dali::TextAbstraction::FontClient::HasItalicStyle() + */ + bool HasItalicStyle( FontId fontId ) const; + + /** * @copydoc Dali::TextAbstraction::FontClient::GetFontMetrics() */ void GetFontMetrics( FontId fontId, FontMetrics& metrics ); @@ -172,9 +182,9 @@ public: bool GetGlyphMetrics( GlyphInfo* array, uint32_t size, GlyphType type, bool horizontal ); /** - * @copydoc Dali::TextAbstraction::FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool softwareItalic, bool softwareBold, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ) + * @copydoc Dali::TextAbstraction::FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ) */ - void CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool softwareItalic, bool softwareBold, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ); + void CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ); /** * @copydoc Dali::TextAbstraction::FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth ) @@ -197,6 +207,11 @@ public: bool IsColorGlyph( FontId fontId, GlyphIndex glyphIndex ); /** + * @copydoc Dali::TextAbstraction::FontClient::CreateEmbeddedItem() + */ + GlyphIndex CreateEmbeddedItem( const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat ); + + /** * @brief Retrieves the pointer to the FreeType Font Face for the given @p fontId. * * @param[in] fontId The font id. @@ -206,6 +221,15 @@ public: FT_FaceRec_* GetFreetypeFace( FontId fontId ); /** + * @brief Retrieves the type of font. + * + * @param[in] fontId The font id. + * + * @return FACE_FONT if the font has been loaded by FreeType, BITMAP_FONT if it's a font that has been loaded from images or INVALID if it's a non valid font. + */ + FontDescription::Type GetFontType( FontId fontId ); + + /** * @copydoc Dali::TextAbstraction::FontClient::AddCustomFontDirectory() */ bool AddCustomFontDirectory( const FontPath& path ); diff --git a/dali/internal/text/text-abstraction/font-client-plugin-impl.cpp b/dali/internal/text/text-abstraction/font-client-plugin-impl.cpp old mode 100644 new mode 100755 index 1d157c9..99da7ab --- a/dali/internal/text/text-abstraction/font-client-plugin-impl.cpp +++ b/dali/internal/text/text-abstraction/font-client-plugin-impl.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // EXTERNAL INCLUDES #include @@ -43,7 +44,6 @@ Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New */ const float FROM_266 = 1.0f / 64.0f; const float POINTS_PER_INCH = 72.f; -const FT_Fixed FONT_SLANT_TANGENT = 0.221694663 * 0x10000; // For support software italic const std::string FONT_FORMAT( "TrueType" ); const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" ); @@ -180,9 +180,11 @@ FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace, mFaceIndex( face ), mMetrics( metrics ), mCharacterSet( nullptr ), - mFixedWidthPixels( 0.0f ), - mFixedHeightPixels( 0.0f ), - mVectorFontId( 0 ), + mFixedSizeIndex( 0 ), + mFixedWidthPixels( 0.f ), + mFixedHeightPixels( 0.f ), + mVectorFontId( 0u ), + mFontId( 0u ), mIsFixedSizeBitmap( false ), mHasColorTables( false ) { @@ -193,6 +195,7 @@ FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace, PointSize26Dot6 requestedPointSize, FaceIndex face, const FontMetrics& metrics, + int fixedSizeIndex, float fixedWidth, float fixedHeight, bool hasColorTables ) @@ -202,9 +205,11 @@ FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace, mFaceIndex( face ), mMetrics( metrics ), mCharacterSet( nullptr ), + mFixedSizeIndex( fixedSizeIndex ), mFixedWidthPixels( fixedWidth ), mFixedHeightPixels( fixedHeight ), - mVectorFontId( 0 ), + mVectorFontId( 0u ), + mFontId( 0u ), mIsFixedSizeBitmap( true ), mHasColorTables( hasColorTables ) { @@ -218,6 +223,7 @@ FontClient::Plugin::Plugin( unsigned int horizontalDpi, mDefaultFontDescription(), mSystemFonts(), mDefaultFonts(), + mFontIdCache(), mFontFaceCache(), mValidatedFontCache(), mFontDescriptionCache( 1u ), @@ -225,6 +231,7 @@ FontClient::Plugin::Plugin( unsigned int horizontalDpi, mFontDescriptionSizeCache(), mVectorFontCache( nullptr ), mEllipsisCache(), + mEmbeddedItemCache(), mDefaultFontDescriptionCached( false ) { mCharacterSetCache.Resize( 1u ); @@ -467,20 +474,44 @@ void FontClient::Plugin::GetDescription( FontId id, { DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n"); DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id ); + const FontId index = id - 1u; - for( const auto& item : mFontDescriptionSizeCache ) + if( ( id > 0u ) && ( index < mFontIdCache.Count() ) ) { - if( item.fontId == id ) + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; + switch( fontIdCacheItem.type ) { - fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId ); - - DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n"); - return; + case FontDescription::FACE_FONT: + { + for( const auto& item : mFontDescriptionSizeCache ) + { + if( item.fontId == fontIdCacheItem.id ) + { + fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId ); + + DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() ); + DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() ); + DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] ); + DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] ); + DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] ); + DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n"); + return; + } + } + break; + } + case FontDescription::BITMAP_FONT: + { + fontDescription.type = FontDescription::BITMAP_FONT; + fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name; + break; + } + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); + fontDescription.type = FontDescription::INVALID; + fontDescription.family.clear(); + } } } @@ -495,11 +526,27 @@ PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id ) const FontId index = id - 1u; if( ( id > 0u ) && - ( index < mFontFaceCache.size() ) ) + ( index < mFontIdCache.Count() ) ) { - DALI_LOG_INFO( gLogFilter, Debug::General, " point size : %d\n", ( *( mFontFaceCache.begin() + index ) ).mRequestedPointSize ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n"); - return ( *( mFontFaceCache.begin() + index ) ).mRequestedPointSize; + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; + + switch( fontIdCacheItem.type ) + { + case FontDescription::FACE_FONT: + { + DALI_LOG_INFO( gLogFilter, Debug::General, " point size : %d\n", ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize ); + DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n"); + return ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize; + } + case FontDescription::BITMAP_FONT: + { + return TextAbstraction::FontClient::DEFAULT_POINT_SIZE; + } + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); + } + } } else { @@ -517,7 +564,7 @@ bool FontClient::Plugin::IsCharacterSupportedByFont( FontId fontId, Character ch DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId ); DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", character ); - if( ( fontId < 1u ) || ( fontId > mFontFaceCache.size() ) ) + if( ( fontId < 1u ) || ( fontId > mFontIdCache.Count() ) ) { DALI_LOG_INFO( gLogFilter, Debug::General, " Invalid font id. Number of items in the cache: %d\n",mFontFaceCache.size()); DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n"); @@ -528,35 +575,62 @@ bool FontClient::Plugin::IsCharacterSupportedByFont( FontId fontId, Character ch bool isSupported = false; - FontFaceCacheItem& cacheItem = mFontFaceCache[fontId]; + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId]; - if( nullptr == cacheItem.mCharacterSet ) + switch( fontIdCacheItem.type ) { - // Create again the character set. - // It can be null if the ResetSystemDefaults() method has been called. + case FontDescription::FACE_FONT: + { + FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id]; - FontDescription description; - description.path = cacheItem.mPath; - description.family = std::move( FontFamily( cacheItem.mFreeTypeFace->family_name ) ); - description.weight = FontWeight::NONE; - description.width = FontWidth::NONE; - description.slant = FontSlant::NONE; + if( nullptr == cacheItem.mCharacterSet ) + { + // Create again the character set. + // It can be null if the ResetSystemDefaults() method has been called. + + FontDescription description; + description.path = cacheItem.mPath; + description.family = std::move( FontFamily( cacheItem.mFreeTypeFace->family_name ) ); + description.weight = FontWeight::NONE; + description.width = FontWidth::NONE; + description.slant = FontSlant::NONE; + + // Note FreeType doesn't give too much info to build a proper font style. + if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC ) + { + description.slant = FontSlant::ITALIC; + } + if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD ) + { + description.weight = FontWeight::BOLD; + } - // Note FreeType doesn't give too much info to build a proper font style. - if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC ) + cacheItem.mCharacterSet = CreateCharacterSetFromDescription( description ); + } + + isSupported = FcCharSetHasChar( cacheItem.mCharacterSet, character ); + break; + } + case FontDescription::BITMAP_FONT: { - description.slant = FontSlant::ITALIC; + const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font; + + for( const auto& glyph : bitmapFont.glyphs ) + { + if( glyph.utf32 == character ) + { + isSupported = true; + break; + } + } + break; } - if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD ) + default: { - description.weight = FontWeight::BOLD; + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); } - - cacheItem.mCharacterSet = CreateCharacterSetFromDescription( description ); } - isSupported = FcCharSetHasChar( cacheItem.mCharacterSet, character ); - DALI_LOG_INFO( gLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false") ); DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n"); return isSupported; @@ -610,9 +684,9 @@ FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList, if( preferColor ) { if( ( fontId > 0 ) && - ( fontId - 1u < mFontFaceCache.size() ) ) + ( fontId - 1u < mFontIdCache.Count() ) ) { - const FontFaceCacheItem& item = mFontFaceCache[fontId - 1u]; + const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id]; foundColor = item.mHasColorTables; } @@ -729,11 +803,11 @@ FontId FontClient::Plugin::GetFontId( const FontPath& path, DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() ); DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize ); - FontId id( 0 ); + FontId id = 0u; if( nullptr != mFreeTypeLibrary ) { - FontId foundId(0); + FontId foundId = 0u; if( FindFont( path, requestedPointSize, faceIndex, foundId ) ) { id = foundId; @@ -763,16 +837,19 @@ FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription, DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize ); // This method uses three vectors which caches: + // * The bitmap font cache // * Pairs of non validated font descriptions and an index to a vector with paths to font file names. // * The path to font file names. // * The font ids of pairs 'font point size, index to the vector with paths to font file names'. - // 1) Checks in the cache if the font's description has been validated before. + // 1) Checks if the font description matches with a previously loaded bitmap font. + // Returns if a font is found. + // 2) Checks in the cache if the font's description has been validated before. // If it was it gets an index to the vector with paths to font file names. Otherwise, // retrieves using font config a path to a font file name which matches with the // font's description. The path is stored in the cache. // - // 2) Checks in the cache if the pair 'font point size, index to the vector with paths to + // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to // font file names' exists. If exists, it gets the font id. If it doesn't it calls // the GetFontId() method with the path to the font file name and the point size to // get the font id. @@ -780,7 +857,13 @@ FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription, // The font id to be returned. FontId fontId = 0u; - // Check first if the font's description have been validated before. + // Check first if the font description matches with a previously loaded bitmap font. + if( FindBitmapFont( fontDescription.family, fontId ) ) + { + return fontId; + } + + // Check if the font's description have been validated before. FontDescriptionId validatedFontId = 0u; if( !FindValidatedFont( fontDescription, @@ -791,8 +874,9 @@ FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription, validatedFontId ); } + FontId fontFaceId = 0u; // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache. - if( !FindFont( validatedFontId, requestedPointSize, fontId ) ) + if( !FindFont( validatedFontId, requestedPointSize, fontFaceId ) ) { // Retrieve the font file name path. const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId ); @@ -803,12 +887,17 @@ FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription, faceIndex, false ); - mFontFaceCache[fontId-1u].mCharacterSet = mCharacterSetCache[validatedFontId]; + fontFaceId = mFontIdCache[fontId-1u].id; + mFontFaceCache[fontFaceId].mCharacterSet = mCharacterSetCache[validatedFontId]; // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries. mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId, requestedPointSize, - fontId ) ); + fontFaceId ) ); + } + else + { + fontId = mFontFaceCache[fontFaceId].mFontId + 1u; } DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId ); @@ -817,6 +906,56 @@ FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription, return fontId; } +FontId FontClient::Plugin::GetFontId( const BitmapFont& bitmapFont ) +{ + for( const auto& item : mBitmapFontCache ) + { + if( bitmapFont.name == item.font.name ) + { + return item.id + 1u; + } + } + + BitmapFontCacheItem bitmapFontCacheItem; + bitmapFontCacheItem.font = bitmapFont; + bitmapFontCacheItem.id = mFontIdCache.Count(); + + // Resize the vector with the pixel buffers. + bitmapFontCacheItem.pixelBuffers.resize( bitmapFont.glyphs.size() ); + + // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero. + unsigned int index = 0u; + for( auto& glyph : bitmapFontCacheItem.font.glyphs ) + { + Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index]; + + if( EqualsZero( glyph.ascender ) && EqualsZero( glyph.descender ) ) + { + // Load the glyph. + pixelBuffer = LoadImageFromFile( glyph.url ); + + if( pixelBuffer ) + { + glyph.ascender = static_cast(pixelBuffer.GetHeight()); + } + } + + bitmapFontCacheItem.font.ascender = std::max( glyph.ascender, bitmapFontCacheItem.font.ascender ); + bitmapFontCacheItem.font.descender = std::min( glyph.descender, bitmapFontCacheItem.font.descender ); + + ++index; + } + + FontIdCacheItem fontIdCacheItem; + fontIdCacheItem.type = FontDescription::BITMAP_FONT; + fontIdCacheItem.id = mBitmapFontCache.size(); + + mBitmapFontCache.push_back( std::move( bitmapFontCacheItem ) ); + mFontIdCache.PushBack( fontIdCacheItem ); + + return bitmapFontCacheItem.id + 1u; +} + void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription, FontDescriptionId& validatedFontId ) { @@ -849,6 +988,7 @@ void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription, DALI_LOG_INFO( gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId ); // Add the path to the cache. + description.type = FontDescription::FACE_FONT; mFontDescriptionCache.push_back( description ); mCharacterSetCache.PushBack( characterSet ); @@ -881,27 +1021,53 @@ void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription, void FontClient::Plugin::GetFontMetrics( FontId fontId, FontMetrics& metrics ) { + const FontId index = fontId - 1u; + if( ( fontId > 0 ) && - ( fontId - 1u < mFontFaceCache.size() ) ) + ( index < mFontIdCache.Count() ) ) { - const FontFaceCacheItem& font = mFontFaceCache[fontId-1]; - - metrics = font.mMetrics; + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; - // Adjust the metrics if the fixed-size font should be down-scaled - if( font.mIsFixedSizeBitmap ) + switch( fontIdCacheItem.type ) { - const float desiredFixedSize = static_cast( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical; + case FontDescription::FACE_FONT: + { + const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id]; + + metrics = font.mMetrics; + + // Adjust the metrics if the fixed-size font should be down-scaled + if( font.mIsFixedSizeBitmap ) + { + const float desiredFixedSize = static_cast( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical; - if( desiredFixedSize > 0.f ) + if( desiredFixedSize > 0.f ) + { + const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels; + + metrics.ascender = metrics.ascender * scaleFactor; + metrics.descender = metrics.descender * scaleFactor; + metrics.height = metrics.height * scaleFactor; + metrics.underlinePosition = metrics.underlinePosition * scaleFactor; + metrics.underlineThickness = metrics.underlineThickness * scaleFactor; + } + } + break; + } + case FontDescription::BITMAP_FONT: { - const float scaleFactor = desiredFixedSize / static_cast( font.mFixedHeightPixels ); + const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id]; - metrics.ascender = floorf( metrics.ascender * scaleFactor ); - metrics.descender = floorf( metrics.descender * scaleFactor ); - metrics.height = floorf( metrics.height * scaleFactor ); - metrics.underlinePosition = floorf( metrics.underlinePosition * scaleFactor ); - metrics.underlineThickness = floorf( metrics.underlineThickness * scaleFactor ); + metrics.ascender = bitmapFontCacheItem.font.ascender; + metrics.descender = bitmapFontCacheItem.font.descender; + metrics.height = metrics.ascender - metrics.descender; + metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition; + metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness; + break; + } + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); } } } @@ -914,17 +1080,23 @@ void FontClient::Plugin::GetFontMetrics( FontId fontId, GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId, Character charcode ) { - GlyphIndex index = 0u; + GlyphIndex glyphIndex = 0u; + const FontId index = fontId - 1u; if( ( fontId > 0u ) && - ( fontId - 1u < mFontFaceCache.size() ) ) + ( index < mFontIdCache.Count() ) ) { - FT_Face ftFace = mFontFaceCache[fontId-1u].mFreeTypeFace; + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; - index = FT_Get_Char_Index( ftFace, charcode ); + if( FontDescription::FACE_FONT == fontIdCacheItem.type ) + { + FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace; + + glyphIndex = FT_Get_Char_Index( ftFace, charcode ); + } } - return index; + return glyphIndex; } bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array, @@ -950,79 +1122,137 @@ bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array, { GlyphInfo& glyph = array[i]; - FontId fontId = glyph.fontId; + FontId index = glyph.fontId - 1u; - if( fontId > 0 && - fontId-1 < mFontFaceCache.size() ) + if( ( glyph.fontId > 0u ) && + ( index < mFontIdCache.Count() ) ) { - const FontFaceCacheItem& font = mFontFaceCache[fontId-1]; + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; - FT_Face ftFace = font.mFreeTypeFace; + switch( fontIdCacheItem.type ) + { + case FontDescription::FACE_FONT: + { + const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id]; + + FT_Face ftFace = font.mFreeTypeFace; #ifdef FREETYPE_BITMAP_SUPPORT - // Check to see if we should be loading a Fixed Size bitmap? - if ( font.mIsFixedSizeBitmap ) - { - int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR ); - if ( FT_Err_Ok == error ) + // Check to see if we should be loading a Fixed Size bitmap? + if( font.mIsFixedSizeBitmap ) { - glyph.width = font.mFixedWidthPixels; - glyph.height = font.mFixedHeightPixels; - glyph.advance = font.mFixedWidthPixels; - glyph.xBearing = 0.0f; - glyph.yBearing = font.mFixedHeightPixels; + FT_Select_Size( ftFace, font.mFixedSizeIndex ); ///< @todo: needs to be investigated why it's needed to select the size again. + int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR ); + if ( FT_Err_Ok == error ) + { + glyph.width = font.mFixedWidthPixels; + glyph.height = font.mFixedHeightPixels; + glyph.advance = font.mFixedWidthPixels; + glyph.xBearing = 0.0f; + glyph.yBearing = font.mFixedHeightPixels; - // Adjust the metrics if the fixed-size font should be down-scaled - const float desiredFixedSize = static_cast( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical; + // Adjust the metrics if the fixed-size font should be down-scaled + const float desiredFixedSize = static_cast( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical; - if( desiredFixedSize > 0.f ) - { - const float scaleFactor = desiredFixedSize / static_cast( font.mFixedHeightPixels ); + if( desiredFixedSize > 0.f ) + { + const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels; - glyph.width = floorf( glyph.width * scaleFactor ); - glyph.height = floorf( glyph.height * scaleFactor ); - glyph.advance = floorf( glyph.advance * scaleFactor ); - glyph.xBearing = floorf( glyph.xBearing * scaleFactor ); - glyph.yBearing = floorf( glyph.yBearing * scaleFactor ); + glyph.width = glyph.width * scaleFactor ; + glyph.height = glyph.height * scaleFactor; + glyph.advance = glyph.advance * scaleFactor; + glyph.xBearing = glyph.xBearing * scaleFactor; + glyph.yBearing = glyph.yBearing * scaleFactor; - glyph.scaleFactor = scaleFactor; + glyph.scaleFactor = scaleFactor; + } + } + else + { + DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error ); + success = false; } } else - { - DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error ); - success = false; - } - } - else #endif - { - int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_NO_AUTOHINT ); - - if( FT_Err_Ok == error ) { - glyph.width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266; - glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ; - if( horizontal ) + int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_NO_AUTOHINT ); + + if( FT_Err_Ok == error ) { - glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266; - glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266; + glyph.width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266; + glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ; + if( horizontal ) + { + glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266; + glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266; + } + else + { + glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266; + glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266; + } } else { - glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266; - glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266; + success = false; } } - else + break; + } + case FontDescription::BITMAP_FONT: + { + BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id]; + + unsigned int index = 0u; + for( auto& item : bitmapFontCacheItem.font.glyphs ) { - success = false; + if( item.utf32 == glyph.index ) + { + Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index]; + if( !pixelBuffer ) + { + pixelBuffer = LoadImageFromFile( item.url ); + } + + glyph.width = static_cast< float >( pixelBuffer.GetWidth() ); + glyph.height = static_cast< float >( pixelBuffer.GetHeight() ); + glyph.xBearing = 0.f; + glyph.yBearing = glyph.height + item.descender; + glyph.advance = glyph.width; + glyph.scaleFactor = 1.f; + break; + } + ++index; } + + success = true; + break; + } + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); + } } } else { - success = false; + // Check if it's an embedded image. + if( ( 0u == glyph.fontId ) && ( 0u != glyph.index ) && ( glyph.index <= mEmbeddedItemCache.Count() ) ) + { + const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u]; + + glyph.width = static_cast( item.width ); + glyph.height = static_cast( item.height ); + glyph.xBearing = 0.f; + glyph.yBearing = glyph.height; + glyph.advance = glyph.width; + glyph.scaleFactor = 1.f; + } + else + { + success = false; + } } } @@ -1036,14 +1266,14 @@ bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array, #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING bool success( true ); - for( unsigned int i=0; i 0 && - fontId-1 < mFontFaceCache.size() ) + if( ( fontId > 0u ) && + ( fontId - 1u ) < mFontIdCache.Count() ) { - FontFaceCacheItem& font = mFontFaceCache[fontId-1]; + FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id]; if( ! font.mVectorFontId ) { @@ -1072,99 +1302,177 @@ bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array, #endif } -void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool softwareItalic, bool softwareBold, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ) +void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ) { - if( ( fontId > 0 ) && - ( fontId - 1u < mFontFaceCache.size() ) ) - { - FT_Face ftFace = mFontFaceCache[fontId - 1u].mFreeTypeFace; + const FontId index = fontId - 1u; - FT_Error error; + if( ( fontId > 0u ) && + ( index < mFontIdCache.Count() ) ) + { + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; -#ifdef FREETYPE_BITMAP_SUPPORT - // Check to see if this is fixed size bitmap - if ( mFontFaceCache[fontId - 1u].mIsFixedSizeBitmap ) - { - error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR ); - } - else -#endif + switch( fontIdCacheItem.type ) { - error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT ); - } - if( FT_Err_Ok == error ) + case FontDescription::FACE_FONT: { - FT_Glyph glyph; + // For the software italics. + bool isShearRequired = false; - if( softwareBold ) + const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id]; + FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace; + + FT_Error error; + +#ifdef FREETYPE_BITMAP_SUPPORT + // Check to see if this is fixed size bitmap + if( fontFaceCacheItem.mIsFixedSizeBitmap ) { - FT_GlyphSlot_Embolden(ftFace->glyph); + error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR ); } - - if( softwareItalic ) + else +#endif { - // FT Matrix uses 16.16 fixed-point format - FT_Matrix transform = {0x10000, FONT_SLANT_TANGENT, 0x00000, 0x10000}; - FT_Outline_Transform(&ftFace->glyph->outline, &transform); + error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT ); } + if( FT_Err_Ok == error ) + { + if( isBoldRequired && !( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) ) + { + // Does the software bold. + FT_GlyphSlot_Embolden( ftFace->glyph ); + } - error = FT_Get_Glyph( ftFace->glyph, &glyph ); + if( isItalicRequired && !( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) ) + { + // Will do the software italic. + isShearRequired = true; + } - // Convert to bitmap if necessary - if ( FT_Err_Ok == error ) - { - if( glyph->format != FT_GLYPH_FORMAT_BITMAP ) + FT_Glyph glyph; + error = FT_Get_Glyph( ftFace->glyph, &glyph ); + + // Convert to bitmap if necessary + if ( FT_Err_Ok == error ) { - // Check whether we should create a bitmap for the outline - if( glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0 ) + if( glyph->format != FT_GLYPH_FORMAT_BITMAP ) { - // Set up a stroker - FT_Stroker stroker; - error = FT_Stroker_New(mFreeTypeLibrary, &stroker ); - - if ( FT_Err_Ok == error ) + // Check whether we should create a bitmap for the outline + if( glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0 ) { - FT_Stroker_Set( stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 ); - error = FT_Glyph_StrokeBorder( &glyph, stroker, 0, 1 ); + // Set up a stroker + FT_Stroker stroker; + error = FT_Stroker_New( mFreeTypeLibrary, &stroker ); - if ( FT_Err_Ok == error ) + if( FT_Err_Ok == error ) { - FT_Stroker_Done( stroker ); + FT_Stroker_Set( stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 ); + error = FT_Glyph_StrokeBorder( &glyph, stroker, 0, 1 ); + + if( FT_Err_Ok == error ) + { + FT_Stroker_Done( stroker ); + } + else + { + DALI_LOG_ERROR( "FT_Glyph_StrokeBorder Failed with error: %d\n", error ); + } } else { - DALI_LOG_ERROR( "FT_Glyph_StrokeBorder Failed with error: %d\n", error ); + DALI_LOG_ERROR( "FT_Stroker_New Failed with error: %d\n", error ); } } + + error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 ); + if( FT_Err_Ok == error ) + { + FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( glyph ); + ConvertBitmap( data, bitmapGlyph->bitmap, isShearRequired ); + } else { - DALI_LOG_ERROR( "FT_Stroker_New Failed with error: %d\n", error ); + DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error ); } } - - error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 ); - if ( FT_Err_Ok == error ) - { - FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( glyph ); - ConvertBitmap( data, bitmapGlyph->bitmap ); - } else { - DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error ); + ConvertBitmap( data, ftFace->glyph->bitmap, isShearRequired ); } + + // Created FT_Glyph object must be released with FT_Done_Glyph + FT_Done_Glyph( glyph ); } - else + } + else + { + DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error ); + } + break; + } + case FontDescription::BITMAP_FONT: + { + BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id]; + + unsigned int index = 0u; + for( auto& item : bitmapFontCacheItem.font.glyphs ) + { + if( item.utf32 == glyphIndex ) { - ConvertBitmap( data, ftFace->glyph->bitmap ); - } + Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index]; + if( !pixelBuffer ) + { + pixelBuffer = LoadImageFromFile( item.url ); + } - // Created FT_Glyph object must be released with FT_Done_Glyph - FT_Done_Glyph( glyph ); + data.width = pixelBuffer.GetWidth(); + data.height = pixelBuffer.GetHeight(); + + ConvertBitmap( data, data.width, data.height, pixelBuffer.GetBuffer() ); + + // Sets the pixel format. + data.format = pixelBuffer.GetPixelFormat(); + break; + } + ++index; } + break; } - else + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); + } + } + } + else + { + if( ( 0u != glyphIndex ) && ( glyphIndex <= mEmbeddedItemCache.Count() ) ) { - DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error ); + // It's an embedded item. + const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u]; + + data.width = item.width; + data.height = item.height; + if( 0u != item.pixelBufferId ) + { + Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId-1u].pixelBuffer; + if( pixelBuffer ) + { + ConvertBitmap( data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer() ); + + // Sets the pixel format. + data.format = pixelBuffer.GetPixelFormat(); + } + } + else + { + // Creates the output buffer + const unsigned int bufferSize = data.width * data.height * 4u; + data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[]. + + memset( data.buffer, 0u, bufferSize ); + + // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it. + } } } } @@ -1189,17 +1497,18 @@ void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, blobLength = 0; #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING - if( fontId > 0 && - fontId-1 < mFontFaceCache.size() ) + if( ( fontId > 0u ) && + ( fontId - 1u < mFontIdCache.Count() ) ) { - FontFaceCacheItem& font = mFontFaceCache[fontId-1]; + const FontId fontFaceId = mFontIdCache[fontId - 1u].id; + FontFaceCacheItem& font = mFontFaceCache[fontFaceId]; if( ! font.mVectorFontId ) { font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath ); } - mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight ); + mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight ); } #endif } @@ -1212,7 +1521,7 @@ const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requested // First look into the cache if there is an ellipsis glyph for the requested point size. for( const auto& item : mEllipsisCache ) { - if( fabsf( item.requestedPointSize - requestedPointSize ) < Math::MACHINE_EPSILON_1000 ) + if( item.requestedPointSize != requestedPointSize ) { // Use the glyph in the cache. @@ -1236,7 +1545,7 @@ const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requested false ); // Set the character index to access the glyph inside the font. - item.glyph.index = FT_Get_Char_Index( mFontFaceCache[item.glyph.fontId-1].mFreeTypeFace, + item.glyph.index = FT_Get_Char_Index( mFontFaceCache[mFontIdCache[item.glyph.fontId-1u].id].mFreeTypeFace, ELLIPSIS_CHARACTER ); GetBitmapMetrics( &item.glyph, 1u, true ); @@ -1252,17 +1561,37 @@ bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex ) { FT_Error error = -1; + const FontId index = fontId - 1u; + #ifdef FREETYPE_BITMAP_SUPPORT - if( ( fontId > 0 ) && - ( fontId - 1u < mFontFaceCache.size() ) ) + if( ( fontId > 0u ) && + ( index < mFontIdCache.Count() ) ) { - const FontFaceCacheItem& item = mFontFaceCache[fontId - 1u]; - FT_Face ftFace = item.mFreeTypeFace; + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; - // Check to see if this is fixed size bitmap - if( item.mHasColorTables ) + switch( fontIdCacheItem.type ) + { + case FontDescription::FACE_FONT: + { + const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id]; + FT_Face ftFace = item.mFreeTypeFace; + + // Check to see if this is fixed size bitmap + if( item.mHasColorTables ) + { + error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR ); + } + break; + } + case FontDescription::BITMAP_FONT: { - error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR ); + error = FT_Err_Ok; // Will return true; + break; + } + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); + } } } #endif @@ -1274,21 +1603,126 @@ FT_FaceRec_* FontClient::Plugin::GetFreetypeFace( FontId fontId ) { FT_Face fontFace = nullptr; + const FontId index = fontId - 1u; if( ( fontId > 0u ) && - ( fontId - 1u < mFontFaceCache.size() ) ) + ( index < mFontIdCache.Count() ) ) { - fontFace = mFontFaceCache[fontId - 1u].mFreeTypeFace; - } + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; + if( FontDescription::FACE_FONT == fontIdCacheItem.type ) + { + fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace; + } + } return fontFace; } +FontDescription::Type FontClient::Plugin::GetFontType( FontId fontId ) +{ + const FontId index = fontId - 1u; + if( ( fontId > 0u ) && + ( index < mFontIdCache.Count() ) ) + { + return mFontIdCache[index].type; + } + return FontDescription::INVALID; +} + bool FontClient::Plugin::AddCustomFontDirectory( const FontPath& path ) { // NULL as first parameter means the current configuration is used. return FcConfigAppFontAddDir( NULL, reinterpret_cast( path.c_str() ) ); } +GlyphIndex FontClient::Plugin::CreateEmbeddedItem( const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat ) +{ + EmbeddedItem embeddedItem; + + embeddedItem.pixelBufferId = 0u; + embeddedItem.width = description.width; + embeddedItem.height = description.height; + + pixelFormat = Pixel::A8; + + if( !description.url.empty() ) + { + // Check if the url is in the cache. + PixelBufferId index = 0u; + + for( const auto& cacheItem : mPixelBufferCache ) + { + ++index; + if( cacheItem.url == description.url ) + { + // The url is in the pixel buffer cache. + // Set the index +1 to the vector. + embeddedItem.pixelBufferId = index; + break; + } + } + + Devel::PixelBuffer pixelBuffer; + if( 0u == embeddedItem.pixelBufferId ) + { + // The pixel buffer is not in the cache. Create one and cache it. + + // Load the image from the url. + pixelBuffer = LoadImageFromFile( description.url ); + + // Create the cache item. + PixelBufferCacheItem pixelBufferCacheItem; + pixelBufferCacheItem.pixelBuffer = pixelBuffer; + pixelBufferCacheItem.url = description.url; + + // Store the cache item in the cache. + mPixelBufferCache.push_back( std::move( pixelBufferCacheItem ) ); + + // Set the pixel buffer id to the embedded item. + embeddedItem.pixelBufferId = mPixelBufferCache.size(); + } + else + { + // Retrieve the pixel buffer from the cache to set the pixel format. + pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId-1u].pixelBuffer; + } + + if( pixelBuffer ) + { + // Set the size of the embedded item if it has not been set. + if( 0u == embeddedItem.width ) + { + embeddedItem.width = static_cast( pixelBuffer.GetWidth() ); + } + + if( 0u == embeddedItem.height ) + { + embeddedItem.height = static_cast( pixelBuffer.GetHeight() ); + } + + // Set the pixel format. + pixelFormat = pixelBuffer.GetPixelFormat(); + } + } + + // Find if the same embeddedItem has already been created. + unsigned int index = 0u; + for( const auto& item : mEmbeddedItemCache ) + { + ++index; + if( ( item.pixelBufferId == embeddedItem.pixelBufferId ) && + ( item.width == embeddedItem.width ) && + ( item.height == embeddedItem.height ) ) + { + return index; + } + } + + // Cache the embedded item. + mEmbeddedItemCache.PushBack( embeddedItem ); + + return mEmbeddedItemCache.Count(); +} + void FontClient::Plugin::InitSystemFonts() { DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" ); @@ -1530,6 +1964,7 @@ FontId FontClient::Plugin::CreateFont( const FontPath& path, const bool isScalable = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_SCALABLE ) ); const bool hasFixedSizedBitmaps = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES ) ) && ( 0 != ftFace->num_fixed_sizes ); const bool hasColorTables = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_COLOR ) ); + FontId fontFaceId = 0u; DALI_LOG_INFO( gLogFilter, Debug::General, " isScalable : [%s]\n", ( isScalable ? "true" : "false" ) ); DALI_LOG_INFO( gLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", ( hasFixedSizedBitmaps ? "true" : "false" ) ); @@ -1569,8 +2004,8 @@ FontId FontClient::Plugin::CreateFont( const FontPath& path, } else { - float fixedWidth = static_cast< float >( ftFace->available_sizes[ fixedSizeIndex ].width ); - float fixedHeight = static_cast< float >( ftFace->available_sizes[ fixedSizeIndex ].height ); + const float fixedWidth = static_cast( ftFace->available_sizes[ fixedSizeIndex ].width ); + const float fixedHeight = static_cast( ftFace->available_sizes[ fixedSizeIndex ].height ); // Indicate that the font is a fixed sized bitmap FontMetrics metrics( fixedHeight, // The ascender in pixels. @@ -1579,8 +2014,26 @@ FontId FontClient::Plugin::CreateFont( const FontPath& path, 0.0f, 0.0f ); - mFontFaceCache.push_back( FontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedWidth, fixedHeight, hasColorTables ) ); - id = mFontFaceCache.size(); + // Create the FreeType font face item to cache. + FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables ); + + // Set the index to the font's id cache. + fontFaceCacheItem.mFontId = mFontIdCache.Count(); + + // Create the font id item to cache. + FontIdCacheItem fontIdCacheItem; + fontIdCacheItem.type = FontDescription::FACE_FONT; + + // Set the index to the FreeType font face cache. + fontIdCacheItem.id = mFontFaceCache.size(); + fontFaceId = fontIdCacheItem.id + 1u; + + // Cache the items. + mFontFaceCache.push_back( fontFaceCacheItem ); + mFontIdCache.PushBack( fontIdCacheItem ); + + // Set the font id to be returned. + id = mFontIdCache.Count(); } } else @@ -1602,8 +2055,26 @@ FontId FontClient::Plugin::CreateFont( const FontPath& path, static_cast< float >( ftFace->underline_position ) * FROM_266, static_cast< float >( ftFace->underline_thickness ) * FROM_266 ); - mFontFaceCache.push_back( FontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics ) ); - id = mFontFaceCache.size(); + // Create the FreeType font face item to cache. + FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics ); + + // Set the index to the font's id cache. + fontFaceCacheItem.mFontId = mFontIdCache.Count(); + + // Create the font id item to cache. + FontIdCacheItem fontIdCacheItem; + fontIdCacheItem.type = FontDescription::FACE_FONT; + + // Set the index to the FreeType font face cache. + fontIdCacheItem.id = mFontFaceCache.size(); + fontFaceId = fontIdCacheItem.id + 1u; + + // Cache the items. + mFontFaceCache.push_back( fontFaceCacheItem ); + mFontIdCache.PushBack( fontIdCacheItem ); + + // Set the font id to be returned. + id = mFontIdCache.Count(); } else { @@ -1611,11 +2082,11 @@ FontId FontClient::Plugin::CreateFont( const FontPath& path, } } - if( 0u != id ) + if( 0u != fontFaceId ) { if( cacheDescription ) { - CacheFontPath( ftFace, id, requestedPointSize, path ); + CacheFontPath( ftFace, fontFaceId, requestedPointSize, path ); } } } @@ -1630,7 +2101,37 @@ FontId FontClient::Plugin::CreateFont( const FontPath& path, return id; } -void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap ) +void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer ) +{ + // Set the input dimensions. + const ImageDimensions inputDimensions( srcWidth, srcHeight ); + + // Set the output dimensions. + // If the output dimension is not given, the input dimension is set + // and won't be downscaling. + data.width = ( data.width == 0 ) ? srcWidth : data.width; + data.height = ( data.height == 0 ) ? srcHeight : data.height; + const ImageDimensions desiredDimensions( data.width, data.height ); + + // Creates the output buffer + const unsigned int bufferSize = data.width * data.height * 4u; + data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[]. + + if( inputDimensions == desiredDimensions ) + { + // There isn't downscaling. + memcpy( data.buffer, srcBuffer, bufferSize ); + } + else + { + Dali::Internal::Platform::LanczosSample4BPP( srcBuffer, + inputDimensions, + data.buffer, + desiredDimensions ); + } +} + +void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired ) { if( srcBitmap.width*srcBitmap.rows > 0 ) { @@ -1640,12 +2141,72 @@ void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBuffer { if( srcBitmap.pitch == static_cast( srcBitmap.width ) ) { - const unsigned int bufferSize = srcBitmap.width * srcBitmap.rows; + uint8_t* pixelsIn = srcBitmap.buffer; + unsigned int width = srcBitmap.width; + unsigned height = srcBitmap.rows; + + std::unique_ptr pixelsOutPtr( nullptr, free ); + + if( isShearRequired ) + { + /** + * Glyphs' bitmaps with no slant retrieved from FreeType: + * __________ ____ + * |XXXXXXXX| |XX| + * | XX | |XX| + * | XX | |XX| + * | XX | |XX| + * | XX | |XX| + * | XX | |XX| + * ---------- ---- + * + * Expected glyphs' bitmaps with italic slant: + * ____________ ______ + * | XXXXXXXX| | XX| + * | XX | | XX| + * | XX | | XX | + * | XX | | XX | + * | XX | |XX | + * | XX | |XX | + * ------------ ------ + * + * Glyphs' bitmaps with software italic slant retrieved from FreeType: + * __________ ______ + * |XXXXXXXX| | XX| + * | XX | | XX| + * | XX | | XX | + * | XX | | XX | + * | XX | |XX | + * | XX | |XX | + * ---------- ------ + * + * This difference in some bitmaps' width causes an overlap of some glyphs. This is the reason why a shear operation is done here instead of relying on the experimental FT_GlyphSlot_Oblique() implementation. + */ + unsigned int widthOut = 0u; + unsigned int heightOut = 0u; + uint8_t* pixelsOut = nullptr; + + Dali::Internal::Platform::HorizontalShear( pixelsIn, + width, + height, + 1u, + -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE, + pixelsOut, + widthOut, + heightOut ); + + width = widthOut; + height = heightOut; + pixelsIn = pixelsOut; + pixelsOutPtr.reset( pixelsOut ); + } + + const unsigned int bufferSize = width * height; data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[]. - data.width = srcBitmap.width; - data.height = srcBitmap.rows; - data.format = Pixel::L8; - memcpy( data.buffer, srcBitmap.buffer, bufferSize ); + data.width = width; + data.height = height; + data.format = Pixel::L8; // Sets the pixel format. + memcpy( data.buffer, pixelsIn, bufferSize ); } break; } @@ -1655,32 +2216,9 @@ void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBuffer { if( srcBitmap.pitch == static_cast( srcBitmap.width << 2u ) ) { - // Set the input dimensions. - const ImageDimensions inputDimensions( srcBitmap.width, srcBitmap.rows ); - - // Set the output dimensions. - // If the output dimension is not given, the input dimension is set - // and won't be downscaling. - data.width = ( data.width == 0 ) ? srcBitmap.width : data.width; - data.height = ( data.height == 0 ) ? srcBitmap.rows : data.height; - const ImageDimensions desiredDimensions( data.width, data.height ); - - // Creates the output buffer - const unsigned int bufferSize = data.width * data.height * 4u; - data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[]. + ConvertBitmap( data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer ); - if( inputDimensions == desiredDimensions ) - { - // There isn't downscaling. - memcpy( data.buffer, srcBitmap.buffer, bufferSize ); - } - else - { - Dali::Internal::Platform::LanczosSample4BPP( srcBitmap.buffer, - inputDimensions, - data.buffer, - desiredDimensions ); - } + // Sets the pixel format. data.format = Pixel::BGRA8888; } break; @@ -1708,11 +2246,11 @@ bool FontClient::Plugin::FindFont( const FontPath& path, fontId = 0u; for( const auto& cacheItem : mFontFaceCache ) { - ++fontId; if( cacheItem.mRequestedPointSize == requestedPointSize && cacheItem.mFaceIndex == faceIndex && cacheItem.mPath == path ) { + fontId = cacheItem.mFontId + 1u; DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId ); DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" ); @@ -1724,7 +2262,6 @@ bool FontClient::Plugin::FindFont( const FontPath& path, DALI_LOG_INFO( gLogFilter, Debug::General, " font not found\n" ); DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" ); - fontId = 0u; return false; } @@ -1826,6 +2363,22 @@ bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId, return false; } +bool FontClient::Plugin::FindBitmapFont( const FontFamily& bitmapFont, FontId& fontId ) const +{ + fontId = 0u; + + for( const auto& item : mBitmapFontCache ) + { + if( bitmapFont == item.font.name ) + { + fontId = item.id + 1u; + return true; + } + } + + return false; +} + bool FontClient::Plugin::IsScalable( const FontPath& path ) { bool isScalable = false; @@ -1925,6 +2478,32 @@ void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription, FcPatternDestroy( fontFamilyPattern ); } +bool FontClient::Plugin::HasItalicStyle( FontId fontId ) const +{ + bool hasItalicStyle = false; + + const FontId index = fontId - 1u; + + if( ( fontId > 0 ) && + ( index < mFontIdCache.Count() ) ) + { + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; + + if( FontDescription::FACE_FONT == fontIdCacheItem.type ) + { + const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id]; + + hasItalicStyle = 0u != ( font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC ); + } + } + else + { + DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId ); + } + + return hasItalicStyle; +} + void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path ) { FontDescription description; @@ -1963,9 +2542,11 @@ void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Do mMatchedFcPatternCache.PushBack( match ); - mFontFaceCache[id-1u].mCharacterSet = characterSet; + const FontId fontFaceId = id - 1u; + mFontFaceCache[fontFaceId].mCharacterSet = characterSet; // Add the path to the cache. + description.type = FontDescription::FACE_FONT; mFontDescriptionCache.push_back( description ); mCharacterSetCache.PushBack( characterSet ); @@ -1976,7 +2557,7 @@ void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Do // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries. mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId, requestedPointSize, - id ) ); + fontFaceId ) ); } } diff --git a/dali/internal/text/text-abstraction/font-client-plugin-impl.h b/dali/internal/text/text-abstraction/font-client-plugin-impl.h old mode 100644 new mode 100755 index 9bbc70c..514b3d8 --- a/dali/internal/text/text-abstraction/font-client-plugin-impl.h +++ b/dali/internal/text/text-abstraction/font-client-plugin-impl.h @@ -19,9 +19,11 @@ */ // INTERNAL INCLUDES +#include #include #include #include +#include #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING #include @@ -52,9 +54,18 @@ namespace Internal { /** - *@brief Type used for indices addressing the vector with front descriptions of validated fonts. + * @brief Type used for indices addressing the vector with front descriptions of validated fonts. */ typedef uint32_t FontDescriptionId; + +/** + * @brief Type used for indices addressing the vector with pixel buffers. + */ +typedef uint32_t PixelBufferId; + +/** + * @brief Vector of character sets. + */ typedef Vector<_FcCharSet*> CharacterSetList; /** @@ -62,6 +73,12 @@ typedef Vector<_FcCharSet*> CharacterSetList; */ struct FontClient::Plugin { + struct FontIdCacheItem + { + FontDescription::Type type; ///< The type of font. + FontId id; ///< Index to the cache of fonts for the specified type. + }; + /** * @brief Caches an list of fallback fonts for a given font-description */ @@ -118,6 +135,7 @@ struct FontClient::Plugin PointSize26Dot6 requestedPointSize, FaceIndex face, const FontMetrics& metrics, + int fixedSizeIndex, float fixedWidth, float fixedHeight, bool hasColorTables ); @@ -128,9 +146,11 @@ struct FontClient::Plugin FaceIndex mFaceIndex; ///< The face index. FontMetrics mMetrics; ///< The font metrics. _FcCharSet* mCharacterSet; ///< Pointer with the range of characters. - FT_Short mFixedWidthPixels; ///< The height in pixels (fixed size bitmaps only) - FT_Short mFixedHeightPixels; ///< The height in pixels (fixed size bitmaps only) + int mFixedSizeIndex; ///< Index to the fixed size table for the requested size. + float mFixedWidthPixels; ///< The height in pixels (fixed size bitmaps only) + float mFixedHeightPixels; ///< The height in pixels (fixed size bitmaps only) unsigned int mVectorFontId; ///< The ID of the equivalent vector-based font + FontId mFontId; ///< Index to the vector with the cache of font's ids. bool mIsFixedSizeBitmap : 1; ///< Whether the font has fixed size bitmaps. bool mHasColorTables : 1; ///< Whether the font has color tables. }; @@ -141,6 +161,35 @@ struct FontClient::Plugin GlyphInfo glyph; }; + /** + * @brief Caches pixel buffers. + */ + struct PixelBufferCacheItem + { + Devel::PixelBuffer pixelBuffer; ///< The pixel buffer loaded from the url. + std::string url; ///< The url. + }; + + /** + * @brief Caches embedded items. + */ + struct EmbeddedItem + { + PixelBufferId pixelBufferId; ///< Index to the vector of pixel buffers + unsigned int width; ///< The desired width. + unsigned int height; ///< The desired height. + }; + + /** + * @brief Stores a bitmap font and its pixel buffers per glyph. + */ + struct BitmapFontCacheItem + { + BitmapFont font; ///< The bitmap font. + std::vector pixelBuffers; ///< The pixel buffers of the glyphs. + FontId id; ///< Index to the vector with the cache of font's ids. + }; + /** * Constructor. * @@ -254,6 +303,11 @@ struct FontClient::Plugin FaceIndex faceIndex ); /** + * @copydoc Dali::TextAbstraction::FontClient::GetFontId( const BitmapFont& bitmapFont ) + */ + FontId GetFontId( const BitmapFont& bitmapFont ); + + /** * @copydoc Dali::TextAbstraction::FontClient::IsScalable( const FontPath& path ) */ bool IsScalable( const FontPath& path ); @@ -275,6 +329,11 @@ struct FontClient::Plugin Dali::Vector< PointSize26Dot6 >& sizes ); /** + * @copydoc Dali::TextAbstraction::FontClient::HasItalicStyle() + */ + bool HasItalicStyle( FontId fontId ) const; + + /** * @copydoc Dali::TextAbstraction::FontClient::GetFontMetrics() */ void GetFontMetrics( FontId fontId, FontMetrics& metrics ); @@ -300,9 +359,9 @@ struct FontClient::Plugin bool GetVectorMetrics( GlyphInfo* array, uint32_t size, bool horizontal ); /** - * @copydoc Dali::TextAbstraction::FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool softwareItalic, bool softwareBold, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ) + * @copydoc Dali::TextAbstraction::FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ) */ - void CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool softwareItalic, bool softwareBold, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ); + void CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ); /** * @copydoc Dali::TextAbstraction::FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth ) @@ -325,11 +384,21 @@ struct FontClient::Plugin bool IsColorGlyph( FontId fontId, GlyphIndex glyphIndex ); /** + * @copydoc Dali::TextAbstraction::FontClient::CreateEmbeddedItem() + */ + GlyphIndex CreateEmbeddedItem( const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat ); + + /** * @copydoc Dali::TextAbstraction::Internal::FontClient::GetFreetypeFace() */ FT_FaceRec_* GetFreetypeFace( FontId fontId ); /** + * @copydoc Dali::TextAbstraction::Internal::FontClient::GetFontType() + */ + FontDescription::Type GetFontType( FontId fontId ); + + /** * @copydoc Dali::TextAbstraction::FontClient::AddCustomFontDirectory() */ bool AddCustomFontDirectory( const FontPath& path ); @@ -406,12 +475,23 @@ private: bool cacheDescription ); /** + * @brief Copy the color bitmap given in @p srcBuffer to @p data. + * + * @param[out] data The bitmap data. + * @param[in] srcWidth The width of the bitmap. + * @param[in] srcHeight The height of the bitmap. + * @param[in] srcBuffer The buffer of the bitmap. + */ + void ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer ); + + /** * @brief Copy the FreeType bitmap to the given buffer. * * @param[out] data The bitmap data. * @param[in] srcBitmap The FreeType bitmap. + * @param[in] isShearRequired Whether the bitmap needs a shear transform (for software italics). */ - void ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap ); + void ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired ); /** * @brief Finds in the cache if there is a triplet with the path to the font file name, the font point size and the face index. @@ -464,6 +544,16 @@ private: FontId& fontId ); /** + * @brief Finds in the cache a bitmap font with the @p bitmapFont family name. + * + * @param[in] bitmapFont The font's family name. + * @param[out] fontId The id of the font. + * + * @return Whether the font has been found. + */ + bool FindBitmapFont( const FontFamily& bitmapFont, FontId& fontId ) const; + + /** * @brief Validate a font description. * * @param[in] fontDescription The font to validate. @@ -528,6 +618,7 @@ private: std::vector mFallbackCache; ///< Cached fallback font lists. + Vector mFontIdCache; std::vector mFontFaceCache; ///< Caches the FreeType face and font metrics of the triplet 'path to the font file name, font point size and face index'. std::vector mValidatedFontCache; ///< Caches indices to the vector of font descriptions for a given font. FontList mFontDescriptionCache; ///< Caches font descriptions for the validated font. @@ -537,6 +628,9 @@ private: VectorFontCache* mVectorFontCache; ///< Separate cache for vector data blobs etc. Vector mEllipsisCache; ///< Caches ellipsis glyphs for a particular point size. Vector<_FcPattern*> mMatchedFcPatternCache; ///< Contain matched FcPattern pointer. + std::vector mPixelBufferCache; ///< Caches the pixel buffer of a url. + Vector mEmbeddedItemCache; ///< Cache embedded items. + std::vector mBitmapFontCache; ///< Stores bitmap fonts. bool mDefaultFontDescriptionCached : 1; ///< Whether the default font is cached or not }; diff --git a/dali/internal/text/text-abstraction/shaping-impl.cpp b/dali/internal/text/text-abstraction/shaping-impl.cpp old mode 100644 new mode 100755 index f72475f..2b62286 --- a/dali/internal/text/text-abstraction/shaping-impl.cpp +++ b/dali/internal/text/text-abstraction/shaping-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,15 @@ #include #include +namespace +{ + +#if defined(DEBUG_ENABLED) +Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT"); +#endif + +} + namespace Dali { @@ -136,118 +145,158 @@ struct Shaping::Plugin mOffset.Clear(); mFontId = fontId; - // Reserve some space to avoid reallocations. - const Length numberOfGlyphs = static_cast( 1.3f * static_cast( numberOfCharacters ) ); - mIndices.Reserve( numberOfGlyphs ); - mAdvance.Reserve( numberOfGlyphs ); - mCharacterMap.Reserve( numberOfGlyphs ); - mOffset.Reserve( 2u * numberOfGlyphs ); - TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); TextAbstraction::Internal::FontClient& fontClientImpl = TextAbstraction::GetImplementation( fontClient ); - // Create a FreeType font's face. - FT_Face face; + const FontDescription::Type type = fontClientImpl.GetFontType( fontId ); - face = fontClientImpl.GetFreetypeFace( fontId ); - if( nullptr == face ) + switch( type ) { - // Nothing to do if the face is null. - return 0u; - } + case FontDescription::FACE_FONT: + { + // Reserve some space to avoid reallocations. + const Length numberOfGlyphs = static_cast( 1.3f * static_cast( numberOfCharacters ) ); + mIndices.Reserve( numberOfGlyphs ); + mAdvance.Reserve( numberOfGlyphs ); + mCharacterMap.Reserve( numberOfGlyphs ); + mOffset.Reserve( 2u * numberOfGlyphs ); + + // Retrieve a FreeType font's face. + FT_Face face = fontClientImpl.GetFreetypeFace( fontId ); + if( nullptr == face ) + { + // Nothing to do if the face is null. + return 0u; + } - /* Get our harfbuzz font struct */ - hb_font_t* harfBuzzFont; - harfBuzzFont = hb_ft_font_create( face, NULL ); + unsigned int horizontalDpi = 0u; + unsigned int verticalDpi = 0u; + fontClient.GetDpi( horizontalDpi, verticalDpi ); - /* Create a buffer for harfbuzz to use */ - hb_buffer_t* harfBuzzBuffer = hb_buffer_create(); + FT_Set_Char_Size( face, + 0u, + fontClient.GetPointSize( fontId ), + horizontalDpi, + verticalDpi ); - const bool rtlDirection = IsRightToLeftScript( script ); - hb_buffer_set_direction( harfBuzzBuffer, - rtlDirection ? HB_DIRECTION_RTL : HB_DIRECTION_LTR ); /* or LTR */ + /* Get our harfbuzz font struct */ + hb_font_t* harfBuzzFont; + harfBuzzFont = hb_ft_font_create( face, NULL ); - hb_buffer_set_script( harfBuzzBuffer, - SCRIPT_TO_HARFBUZZ[ script ] ); /* see hb-unicode.h */ + /* Create a buffer for harfbuzz to use */ + hb_buffer_t* harfBuzzBuffer = hb_buffer_create(); + const bool rtlDirection = IsRightToLeftScript( script ); + hb_buffer_set_direction( harfBuzzBuffer, + rtlDirection ? HB_DIRECTION_RTL : HB_DIRECTION_LTR ); /* or LTR */ - char* currentLocale = setlocale(LC_MESSAGES,NULL); + hb_buffer_set_script( harfBuzzBuffer, + SCRIPT_TO_HARFBUZZ[ script ] ); /* see hb-unicode.h */ - std::istringstream stringStream( currentLocale ); - std::string localeString; - std::getline(stringStream, localeString, '_'); - hb_buffer_set_language( harfBuzzBuffer, hb_language_from_string( localeString.c_str(), localeString.size() ) ); - /* Layout the text */ - hb_buffer_add_utf32( harfBuzzBuffer, text, numberOfCharacters, 0u, numberOfCharacters ); + char* currentLocale = setlocale(LC_MESSAGES,NULL); - hb_shape( harfBuzzFont, harfBuzzBuffer, NULL, 0u ); + std::istringstream stringStream( currentLocale ); + std::string localeString; + std::getline(stringStream, localeString, '_'); + hb_buffer_set_language( harfBuzzBuffer, hb_language_from_string( localeString.c_str(), localeString.size() ) ); - /* Get glyph data */ - unsigned int glyphCount; - hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( harfBuzzBuffer, &glyphCount ); - hb_glyph_position_t *glyphPositions = hb_buffer_get_glyph_positions( harfBuzzBuffer, &glyphCount ); - const GlyphIndex lastGlyphIndex = glyphCount - 1u; - for( GlyphIndex i = 0u; i < glyphCount; ) - { - if( rtlDirection ) - { - // If the direction is right to left, Harfbuzz retrieves the glyphs in the visual order. - // The glyphs are needed in the logical order to layout the text in lines. - // Do not change the order of the glyphs if they belong to the same cluster. - GlyphIndex rtlIndex = lastGlyphIndex - i; + /* Layout the text */ + hb_buffer_add_utf32( harfBuzzBuffer, text, numberOfCharacters, 0u, numberOfCharacters ); - unsigned int cluster = glyphInfo[rtlIndex].cluster; - unsigned int previousCluster = cluster; - Length numberOfGlyphsInCluster = 0u; + hb_shape( harfBuzzFont, harfBuzzBuffer, NULL, 0u ); - while( ( cluster == previousCluster ) ) + /* Get glyph data */ + unsigned int glyphCount; + hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( harfBuzzBuffer, &glyphCount ); + hb_glyph_position_t *glyphPositions = hb_buffer_get_glyph_positions( harfBuzzBuffer, &glyphCount ); + const GlyphIndex lastGlyphIndex = glyphCount - 1u; + for( GlyphIndex i = 0u; i < glyphCount; ) + { + if( rtlDirection ) { - ++numberOfGlyphsInCluster; - previousCluster = cluster; + // If the direction is right to left, Harfbuzz retrieves the glyphs in the visual order. + // The glyphs are needed in the logical order to layout the text in lines. + // Do not change the order of the glyphs if they belong to the same cluster. + GlyphIndex rtlIndex = lastGlyphIndex - i; - if( rtlIndex > 0u ) - { - --rtlIndex; + unsigned int cluster = glyphInfo[rtlIndex].cluster; + unsigned int previousCluster = cluster; + Length numberOfGlyphsInCluster = 0u; - cluster = glyphInfo[rtlIndex].cluster; - } - else + while( ( cluster == previousCluster ) ) { - break; + ++numberOfGlyphsInCluster; + previousCluster = cluster; + + if( rtlIndex > 0u ) + { + --rtlIndex; + + cluster = glyphInfo[rtlIndex].cluster; + } + else + { + break; + } } - } - rtlIndex = lastGlyphIndex - ( i + ( numberOfGlyphsInCluster - 1u ) ); + rtlIndex = lastGlyphIndex - ( i + ( numberOfGlyphsInCluster - 1u ) ); - for( GlyphIndex j = 0u; j < numberOfGlyphsInCluster; ++j ) - { - const GlyphIndex index = rtlIndex + j; + for( GlyphIndex j = 0u; j < numberOfGlyphsInCluster; ++j ) + { + const GlyphIndex index = rtlIndex + j; + + mIndices.PushBack( glyphInfo[index].codepoint ); + mAdvance.PushBack( floor( glyphPositions[index].x_advance * FROM_266 ) ); + mCharacterMap.PushBack( glyphInfo[index].cluster ); + mOffset.PushBack( floor( glyphPositions[index].x_offset * FROM_266 ) ); + mOffset.PushBack( floor( glyphPositions[index].y_offset * FROM_266 ) ); + } - mIndices.PushBack( glyphInfo[index].codepoint ); - mAdvance.PushBack( floor( glyphPositions[index].x_advance * FROM_266 ) ); - mCharacterMap.PushBack( glyphInfo[index].cluster ); - mOffset.PushBack( floor( glyphPositions[index].x_offset * FROM_266 ) ); - mOffset.PushBack( floor( glyphPositions[index].y_offset * FROM_266 ) ); + i += numberOfGlyphsInCluster; } + else + { + mIndices.PushBack( glyphInfo[i].codepoint ); + mAdvance.PushBack( floor( glyphPositions[i].x_advance * FROM_266 ) ); + mCharacterMap.PushBack( glyphInfo[i].cluster ); + mOffset.PushBack( floor( glyphPositions[i].x_offset * FROM_266 ) ); + mOffset.PushBack( floor( glyphPositions[i].y_offset * FROM_266 ) ); - i += numberOfGlyphsInCluster; + ++i; + } } - else - { - mIndices.PushBack( glyphInfo[i].codepoint ); - mAdvance.PushBack( floor( glyphPositions[i].x_advance * FROM_266 ) ); - mCharacterMap.PushBack( glyphInfo[i].cluster ); - mOffset.PushBack( floor( glyphPositions[i].x_offset * FROM_266 ) ); - mOffset.PushBack( floor( glyphPositions[i].y_offset * FROM_266 ) ); - ++i; + /* Cleanup */ + hb_buffer_destroy( harfBuzzBuffer ); + hb_font_destroy( harfBuzzFont ); + break; + } + case FontDescription::BITMAP_FONT: + { + // Reserve some space to avoid reallocations. + // The advance and offset tables can be initialized with zeros as it's not needed to get metrics from the bitmaps here. + mIndices.Resize( numberOfCharacters ); + mAdvance.Resize( numberOfCharacters, 0u ); + mCharacterMap.Reserve( numberOfCharacters ); + mOffset.Resize( 2u * numberOfCharacters, 0.f ); + + // The utf32 character can be used as the glyph's index. + std::copy( text, text + numberOfCharacters, mIndices.Begin() ); + + // The glyph to character map is 1 to 1. + for( unsigned int index = 0u; index < numberOfCharacters; ++index ) + { + mCharacterMap.PushBack( index ); } + break; + } + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); + } } - - /* Cleanup */ - hb_buffer_destroy( harfBuzzBuffer ); - hb_font_destroy( harfBuzzFont ); return mIndices.Count(); } diff --git a/dali/internal/text/text-abstraction/text-renderer-impl.cpp b/dali/internal/text/text-abstraction/text-renderer-impl.cpp new file mode 100755 index 0000000..449eaa1 --- /dev/null +++ b/dali/internal/text/text-abstraction/text-renderer-impl.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace TextAbstraction +{ + +namespace Internal +{ + +TextRenderer::TextRenderer() +{ +} + +TextRenderer::~TextRenderer() +{ +} + +TextAbstraction::TextRenderer TextRenderer::Get() +{ + TextAbstraction::TextRenderer shapingHandle; + + SingletonService service(SingletonService::Get()); + if (service) + { + // Check whether the singleton is already created + Dali::BaseHandle handle = service.GetSingleton(typeid(TextAbstraction::TextRenderer)); + if (handle) + { + // If so, downcast the handle + TextRenderer* impl = dynamic_cast< Internal::TextRenderer* >(handle.GetObjectPtr()); + shapingHandle = TextAbstraction::TextRenderer(impl); + } + else // create and register the object + { + shapingHandle = TextAbstraction::TextRenderer(new TextRenderer); + service.Register(typeid(shapingHandle), shapingHandle); + } + } + + return shapingHandle; +} + +Devel::PixelBuffer TextRenderer::Render(const TextAbstraction::TextRenderer::Parameters& parameters) +{ + return RenderTextCairo(parameters); +} + +} // namespace Internal + +} // namespace TextAbstraction + +} // namespace Dali diff --git a/dali/internal/text/text-abstraction/text-renderer-impl.h b/dali/internal/text/text-abstraction/text-renderer-impl.h new file mode 100755 index 0000000..5133ff4 --- /dev/null +++ b/dali/internal/text/text-abstraction/text-renderer-impl.h @@ -0,0 +1,93 @@ +#ifndef DALI_INTERNAL_TEXT_ABSTRACTION_TEXT_RENDERER_IMPL_H +#define DALI_INTERNAL_TEXT_ABSTRACTION_TEXT_RENDERER_IMPL_H + +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace TextAbstraction +{ + +namespace Internal +{ + +/** + * Implementation of the TextRenderer + */ +class TextRenderer : public BaseObject +{ +public: + + /** + * Constructor + */ + TextRenderer(); + + /** + * Destructor + */ + ~TextRenderer(); + + /** + * @copydoc Dali::TextRenderer::Get() + */ + static TextAbstraction::TextRenderer Get(); + + /** + * @copydoc Dali::TextRenderer::Render() + */ + Devel::PixelBuffer Render(const TextAbstraction::TextRenderer::Parameters& parameters); + +private: + + // Undefined copy constructor. + TextRenderer(const TextRenderer&); + + // Undefined assignment constructor. + TextRenderer& operator=(const TextRenderer&); + +}; // class TextRenderer + +} // namespace Internal + +} // namespace TextAbstraction + +inline static TextAbstraction::Internal::TextRenderer& GetImplementation(TextAbstraction::TextRenderer& textRenderer) +{ + DALI_ASSERT_ALWAYS(textRenderer && "textRenderer handle is empty"); + BaseObject& handle = textRenderer.GetBaseObject(); + return static_cast(handle); +} + +inline static const TextAbstraction::Internal::TextRenderer& GetImplementation(const TextAbstraction::TextRenderer& textRenderer) +{ + DALI_ASSERT_ALWAYS(textRenderer && "textRenderer handle is empty"); + const BaseObject& handle = textRenderer.GetBaseObject(); + return static_cast(handle); +} + +} // namespace Dali + +#endif // DALI_INTERNAL_TEXT_ABSTRACTION_TEXT_RENDERER_IMPL_H diff --git a/packaging/dali-adaptor.spec b/packaging/dali-adaptor.spec index fc44496..758d052 100644 --- a/packaging/dali-adaptor.spec +++ b/packaging/dali-adaptor.spec @@ -86,6 +86,7 @@ BuildRequires: pkgconfig(capi-system-info) BuildRequires: pkgconfig(capi-system-sensor) BuildRequires: pkgconfig(libcrypto) +BuildRequires: pkgconfig(cairo) %if %{with wayland}