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_CANVAS_H_
27 #define _TVG_CANVAS_H_
28 
29 #include "tvgPaint.h"
30 
31 
32 enum Status : uint8_t {Synced = 0, Updating, Drawing, Damaged};
33 
34 struct Canvas::Impl
35 {
36     list<Paint*> paints;
37     RenderMethod* renderer;
38     RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
39     Status status = Status::Synced;
40 
ImplImpl41     Impl(RenderMethod* pRenderer) : renderer(pRenderer)
42     {
43         renderer->ref();
44     }
45 
~ImplImpl46     ~Impl()
47     {
48         //make it sure any deferred jobs
49         renderer->sync();
50         renderer->clear();
51 
52         clearPaints();
53 
54         if (renderer->unref() == 0) delete(renderer);
55     }
56 
clearPaintsImpl57     void clearPaints()
58     {
59         for (auto paint : paints) {
60             if (P(paint)->unref() == 0) delete(paint);
61         }
62         paints.clear();
63     }
64 
pushImpl65     Result push(unique_ptr<Paint> paint)
66     {
67         //You cannot push paints during rendering.
68         if (status == Status::Drawing) return Result::InsufficientCondition;
69 
70         auto p = paint.release();
71         if (!p) return Result::MemoryCorruption;
72         PP(p)->ref();
73         paints.push_back(p);
74 
75         return update(p, true);
76     }
77 
clearImpl78     Result clear(bool free)
79     {
80         //Clear render target before drawing
81         if (!renderer->clear()) return Result::InsufficientCondition;
82 
83         //Free paints
84         if (free) clearPaints();
85 
86         status = Status::Synced;
87 
88         return Result::Success;
89     }
90 
updateImpl91     Result update(Paint* paint, bool force)
92     {
93         if (paints.empty() || status == Status::Drawing) return Result::InsufficientCondition;
94 
95         Array<RenderData> clips;
96         auto flag = RenderUpdateFlag::None;
97         if (status == Status::Damaged || force) flag = RenderUpdateFlag::All;
98 
99         auto m = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1};
100 
101         if (paint) {
102             paint->pImpl->update(renderer, m, clips, 255, flag);
103         } else {
104             for (auto paint : paints) {
105                 paint->pImpl->update(renderer, m, clips, 255, flag);
106             }
107         }
108         status = Status::Updating;
109         return Result::Success;
110     }
111 
drawImpl112     Result draw()
113     {
114         if (status == Status::Damaged) update(nullptr, false);
115         if (status == Status::Drawing || paints.empty() || !renderer->preRender()) return Result::InsufficientCondition;
116 
117         bool rendered = false;
118         for (auto paint : paints) {
119             if (paint->pImpl->render(renderer)) rendered = true;
120         }
121 
122         if (!rendered || !renderer->postRender()) return Result::InsufficientCondition;
123 
124         status = Status::Drawing;
125         return Result::Success;
126     }
127 
syncImpl128     Result sync()
129     {
130         if (status == Status::Synced || status == Status::Damaged) return Result::InsufficientCondition;
131 
132         if (renderer->sync()) {
133             status = Status::Synced;
134             return Result::Success;
135         }
136 
137         return Result::Unknown;
138     }
139 
viewportImpl140     Result viewport(int32_t x, int32_t y, int32_t w, int32_t h)
141     {
142         if (status != Status::Damaged && status != Status::Synced) return Result::InsufficientCondition;
143 
144         RenderRegion val = {x, y, w, h};
145         //intersect if the target buffer is already set.
146         auto surface = renderer->mainSurface();
147         if (surface && surface->w > 0 && surface->h > 0) {
148             val.intersect({0, 0, (int32_t)surface->w, (int32_t)surface->h});
149         }
150         if (vport == val) return Result::Success;
151         renderer->viewport(val);
152         vport = val;
153         status = Status::Damaged;
154         return Result::Success;
155     }
156 };
157 
158 #endif /* _TVG_CANVAS_H_ */
159 
160 #endif /* LV_USE_THORVG_INTERNAL */
161 
162