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_SHAPE_H_ 27 #define _TVG_SHAPE_H_ 28 29 #include <memory.h> 30 #include "tvgMath.h" 31 #include "tvgPaint.h" 32 33 34 struct Shape::Impl 35 { 36 RenderShape rs; //shape data 37 RenderData rd = nullptr; //engine data 38 Shape* shape; 39 uint8_t flag = RenderUpdateFlag::None; 40 41 uint8_t opacity; //for composition 42 bool needComp = false; //composite or not 43 ImplImpl44 Impl(Shape* s) : shape(s) 45 { 46 } 47 ~ImplImpl48 ~Impl() 49 { 50 if (auto renderer = PP(shape)->renderer) { 51 renderer->dispose(rd); 52 } 53 } 54 renderImpl55 bool render(RenderMethod* renderer) 56 { 57 if (!rd) return false; 58 59 RenderCompositor* cmp = nullptr; 60 61 renderer->blend(PP(shape)->blendMethod); 62 63 if (needComp) { 64 cmp = renderer->target(bounds(renderer), renderer->colorSpace()); 65 renderer->beginComposite(cmp, CompositeMethod::None, opacity); 66 } 67 68 auto ret = renderer->renderShape(rd); 69 if (cmp) renderer->endComposite(cmp); 70 return ret; 71 } 72 needCompositionImpl73 bool needComposition(uint8_t opacity) 74 { 75 if (opacity == 0) return false; 76 77 //Shape composition is only necessary when stroking & fill are valid. 78 if (!rs.stroke || rs.stroke->width < FLOAT_EPSILON || (!rs.stroke->fill && rs.stroke->color[3] == 0)) return false; 79 if (!rs.fill && rs.color[3] == 0) return false; 80 81 //translucent fill & stroke 82 if (opacity < 255) return true; 83 84 //Composition test 85 const Paint* target; 86 auto method = shape->composite(&target); 87 if (!target || method == CompositeMethod::ClipPath) return false; 88 if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) { 89 if (target->type() == Type::Shape) { 90 auto shape = static_cast<const Shape*>(target); 91 if (!shape->fill()) { 92 uint8_t r, g, b, a; 93 shape->fillColor(&r, &g, &b, &a); 94 if (a == 0 || a == 255) { 95 if (method == CompositeMethod::LumaMask || method == CompositeMethod::InvLumaMask) { 96 if ((r == 255 && g == 255 && b == 255) || (r == 0 && g == 0 && b == 0)) return false; 97 } else return false; 98 } 99 } 100 } 101 } 102 103 return true; 104 } 105 updateImpl106 RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) 107 { 108 if (static_cast<RenderUpdateFlag>(pFlag | flag) == RenderUpdateFlag::None) return rd; 109 110 if ((needComp = needComposition(opacity))) { 111 /* Overriding opacity value. If this scene is half-translucent, 112 It must do intermediate composition with that opacity value. */ 113 this->opacity = opacity; 114 opacity = 255; 115 } 116 117 rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper); 118 flag = RenderUpdateFlag::None; 119 return rd; 120 } 121 boundsImpl122 RenderRegion bounds(RenderMethod* renderer) 123 { 124 if (!rd) return {0, 0, 0, 0}; 125 return renderer->region(rd); 126 } 127 boundsImpl128 bool bounds(float* x, float* y, float* w, float* h, bool stroking) 129 { 130 //Path bounding size 131 if (rs.path.pts.count > 0 ) { 132 auto pts = rs.path.pts.begin(); 133 Point min = { pts->x, pts->y }; 134 Point max = { pts->x, pts->y }; 135 136 for (auto pts2 = pts + 1; pts2 < rs.path.pts.end(); ++pts2) { 137 if (pts2->x < min.x) min.x = pts2->x; 138 if (pts2->y < min.y) min.y = pts2->y; 139 if (pts2->x > max.x) max.x = pts2->x; 140 if (pts2->y > max.y) max.y = pts2->y; 141 } 142 143 if (x) *x = min.x; 144 if (y) *y = min.y; 145 if (w) *w = max.x - min.x; 146 if (h) *h = max.y - min.y; 147 } 148 149 //Stroke feathering 150 if (stroking && rs.stroke) { 151 if (x) *x -= rs.stroke->width * 0.5f; 152 if (y) *y -= rs.stroke->width * 0.5f; 153 if (w) *w += rs.stroke->width; 154 if (h) *h += rs.stroke->width; 155 } 156 return rs.path.pts.count > 0 ? true : false; 157 } 158 reserveCmdImpl159 void reserveCmd(uint32_t cmdCnt) 160 { 161 rs.path.cmds.reserve(cmdCnt); 162 } 163 reservePtsImpl164 void reservePts(uint32_t ptsCnt) 165 { 166 rs.path.pts.reserve(ptsCnt); 167 } 168 growImpl169 void grow(uint32_t cmdCnt, uint32_t ptsCnt) 170 { 171 rs.path.cmds.grow(cmdCnt); 172 rs.path.pts.grow(ptsCnt); 173 } 174 appendImpl175 void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) 176 { 177 memcpy(rs.path.cmds.end(), cmds, sizeof(PathCommand) * cmdCnt); 178 memcpy(rs.path.pts.end(), pts, sizeof(Point) * ptsCnt); 179 rs.path.cmds.count += cmdCnt; 180 rs.path.pts.count += ptsCnt; 181 } 182 moveToImpl183 void moveTo(float x, float y) 184 { 185 rs.path.cmds.push(PathCommand::MoveTo); 186 rs.path.pts.push({x, y}); 187 } 188 lineToImpl189 void lineTo(float x, float y) 190 { 191 rs.path.cmds.push(PathCommand::LineTo); 192 rs.path.pts.push({x, y}); 193 } 194 cubicToImpl195 void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) 196 { 197 rs.path.cmds.push(PathCommand::CubicTo); 198 rs.path.pts.push({cx1, cy1}); 199 rs.path.pts.push({cx2, cy2}); 200 rs.path.pts.push({x, y}); 201 } 202 closeImpl203 void close() 204 { 205 //Don't close multiple times. 206 if (rs.path.cmds.count > 0 && rs.path.cmds.last() == PathCommand::Close) return; 207 208 rs.path.cmds.push(PathCommand::Close); 209 } 210 strokeWidthImpl211 void strokeWidth(float width) 212 { 213 if (!rs.stroke) rs.stroke = new RenderStroke(); 214 rs.stroke->width = width; 215 flag |= RenderUpdateFlag::Stroke; 216 } 217 strokeTrimImpl218 void strokeTrim(float begin, float end, bool simultaneous) 219 { 220 if (!rs.stroke) { 221 if (begin == 0.0f && end == 1.0f) return; 222 rs.stroke = new RenderStroke(); 223 } 224 225 if (tvg::equal(rs.stroke->trim.begin, begin) && tvg::equal(rs.stroke->trim.end, end) && 226 rs.stroke->trim.simultaneous == simultaneous) return; 227 228 rs.stroke->trim.begin = begin; 229 rs.stroke->trim.end = end; 230 rs.stroke->trim.simultaneous = simultaneous; 231 flag |= RenderUpdateFlag::Stroke; 232 } 233 strokeTrimImpl234 bool strokeTrim(float* begin, float* end) 235 { 236 if (rs.stroke) { 237 if (begin) *begin = rs.stroke->trim.begin; 238 if (end) *end = rs.stroke->trim.end; 239 return rs.stroke->trim.simultaneous; 240 } else { 241 if (begin) *begin = 0.0f; 242 if (end) *end = 1.0f; 243 return false; 244 } 245 } 246 strokeCapImpl247 void strokeCap(StrokeCap cap) 248 { 249 if (!rs.stroke) rs.stroke = new RenderStroke(); 250 rs.stroke->cap = cap; 251 flag |= RenderUpdateFlag::Stroke; 252 } 253 strokeJoinImpl254 void strokeJoin(StrokeJoin join) 255 { 256 if (!rs.stroke) rs.stroke = new RenderStroke(); 257 rs.stroke->join = join; 258 flag |= RenderUpdateFlag::Stroke; 259 } 260 strokeMiterlimitImpl261 void strokeMiterlimit(float miterlimit) 262 { 263 if (!rs.stroke) rs.stroke = new RenderStroke(); 264 rs.stroke->miterlimit = miterlimit; 265 flag |= RenderUpdateFlag::Stroke; 266 } 267 strokeColorImpl268 void strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) 269 { 270 if (!rs.stroke) rs.stroke = new RenderStroke(); 271 if (rs.stroke->fill) { 272 delete(rs.stroke->fill); 273 rs.stroke->fill = nullptr; 274 flag |= RenderUpdateFlag::GradientStroke; 275 } 276 277 rs.stroke->color[0] = r; 278 rs.stroke->color[1] = g; 279 rs.stroke->color[2] = b; 280 rs.stroke->color[3] = a; 281 282 flag |= RenderUpdateFlag::Stroke; 283 } 284 strokeFillImpl285 Result strokeFill(unique_ptr<Fill> f) 286 { 287 auto p = f.release(); 288 if (!p) return Result::MemoryCorruption; 289 290 if (!rs.stroke) rs.stroke = new RenderStroke(); 291 if (rs.stroke->fill && rs.stroke->fill != p) delete(rs.stroke->fill); 292 rs.stroke->fill = p; 293 rs.stroke->color[3] = 0; 294 295 flag |= RenderUpdateFlag::Stroke; 296 flag |= RenderUpdateFlag::GradientStroke; 297 298 return Result::Success; 299 } 300 strokeDashImpl301 Result strokeDash(const float* pattern, uint32_t cnt, float offset) 302 { 303 if ((cnt == 1) || (!pattern && cnt > 0) || (pattern && cnt == 0)) { 304 return Result::InvalidArguments; 305 } 306 307 for (uint32_t i = 0; i < cnt; i++) { 308 if (pattern[i] < FLOAT_EPSILON) return Result::InvalidArguments; 309 } 310 311 //Reset dash 312 if (!pattern && cnt == 0) { 313 free(rs.stroke->dashPattern); 314 rs.stroke->dashPattern = nullptr; 315 } else { 316 if (!rs.stroke) rs.stroke = new RenderStroke(); 317 if (rs.stroke->dashCnt != cnt) { 318 free(rs.stroke->dashPattern); 319 rs.stroke->dashPattern = nullptr; 320 } 321 if (!rs.stroke->dashPattern) { 322 rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt)); 323 if (!rs.stroke->dashPattern) return Result::FailedAllocation; 324 } 325 for (uint32_t i = 0; i < cnt; ++i) { 326 rs.stroke->dashPattern[i] = pattern[i]; 327 } 328 } 329 rs.stroke->dashCnt = cnt; 330 rs.stroke->dashOffset = offset; 331 flag |= RenderUpdateFlag::Stroke; 332 333 return Result::Success; 334 } 335 strokeFirstImpl336 bool strokeFirst() 337 { 338 if (!rs.stroke) return true; 339 return rs.stroke->strokeFirst; 340 } 341 strokeFirstImpl342 void strokeFirst(bool strokeFirst) 343 { 344 if (!rs.stroke) rs.stroke = new RenderStroke(); 345 rs.stroke->strokeFirst = strokeFirst; 346 flag |= RenderUpdateFlag::Stroke; 347 } 348 updateImpl349 void update(RenderUpdateFlag flag) 350 { 351 this->flag |= flag; 352 } 353 duplicateImpl354 Paint* duplicate(Paint* ret) 355 { 356 auto shape = static_cast<Shape*>(ret); 357 if (shape) shape->reset(); 358 else shape = Shape::gen().release(); 359 360 auto dup = shape->pImpl; 361 delete(dup->rs.fill); 362 363 //Default Properties 364 dup->flag = RenderUpdateFlag::All; 365 dup->rs.rule = rs.rule; 366 367 //Color 368 memcpy(dup->rs.color, rs.color, sizeof(rs.color)); 369 370 //Path 371 dup->rs.path.cmds.push(rs.path.cmds); 372 dup->rs.path.pts.push(rs.path.pts); 373 374 //Stroke 375 if (rs.stroke) { 376 if (!dup->rs.stroke) dup->rs.stroke = new RenderStroke; 377 *dup->rs.stroke = *rs.stroke; 378 } else { 379 delete(dup->rs.stroke); 380 dup->rs.stroke = nullptr; 381 } 382 383 //Fill 384 if (rs.fill) dup->rs.fill = rs.fill->duplicate(); 385 else dup->rs.fill = nullptr; 386 387 return shape; 388 } 389 resetImpl390 void reset() 391 { 392 PP(shape)->reset(); 393 rs.path.cmds.clear(); 394 rs.path.pts.clear(); 395 396 rs.color[3] = 0; 397 rs.rule = FillRule::Winding; 398 399 delete(rs.stroke); 400 rs.stroke = nullptr; 401 402 delete(rs.fill); 403 rs.fill = nullptr; 404 } 405 iteratorImpl406 Iterator* iterator() 407 { 408 return nullptr; 409 } 410 }; 411 412 #endif //_TVG_SHAPE_H_ 413 414 #endif /* LV_USE_THORVG_INTERNAL */ 415 416