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_SCENE_H_
27 #define _TVG_SCENE_H_
28 
29 #include "tvgMath.h"
30 #include "tvgPaint.h"
31 
32 struct SceneIterator : Iterator
33 {
34     list<Paint*>* paints;
35     list<Paint*>::iterator itr;
36 
SceneIteratorSceneIterator37     SceneIterator(list<Paint*>* p) : paints(p)
38     {
39         begin();
40     }
41 
nextSceneIterator42     const Paint* next() override
43     {
44         if (itr == paints->end()) return nullptr;
45         auto paint = *itr;
46         ++itr;
47         return paint;
48     }
49 
countSceneIterator50     uint32_t count() override
51     {
52        return paints->size();
53     }
54 
beginSceneIterator55     void begin() override
56     {
57         itr = paints->begin();
58     }
59 };
60 
61 struct Scene::Impl
62 {
63     list<Paint*> paints;
64     RenderData rd = nullptr;
65     Scene* scene = nullptr;
66     RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
67     Array<RenderEffect*>* effects = nullptr;
68     uint8_t opacity;         //for composition
69     bool needComp = false;   //composite or not
70 
ImplImpl71     Impl(Scene* s) : scene(s)
72     {
73     }
74 
~ImplImpl75     ~Impl()
76     {
77         resetEffects();
78 
79         for (auto paint : paints) {
80             if (P(paint)->unref() == 0) delete(paint);
81         }
82 
83         if (auto renderer = PP(scene)->renderer) {
84             renderer->dispose(rd);
85         }
86     }
87 
needCompositionImpl88     bool needComposition(uint8_t opacity)
89     {
90         if (opacity == 0 || paints.empty()) return false;
91 
92         //post effects requires composition
93         if (effects) return true;
94 
95         //Masking may require composition (even if opacity == 255)
96         auto compMethod = scene->composite(nullptr);
97         if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true;
98 
99         //Blending may require composition (even if opacity == 255)
100         if (PP(scene)->blendMethod != BlendMethod::Normal) return true;
101 
102         //Half translucent requires intermediate composition.
103         if (opacity == 255) return false;
104 
105         //If scene has several children or only scene, it may require composition.
106         //OPTIMIZE: the bitmap type of the picture would not need the composition.
107         //OPTIMIZE: a single paint of a scene would not need the composition.
108         if (paints.size() == 1 && paints.front()->type() == Type::Shape) return false;
109 
110         return true;
111     }
112 
updateImpl113     RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper)
114     {
115         this->vport = renderer->viewport();
116 
117         if ((needComp = needComposition(opacity))) {
118             /* Overriding opacity value. If this scene is half-translucent,
119                It must do intermediate composition with that opacity value. */
120             this->opacity = opacity;
121             opacity = 255;
122         }
123         for (auto paint : paints) {
124             paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
125         }
126 
127         return nullptr;
128     }
129 
renderImpl130     bool render(RenderMethod* renderer)
131     {
132         RenderCompositor* cmp = nullptr;
133         auto ret = true;
134 
135         renderer->blend(PP(scene)->blendMethod);
136 
137         if (needComp) {
138             cmp = renderer->target(bounds(renderer), renderer->colorSpace());
139             renderer->beginComposite(cmp, CompositeMethod::None, opacity);
140         }
141 
142         for (auto paint : paints) {
143             ret &= paint->pImpl->render(renderer);
144         }
145 
146         if (cmp) {
147             //Apply post effects if any.
148             if (effects) {
149                 for (auto e = effects->begin(); e < effects->end(); ++e) {
150                     renderer->effect(cmp, *e);
151                 }
152             }
153             renderer->endComposite(cmp);
154         }
155 
156         return ret;
157     }
158 
boundsImpl159     RenderRegion bounds(RenderMethod* renderer) const
160     {
161         if (paints.empty()) return {0, 0, 0, 0};
162 
163         int32_t x1 = INT32_MAX;
164         int32_t y1 = INT32_MAX;
165         int32_t x2 = 0;
166         int32_t y2 = 0;
167 
168         for (auto paint : paints) {
169             auto region = paint->pImpl->bounds(renderer);
170 
171             //Merge regions
172             if (region.x < x1) x1 = region.x;
173             if (x2 < region.x + region.w) x2 = (region.x + region.w);
174             if (region.y < y1) y1 = region.y;
175             if (y2 < region.y + region.h) y2 = (region.y + region.h);
176         }
177 
178         //Extends the render region if post effects require
179         int32_t ex = 0, ey = 0, ew = 0, eh = 0;
180         if (effects) {
181             for (auto e = effects->begin(); e < effects->end(); ++e) {
182                 auto effect = *e;
183                 if (effect->rd || renderer->prepare(effect)) {
184                     ex = std::min(ex, effect->extend.x);
185                     ey = std::min(ey, effect->extend.y);
186                     ew = std::max(ew, effect->extend.w);
187                     eh = std::max(eh, effect->extend.h);
188                 }
189             }
190         }
191 
192         auto ret = RenderRegion{x1 + ex, y1 + ey, (x2 - x1) + ew, (y2 - y1) + eh};
193         ret.intersect(this->vport);
194         return ret;
195     }
196 
boundsImpl197     bool bounds(float* px, float* py, float* pw, float* ph, bool stroking)
198     {
199         if (paints.empty()) return false;
200 
201         auto x1 = FLT_MAX;
202         auto y1 = FLT_MAX;
203         auto x2 = -FLT_MAX;
204         auto y2 = -FLT_MAX;
205 
206         for (auto paint : paints) {
207             auto x = FLT_MAX;
208             auto y = FLT_MAX;
209             auto w = 0.0f;
210             auto h = 0.0f;
211 
212             if (!P(paint)->bounds(&x, &y, &w, &h, true, stroking)) continue;
213 
214             //Merge regions
215             if (x < x1) x1 = x;
216             if (x2 < x + w) x2 = (x + w);
217             if (y < y1) y1 = y;
218             if (y2 < y + h) y2 = (y + h);
219         }
220 
221         if (px) *px = x1;
222         if (py) *py = y1;
223         if (pw) *pw = (x2 - x1);
224         if (ph) *ph = (y2 - y1);
225 
226         return true;
227     }
228 
duplicateImpl229     Paint* duplicate(Paint* ret)
230     {
231         if (ret) TVGERR("RENDERER", "TODO: duplicate()");
232 
233         auto scene = Scene::gen().release();
234         auto dup = scene->pImpl;
235 
236         for (auto paint : paints) {
237             auto cdup = paint->duplicate();
238             P(cdup)->ref();
239             dup->paints.push_back(cdup);
240         }
241 
242         if (effects) TVGERR("RENDERER", "TODO: Duplicate Effects?");
243 
244         return scene;
245     }
246 
clearImpl247     void clear(bool free)
248     {
249         for (auto paint : paints) {
250             if (P(paint)->unref() == 0 && free) delete(paint);
251         }
252         paints.clear();
253     }
254 
iteratorImpl255     Iterator* iterator()
256     {
257         return new SceneIterator(&paints);
258     }
259 
260     Result resetEffects();
261 };
262 
263 #endif //_TVG_SCENE_H_
264 
265 #endif /* LV_USE_THORVG_INTERNAL */
266 
267