1 /*
2  * Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved.
3 
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10 
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13 
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 
23 #include "../../lv_conf_internal.h"
24 #if LV_USE_THORVG_INTERNAL
25 
26 #ifndef _TVG_TEXT_H
27 #define _TVG_TEXT_H
28 
29 #include <cstring>
30 #include "tvgShape.h"
31 #include "tvgFill.h"
32 #include "tvgLoader.h"
33 
34 struct Text::Impl
35 {
36     FontLoader* loader = nullptr;
37     Text* paint;
38     Shape* shape;
39     char* utf8 = nullptr;
40     float fontSize;
41     bool italic = false;
42     bool changed = false;
43 
ImplImpl44     Impl(Text* p) : paint(p), shape(Shape::gen().release())
45     {
46     }
47 
~ImplImpl48     ~Impl()
49     {
50         free(utf8);
51         LoaderMgr::retrieve(loader);
52         delete(shape);
53     }
54 
textImpl55     Result text(const char* utf8)
56     {
57         free(this->utf8);
58         if (utf8) this->utf8 = strdup(utf8);
59         else this->utf8 = nullptr;
60         changed = true;
61 
62         return Result::Success;
63     }
64 
fontImpl65     Result font(const char* name, float size, const char* style)
66     {
67         auto loader = LoaderMgr::loader(name);
68         if (!loader) return Result::InsufficientCondition;
69 
70         if (style && strstr(style, "italic")) italic = true;
71         else italic = false;
72 
73         fontSize = size;
74 
75         //Same resource has been loaded.
76         if (this->loader == loader) {
77             this->loader->sharing--;  //make it sure the reference counting.
78             return Result::Success;
79         } else if (this->loader) {
80             LoaderMgr::retrieve(this->loader);
81         }
82         this->loader = static_cast<FontLoader*>(loader);
83 
84         changed = true;
85         return Result::Success;
86     }
87 
boundsImpl88     RenderRegion bounds(RenderMethod* renderer)
89     {
90         return P(shape)->bounds(renderer);
91     }
92 
renderImpl93     bool render(RenderMethod* renderer)
94     {
95         if (!loader) return true;
96         renderer->blend(PP(paint)->blendMethod);
97         return PP(shape)->render(renderer);
98     }
99 
loadImpl100     bool load()
101     {
102         if (!loader) return false;
103 
104         loader->request(shape, utf8);
105         //reload
106         if (changed) {
107             loader->read();
108             changed = false;
109         }
110         return loader->transform(shape, fontSize, italic);
111     }
112 
updateImpl113     RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper)
114     {
115         if (!load()) return nullptr;
116 
117         //transform the gradient coordinates based on the final scaled font.
118         auto fill = P(shape)->rs.fill;
119         if (fill && P(shape)->flag & RenderUpdateFlag::Gradient) {
120             auto scale = 1.0f / loader->scale;
121             if (fill->type() == Type::LinearGradient) {
122                 P(static_cast<LinearGradient*>(fill))->x1 *= scale;
123                 P(static_cast<LinearGradient*>(fill))->y1 *= scale;
124                 P(static_cast<LinearGradient*>(fill))->x2 *= scale;
125                 P(static_cast<LinearGradient*>(fill))->y2 *= scale;
126             } else {
127                 P(static_cast<RadialGradient*>(fill))->cx *= scale;
128                 P(static_cast<RadialGradient*>(fill))->cy *= scale;
129                 P(static_cast<RadialGradient*>(fill))->r *= scale;
130                 P(static_cast<RadialGradient*>(fill))->fx *= scale;
131                 P(static_cast<RadialGradient*>(fill))->fy *= scale;
132                 P(static_cast<RadialGradient*>(fill))->fr *= scale;
133             }
134         }
135         return PP(shape)->update(renderer, transform, clips, opacity, pFlag, false);
136     }
137 
boundsImpl138     bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking)
139     {
140         if (!load()) return false;
141         PP(shape)->bounds(x, y, w, h, true, true, false);
142         return true;
143     }
144 
duplicateImpl145     Paint* duplicate(Paint* ret)
146     {
147         if (ret) TVGERR("RENDERER", "TODO: duplicate()");
148 
149         load();
150 
151         auto text = Text::gen().release();
152         auto dup = text->pImpl;
153         P(shape)->duplicate(dup->shape);
154 
155         if (loader) {
156             dup->loader = loader;
157             ++dup->loader->sharing;
158         }
159 
160         dup->utf8 = strdup(utf8);
161         dup->italic = italic;
162         dup->fontSize = fontSize;
163 
164         return text;
165     }
166 
iteratorImpl167     Iterator* iterator()
168     {
169         return nullptr;
170     }
171 };
172 
173 
174 
175 #endif //_TVG_TEXT_H
176 
177 #endif /* LV_USE_THORVG_INTERNAL */
178 
179