1 /*
2  * Copyright (c) 2020 - 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_SHAPE_H_
27 #define _TVG_SHAPE_H_
28 
29 #include <memory.h>
30 #include "tvgMath.h"
31 #include "tvgPaint.h"
32 
33 
34 struct Shape::Impl
35 {
36     RenderShape rs;                     //shape data
37     RenderData rd = nullptr;            //engine data
38     Shape* shape;
39     uint8_t flag = RenderUpdateFlag::None;
40 
41     uint8_t opacity;                    //for composition
42     bool needComp = false;              //composite or not
43 
ImplImpl44     Impl(Shape* s) : shape(s)
45     {
46     }
47 
~ImplImpl48     ~Impl()
49     {
50         if (auto renderer = PP(shape)->renderer) {
51             renderer->dispose(rd);
52         }
53     }
54 
renderImpl55     bool render(RenderMethod* renderer)
56     {
57         if (!rd) return false;
58 
59         RenderCompositor* cmp = nullptr;
60 
61         renderer->blend(PP(shape)->blendMethod);
62 
63         if (needComp) {
64             cmp = renderer->target(bounds(renderer), renderer->colorSpace());
65             renderer->beginComposite(cmp, CompositeMethod::None, opacity);
66         }
67 
68         auto ret = renderer->renderShape(rd);
69         if (cmp) renderer->endComposite(cmp);
70         return ret;
71     }
72 
needCompositionImpl73     bool needComposition(uint8_t opacity)
74     {
75         if (opacity == 0) return false;
76 
77         //Shape composition is only necessary when stroking & fill are valid.
78         if (!rs.stroke || rs.stroke->width < FLOAT_EPSILON || (!rs.stroke->fill && rs.stroke->color[3] == 0)) return false;
79         if (!rs.fill && rs.color[3] == 0) return false;
80 
81         //translucent fill & stroke
82         if (opacity < 255) return true;
83 
84         //Composition test
85         const Paint* target;
86         auto method = shape->composite(&target);
87         if (!target || method == CompositeMethod::ClipPath) return false;
88         if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) {
89             if (target->type() == Type::Shape) {
90                 auto shape = static_cast<const Shape*>(target);
91                 if (!shape->fill()) {
92                     uint8_t r, g, b, a;
93                     shape->fillColor(&r, &g, &b, &a);
94                     if (a == 0 || a == 255) {
95                         if (method == CompositeMethod::LumaMask || method == CompositeMethod::InvLumaMask) {
96                             if ((r == 255 && g == 255 && b == 255) || (r == 0 && g == 0 && b == 0)) return false;
97                         } else return false;
98                     }
99                 }
100             }
101         }
102 
103         return true;
104     }
105 
updateImpl106     RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
107     {
108         if (static_cast<RenderUpdateFlag>(pFlag | flag) == RenderUpdateFlag::None) return rd;
109 
110         if ((needComp = needComposition(opacity))) {
111             /* Overriding opacity value. If this scene is half-translucent,
112                It must do intermediate composition with that opacity value. */
113             this->opacity = opacity;
114             opacity = 255;
115         }
116 
117         rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
118         flag = RenderUpdateFlag::None;
119         return rd;
120     }
121 
boundsImpl122     RenderRegion bounds(RenderMethod* renderer)
123     {
124         if (!rd) return {0, 0, 0, 0};
125         return renderer->region(rd);
126     }
127 
boundsImpl128     bool bounds(float* x, float* y, float* w, float* h, bool stroking)
129     {
130         //Path bounding size
131         if (rs.path.pts.count > 0 ) {
132             auto pts = rs.path.pts.begin();
133             Point min = { pts->x, pts->y };
134             Point max = { pts->x, pts->y };
135 
136             for (auto pts2 = pts + 1; pts2 < rs.path.pts.end(); ++pts2) {
137                 if (pts2->x < min.x) min.x = pts2->x;
138                 if (pts2->y < min.y) min.y = pts2->y;
139                 if (pts2->x > max.x) max.x = pts2->x;
140                 if (pts2->y > max.y) max.y = pts2->y;
141             }
142 
143             if (x) *x = min.x;
144             if (y) *y = min.y;
145             if (w) *w = max.x - min.x;
146             if (h) *h = max.y - min.y;
147         }
148 
149         //Stroke feathering
150         if (stroking && rs.stroke) {
151             if (x) *x -= rs.stroke->width * 0.5f;
152             if (y) *y -= rs.stroke->width * 0.5f;
153             if (w) *w += rs.stroke->width;
154             if (h) *h += rs.stroke->width;
155         }
156         return rs.path.pts.count > 0 ? true : false;
157     }
158 
reserveCmdImpl159     void reserveCmd(uint32_t cmdCnt)
160     {
161         rs.path.cmds.reserve(cmdCnt);
162     }
163 
reservePtsImpl164     void reservePts(uint32_t ptsCnt)
165     {
166         rs.path.pts.reserve(ptsCnt);
167     }
168 
growImpl169     void grow(uint32_t cmdCnt, uint32_t ptsCnt)
170     {
171         rs.path.cmds.grow(cmdCnt);
172         rs.path.pts.grow(ptsCnt);
173     }
174 
appendImpl175     void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
176     {
177         memcpy(rs.path.cmds.end(), cmds, sizeof(PathCommand) * cmdCnt);
178         memcpy(rs.path.pts.end(), pts, sizeof(Point) * ptsCnt);
179         rs.path.cmds.count += cmdCnt;
180         rs.path.pts.count += ptsCnt;
181     }
182 
moveToImpl183     void moveTo(float x, float y)
184     {
185         rs.path.cmds.push(PathCommand::MoveTo);
186         rs.path.pts.push({x, y});
187     }
188 
lineToImpl189     void lineTo(float x, float y)
190     {
191         rs.path.cmds.push(PathCommand::LineTo);
192         rs.path.pts.push({x, y});
193     }
194 
cubicToImpl195     void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
196     {
197         rs.path.cmds.push(PathCommand::CubicTo);
198         rs.path.pts.push({cx1, cy1});
199         rs.path.pts.push({cx2, cy2});
200         rs.path.pts.push({x, y});
201     }
202 
closeImpl203     void close()
204     {
205         //Don't close multiple times.
206         if (rs.path.cmds.count > 0 && rs.path.cmds.last() == PathCommand::Close) return;
207 
208         rs.path.cmds.push(PathCommand::Close);
209     }
210 
strokeWidthImpl211     void strokeWidth(float width)
212     {
213         if (!rs.stroke) rs.stroke = new RenderStroke();
214         rs.stroke->width = width;
215         flag |= RenderUpdateFlag::Stroke;
216     }
217 
strokeTrimImpl218     void strokeTrim(float begin, float end, bool simultaneous)
219     {
220         if (!rs.stroke) {
221             if (begin == 0.0f && end == 1.0f) return;
222             rs.stroke = new RenderStroke();
223         }
224 
225         if (tvg::equal(rs.stroke->trim.begin, begin) && tvg::equal(rs.stroke->trim.end, end) &&
226             rs.stroke->trim.simultaneous == simultaneous) return;
227 
228         rs.stroke->trim.begin = begin;
229         rs.stroke->trim.end = end;
230         rs.stroke->trim.simultaneous = simultaneous;
231         flag |= RenderUpdateFlag::Stroke;
232     }
233 
strokeTrimImpl234     bool strokeTrim(float* begin, float* end)
235     {
236         if (rs.stroke) {
237             if (begin) *begin = rs.stroke->trim.begin;
238             if (end) *end = rs.stroke->trim.end;
239             return rs.stroke->trim.simultaneous;
240         } else {
241             if (begin) *begin = 0.0f;
242             if (end) *end = 1.0f;
243             return false;
244         }
245     }
246 
strokeCapImpl247     void strokeCap(StrokeCap cap)
248     {
249         if (!rs.stroke) rs.stroke = new RenderStroke();
250         rs.stroke->cap = cap;
251         flag |= RenderUpdateFlag::Stroke;
252     }
253 
strokeJoinImpl254     void strokeJoin(StrokeJoin join)
255     {
256         if (!rs.stroke) rs.stroke = new RenderStroke();
257         rs.stroke->join = join;
258         flag |= RenderUpdateFlag::Stroke;
259     }
260 
strokeMiterlimitImpl261     void strokeMiterlimit(float miterlimit)
262     {
263         if (!rs.stroke) rs.stroke = new RenderStroke();
264         rs.stroke->miterlimit = miterlimit;
265         flag |= RenderUpdateFlag::Stroke;
266     }
267 
strokeColorImpl268     void strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
269     {
270         if (!rs.stroke) rs.stroke = new RenderStroke();
271         if (rs.stroke->fill) {
272             delete(rs.stroke->fill);
273             rs.stroke->fill = nullptr;
274             flag |= RenderUpdateFlag::GradientStroke;
275         }
276 
277         rs.stroke->color[0] = r;
278         rs.stroke->color[1] = g;
279         rs.stroke->color[2] = b;
280         rs.stroke->color[3] = a;
281 
282         flag |= RenderUpdateFlag::Stroke;
283     }
284 
strokeFillImpl285     Result strokeFill(unique_ptr<Fill> f)
286     {
287         auto p = f.release();
288         if (!p) return Result::MemoryCorruption;
289 
290         if (!rs.stroke) rs.stroke = new RenderStroke();
291         if (rs.stroke->fill && rs.stroke->fill != p) delete(rs.stroke->fill);
292         rs.stroke->fill = p;
293         rs.stroke->color[3] = 0;
294 
295         flag |= RenderUpdateFlag::Stroke;
296         flag |= RenderUpdateFlag::GradientStroke;
297 
298         return Result::Success;
299     }
300 
strokeDashImpl301     Result strokeDash(const float* pattern, uint32_t cnt, float offset)
302     {
303         if ((cnt == 1) || (!pattern && cnt > 0) || (pattern && cnt == 0)) {
304             return Result::InvalidArguments;
305         }
306 
307         for (uint32_t i = 0; i < cnt; i++) {
308             if (pattern[i] < FLOAT_EPSILON) return Result::InvalidArguments;
309         }
310 
311         //Reset dash
312         if (!pattern && cnt == 0) {
313             free(rs.stroke->dashPattern);
314             rs.stroke->dashPattern = nullptr;
315         } else {
316             if (!rs.stroke) rs.stroke = new RenderStroke();
317             if (rs.stroke->dashCnt != cnt) {
318                 free(rs.stroke->dashPattern);
319                 rs.stroke->dashPattern = nullptr;
320             }
321             if (!rs.stroke->dashPattern) {
322                 rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
323                 if (!rs.stroke->dashPattern) return Result::FailedAllocation;
324             }
325             for (uint32_t i = 0; i < cnt; ++i) {
326                 rs.stroke->dashPattern[i] = pattern[i];
327             }
328         }
329         rs.stroke->dashCnt = cnt;
330         rs.stroke->dashOffset = offset;
331         flag |= RenderUpdateFlag::Stroke;
332 
333         return Result::Success;
334     }
335 
strokeFirstImpl336     bool strokeFirst()
337     {
338         if (!rs.stroke) return true;
339         return rs.stroke->strokeFirst;
340     }
341 
strokeFirstImpl342     void strokeFirst(bool strokeFirst)
343     {
344         if (!rs.stroke) rs.stroke = new RenderStroke();
345         rs.stroke->strokeFirst = strokeFirst;
346         flag |= RenderUpdateFlag::Stroke;
347     }
348 
updateImpl349     void update(RenderUpdateFlag flag)
350     {
351         this->flag |= flag;
352     }
353 
duplicateImpl354     Paint* duplicate(Paint* ret)
355     {
356         auto shape = static_cast<Shape*>(ret);
357         if (shape) shape->reset();
358         else shape = Shape::gen().release();
359 
360         auto dup = shape->pImpl;
361         delete(dup->rs.fill);
362 
363         //Default Properties
364         dup->flag = RenderUpdateFlag::All;
365         dup->rs.rule = rs.rule;
366 
367         //Color
368         memcpy(dup->rs.color, rs.color, sizeof(rs.color));
369 
370         //Path
371         dup->rs.path.cmds.push(rs.path.cmds);
372         dup->rs.path.pts.push(rs.path.pts);
373 
374         //Stroke
375         if (rs.stroke) {
376             if (!dup->rs.stroke) dup->rs.stroke = new RenderStroke;
377             *dup->rs.stroke = *rs.stroke;
378         } else {
379             delete(dup->rs.stroke);
380             dup->rs.stroke = nullptr;
381         }
382 
383         //Fill
384         if (rs.fill) dup->rs.fill = rs.fill->duplicate();
385         else dup->rs.fill = nullptr;
386 
387         return shape;
388     }
389 
resetImpl390     void reset()
391     {
392         PP(shape)->reset();
393         rs.path.cmds.clear();
394         rs.path.pts.clear();
395 
396         rs.color[3] = 0;
397         rs.rule = FillRule::Winding;
398 
399         delete(rs.stroke);
400         rs.stroke = nullptr;
401 
402         delete(rs.fill);
403         rs.fill = nullptr;
404     }
405 
iteratorImpl406     Iterator* iterator()
407     {
408         return nullptr;
409     }
410 };
411 
412 #endif //_TVG_SHAPE_H_
413 
414 #endif /* LV_USE_THORVG_INTERNAL */
415 
416