common: fix wrong viewport region.
[platform/core/graphics/tizenvg.git] / src / lib / tvgPaint.h
1 /*
2  * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. 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 #ifndef _TVG_PAINT_H_
23 #define _TVG_PAINT_H_
24
25 #include <float.h>
26 #include <math.h>
27 #include "tvgRender.h"
28
29 static inline bool FLT_SAME(float a, float b)
30 {
31     return (fabsf(a - b) < FLT_EPSILON);
32 }
33
34 static bool _clipPathFastTrack(Paint* cmpTarget, const RenderTransform* transform, RenderRegion& viewport)
35 {
36     /* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */
37     auto shape = static_cast<Shape*>(cmpTarget);
38
39     //Rectangle Candidates?
40     const Point* pts;
41      if (shape->pathCoords(&pts) != 4) return false;
42
43     //No Rotation?
44     if (transform) {
45         if (transform->m.e12 != 0 || transform->m.e21 != 0 || transform->m.e11 != transform->m.e22) return false;
46     }
47
48     //Othogonal Rectangle?
49     auto pt1 = pts + 0;
50     auto pt2 = pts + 1;
51     auto pt3 = pts + 2;
52     auto pt4 = pts + 3;
53
54     if ((FLT_SAME(pt1->x, pt2->x) && FLT_SAME(pt2->y, pt3->y) && FLT_SAME(pt3->x, pt4->x) && FLT_SAME(pt1->y, pt4->y)) ||
55         (FLT_SAME(pt2->x, pt3->x) && FLT_SAME(pt1->y, pt2->y) && FLT_SAME(pt1->x, pt4->x) && FLT_SAME(pt3->y, pt4->y))) {
56
57         auto x1 = pt1->x;
58         auto y1 = pt1->y;
59         auto x2 = pt3->x;
60         auto y2 = pt3->y;
61
62         if (transform) {
63             x1 = x1 * transform->m.e11 + transform->m.e13;
64             y1 = y1 * transform->m.e22 + transform->m.e23;
65             x2 = x2 * transform->m.e11 + transform->m.e13;
66             y2 = y2 * transform->m.e22 + transform->m.e23;
67         }
68
69         viewport.x = static_cast<uint32_t>(x1);
70         viewport.y = static_cast<uint32_t>(y1);
71         viewport.w = static_cast<uint32_t>(roundf(x2 - x1 + 0.5f));
72         viewport.h = static_cast<uint32_t>(roundf(y2 - y1 + 0.5f));
73
74         return true;
75     }
76
77     return false;
78 }
79
80
81 namespace tvg
82 {
83     enum class PaintType { Shape = 0, Scene, Picture };
84
85     struct StrategyMethod
86     {
87         virtual ~StrategyMethod() {}
88
89         virtual bool dispose(RenderMethod& renderer) = 0;
90         virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag) = 0;   //Return engine data if it has.
91         virtual bool render(RenderMethod& renderer) = 0;
92         virtual bool bounds(float* x, float* y, float* w, float* h) const = 0;
93         virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
94         virtual Paint* duplicate() = 0;
95     };
96
97     struct Paint::Impl
98     {
99         StrategyMethod* smethod = nullptr;
100         RenderTransform *rTransform = nullptr;
101         uint32_t flag = RenderUpdateFlag::None;
102
103         Paint* cmpTarget = nullptr;
104         CompositeMethod cmpMethod = CompositeMethod::None;
105
106         uint8_t opacity = 255;
107
108         PaintType type;
109
110         ~Impl() {
111             if (cmpTarget) delete(cmpTarget);
112             if (smethod) delete(smethod);
113             if (rTransform) delete(rTransform);
114         }
115
116         void method(StrategyMethod* method)
117         {
118             smethod = method;
119         }
120
121         bool rotate(float degree)
122         {
123             if (rTransform) {
124                 if (fabsf(degree - rTransform->degree) <= FLT_EPSILON) return true;
125             } else {
126                 if (fabsf(degree) <= FLT_EPSILON) return true;
127                 rTransform = new RenderTransform();
128                 if (!rTransform) return false;
129             }
130             rTransform->degree = degree;
131             if (!rTransform->overriding) flag |= RenderUpdateFlag::Transform;
132
133             return true;
134         }
135
136         bool scale(float factor)
137         {
138             if (rTransform) {
139                 if (fabsf(factor - rTransform->scale) <= FLT_EPSILON) return true;
140             } else {
141                 if (fabsf(factor) <= FLT_EPSILON) return true;
142                 rTransform = new RenderTransform();
143                 if (!rTransform) return false;
144             }
145             rTransform->scale = factor;
146             if (!rTransform->overriding) flag |= RenderUpdateFlag::Transform;
147
148             return true;
149         }
150
151         bool translate(float x, float y)
152         {
153             if (rTransform) {
154                 if (fabsf(x - rTransform->x) <= FLT_EPSILON && fabsf(y - rTransform->y) <= FLT_EPSILON) return true;
155             } else {
156                 if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON) return true;
157                 rTransform = new RenderTransform();
158                 if (!rTransform) return false;
159             }
160             rTransform->x = x;
161             rTransform->y = y;
162             if (!rTransform->overriding) flag |= RenderUpdateFlag::Transform;
163
164             return true;
165         }
166
167         bool transform(const Matrix& m)
168         {
169             if (!rTransform) {
170                 rTransform = new RenderTransform();
171                 if (!rTransform) return false;
172             }
173             rTransform->override(m);
174             flag |= RenderUpdateFlag::Transform;
175
176             return true;
177         }
178
179         bool bounds(float* x, float* y, float* w, float* h) const
180         {
181             return smethod->bounds(x, y, w, h);
182         }
183
184         RenderRegion bounds(RenderMethod& renderer) const
185         {
186             return smethod->bounds(renderer);
187         }
188
189         bool dispose(RenderMethod& renderer)
190         {
191             if (cmpTarget) cmpTarget->pImpl->dispose(renderer);
192             return smethod->dispose(renderer);
193         }
194
195         void* update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag)
196         {
197             if (flag & RenderUpdateFlag::Transform) {
198                 if (!rTransform) return nullptr;
199                 if (!rTransform->update()) {
200                     delete(rTransform);
201                     rTransform = nullptr;
202                 }
203             }
204
205             /* 1. Composition Pre Processing */
206             void *cmpData = nullptr;
207             RenderRegion viewport;
208             bool cmpFastTrack = false;
209
210             if (cmpTarget) {
211                 /* If transform has no rotation factors && ClipPath is a simple rectangle,
212                    we can avoid regular ClipPath sequence but use viewport for performance */
213                 if (cmpMethod == CompositeMethod::ClipPath) {
214                     RenderRegion viewport2;
215                     if ((cmpFastTrack = _clipPathFastTrack(cmpTarget, pTransform, viewport2))) {
216                         viewport = renderer.viewport();
217                         viewport2.merge(viewport);
218                         renderer.viewport(viewport2);
219                     }
220                 }
221
222                 if (!cmpFastTrack) {
223                     cmpData = cmpTarget->pImpl->update(renderer, pTransform, 255, clips, pFlag);
224                     if (cmpMethod == CompositeMethod::ClipPath) clips.push(cmpData);
225                 }
226             }
227
228             /* 2. Main Update */
229             void *edata = nullptr;
230             auto newFlag = static_cast<RenderUpdateFlag>(pFlag | flag);
231             flag = RenderUpdateFlag::None;
232             opacity = (opacity * this->opacity) / 255;
233
234             if (rTransform && pTransform) {
235                 RenderTransform outTransform(pTransform, rTransform);
236                 edata = smethod->update(renderer, &outTransform, opacity, clips, newFlag);
237             } else {
238                 auto outTransform = pTransform ? pTransform : rTransform;
239                 edata = smethod->update(renderer, outTransform, opacity, clips, newFlag);
240             }
241
242             /* 3. Composition Post Processing */
243             if (cmpFastTrack) renderer.viewport(viewport);
244             else if (cmpData && cmpMethod == CompositeMethod::ClipPath) clips.pop();
245
246             return edata;
247         }
248
249         bool render(RenderMethod& renderer)
250         {
251             Compositor* cmp = nullptr;
252
253             /* Note: only ClipPath is processed in update() step.
254                Create a composition image. */
255             if (cmpTarget && cmpMethod != CompositeMethod::ClipPath) {
256                 auto region = cmpTarget->pImpl->bounds(renderer);
257                 if (region.w == 0 || region.h == 0) return false;
258                 cmp = renderer.target(region);
259                 renderer.beginComposite(cmp, CompositeMethod::None, 255);
260                 cmpTarget->pImpl->render(renderer);
261             }
262
263             if (cmp) renderer.beginComposite(cmp, cmpMethod, cmpTarget->pImpl->opacity);
264
265             auto ret = smethod->render(renderer);
266
267             if (cmp) renderer.endComposite(cmp);
268
269             return ret;
270         }
271
272         Paint* duplicate()
273         {
274             auto ret = smethod->duplicate();
275             if (!ret) return nullptr;
276
277             //duplicate Transform
278             if (rTransform) {
279                 ret->pImpl->rTransform = new RenderTransform();
280                 if (ret->pImpl->rTransform) {
281                     *ret->pImpl->rTransform = *rTransform;
282                     ret->pImpl->flag |= RenderUpdateFlag::Transform;
283                 }
284             }
285
286             ret->pImpl->opacity = opacity;
287
288             if (cmpTarget) ret->pImpl->cmpTarget = cmpTarget->duplicate();
289
290             ret->pImpl->cmpMethod = cmpMethod;
291
292             return ret;
293         }
294
295         bool composite(Paint* target, CompositeMethod method)
296         {
297             if ((!target && method != CompositeMethod::None) || (target && method == CompositeMethod::None)) return false;
298             if (cmpTarget) delete(cmpTarget);
299             cmpTarget = target;
300             cmpMethod = method;
301             return true;
302         }
303     };
304
305
306     template<class T>
307     struct PaintMethod : StrategyMethod
308     {
309         T* inst = nullptr;
310
311         PaintMethod(T* _inst) : inst(_inst) {}
312         ~PaintMethod() {}
313
314         bool bounds(float* x, float* y, float* w, float* h) const override
315         {
316             return inst->bounds(x, y, w, h);
317         }
318
319         RenderRegion bounds(RenderMethod& renderer) const override
320         {
321             return inst->bounds(renderer);
322         }
323
324         bool dispose(RenderMethod& renderer) override
325         {
326             return inst->dispose(renderer);
327         }
328
329         void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flag) override
330         {
331             return inst->update(renderer, transform, opacity, clips, flag);
332         }
333
334         bool render(RenderMethod& renderer) override
335         {
336             return inst->render(renderer);
337         }
338
339         Paint* duplicate() override
340         {
341             return inst->duplicate();
342         }
343     };
344 }
345
346 #endif //_TVG_PAINT_H_