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 | |
34 | struct 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 | |
61 | struct 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 | |