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_RENDER_H_
24#define _TVG_RENDER_H_
25
26#include "tvgCommon.h"
27#include "tvgArray.h"
28
29namespace tvg
30{
31
32using RenderData = void*;
33using pixel_t = uint32_t;
34
35enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255};
36
37struct Surface;
38
39enum ColorSpace
40{
41 ABGR8888 = 0, //The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied.
42 ARGB8888, //The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied.
43 ABGR8888S, //The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied.
44 ARGB8888S, //The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied.
45 Grayscale8, //One single channel data.
46 Unsupported //TODO: Change to the default, At the moment, we put it in the last to align with SwCanvas::Colorspace.
47};
48
49struct Surface
50{
51 union {
52 pixel_t* data; //system based data pointer
53 uint32_t* buf32; //for explicit 32bits channels
54 uint8_t* buf8; //for explicit 8bits grayscale
55 };
56 uint32_t stride;
57 uint32_t w, h;
58 ColorSpace cs;
59 uint8_t channelSize;
60
61 bool premultiplied; //Alpha-premultiplied
62 bool owner; //Only owner could modify the buffer
63};
64
65struct Compositor
66{
67 CompositeMethod method;
68 uint8_t opacity;
69};
70
71struct RenderMesh
72{
73 Polygon* triangles = nullptr;
74 uint32_t triangleCnt = 0;
75
76 ~RenderMesh()
77 {
78 free(triangles);
79 }
80};
81
82struct RenderRegion
83{
84 int32_t x, y, w, h;
85
86 void intersect(const RenderRegion& rhs)
87 {
88 auto x1 = x + w;
89 auto y1 = y + h;
90 auto x2 = rhs.x + rhs.w;
91 auto y2 = rhs.y + rhs.h;
92
93 x = (x > rhs.x) ? x : rhs.x;
94 y = (y > rhs.y) ? y : rhs.y;
95 w = ((x1 < x2) ? x1 : x2) - x;
96 h = ((y1 < y2) ? y1 : y2) - y;
97
98 if (w < 0) w = 0;
99 if (h < 0) h = 0;
100 }
101
102 void add(const RenderRegion& rhs)
103 {
104 if (rhs.x < x) {
105 w += (x - rhs.x);
106 x = rhs.x;
107 }
108 if (rhs.y < y) {
109 h += (y - rhs.y);
110 y = rhs.y;
111 }
112 if (rhs.x + rhs.w > x + w) w = (rhs.x + rhs.w) - x;
113 if (rhs.y + rhs.h > y + h) h = (rhs.y + rhs.h) - y;
114 }
115};
116
117struct RenderTransform
118{
119 Matrix m; //3x3 Matrix Elements
120 float x = 0.0f;
121 float y = 0.0f;
122 float degree = 0.0f; //rotation degree
123 float scale = 1.0f; //scale factor
124 bool overriding = false; //user transform?
125
126 bool update();
127 void override(const Matrix& m);
128
129 RenderTransform();
130 RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
131};
132
133struct RenderStroke
134{
135 float width = 0.0f;
136 uint8_t color[4] = {0, 0, 0, 0};
137 Fill *fill = nullptr;
138 float* dashPattern = nullptr;
139 uint32_t dashCnt = 0;
140 StrokeCap cap = StrokeCap::Square;
141 StrokeJoin join = StrokeJoin::Bevel;
142 float miterlimit = 4.0f;
143 bool strokeFirst = false;
144
145 ~RenderStroke()
146 {
147 free(dashPattern);
148 delete(fill);
149 }
150};
151
152struct RenderShape
153{
154 struct
155 {
156 Array<PathCommand> cmds;
157 Array<Point> pts;
158 } path;
159
160 Fill *fill = nullptr;
161 RenderStroke *stroke = nullptr;
162 uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
163 FillRule rule = FillRule::Winding;
164
165 ~RenderShape()
166 {
167 delete(fill);
168 delete(stroke);
169 }
170
171 void fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
172 {
173 if (r) *r = color[0];
174 if (g) *g = color[1];
175 if (b) *b = color[2];
176 if (a) *a = color[3];
177 }
178
179 float strokeWidth() const
180 {
181 if (!stroke) return 0;
182 return stroke->width;
183 }
184
185 bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
186 {
187 if (!stroke) return false;
188
189 if (r) *r = stroke->color[0];
190 if (g) *g = stroke->color[1];
191 if (b) *b = stroke->color[2];
192 if (a) *a = stroke->color[3];
193
194 return true;
195 }
196
197 const Fill* strokeFill() const
198 {
199 if (!stroke) return nullptr;
200 return stroke->fill;
201 }
202
203 uint32_t strokeDash(const float** dashPattern) const
204 {
205 if (!stroke) return 0;
206 if (dashPattern) *dashPattern = stroke->dashPattern;
207 return stroke->dashCnt;
208 }
209
210 StrokeCap strokeCap() const
211 {
212 if (!stroke) return StrokeCap::Square;
213 return stroke->cap;
214 }
215
216 StrokeJoin strokeJoin() const
217 {
218 if (!stroke) return StrokeJoin::Bevel;
219 return stroke->join;
220 }
221
222 float strokeMiterlimit() const
223 {
224 if (!stroke) return 4.0f;
225
226 return stroke->miterlimit;;
227 }
228};
229
230class RenderMethod
231{
232public:
233 virtual ~RenderMethod() {}
234 virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
235 virtual RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
236 virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
237 virtual bool preRender() = 0;
238 virtual bool renderShape(RenderData data) = 0;
239 virtual bool renderImage(RenderData data) = 0;
240 virtual bool postRender() = 0;
241 virtual bool dispose(RenderData data) = 0;
242 virtual RenderRegion region(RenderData data) = 0;
243 virtual RenderRegion viewport() = 0;
244 virtual bool viewport(const RenderRegion& vp) = 0;
245 virtual bool blend(BlendMethod method) = 0;
246 virtual ColorSpace colorSpace() = 0;
247
248 virtual bool clear() = 0;
249 virtual bool sync() = 0;
250
251 virtual Compositor* target(const RenderRegion& region, ColorSpace cs) = 0;
252 virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) = 0;
253 virtual bool endComposite(Compositor* cmp) = 0;
254};
255
256static inline bool MASK_OPERATION(CompositeMethod method)
257{
258 switch(method) {
259 case CompositeMethod::AlphaMask:
260 case CompositeMethod::InvAlphaMask:
261 case CompositeMethod::LumaMask:
262 case CompositeMethod::InvLumaMask:
263 return false;
264 case CompositeMethod::AddMask:
265 case CompositeMethod::SubtractMask:
266 case CompositeMethod::IntersectMask:
267 case CompositeMethod::DifferenceMask:
268 return true;
269 default:
270 TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method);
271 return false;
272 }
273}
274
275static inline uint8_t CHANNEL_SIZE(ColorSpace cs)
276{
277 switch(cs) {
278 case ColorSpace::ABGR8888:
279 case ColorSpace::ABGR8888S:
280 case ColorSpace::ARGB8888:
281 case ColorSpace::ARGB8888S:
282 return sizeof(uint32_t);
283 case ColorSpace::Grayscale8:
284 return sizeof(uint8_t);
285 case ColorSpace::Unsupported:
286 default:
287 TVGERR("SW_ENGINE", "Unsupported Channel Size! = %d", (int)cs);
288 return 0;
289 }
290}
291
292static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, CompositeMethod method)
293{
294 switch(method) {
295 case CompositeMethod::AlphaMask:
296 case CompositeMethod::InvAlphaMask:
297 return ColorSpace::Grayscale8;
298 case CompositeMethod::LumaMask:
299 case CompositeMethod::InvLumaMask:
300 case CompositeMethod::AddMask:
301 case CompositeMethod::SubtractMask:
302 case CompositeMethod::IntersectMask:
303 case CompositeMethod::DifferenceMask:
304 return renderer.colorSpace();
305 default:
306 TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method);
307 return ColorSpace::Unsupported;
308 }
309}
310
311static inline uint8_t MULTIPLY(uint8_t c, uint8_t a)
312{
313 return (((c) * (a) + 0xff) >> 8);
314}
315
316
317}
318
319#endif //_TVG_RENDER_H_
320