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_PICTURE_IMPL_H_
24#define _TVG_PICTURE_IMPL_H_
25
26#include <string>
27#include "tvgPaint.h"
28#include "tvgLoader.h"
29
30/************************************************************************/
31/* Internal Class Implementation */
32/************************************************************************/
33
34struct PictureIterator : Iterator
35{
36 Paint* paint = nullptr;
37 Paint* ptr = nullptr;
38
39 PictureIterator(Paint* p) : paint(p) {}
40
41 const Paint* next() override
42 {
43 if (!ptr) ptr = paint;
44 else ptr = nullptr;
45 return ptr;
46 }
47
48 uint32_t count() override
49 {
50 if (paint) return 1;
51 else return 0;
52 }
53
54 void begin() override
55 {
56 ptr = nullptr;
57 }
58};
59
60
61struct Picture::Impl
62{
63 shared_ptr<LoadModule> loader = nullptr;
64
65 Paint* paint = nullptr; //vector picture uses
66 Surface* surface = nullptr; //bitmap picture uses
67 RenderData rd = nullptr; //engine data
68 float w = 0, h = 0;
69 RenderMesh rm; //mesh data
70 Picture* picture = nullptr;
71 bool resizing = false;
72 bool needComp = false; //need composition
73 bool animated = false; //picture is belonged to Animation
74
75 Impl(Picture* p) : picture(p)
76 {
77 }
78
79 ~Impl()
80 {
81 delete(paint);
82 delete(surface);
83 }
84
85 bool dispose(RenderMethod& renderer)
86 {
87 bool ret = true;
88 if (paint) ret = paint->pImpl->dispose(renderer);
89 else if (surface) ret = renderer.dispose(rd);
90 rd = nullptr;
91
92 return ret;
93 }
94
95 RenderUpdateFlag load()
96 {
97 if (loader) {
98 if (!paint) {
99 if (auto p = loader->paint()) {
100 paint = p.release();
101 loader->close();
102 if (w != loader->w || h != loader->h) {
103 if (!resizing) {
104 w = loader->w;
105 h = loader->h;
106 }
107 loader->resize(paint, w, h);
108 resizing = false;
109 }
110 if (paint) return RenderUpdateFlag::None;
111 }
112 } else loader->sync();
113
114 if (!surface) {
115 if ((surface = loader->bitmap().release())) {
116 loader->close();
117 return RenderUpdateFlag::Image;
118 }
119 }
120 }
121 return RenderUpdateFlag::None;
122 }
123
124 RenderTransform resizeTransform(const RenderTransform* pTransform)
125 {
126 //Overriding Transformation by the desired image size
127 auto sx = w / loader->w;
128 auto sy = h / loader->h;
129 auto scale = sx < sy ? sx : sy;
130
131 RenderTransform tmp;
132 tmp.m = {scale, 0, 0, 0, scale, 0, 0, 0, 1};
133
134 if (!pTransform) return tmp;
135 else return RenderTransform(pTransform, &tmp);
136 }
137
138 bool needComposition(uint8_t opacity)
139 {
140 //In this case, paint(scene) would try composition itself.
141 if (opacity < 255) return false;
142
143 //Composition test
144 const Paint* target;
145 auto method = picture->composite(&target);
146 if (!target || method == tvg::CompositeMethod::ClipPath) return false;
147 if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false;
148
149 return true;
150 }
151
152 RenderData update(RenderMethod &renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
153 {
154 auto flag = load();
155
156 if (surface) {
157 auto transform = resizeTransform(pTransform);
158 rd = renderer.prepare(surface, &rm, rd, &transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag));
159 } else if (paint) {
160 if (resizing) {
161 loader->resize(paint, w, h);
162 resizing = false;
163 }
164 needComp = needComposition(opacity) ? true : false;
165 rd = paint->pImpl->update(renderer, pTransform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
166 }
167 return rd;
168 }
169
170 bool render(RenderMethod &renderer)
171 {
172 bool ret = false;
173 if (surface) return renderer.renderImage(rd);
174 else if (paint) {
175 Compositor* cmp = nullptr;
176 if (needComp) {
177 cmp = renderer.target(bounds(renderer), renderer.colorSpace());
178 renderer.beginComposite(cmp, CompositeMethod::None, 255);
179 }
180 ret = paint->pImpl->render(renderer);
181 if (cmp) renderer.endComposite(cmp);
182 }
183 return ret;
184 }
185
186 bool size(float w, float h)
187 {
188 this->w = w;
189 this->h = h;
190 resizing = true;
191 return true;
192 }
193
194 bool bounds(float* x, float* y, float* w, float* h)
195 {
196 if (rm.triangleCnt > 0) {
197 auto triangles = rm.triangles;
198 auto min = triangles[0].vertex[0].pt;
199 auto max = triangles[0].vertex[0].pt;
200
201 for (uint32_t i = 0; i < rm.triangleCnt; ++i) {
202 if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x;
203 else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x;
204 if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y;
205 else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y;
206
207 if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x;
208 else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x;
209 if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y;
210 else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y;
211
212 if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x;
213 else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x;
214 if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y;
215 else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y;
216 }
217 if (x) *x = min.x;
218 if (y) *y = min.y;
219 if (w) *w = max.x - min.x;
220 if (h) *h = max.y - min.y;
221 } else {
222 if (x) *x = 0;
223 if (y) *y = 0;
224 if (w) *w = this->w;
225 if (h) *h = this->h;
226 }
227 return true;
228 }
229
230 RenderRegion bounds(RenderMethod& renderer)
231 {
232 if (rd) return renderer.region(rd);
233 if (paint) return paint->pImpl->bounds(renderer);
234 return {0, 0, 0, 0};
235 }
236
237 Result load(const string& path)
238 {
239 if (paint || surface) return Result::InsufficientCondition;
240 if (loader) loader->close();
241 bool invalid; //Invalid Path
242 loader = LoaderMgr::loader(path, &invalid);
243 if (!loader) {
244 if (invalid) return Result::InvalidArguments;
245 return Result::NonSupport;
246 }
247 if (!loader->read()) return Result::Unknown;
248 w = loader->w;
249 h = loader->h;
250 return Result::Success;
251 }
252
253 Result load(const char* data, uint32_t size, const string& mimeType, bool copy)
254 {
255 if (paint || surface) return Result::InsufficientCondition;
256 if (loader) loader->close();
257 loader = LoaderMgr::loader(data, size, mimeType, copy);
258 if (!loader) return Result::NonSupport;
259 if (!loader->read()) return Result::Unknown;
260 w = loader->w;
261 h = loader->h;
262 return Result::Success;
263 }
264
265 Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy)
266 {
267 if (paint || surface) return Result::InsufficientCondition;
268 if (loader) loader->close();
269 loader = LoaderMgr::loader(data, w, h, copy);
270 if (!loader) return Result::FailedAllocation;
271 this->w = loader->w;
272 this->h = loader->h;
273 return Result::Success;
274 }
275
276 void mesh(const Polygon* triangles, const uint32_t triangleCnt)
277 {
278 if (triangles && triangleCnt > 0) {
279 this->rm.triangleCnt = triangleCnt;
280 this->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCnt);
281 memcpy(this->rm.triangles, triangles, sizeof(Polygon) * triangleCnt);
282 } else {
283 free(this->rm.triangles);
284 this->rm.triangles = nullptr;
285 this->rm.triangleCnt = 0;
286 }
287 }
288
289 Paint* duplicate()
290 {
291 load();
292
293 auto ret = Picture::gen();
294
295 auto dup = ret.get()->pImpl;
296 if (paint) dup->paint = paint->duplicate();
297
298 dup->loader = loader;
299 if (surface) {
300 dup->surface = new Surface;
301 *dup->surface = *surface;
302 //TODO: A dupilcation is not a proxy... it needs copy of the pixel data?
303 dup->surface->owner = false;
304 }
305 dup->w = w;
306 dup->h = h;
307 dup->resizing = resizing;
308
309 if (rm.triangleCnt > 0) {
310 dup->rm.triangleCnt = rm.triangleCnt;
311 dup->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * rm.triangleCnt);
312 memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt);
313 }
314
315 return ret.release();
316 }
317
318 Iterator* iterator()
319 {
320 load();
321 return new PictureIterator(paint);
322 }
323};
324
325#endif //_TVG_PICTURE_IMPL_H_
326