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_SCENE_IMPL_H_
24#define _TVG_SCENE_IMPL_H_
25
26#include <float.h>
27#include "tvgPaint.h"
28
29/************************************************************************/
30/* Internal Class Implementation */
31/************************************************************************/
32
33struct SceneIterator : Iterator
34{
35 list<Paint*>* paints;
36 list<Paint*>::iterator itr;
37
38 SceneIterator(list<Paint*>* p) : paints(p)
39 {
40 begin();
41 }
42
43 const Paint* next() override
44 {
45 if (itr == paints->end()) return nullptr;
46 auto paint = *itr;
47 ++itr;
48 return paint;
49 }
50
51 uint32_t count() override
52 {
53 return paints->size();
54 }
55
56 void begin() override
57 {
58 itr = paints->begin();
59 }
60};
61
62struct Scene::Impl
63{
64 list<Paint*> paints;
65 RenderMethod* renderer = nullptr; //keep it for explicit clear
66 RenderData rd = nullptr;
67 Scene* scene = nullptr;
68 uint8_t opacity; //for composition
69 bool needComp; //composite or not
70
71 Impl(Scene* s) : scene(s)
72 {
73 }
74
75 ~Impl()
76 {
77 for (auto paint : paints) {
78 delete(paint);
79 }
80 }
81
82 bool dispose(RenderMethod& renderer)
83 {
84 for (auto paint : paints) {
85 paint->pImpl->dispose(renderer);
86 }
87
88 auto ret = renderer.dispose(rd);
89 this->renderer = nullptr;
90 this->rd = nullptr;
91
92 return ret;
93 }
94
95 bool needComposition(uint8_t opacity)
96 {
97 if (opacity == 0 || paints.empty()) return false;
98
99 //Masking may require composition (even if opacity == 255)
100 auto compMethod = scene->composite(nullptr);
101 if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true;
102
103 //Blending may require composition (even if opacity == 255)
104 if (scene->blend() != BlendMethod::Normal) return true;
105
106 //Half translucent requires intermediate composition.
107 if (opacity == 255) return false;
108
109 //If scene has several children or only scene, it may require composition.
110 //OPTIMIZE: the bitmap type of the picture would not need the composition.
111 //OPTIMIZE: a single paint of a scene would not need the composition.
112 if (paints.size() == 1 && paints.front()->identifier() == TVG_CLASS_ID_SHAPE) return false;
113
114 return true;
115 }
116
117 RenderData update(RenderMethod &renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper)
118 {
119 if ((needComp = needComposition(opacity))) {
120 /* Overriding opacity value. If this scene is half-translucent,
121 It must do intermeidate composition with that opacity value. */
122 this->opacity = opacity;
123 opacity = 255;
124 }
125
126 this->renderer = &renderer;
127
128 if (clipper) {
129 Array<RenderData> rds;
130 rds.reserve(paints.size());
131 for (auto paint : paints) {
132 rds.push(paint->pImpl->update(renderer, transform, clips, opacity, flag, true));
133 }
134 rd = renderer.prepare(rds, rd, transform, clips, opacity, flag);
135 return rd;
136 } else {
137 for (auto paint : paints) {
138 paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
139 }
140 return nullptr;
141 }
142 }
143
144 bool render(RenderMethod& renderer)
145 {
146 Compositor* cmp = nullptr;
147
148 if (needComp) {
149 cmp = renderer.target(bounds(renderer), renderer.colorSpace());
150 renderer.beginComposite(cmp, CompositeMethod::None, opacity);
151 }
152
153 for (auto paint : paints) {
154 if (!paint->pImpl->render(renderer)) return false;
155 }
156
157 if (cmp) renderer.endComposite(cmp);
158
159 return true;
160 }
161
162 RenderRegion bounds(RenderMethod& renderer) const
163 {
164 if (paints.empty()) return {0, 0, 0, 0};
165
166 int32_t x1 = INT32_MAX;
167 int32_t y1 = INT32_MAX;
168 int32_t x2 = 0;
169 int32_t y2 = 0;
170
171 for (auto paint : paints) {
172 auto region = paint->pImpl->bounds(renderer);
173
174 //Merge regions
175 if (region.x < x1) x1 = region.x;
176 if (x2 < region.x + region.w) x2 = (region.x + region.w);
177 if (region.y < y1) y1 = region.y;
178 if (y2 < region.y + region.h) y2 = (region.y + region.h);
179 }
180
181 return {x1, y1, (x2 - x1), (y2 - y1)};
182 }
183
184 bool bounds(float* px, float* py, float* pw, float* ph)
185 {
186 if (paints.empty()) return false;
187
188 auto x1 = FLT_MAX;
189 auto y1 = FLT_MAX;
190 auto x2 = -FLT_MAX;
191 auto y2 = -FLT_MAX;
192
193 for (auto paint : paints) {
194 auto x = FLT_MAX;
195 auto y = FLT_MAX;
196 auto w = 0.0f;
197 auto h = 0.0f;
198
199 if (paint->bounds(&x, &y, &w, &h, true) != tvg::Result::Success) continue;
200
201 //Merge regions
202 if (x < x1) x1 = x;
203 if (x2 < x + w) x2 = (x + w);
204 if (y < y1) y1 = y;
205 if (y2 < y + h) y2 = (y + h);
206 }
207
208 if (px) *px = x1;
209 if (py) *py = y1;
210 if (pw) *pw = (x2 - x1);
211 if (ph) *ph = (y2 - y1);
212
213 return true;
214 }
215
216 Paint* duplicate()
217 {
218 auto ret = Scene::gen();
219
220 auto dup = ret.get()->pImpl;
221
222 for (auto paint : paints) {
223 dup->paints.push_back(paint->duplicate());
224 }
225
226 return ret.release();
227 }
228
229 void clear(bool free)
230 {
231 auto dispose = renderer ? true : false;
232
233 for (auto paint : paints) {
234 if (dispose) paint->pImpl->dispose(*renderer);
235 if (free) delete(paint);
236 }
237 paints.clear();
238 renderer = nullptr;
239 }
240
241 Iterator* iterator()
242 {
243 return new SceneIterator(&paints);
244 }
245};
246
247#endif //_TVG_SCENE_IMPL_H_
248