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