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