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
$(OPENGLES20_CFLAGS) \
$(FREETYPE_CFLAGS) \
$(FONTCONFIG_CFLAGS) \
+ $(CAIRO_CFLAGS) \
$(PNG_CFLAGS) \
$(DLOG_CFLAGS) \
$(VCONF_CFLAGS) \
$(OPENGLES20_LIBS) \
$(FREETYPE_LIBS) \
$(FONTCONFIG_LIBS) \
+ $(CAIRO_LIBS) \
$(PNG_LIBS) \
$(DLOG_LIBS) \
$(VCONF_LIBS) \
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]) ]
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 );
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
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 \
$(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
--- /dev/null
+/*
+ * 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 <dali/devel-api/text-abstraction/bitmap-font.h>
+
+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
+
--- /dev/null
+#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 <dali/public-api/common/dali-common.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <dali/public-api/dali-adaptor-common.h>
+#include <string>
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
+
+
+
+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<BitmapGlyph> 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
/*
- * 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.
{
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 ),
faceIndex );
}
+FontId FontClient::GetFontId( const BitmapFont& bitmapFont )
+{
+ return GetImplementation(*this).GetFontId( bitmapFont );
+}
+
bool FontClient::IsScalable( const FontPath& path )
{
return GetImplementation(*this).IsScalable( path );
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 );
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 )
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 )
{
#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.
struct FontMetrics;
struct GlyphInfo;
+struct BitmapFont;
namespace Internal DALI_INTERNAL
{
{
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.
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:
/**
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.
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.
////////////////////////////////////////
*
* @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.
*/
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().
-#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.
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()
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<FontDescription> FontList;
} // namespace Dali
-#endif // __DALI_TEXT_ABSTRACTION_FONT_LIST_H__
+#endif // DALI_TEXT_ABSTRACTION_FONT_LIST_H
{
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 }
{
}
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 }
{
}
-#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.
float descenderPixels,
float heightPixels,
float underlinePositionPixels,
- float underlinePositionThickness );
+ float underlineThicknessPixels );
float ascender; ///< The ascender in pixels.
float descender; ///< The descender in pixels.
} // TextAbstraction
-#endif //__DALI_TEXT_ABSTRACTION_FONT_METRICS_H__
+#endif //DALI_TEXT_ABSTRACTION_FONT_METRICS_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.
{
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 }
{
}
-#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.
*/
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
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.
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
-#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.
*/
#include <dali/devel-api/text-abstraction/bidirectional-support.h>
+#include <dali/devel-api/text-abstraction/bitmap-font.h>
#include <dali/devel-api/text-abstraction/font-client.h>
#include <dali/devel-api/text-abstraction/font-metrics.h>
#include <dali/devel-api/text-abstraction/glyph-info.h>
#include <dali/devel-api/text-abstraction/segmentation.h>
#include <dali/devel-api/text-abstraction/shaping.h>
-#endif //__DALI_TEXT_ABSTRACTION_H__
+#endif // DALI_TEXT_ABSTRACTION_H
--- /dev/null
+/*
+ * 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 <dali/devel-api/text-abstraction/text-renderer-layout-helper.h>
+
+// EXTERNAL INCLUDES
+#include <cmath>
+
+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
--- /dev/null
+#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 <dali/public-api/common/dali-common.h>
+#include <dali/public-api/dali-adaptor-common.h>
+
+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
--- /dev/null
+/*
+* 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 <dali/devel-api/text-abstraction/text-renderer.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/text/text-abstraction/text-renderer-impl.h>
+
+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
--- /dev/null
+#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 <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/object/base-handle.h>
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/glyph-info.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+
+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<GlyphInfo>& glyphs,
+ Vector<Vector2>& positions,
+ Vector<Vector4>& colors,
+ Vector<ColorIndex>& colorIndices,
+ Vector<ColorBlendingMode>& blendingMode,
+ Vector<bool>& 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<GlyphInfo>& glyphs; ///< The glyphs to be rendered.
+ Vector<Vector2>& positions; ///< The position for each glyph.
+ Vector<Vector4>& colors; ///< Colors of the glyphs.
+ Vector<ColorIndex>& colorIndices; ///< Indices to the vector of colors for each glyphs.
+ Vector<ColorBlendingMode>& blendingMode; ///< How each glyph is going to be blended with the color of the text.
+ Vector<bool>& 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
/*
- * 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.
if( !fastRotationPerformed )
{
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "fast rotation failed\n");
// The fast rotation failed.
return;
}
if( !fastRotationPerformed )
{
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "fast rotation failed\n");
// The fast rotation failed.
return;
}
if( !fastRotationPerformed )
{
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "fast rotation failed\n");
// The fast rotation failed.
return;
}
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;
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;
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;
// @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<unsigned int>( absRadians * static_cast<float>( heightIn ) );
+ heightOut = heightIn;
+
+ // Allocate the buffer for the shear.
+ pixelsOut = static_cast<uint8_t*>( 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<float>( y ) ) : ( 0.5f + static_cast<float>( y ) - static_cast<float>( heightOut ) ) );
+
+ const int intShear = static_cast<int>( floor( shear ) );
+ HorizontalSkew( pixelsIn, widthIn, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast<float>( intShear ) );
+ }
+}
+
} /* namespace Platform */
} /* namespace Internal */
} /* namespace Dali */
/*
- * 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.
*
*/
-#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 <stdint.h>
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 );
+
/**@}*/
/**
} /* namespace Internal */
} /* namespace Dali */
-#endif /* DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H_ */
+#endif /* DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H */
return mBuffer;
}
+const unsigned char* const PixelBuffer::GetConstBuffer() const
+{
+ return mBuffer;
+}
+
unsigned int PixelBuffer::GetBufferSize() const
{
return mBufferSize;
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
*/
# 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
--- /dev/null
+/*
+ * 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 <dali/internal/text/text-abstraction/cairo-renderer.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/common/constants.h>
+#include <cairo.h>
+#include <cairo-ft.h>
+#include <cstring>
+#include <memory>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+#include FT_OUTLINE_H
+#include FT_STROKER_H
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/devel-api/text-abstraction/text-renderer-layout-helper.h>
+#include <dali/integration-api/debug.h>
+#include <dali/internal/imaging/common/image-operations.h>
+#include <dali/internal/text/text-abstraction/font-client-impl.h>
+
+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<cairo_path_t, void(*)(cairo_path_t*)> 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<cairo_glyph_t> 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<GlyphRun> 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<double>( 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<double>( 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<int>( 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<cairo_surface_t, void(*)(cairo_surface_t*)> 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<cairo_surface_t, void(*)(cairo_surface_t*)> 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<cairo_t, void(*)(cairo_t*)> 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<cairo_t, void(*)(cairo_t*)> 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<double>( parameters.centerX );
+ circularTextParameters.centerY = static_cast<double>( parameters.centerY );
+ circularTextParameters.radius = static_cast<double>( 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<GlyphBuffer> 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<double>( data.width );
+ const double halfHeight = 0.5 * static_cast<double>( 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<double>( data.width );
+ glyphY = centerY + 0.5 * static_cast<double>( 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<float>( data.width ) < 0.f ) ||
+ ( glyphX > static_cast<float>( strideWidth ) ) ||
+ ( glyphY < 0.f ) ||
+ ( glyphY - static_cast<float>( data.height ) > static_cast<float>( 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<unsigned int>( std::abs( glyphX ) );
+ srcWidth -= xSrcIndex;
+ }
+ else
+ {
+ glyphUintX = static_cast<unsigned int>( glyphX );
+ }
+
+ if( glyphUintX + srcWidth > static_cast<unsigned int>( strideWidth ) )
+ {
+ srcWidth -= ( ( glyphUintX + srcWidth ) - strideWidth );
+ }
+
+ if( glyphY - static_cast<float>( srcHeight ) < 0.f )
+ {
+ ySrcIndex = static_cast<unsigned int>( std::abs( glyphY - static_cast<float>( srcHeight ) ) );
+ srcHeight -= ySrcIndex;
+ }
+ else
+ {
+ glyphUintY = static_cast<unsigned int>( glyphY - static_cast<float>( 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<float>( alpha );
+
+ // Write the RGBA modulated with the given default color.
+ const float* const colorPtr = color.AsFloat();
+ *( buffer + dstIndex + 0u ) = static_cast<unsigned char>( TO_UCHAR * colorPtr[0u] * srcAlpha );
+ *( buffer + dstIndex + 1u ) = static_cast<unsigned char>( TO_UCHAR * colorPtr[1u] * srcAlpha );
+ *( buffer + dstIndex + 2u ) = static_cast<unsigned char>( TO_UCHAR * colorPtr[2u] * srcAlpha );
+ *( buffer + dstIndex + 3u ) = static_cast<unsigned char>( 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<float>(alpha) * colorPtr[3u];
+
+ *(buffer + dstIndex + 0u) = static_cast<unsigned char>( static_cast<float>( *(data.buffer + srcIndex + 0u) ) * colorPtr[0u] );
+ *(buffer + dstIndex + 1u) = static_cast<unsigned char>( static_cast<float>( *(data.buffer + srcIndex + 1u) ) * colorPtr[1u] );
+ *(buffer + dstIndex + 2u) = static_cast<unsigned char>( static_cast<float>( *(data.buffer + srcIndex + 2u) ) * colorPtr[2u] );
+
+ // Write the alpha.
+ *(buffer + dstIndex + 3u) = static_cast<unsigned char>( 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<float>(alpha) * colorPtr[3u];
+
+ *(buffer + dstIndex + 0u) = static_cast<unsigned char>( static_cast<float>( *(data.buffer + srcIndex + 2u) ) * colorPtr[0u] );
+ *(buffer + dstIndex + 1u) = static_cast<unsigned char>( static_cast<float>( *(data.buffer + srcIndex + 1u) ) * colorPtr[1u] );
+ *(buffer + dstIndex + 2u) = static_cast<unsigned char>( static_cast<float>( *(data.buffer + srcIndex + 0u) ) * colorPtr[2u] );
+
+ // Write the alpha.
+ *(buffer + dstIndex + 3u) = static_cast<unsigned char>( 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<double>( color.b ),
+ static_cast<double>( color.g ),
+ static_cast<double>( color.r ),
+ static_cast<double>( color.a ) );
+
+ // Create the Cairo's font from the FreeType font.
+ std::unique_ptr<cairo_font_face_t, void(*)(cairo_font_face_t*)> 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<cairo_destroy_func_t>( 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
--- /dev/null
+#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 <dali/devel-api/text-abstraction/text-renderer.h>
+
+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
+
/*
- * 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.
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();
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 )
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 )
{
CreatePlugin();
- return mPlugin->CreateVectorBlob( fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
+ mPlugin->CreateVectorBlob( fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
}
const GlyphInfo& FontClient::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
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();
return mPlugin->GetFreetypeFace( fontId );
}
+FontDescription::Type FontClient::GetFontType( FontId fontId )
+{
+ CreatePlugin();
+
+ return mPlugin->GetFontType( fontId );
+}
+
bool FontClient::AddCustomFontDirectory( const FontPath& path )
{
CreatePlugin();
#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.
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 );
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 );
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 )
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.
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 );
#include <dali/internal/text/text-abstraction/font-client-helper.h>
#include <dali/internal/imaging/common/image-operations.h>
#include <dali/internal/adaptor/common/adaptor-impl.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
// EXTERNAL INCLUDES
#include <fontconfig/fontconfig.h>
*/
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" );
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 )
{
PointSize26Dot6 requestedPointSize,
FaceIndex face,
const FontMetrics& metrics,
+ int fixedSizeIndex,
float fixedWidth,
float fixedHeight,
bool hasColorTables )
mFaceIndex( face ),
mMetrics( metrics ),
mCharacterSet( nullptr ),
+ mFixedSizeIndex( fixedSizeIndex ),
mFixedWidthPixels( fixedWidth ),
mFixedHeightPixels( fixedHeight ),
- mVectorFontId( 0 ),
+ mVectorFontId( 0u ),
+ mFontId( 0u ),
mIsFixedSizeBitmap( true ),
mHasColorTables( hasColorTables )
{
mDefaultFontDescription(),
mSystemFonts(),
mDefaultFonts(),
+ mFontIdCache(),
mFontFaceCache(),
mValidatedFontCache(),
mFontDescriptionCache( 1u ),
mFontDescriptionSizeCache(),
mVectorFontCache( nullptr ),
mEllipsisCache(),
+ mEmbeddedItemCache(),
mDefaultFontDescriptionCached( false )
{
mCharacterSetCache.Resize( 1u );
{
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();
+ }
}
}
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
{
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");
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;
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;
}
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;
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.
// 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,
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 );
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 );
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<float>(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 )
{
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 );
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<float>( 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<float>( 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<float>( 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");
}
}
}
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,
{
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<float>( 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<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
- if( desiredFixedSize > 0.f )
- {
- const float scaleFactor = desiredFixedSize / static_cast<float>( 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<float>( item.width );
+ glyph.height = static_cast<float>( item.height );
+ glyph.xBearing = 0.f;
+ glyph.yBearing = glyph.height;
+ glyph.advance = glyph.width;
+ glyph.scaleFactor = 1.f;
+ }
+ else
+ {
+ success = false;
+ }
}
}
#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
bool success( true );
- for( unsigned int i=0; i<size; ++i )
+ for( unsigned int i = 0u; i < size; ++i )
{
FontId fontId = array[i].fontId;
- if( fontId > 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 )
{
#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.
+ }
}
}
}
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
}
// 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.
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 );
{
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
{
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<const FcChar8 *>( 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<unsigned int>( pixelBuffer.GetWidth() );
+ }
+
+ if( 0u == embeddedItem.height )
+ {
+ embeddedItem.height = static_cast<unsigned int>( 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" );
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" ) );
}
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<float>( ftFace->available_sizes[ fixedSizeIndex ].width );
+ const float fixedHeight = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].height );
// Indicate that the font is a fixed sized bitmap
FontMetrics metrics( fixedHeight, // The ascender in pixels.
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
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
{
}
}
- if( 0u != id )
+ if( 0u != fontFaceId )
{
if( cacheDescription )
{
- CacheFontPath( ftFace, id, requestedPointSize, path );
+ CacheFontPath( ftFace, fontFaceId, requestedPointSize, 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 )
{
{
if( srcBitmap.pitch == static_cast<int>( 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<uint8_t, void(*)(void*)> 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;
}
{
if( srcBitmap.pitch == static_cast<int>( 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;
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" );
DALI_LOG_INFO( gLogFilter, Debug::General, " font not found\n" );
DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
- fontId = 0u;
return false;
}
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;
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;
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 );
// Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
requestedPointSize,
- id ) );
+ fontFaceId ) );
}
}
*/
// INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/bitmap-font.h>
#include <dali/devel-api/text-abstraction/font-metrics.h>
#include <dali/devel-api/text-abstraction/glyph-info.h>
#include <dali/internal/text/text-abstraction/font-client-impl.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
#include <dali/internal/text/glyphy/vector-font-cache.h>
{
/**
- *@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;
/**
*/
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
*/
PointSize26Dot6 requestedPointSize,
FaceIndex face,
const FontMetrics& metrics,
+ int fixedSizeIndex,
float fixedWidth,
float fixedHeight,
bool hasColorTables );
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.
};
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<Devel::PixelBuffer> pixelBuffers; ///< The pixel buffers of the glyphs.
+ FontId id; ///< Index to the vector with the cache of font's ids.
+ };
+
/**
* Constructor.
*
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 );
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 );
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 )
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 );
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.
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.
std::vector<FallbackCacheItem> mFallbackCache; ///< Cached fallback font lists.
+ Vector<FontIdCacheItem> mFontIdCache;
std::vector<FontFaceCacheItem> 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<FontDescriptionCacheItem> mValidatedFontCache; ///< Caches indices to the vector of font descriptions for a given font.
FontList mFontDescriptionCache; ///< Caches font descriptions for the validated font.
VectorFontCache* mVectorFontCache; ///< Separate cache for vector data blobs etc.
Vector<EllipsisItem> mEllipsisCache; ///< Caches ellipsis glyphs for a particular point size.
Vector<_FcPattern*> mMatchedFcPatternCache; ///< Contain matched FcPattern pointer.
+ std::vector<PixelBufferCacheItem> mPixelBufferCache; ///< Caches the pixel buffer of a url.
+ Vector<EmbeddedItem> mEmbeddedItemCache; ///< Cache embedded items.
+ std::vector<BitmapFontCacheItem> mBitmapFontCache; ///< Stores bitmap fonts.
bool mDefaultFontDescriptionCached : 1; ///< Whether the default font is cached or not
};
/*
- * 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.
#include <harfbuzz/hb.h>
#include <harfbuzz/hb-ft.h>
+namespace
+{
+
+#if defined(DEBUG_ENABLED)
+Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
+#endif
+
+}
+
namespace Dali
{
mOffset.Clear();
mFontId = fontId;
- // Reserve some space to avoid reallocations.
- const Length numberOfGlyphs = static_cast<Length>( 1.3f * static_cast<float>( 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<Length>( 1.3f * static_cast<float>( 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();
}
--- /dev/null
+/*
+ * 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 <dali/internal/text/text-abstraction/text-renderer-impl.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/system/common/singleton-service-impl.h>
+#include <dali/internal/text/text-abstraction/cairo-renderer.h>
+
+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
--- /dev/null
+#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 <dali/public-api/object/base-object.h>
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/text-renderer.h>
+
+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<TextAbstraction::Internal::TextRenderer&>(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<const TextAbstraction::Internal::TextRenderer&>(handle);
+}
+
+} // namespace Dali
+
+#endif // DALI_INTERNAL_TEXT_ABSTRACTION_TEXT_RENDERER_IMPL_H
BuildRequires: pkgconfig(capi-system-sensor)
BuildRequires: pkgconfig(libcrypto)
+BuildRequires: pkgconfig(cairo)
%if %{with wayland}