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_RENDER_H_
27 #define _TVG_RENDER_H_
28
29 #include <math.h>
30 #include <cstdarg>
31 #include "tvgCommon.h"
32 #include "tvgArray.h"
33 #include "tvgLock.h"
34
35 namespace tvg
36 {
37
38 using RenderData = void*;
39 using pixel_t = uint32_t;
40
41 enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255};
42
43 //TODO: Move this in public header unifying with SwCanvas::Colorspace
44 enum ColorSpace : uint8_t
45 {
46 ABGR8888 = 0, //The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied.
47 ARGB8888, //The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied.
48 ABGR8888S, //The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied.
49 ARGB8888S, //The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied.
50 Grayscale8, //One single channel data.
51 Unsupported //TODO: Change to the default, At the moment, we put it in the last to align with SwCanvas::Colorspace.
52 };
53
54 struct RenderSurface
55 {
56 union {
57 pixel_t* data = nullptr; //system based data pointer
58 uint32_t* buf32; //for explicit 32bits channels
59 uint8_t* buf8; //for explicit 8bits grayscale
60 };
61 Key key; //a reserved lock for the thread safety
62 uint32_t stride = 0;
63 uint32_t w = 0, h = 0;
64 ColorSpace cs = ColorSpace::Unsupported;
65 uint8_t channelSize = 0;
66 bool premultiplied = false; //Alpha-premultiplied
67
RenderSurfaceRenderSurface68 RenderSurface()
69 {
70 }
71
RenderSurfaceRenderSurface72 RenderSurface(const RenderSurface* rhs)
73 {
74 data = rhs->data;
75 stride = rhs->stride;
76 w = rhs->w;
77 h = rhs->h;
78 cs = rhs->cs;
79 channelSize = rhs->channelSize;
80 premultiplied = rhs->premultiplied;
81 }
82
83
84 };
85
86 struct RenderCompositor
87 {
88 CompositeMethod method;
89 uint8_t opacity;
90 };
91
92 struct RenderRegion
93 {
94 int32_t x, y, w, h;
95
96 void intersect(const RenderRegion& rhs);
97 void add(const RenderRegion& rhs);
98
99 bool operator==(const RenderRegion& rhs) const
100 {
101 if (x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h) return true;
102 return false;
103 }
104 };
105
106 struct RenderStroke
107 {
108 float width = 0.0f;
109 uint8_t color[4] = {0, 0, 0, 0};
110 Fill *fill = nullptr;
111 float* dashPattern = nullptr;
112 uint32_t dashCnt = 0;
113 float dashOffset = 0.0f;
114 StrokeCap cap = StrokeCap::Square;
115 StrokeJoin join = StrokeJoin::Bevel;
116 float miterlimit = 4.0f;
117 bool strokeFirst = false;
118
119 struct {
120 float begin = 0.0f;
121 float end = 1.0f;
122 bool simultaneous = true;
123 } trim;
124
125 void operator=(const RenderStroke& rhs)
126 {
127 width = rhs.width;
128
129 memcpy(color, rhs.color, sizeof(color));
130
131 delete(fill);
132 if (rhs.fill) fill = rhs.fill->duplicate();
133 else fill = nullptr;
134
135 free(dashPattern);
136 if (rhs.dashCnt > 0) {
137 dashPattern = static_cast<float*>(malloc(sizeof(float) * rhs.dashCnt));
138 memcpy(dashPattern, rhs.dashPattern, sizeof(float) * rhs.dashCnt);
139 } else {
140 dashPattern = nullptr;
141 }
142 dashCnt = rhs.dashCnt;
143 miterlimit = rhs.miterlimit;
144 cap = rhs.cap;
145 join = rhs.join;
146 strokeFirst = rhs.strokeFirst;
147 trim = rhs.trim;
148 }
149
strokeTrimRenderStroke150 bool strokeTrim(float& begin, float& end) const
151 {
152 begin = trim.begin;
153 end = trim.end;
154
155 if (fabsf(end - begin) >= 1.0f) {
156 begin = 0.0f;
157 end = 1.0f;
158 return false;
159 }
160
161 auto loop = true;
162
163 if (begin > 1.0f && end > 1.0f) loop = false;
164 if (begin < 0.0f && end < 0.0f) loop = false;
165 if (begin >= 0.0f && begin <= 1.0f && end >= 0.0f && end <= 1.0f) loop = false;
166
167 if (begin > 1.0f) begin -= 1.0f;
168 if (begin < 0.0f) begin += 1.0f;
169 if (end > 1.0f) end -= 1.0f;
170 if (end < 0.0f) end += 1.0f;
171
172 if ((loop && begin < end) || (!loop && begin > end)) std::swap(begin, end);
173 return true;
174 }
175
~RenderStrokeRenderStroke176 ~RenderStroke()
177 {
178 free(dashPattern);
179 delete(fill);
180 }
181 };
182
183 struct RenderShape
184 {
185 struct
186 {
187 Array<PathCommand> cmds;
188 Array<Point> pts;
189 } path;
190
191 Fill *fill = nullptr;
192 RenderStroke *stroke = nullptr;
193 uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
194 FillRule rule = FillRule::Winding;
195
~RenderShapeRenderShape196 ~RenderShape()
197 {
198 delete(fill);
199 delete(stroke);
200 }
201
fillColorRenderShape202 void fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
203 {
204 if (r) *r = color[0];
205 if (g) *g = color[1];
206 if (b) *b = color[2];
207 if (a) *a = color[3];
208 }
209
strokeWidthRenderShape210 float strokeWidth() const
211 {
212 if (!stroke) return 0;
213 return stroke->width;
214 }
215
strokeTrimRenderShape216 bool strokeTrim() const
217 {
218 if (!stroke) return false;
219 if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false;
220 if (fabsf(stroke->trim.end - stroke->trim.begin) >= 1.0f) return false;
221 return true;
222 }
223
strokeColorRenderShape224 bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
225 {
226 if (!stroke) return false;
227
228 if (r) *r = stroke->color[0];
229 if (g) *g = stroke->color[1];
230 if (b) *b = stroke->color[2];
231 if (a) *a = stroke->color[3];
232
233 return true;
234 }
235
strokeFillRenderShape236 const Fill* strokeFill() const
237 {
238 if (!stroke) return nullptr;
239 return stroke->fill;
240 }
241
strokeDashRenderShape242 uint32_t strokeDash(const float** dashPattern, float* offset) const
243 {
244 if (!stroke) return 0;
245 if (dashPattern) *dashPattern = stroke->dashPattern;
246 if (offset) *offset = stroke->dashOffset;
247 return stroke->dashCnt;
248 }
249
strokeCapRenderShape250 StrokeCap strokeCap() const
251 {
252 if (!stroke) return StrokeCap::Square;
253 return stroke->cap;
254 }
255
strokeJoinRenderShape256 StrokeJoin strokeJoin() const
257 {
258 if (!stroke) return StrokeJoin::Bevel;
259 return stroke->join;
260 }
261
strokeMiterlimitRenderShape262 float strokeMiterlimit() const
263 {
264 if (!stroke) return 4.0f;
265 return stroke->miterlimit;;
266 }
267 };
268
269 struct RenderEffect
270 {
271 RenderData rd = nullptr;
272 RenderRegion extend = {0, 0, 0, 0};
273 SceneEffect type;
274 bool invalid = false;
275
~RenderEffectRenderEffect276 virtual ~RenderEffect()
277 {
278 free(rd);
279 }
280 };
281
282 struct RenderEffectGaussian : RenderEffect
283 {
284 float sigma;
285 uint8_t direction; //0: both, 1: horizontal, 2: vertical
286 uint8_t border; //0: duplicate, 1: wrap
287 uint8_t quality; //0 ~ 100 (optional)
288
genRenderEffectGaussian289 static RenderEffectGaussian* gen(va_list& args)
290 {
291 auto sigma = (float) va_arg(args, double);
292 if (sigma <= 0) return nullptr;
293
294 auto inst = new RenderEffectGaussian;
295 inst->sigma = sigma;
296 inst->direction = std::min(va_arg(args, int), 2);
297 inst->border = std::min(va_arg(args, int), 1);
298 inst->quality = std::min(va_arg(args, int), 100);
299 inst->type = SceneEffect::GaussianBlur;
300 return inst;
301 }
302 };
303
304 class RenderMethod
305 {
306 private:
307 uint32_t refCnt = 0; //reference count
308 Key key;
309
310 public:
311 uint32_t ref();
312 uint32_t unref();
313
~RenderMethod()314 virtual ~RenderMethod() {}
315 virtual RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
316 virtual RenderData prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
317 virtual bool preRender() = 0;
318 virtual bool renderShape(RenderData data) = 0;
319 virtual bool renderImage(RenderData data) = 0;
320 virtual bool postRender() = 0;
321 virtual void dispose(RenderData data) = 0;
322 virtual RenderRegion region(RenderData data) = 0;
323 virtual RenderRegion viewport() = 0;
324 virtual bool viewport(const RenderRegion& vp) = 0;
325 virtual bool blend(BlendMethod method) = 0;
326 virtual ColorSpace colorSpace() = 0;
327 virtual const RenderSurface* mainSurface() = 0;
328
329 virtual bool clear() = 0;
330 virtual bool sync() = 0;
331
332 virtual RenderCompositor* target(const RenderRegion& region, ColorSpace cs) = 0;
333 virtual bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) = 0;
334 virtual bool endComposite(RenderCompositor* cmp) = 0;
335
336 virtual bool prepare(RenderEffect* effect) = 0;
337 virtual bool effect(RenderCompositor* cmp, const RenderEffect* effect) = 0;
338 };
339
MASK_REGION_MERGING(CompositeMethod method)340 static inline bool MASK_REGION_MERGING(CompositeMethod method)
341 {
342 switch(method) {
343 case CompositeMethod::AlphaMask:
344 case CompositeMethod::InvAlphaMask:
345 case CompositeMethod::LumaMask:
346 case CompositeMethod::InvLumaMask:
347 case CompositeMethod::SubtractMask:
348 case CompositeMethod::IntersectMask:
349 return false;
350 //these might expand the rendering region
351 case CompositeMethod::AddMask:
352 case CompositeMethod::DifferenceMask:
353 case CompositeMethod::LightenMask:
354 case CompositeMethod::DarkenMask:
355 return true;
356 default:
357 TVGERR("RENDERER", "Unsupported Composite Method! = %d", (int)method);
358 return false;
359 }
360 }
361
CHANNEL_SIZE(ColorSpace cs)362 static inline uint8_t CHANNEL_SIZE(ColorSpace cs)
363 {
364 switch(cs) {
365 case ColorSpace::ABGR8888:
366 case ColorSpace::ABGR8888S:
367 case ColorSpace::ARGB8888:
368 case ColorSpace::ARGB8888S:
369 return sizeof(uint32_t);
370 case ColorSpace::Grayscale8:
371 return sizeof(uint8_t);
372 case ColorSpace::Unsupported:
373 default:
374 TVGERR("RENDERER", "Unsupported Channel Size! = %d", (int)cs);
375 return 0;
376 }
377 }
378
COMPOSITE_TO_COLORSPACE(RenderMethod * renderer,CompositeMethod method)379 static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod* renderer, CompositeMethod method)
380 {
381 switch(method) {
382 case CompositeMethod::AlphaMask:
383 case CompositeMethod::InvAlphaMask:
384 case CompositeMethod::AddMask:
385 case CompositeMethod::DifferenceMask:
386 case CompositeMethod::SubtractMask:
387 case CompositeMethod::IntersectMask:
388 case CompositeMethod::LightenMask:
389 case CompositeMethod::DarkenMask:
390 return ColorSpace::Grayscale8;
391 //TODO: Optimize Luma/InvLuma colorspace to Grayscale8
392 case CompositeMethod::LumaMask:
393 case CompositeMethod::InvLumaMask:
394 return renderer->colorSpace();
395 default:
396 TVGERR("RENDERER", "Unsupported Composite Size! = %d", (int)method);
397 return ColorSpace::Unsupported;
398 }
399 }
400
MULTIPLY(uint8_t c,uint8_t a)401 static inline uint8_t MULTIPLY(uint8_t c, uint8_t a)
402 {
403 return (((c) * (a) + 0xff) >> 8);
404 }
405
406 }
407
408 #endif //_TVG_RENDER_H_
409
410 #endif /* LV_USE_THORVG_INTERNAL */
411
412