renderer: introduce a ThorVG Text interface.

Introduced New APIs under the experimental tags.

- Result Text::font(const char* name, float size, const char* style = nullptr);
- Result Text::text(const char* text);
- Result Text::fill(uint8_t r, uint8_t g, uint8_t b);
- static Result Text::load(const std::string& path);
- static Result Text::unload(const std::string& path);
- static Text::std::unique_ptr<Text> gen();
- static Text::uint32_t identifier()

@Issue: https://github.com/thorvg/thorvg/issues/969
This commit is contained in:
Hermet Park 2023-12-21 19:00:04 +09:00 committed by Hermet Park
parent 061222bb59
commit 0f69eef8ed
8 changed files with 439 additions and 1 deletions

View file

@ -1429,6 +1429,138 @@ public:
}; };
/**
* @class Text
*
* @brief A class to represent text objects in a graphical context, allowing for rendering and manipulation of unicode text.
*
* @note Experimental API
*/
class TVG_API Text final : public Paint
{
public:
~Text();
/**
* @brief Sets the font properties for the text.
*
* This function allows you to define the font characteristics used for text rendering.
* It sets the font name, size and optionally the style.
*
* @param[in] name The name of the font. This should correspond to a font available in the canvas.
* @param[in] size The size of the font in points. This determines how large the text will appear.
* @param[in] style The style of the font. It can be used to set the font to 'italic'.
* If not specified, the default style is used. Only 'italic' style is supported currently.
*
* @retval Result::Success when the font properties are set successfully.
* @retval Result::InsufficientCondition when the specified @p name cannot be found.
*
* @note Experimental API
*/
Result font(const char* name, float size, const char* style = nullptr) noexcept;
/**
* @brief Assigns the given unicode text to be rendered.
*
* This function sets the unicode string that will be displayed by the rendering system.
* The text is set according to the specified UTF encoding method, which defaults to UTF-8.
*
* @param[in] text The multi-byte text encoded with utf8 string to be rendered.
*
* @retval Result::Success when succeed.
*
* @note Experimental API
*/
Result text(const char* text) noexcept;
/**
* @brief Sets the text color.
*
* @param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0.
* @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0.
* @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0.
*
* @retval Result::Success when succeed.
* @retval Result::InsufficientCondition when the font has not been set up prior to this operation.
*
* @see Text::font()
*
* @note Experimental API
*/
Result fill(uint8_t r, uint8_t g, uint8_t b) noexcept;
/**
* @brief Sets the gradient fill for all of the figures from the text.
*
* The parts of the text defined as inner are filled.
*
* @param[in] f The unique pointer to the gradient fill.
*
* @retval Result::Success when succeed, Result::MemoryCorruption otherwise.
* @retval Result::InsufficientCondition when the font has not been set up prior to this operation.
*
* @note Either a solid color or a gradient fill is applied, depending on what was set as last.
* @note Experimental API
*
* @see Text::font()
*/
Result fill(std::unique_ptr<Fill> f) noexcept;
/**
* @brief Loads a scalable font data(ttf) from a file.
*
* @param[in] path The path to the font file.
*
* @retval Result::Success When succeed.
* @retval Result::InvalidArguments In case the @p path is invalid.
* @retval Result::NonSupport When trying to load a file with an unknown extension.
* @retval Result::Unknown If an error occurs at a later stage.
*
* @note Experimental API
*
* @see Text::unload(const std::string& path)
*/
static Result load(const std::string& path) noexcept;
/**
* @brief Unloads the specified scalable font data (TTF) that was previously loaded.
*
* This function is used to release resources associated with a font file that has been loaded into memory.
*
* @param[in] path The file path of the loaded font.
*
* @retval Result::Success Successfully unloads the font data.
* @retval Result::InsufficientCondition Fails if the loader is not initialized.
*
* @note If the font data is currently in use, it will not be immediately unloaded.
* @note Experimental API
*
* @see Text::load(const std::string& path)
*/
static Result unload(const std::string& path) noexcept;
/**
* @brief Creates a new Text object.
*
* @return A new Text object.
*
* @note Experimental API
*/
static std::unique_ptr<Text> gen() noexcept;
/**
* @brief Return the unique id value of this class.
*
* This method can be referred for identifying the Text class type.
*
* @return The type id of the Text class.
*/
static uint32_t identifier() noexcept;
_TVG_DECLARE_PRIVATE(Text);
};
/** /**
* @class SwCanvas * @class SwCanvas
* *

View file

@ -171,7 +171,7 @@ bool TtfLoader::resize(Paint* paint, float sx, TVG_UNUSED float sy)
if (!paint) return false; if (!paint) return false;
auto shift = 0.0f; auto shift = 0.0f;
auto dpi = 96.0f / 72.0f; //dpi base? auto dpi = 96.0f / 72.0f; //dpi base?
auto scale = sx * dpi / reader.metrics.unitsPerEm; scale = sx * dpi / reader.metrics.unitsPerEm;
if (italic) shift = -scale * 0.18f; //experimental decision. if (italic) shift = -scale * 0.18f; //experimental decision.
Matrix m = {scale, shift, -(shift * reader.metrics.minw), 0, scale, 0, 0, 0, 1}; Matrix m = {scale, shift, -(shift * reader.metrics.minw), 0, scale, 0, 0, 0, 1};
paint->transform(m); paint->transform(m);

View file

@ -26,6 +26,7 @@ source_file = [
'tvgScene.h', 'tvgScene.h',
'tvgShape.h', 'tvgShape.h',
'tvgTaskScheduler.h', 'tvgTaskScheduler.h',
'tvgText.h',
'tvgAccessor.cpp', 'tvgAccessor.cpp',
'tvgAnimation.cpp', 'tvgAnimation.cpp',
'tvgCanvas.cpp', 'tvgCanvas.cpp',
@ -41,6 +42,7 @@ source_file = [
'tvgShape.cpp', 'tvgShape.cpp',
'tvgSwCanvas.cpp', 'tvgSwCanvas.cpp',
'tvgTaskScheduler.cpp', 'tvgTaskScheduler.cpp',
'tvgText.cpp'
] ]
common_dep = declare_dependency( common_dep = declare_dependency(

View file

@ -61,6 +61,7 @@ using namespace tvg;
#define TVG_CLASS_ID_PICTURE 3 #define TVG_CLASS_ID_PICTURE 3
#define TVG_CLASS_ID_LINEAR 4 #define TVG_CLASS_ID_LINEAR 4
#define TVG_CLASS_ID_RADIAL 5 #define TVG_CLASS_ID_RADIAL 5
#define TVG_CLASS_ID_TEXT 6
enum class FileType { Tvg = 0, Svg, Ttf, Lottie, Raw, Png, Jpg, Webp, Gif, Unknown }; enum class FileType { Tvg = 0, Svg, Ttf, Lottie, Raw, Png, Jpg, Webp, Gif, Unknown };

View file

@ -81,6 +81,8 @@ struct ImageLoader : LoadModule
struct FontLoader : LoadModule struct FontLoader : LoadModule
{ {
float scale = 1.0f;
FontLoader(FileType type) : LoadModule(type) {} FontLoader(FileType type) : LoadModule(type) {}
virtual bool request(Shape* shape, char* text, bool italic = false) = 0; virtual bool request(Shape* shape, char* text, bool italic = false) = 0;

View file

@ -25,6 +25,7 @@
#include "tvgShape.h" #include "tvgShape.h"
#include "tvgPicture.h" #include "tvgPicture.h"
#include "tvgScene.h" #include "tvgScene.h"
#include "tvgText.h"
/************************************************************************/ /************************************************************************/
/* Internal Class Implementation */ /* Internal Class Implementation */
@ -35,6 +36,7 @@
case TVG_CLASS_ID_SHAPE: ret = P((Shape*)paint)->METHOD; break; \ case TVG_CLASS_ID_SHAPE: ret = P((Shape*)paint)->METHOD; break; \
case TVG_CLASS_ID_SCENE: ret = P((Scene*)paint)->METHOD; break; \ case TVG_CLASS_ID_SCENE: ret = P((Scene*)paint)->METHOD; break; \
case TVG_CLASS_ID_PICTURE: ret = P((Picture*)paint)->METHOD; break; \ case TVG_CLASS_ID_PICTURE: ret = P((Picture*)paint)->METHOD; break; \
case TVG_CLASS_ID_TEXT: ret = P((Text*)paint)->METHOD; break; \
default: ret = {}; \ default: ret = {}; \
} }

109
src/renderer/tvgText.cpp Normal file
View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgText.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Text::Text() : pImpl(new Impl)
{
Paint::pImpl->id = TVG_CLASS_ID_TEXT;
}
Text::~Text()
{
delete(pImpl);
}
Result Text::text(const char* text) noexcept
{
return pImpl->text(text);
}
Result Text::font(const char* name, float size, const char* style) noexcept
{
return pImpl->font(name, size, style);
}
Result Text::load(const std::string& path) noexcept
{
bool invalid; //invalid path
if (!LoaderMgr::loader(path, &invalid)) {
if (invalid) return Result::InvalidArguments;
else return Result::NonSupport;
}
return Result::Success;
}
Result Text::unload(const std::string& path) noexcept
{
if (LoaderMgr::retrieve(path)) return Result::Success;
return Result::InsufficientCondition;
}
Result Text::fill(uint8_t r, uint8_t g, uint8_t b) noexcept
{
if (!pImpl->paint) return Result::InsufficientCondition;
return pImpl->fill(r, g, b);
}
Result Text::fill(unique_ptr<Fill> f) noexcept
{
if (!pImpl->paint) return Result::InsufficientCondition;
auto p = f.release();
if (!p) return Result::MemoryCorruption;
return pImpl->fill(p);
}
unique_ptr<Text> Text::gen() noexcept
{
return unique_ptr<Text>(new Text);
}
uint32_t Text::identifier() noexcept
{
return TVG_CLASS_ID_TEXT;
}

190
src/renderer/tvgText.h Normal file
View file

@ -0,0 +1,190 @@
/*
* Copyright (c) 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_TEXT_H
#define _TVG_TEXT_H
#include <cstring>
#include "tvgShape.h"
#include "tvgFill.h"
#ifdef THORVG_TTF_LOADER_SUPPORT
#include "tvgTtfLoader.h"
#else
#include "tvgLoader.h"
#endif
struct Text::Impl
{
RenderData rd = nullptr;
FontLoader* loader = nullptr;
Shape* paint = nullptr;
char* utf8 = nullptr;
float fontSize;
bool italic = false;
bool changed = false;
~Impl()
{
LoaderMgr::retrieve(loader);
delete(paint);
}
Result fill(uint8_t r, uint8_t g, uint8_t b)
{
return paint->fill(r, g, b);
}
Result fill(Fill* f)
{
return paint->fill(cast<Fill>(f));
}
Result text(const char* utf8)
{
free(this->utf8);
if (utf8) this->utf8 = strdup(utf8);
else this->utf8 = nullptr;
changed = true;
return Result::Success;
}
Result font(const char* name, float size, const char* style)
{
auto loader = LoaderMgr::loader(name);
if (!loader) return Result::InsufficientCondition;
//Same resource has been loaded.
if (this->loader == loader) {
this->loader->sharing--; //make it sure the reference counting.
return Result::Success;
} else if (this->loader) {
LoaderMgr::retrieve(this->loader);
}
this->loader = static_cast<FontLoader*>(loader);
if (!paint) paint = Shape::gen().release();
fontSize = size;
if (style && strstr(style, "italic")) italic = true;
changed = true;
return Result::Success;
}
RenderRegion bounds(RenderMethod& renderer)
{
return renderer.region(rd);
}
bool render(RenderMethod& renderer)
{
if (paint) return PP(paint)->render(renderer);
return false;
}
bool load()
{
if (!loader) return false;
//reload
if (changed) {
loader->request(paint, utf8, italic);
loader->read();
changed = false;
}
if (paint) {
loader->resize(paint, fontSize, fontSize);
return true;
}
return false;
}
RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
{
if (!load()) return nullptr;
//transform the gradient coordinates based on the final scaled font.
if (P(paint)->flag & RenderUpdateFlag::Gradient) {
auto fill = P(paint)->rs.fill;
auto scale = 1.0f / loader->scale;
if (fill->identifier() == TVG_CLASS_ID_LINEAR) {
P(static_cast<LinearGradient*>(fill))->x1 *= scale;
P(static_cast<LinearGradient*>(fill))->y1 *= scale;
P(static_cast<LinearGradient*>(fill))->x2 *= scale;
P(static_cast<LinearGradient*>(fill))->y2 *= scale;
} else {
P(static_cast<RadialGradient*>(fill))->cx *= scale;
P(static_cast<RadialGradient*>(fill))->cy *= scale;
P(static_cast<RadialGradient*>(fill))->r *= scale;
P(static_cast<RadialGradient*>(fill))->fx *= scale;
P(static_cast<RadialGradient*>(fill))->fy *= scale;
P(static_cast<RadialGradient*>(fill))->fr *= scale;
}
}
rd = PP(paint)->update(renderer, transform, clips, opacity, pFlag, clipper);
return rd;
}
bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking)
{
if (!load() || !paint) return false;
paint->bounds(x, y, w, h, true);
return true;
}
bool dispose(RenderMethod& renderer)
{
renderer.dispose(rd);
this->rd = nullptr;
return true;
}
Paint* duplicate()
{
load();
auto ret = Text::gen().release();
auto dup = ret->pImpl;
if (paint) dup->paint = static_cast<Shape*>(paint->duplicate());
if (loader) {
dup->loader = loader;
++dup->loader->sharing;
}
dup->utf8 = strdup(utf8);
dup->italic = italic;
dup->fontSize = fontSize;
return ret;
}
Iterator* iterator()
{
return nullptr;
}
};
#endif //_TVG_TEXT_H