1/*
2 * Copyright (c) 2020 - 2023 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#ifndef _TVG_SHAPE_IMPL_H_
24#define _TVG_SHAPE_IMPL_H_
25
26#include <memory.h>
27#include "tvgMath.h"
28#include "tvgPaint.h"
29
30/************************************************************************/
31/* Internal Class Implementation */
32/************************************************************************/
33
34struct Shape::Impl
35{
36 RenderShape rs; //shape data
37 RenderData rd = nullptr; //engine data
38 Shape* shape;
39 uint8_t flag = RenderUpdateFlag::None;
40 uint8_t opacity; //for composition
41 bool needComp; //composite or not
42
43 Impl(Shape* s) : shape(s)
44 {
45 }
46
47 bool dispose(RenderMethod& renderer)
48 {
49 auto ret = renderer.dispose(rd);
50 rd = nullptr;
51 return ret;
52 }
53
54 bool render(RenderMethod& renderer)
55 {
56 Compositor* cmp = nullptr;
57 bool ret;
58
59 if (needComp) {
60 cmp = renderer.target(bounds(renderer), renderer.colorSpace());
61 renderer.beginComposite(cmp, CompositeMethod::None, opacity);
62 }
63 ret = renderer.renderShape(rd);
64 if (cmp) renderer.endComposite(cmp);
65 return ret;
66 }
67
68 bool needComposition(uint8_t opacity)
69 {
70 if (opacity == 0) return false;
71
72 //Shape composition is only necessary when stroking & fill are valid.
73 if (!rs.stroke || rs.stroke->width < FLT_EPSILON || rs.stroke->color[3] == 0) return false;
74 if (!rs.fill && rs.color[3] == 0) return false;
75
76 //translucent fill & stroke
77 if (opacity < 255) return true;
78
79 //Composition test
80 const Paint* target;
81 auto method = shape->composite(&target);
82 if (!target || method == tvg::CompositeMethod::ClipPath) return false;
83 if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false;
84
85 return true;
86 }
87
88 RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
89 {
90 if ((needComp = needComposition(opacity))) {
91 /* Overriding opacity value. If this scene is half-translucent,
92 It must do intermeidate composition with that opacity value. */
93 this->opacity = opacity;
94 opacity = 255;
95 }
96
97 rd = renderer.prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
98 flag = RenderUpdateFlag::None;
99 return rd;
100 }
101
102 RenderRegion bounds(RenderMethod& renderer)
103 {
104 return renderer.region(rd);
105 }
106
107 bool bounds(float* x, float* y, float* w, float* h)
108 {
109 //Path bounding size
110 if (rs.path.pts.count > 0 ) {
111 auto pts = rs.path.pts.data;
112 Point min = { pts->x, pts->y };
113 Point max = { pts->x, pts->y };
114
115 for (auto pts2 = pts + 1; pts2 < rs.path.pts.end(); ++pts2) {
116 if (pts2->x < min.x) min.x = pts2->x;
117 if (pts2->y < min.y) min.y = pts2->y;
118 if (pts2->x > max.x) max.x = pts2->x;
119 if (pts2->y > max.y) max.y = pts2->y;
120 }
121
122 if (x) *x = min.x;
123 if (y) *y = min.y;
124 if (w) *w = max.x - min.x;
125 if (h) *h = max.y - min.y;
126 }
127
128 //Stroke feathering
129 if (rs.stroke) {
130 if (x) *x -= rs.stroke->width * 0.5f;
131 if (y) *y -= rs.stroke->width * 0.5f;
132 if (w) *w += rs.stroke->width;
133 if (h) *h += rs.stroke->width;
134 }
135 return rs.path.pts.count > 0 ? true : false;
136 }
137
138 void reserveCmd(uint32_t cmdCnt)
139 {
140 rs.path.cmds.reserve(cmdCnt);
141 }
142
143 void reservePts(uint32_t ptsCnt)
144 {
145 rs.path.pts.reserve(ptsCnt);
146 }
147
148 void grow(uint32_t cmdCnt, uint32_t ptsCnt)
149 {
150 rs.path.cmds.grow(cmdCnt);
151 rs.path.pts.grow(ptsCnt);
152 }
153
154 void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
155 {
156 memcpy(rs.path.cmds.end(), cmds, sizeof(PathCommand) * cmdCnt);
157 memcpy(rs.path.pts.end(), pts, sizeof(Point) * ptsCnt);
158 rs.path.cmds.count += cmdCnt;
159 rs.path.pts.count += ptsCnt;
160
161 flag |= RenderUpdateFlag::Path;
162 }
163
164 void moveTo(float x, float y)
165 {
166 rs.path.cmds.push(PathCommand::MoveTo);
167 rs.path.pts.push({x, y});
168
169 flag |= RenderUpdateFlag::Path;
170 }
171
172 void lineTo(float x, float y)
173 {
174 rs.path.cmds.push(PathCommand::LineTo);
175 rs.path.pts.push({x, y});
176
177 flag |= RenderUpdateFlag::Path;
178 }
179
180 void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
181 {
182 rs.path.cmds.push(PathCommand::CubicTo);
183 rs.path.pts.push({cx1, cy1});
184 rs.path.pts.push({cx2, cy2});
185 rs.path.pts.push({x, y});
186
187 flag |= RenderUpdateFlag::Path;
188 }
189
190 void close()
191 {
192 //Don't close multiple times.
193 if (rs.path.cmds.count > 0 && rs.path.cmds.last() == PathCommand::Close) return;
194
195 rs.path.cmds.push(PathCommand::Close);
196
197 flag |= RenderUpdateFlag::Path;
198 }
199
200 bool strokeWidth(float width)
201 {
202 //TODO: Size Exception?
203
204 if (!rs.stroke) rs.stroke = new RenderStroke();
205 rs.stroke->width = width;
206 flag |= RenderUpdateFlag::Stroke;
207
208 return true;
209 }
210
211 bool strokeCap(StrokeCap cap)
212 {
213 if (!rs.stroke) rs.stroke = new RenderStroke();
214 rs.stroke->cap = cap;
215 flag |= RenderUpdateFlag::Stroke;
216
217 return true;
218 }
219
220 bool strokeJoin(StrokeJoin join)
221 {
222 if (!rs.stroke) rs.stroke = new RenderStroke();
223 rs.stroke->join = join;
224 flag |= RenderUpdateFlag::Stroke;
225
226 return true;
227 }
228
229 bool strokeMiterlimit(float miterlimit)
230 {
231 if (!rs.stroke) rs.stroke = new RenderStroke();
232 rs.stroke->miterlimit = miterlimit;
233 flag |= RenderUpdateFlag::Stroke;
234
235 return true;
236 }
237
238 bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
239 {
240 if (!rs.stroke) rs.stroke = new RenderStroke();
241 if (rs.stroke->fill) {
242 delete(rs.stroke->fill);
243 rs.stroke->fill = nullptr;
244 flag |= RenderUpdateFlag::GradientStroke;
245 }
246
247 rs.stroke->color[0] = r;
248 rs.stroke->color[1] = g;
249 rs.stroke->color[2] = b;
250 rs.stroke->color[3] = a;
251
252 flag |= RenderUpdateFlag::Stroke;
253
254 return true;
255 }
256
257 Result strokeFill(unique_ptr<Fill> f)
258 {
259 auto p = f.release();
260 if (!p) return Result::MemoryCorruption;
261
262 if (!rs.stroke) rs.stroke = new RenderStroke();
263 if (rs.stroke->fill && rs.stroke->fill != p) delete(rs.stroke->fill);
264 rs.stroke->fill = p;
265
266 flag |= RenderUpdateFlag::Stroke;
267 flag |= RenderUpdateFlag::GradientStroke;
268
269 return Result::Success;
270 }
271
272 bool strokeDash(const float* pattern, uint32_t cnt)
273 {
274 //Reset dash
275 if (!pattern && cnt == 0) {
276 free(rs.stroke->dashPattern);
277 rs.stroke->dashPattern = nullptr;
278 } else {
279 if (!rs.stroke) rs.stroke = new RenderStroke();
280 if (rs.stroke->dashCnt != cnt) {
281 free(rs.stroke->dashPattern);
282 rs.stroke->dashPattern = nullptr;
283 }
284 if (!rs.stroke->dashPattern) {
285 rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
286 if (!rs.stroke->dashPattern) return false;
287 }
288 for (uint32_t i = 0; i < cnt; ++i) {
289 rs.stroke->dashPattern[i] = pattern[i];
290 }
291 }
292 rs.stroke->dashCnt = cnt;
293 flag |= RenderUpdateFlag::Stroke;
294
295 return true;
296 }
297
298 bool strokeFirst()
299 {
300 if (!rs.stroke) return true;
301 return rs.stroke->strokeFirst;
302 }
303
304 bool strokeFirst(bool strokeFirst)
305 {
306 if (!rs.stroke) rs.stroke = new RenderStroke();
307 rs.stroke->strokeFirst = strokeFirst;
308 flag |= RenderUpdateFlag::Stroke;
309
310 return true;
311 }
312
313 void update(RenderUpdateFlag flag)
314 {
315 this->flag |= flag;
316 }
317
318 Paint* duplicate()
319 {
320 auto ret = Shape::gen();
321
322 auto dup = ret.get()->pImpl;
323 dup->rs.rule = rs.rule;
324
325 //Color
326 memcpy(dup->rs.color, rs.color, sizeof(rs.color));
327 dup->flag = RenderUpdateFlag::Color;
328
329 //Path
330 if (rs.path.cmds.count > 0 && rs.path.pts.count > 0) {
331 dup->rs.path.cmds = rs.path.cmds;
332 dup->rs.path.pts = rs.path.pts;
333 dup->flag |= RenderUpdateFlag::Path;
334 }
335
336 //Stroke
337 if (rs.stroke) {
338 dup->rs.stroke = new RenderStroke();
339 dup->rs.stroke->width = rs.stroke->width;
340 dup->rs.stroke->dashCnt = rs.stroke->dashCnt;
341 dup->rs.stroke->cap = rs.stroke->cap;
342 dup->rs.stroke->join = rs.stroke->join;
343 dup->rs.stroke->strokeFirst = rs.stroke->strokeFirst;
344 memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color));
345
346 if (rs.stroke->dashCnt > 0) {
347 dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt));
348 memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt);
349 }
350
351 dup->flag |= RenderUpdateFlag::Stroke;
352
353 if (rs.stroke->fill) {
354 dup->rs.stroke->fill = rs.stroke->fill->duplicate();
355 dup->flag |= RenderUpdateFlag::GradientStroke;
356 }
357 }
358
359 //Fill
360 if (rs.fill) {
361 dup->rs.fill = rs.fill->duplicate();
362 dup->flag |= RenderUpdateFlag::Gradient;
363 }
364
365 return ret.release();
366 }
367
368 Iterator* iterator()
369 {
370 return nullptr;
371 }
372};
373
374#endif //_TVG_SHAPE_IMPL_H_
375