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#include "tvgMath.h"
24#include "tvgSwCommon.h"
25#include "tvgTaskScheduler.h"
26#include "tvgSwRenderer.h"
27
28/************************************************************************/
29/* Internal Class Implementation */
30/************************************************************************/
31static int32_t initEngineCnt = false;
32static int32_t rendererCnt = 0;
33static SwMpool* globalMpool = nullptr;
34static uint32_t threadsCnt = 0;
35
36struct SwTask : Task
37{
38 SwSurface* surface = nullptr;
39 SwMpool* mpool = nullptr;
40 SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region
41 Matrix* transform = nullptr;
42 Array<RenderData> clips;
43 RenderUpdateFlag flags = RenderUpdateFlag::None;
44 uint8_t opacity;
45 bool pushed = false; //Pushed into task list?
46 bool disposed = false; //Disposed task?
47
48 RenderRegion bounds() const
49 {
50 RenderRegion region;
51
52 //Range over?
53 region.x = bbox.min.x > 0 ? bbox.min.x : 0;
54 region.y = bbox.min.y > 0 ? bbox.min.y : 0;
55 region.w = bbox.max.x - region.x;
56 region.h = bbox.max.y - region.y;
57 if (region.w < 0) region.w = 0;
58 if (region.h < 0) region.h = 0;
59
60 return region;
61 }
62
63 virtual bool dispose() = 0;
64 virtual bool clip(SwRleData* target) = 0;
65 virtual SwRleData* rle() = 0;
66
67 virtual ~SwTask()
68 {
69 free(transform);
70 }
71};
72
73
74struct SwShapeTask : SwTask
75{
76 SwShape shape;
77 const RenderShape* rshape = nullptr;
78 bool cmpStroking = false;
79 bool clipper = false;
80
81 bool clip(SwRleData* target) override
82 {
83 if (shape.fastTrack) rleClipRect(target, &bbox);
84 else if (shape.rle) rleClipPath(target, shape.rle);
85 else return false;
86
87 return true;
88 }
89
90 SwRleData* rle() override
91 {
92 if (!shape.rle && shape.fastTrack) {
93 shape.rle = rleRender(&shape.bbox);
94 }
95 return shape.rle;
96 }
97
98 void run(unsigned tid) override
99 {
100 if (opacity == 0 && !clipper) return; //Invisible
101
102 uint8_t strokeAlpha = 0;
103 auto visibleStroke = false;
104 bool visibleFill = false;
105 auto clipRegion = bbox;
106
107 if (HALF_STROKE(rshape->strokeWidth()) > 0) {
108 rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
109 visibleStroke = rshape->strokeFill() || (MULTIPLY(strokeAlpha, opacity) > 0);
110 }
111
112 //This checks also for the case, if the invisible shape turned to visible by alpha.
113 auto prepareShape = false;
114 if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true;
115
116 //Shape
117 if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
118 uint8_t alpha = 0;
119 rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
120 alpha = MULTIPLY(alpha, opacity);
121 visibleFill = (alpha > 0 || rshape->fill);
122 if (visibleFill || visibleStroke || clipper) {
123 shapeReset(&shape);
124 if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
125 }
126 }
127
128 //Fill
129 if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
130 if (visibleFill || clipper) {
131 /* We assume that if stroke width is bigger than 2,
132 shape outline below stroke could be full covered by stroke drawing.
133 Thus it turns off antialising in that condition.
134 Also, it shouldn't be dash style. */
135 auto antiAlias = strokeAlpha < 255 || rshape->strokeWidth() <= 2 || rshape->strokeDash(nullptr) > 0 || (rshape->stroke && rshape->stroke->strokeFirst);
136
137 if (!shapeGenRle(&shape, rshape, antiAlias)) goto err;
138 }
139 if (auto fill = rshape->fill) {
140 auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
141 if (ctable) shapeResetFill(&shape);
142 if (!shapeGenFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
143 } else {
144 shapeDelFill(&shape);
145 }
146 }
147
148 //Stroke
149 if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
150 if (visibleStroke) {
151 shapeResetStroke(&shape, rshape, transform);
152 if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err;
153
154 if (auto fill = rshape->strokeFill()) {
155 auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
156 if (ctable) shapeResetStrokeFill(&shape);
157 if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
158 } else {
159 shapeDelStrokeFill(&shape);
160 }
161 } else {
162 shapeDelStroke(&shape);
163 }
164 }
165
166 //Clear current task memorypool here if the clippers would use the same memory pool
167 shapeDelOutline(&shape, mpool, tid);
168
169 //Clip Path
170 for (auto clip = clips.data; clip < clips.end(); ++clip) {
171 auto clipper = static_cast<SwTask*>(*clip);
172 //Clip shape rle
173 if (shape.rle && !clipper->clip(shape.rle)) goto err;
174 //Clip stroke rle
175 if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err;
176 }
177 return;
178
179 err:
180 shapeReset(&shape);
181 shapeDelOutline(&shape, mpool, tid);
182 }
183
184 bool dispose() override
185 {
186 shapeFree(&shape);
187 return true;
188 }
189};
190
191
192struct SwSceneTask : SwTask
193{
194 Array<RenderData> scene; //list of paints render data (SwTask)
195 SwRleData* sceneRle = nullptr;
196
197 bool clip(SwRleData* target) override
198 {
199 //Only one shape
200 if (scene.count == 1) {
201 return static_cast<SwTask*>(*scene.data)->clip(target);
202 }
203
204 //More than one shapes
205 if (sceneRle) rleClipPath(target, sceneRle);
206 else TVGLOG("SW_ENGINE", "No clippers in a scene?");
207
208 return true;
209 }
210
211 SwRleData* rle() override
212 {
213 return sceneRle;
214 }
215
216 void run(unsigned tid) override
217 {
218 //TODO: Skip the run if the scene hans't changed.
219 if (!sceneRle) sceneRle = static_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
220 else rleReset(sceneRle);
221
222 //Merge shapes if it has more than one shapes
223 if (scene.count > 1) {
224 //Merge first two clippers
225 auto clipper1 = static_cast<SwTask*>(*scene.data);
226 auto clipper2 = static_cast<SwTask*>(*(scene.data + 1));
227
228 rleMerge(sceneRle, clipper1->rle(), clipper2->rle());
229
230 //Unify the remained clippers
231 for (auto rd = scene.data + 2; rd < scene.end(); ++rd) {
232 auto clipper = static_cast<SwTask*>(*rd);
233 rleMerge(sceneRle, sceneRle, clipper->rle());
234 }
235 }
236 }
237
238 bool dispose() override
239 {
240 rleFree(sceneRle);
241 return true;
242 }
243};
244
245
246struct SwImageTask : SwTask
247{
248 SwImage image;
249 Surface* source; //Image source
250 const RenderMesh* mesh = nullptr; //Should be valid ptr in action
251
252 bool clip(SwRleData* target) override
253 {
254 TVGERR("SW_ENGINE", "Image is used as ClipPath?");
255 return true;
256 }
257
258 SwRleData* rle() override
259 {
260 TVGERR("SW_ENGINE", "Image is used as Scene ClipPath?");
261 return nullptr;
262 }
263
264 void run(unsigned tid) override
265 {
266 auto clipRegion = bbox;
267
268 //Convert colorspace if it's not aligned.
269 if (source->owner) {
270 if (source->cs != surface->cs) rasterConvertCS(source, surface->cs);
271 if (!source->premultiplied) rasterPremultiply(source);
272 }
273
274 image.data = source->data;
275 image.w = source->w;
276 image.h = source->h;
277 image.stride = source->stride;
278 image.channelSize = source->channelSize;
279
280 //Invisible shape turned to visible by alpha.
281 if ((flags & (RenderUpdateFlag::Image | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) && (opacity > 0)) {
282 imageReset(&image);
283 if (!image.data || image.w == 0 || image.h == 0) goto end;
284
285 if (!imagePrepare(&image, mesh, transform, clipRegion, bbox, mpool, tid)) goto end;
286
287 // TODO: How do we clip the triangle mesh? Only clip non-meshed images for now
288 if (mesh->triangleCnt == 0 && clips.count > 0) {
289 if (!imageGenRle(&image, bbox, false)) goto end;
290 if (image.rle) {
291 //Clear current task memorypool here if the clippers would use the same memory pool
292 imageDelOutline(&image, mpool, tid);
293 for (auto clip = clips.data; clip < clips.end(); ++clip) {
294 auto clipper = static_cast<SwTask*>(*clip);
295 if (!clipper->clip(image.rle)) goto err;
296 }
297 return;
298 }
299 }
300 }
301 goto end;
302 err:
303 rleReset(image.rle);
304 end:
305 imageDelOutline(&image, mpool, tid);
306 }
307
308 bool dispose() override
309 {
310 imageFree(&image);
311 return true;
312 }
313};
314
315
316static void _termEngine()
317{
318 if (rendererCnt > 0) return;
319
320 mpoolTerm(globalMpool);
321 globalMpool = nullptr;
322}
323
324
325static void _renderFill(SwShapeTask* task, SwSurface* surface, uint8_t opacity)
326{
327 uint8_t r, g, b, a;
328 if (auto fill = task->rshape->fill) {
329 rasterGradientShape(surface, &task->shape, fill->identifier());
330 } else {
331 task->rshape->fillColor(&r, &g, &b, &a);
332 a = MULTIPLY(opacity, a);
333 if (a > 0) rasterShape(surface, &task->shape, r, g, b, a);
334 }
335}
336
337static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint8_t opacity)
338{
339 uint8_t r, g, b, a;
340 if (auto strokeFill = task->rshape->strokeFill()) {
341 rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
342 } else {
343 if (task->rshape->strokeColor(&r, &g, &b, &a)) {
344 a = MULTIPLY(opacity, a);
345 if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
346 }
347 }
348}
349
350/************************************************************************/
351/* External Class Implementation */
352/************************************************************************/
353
354SwRenderer::~SwRenderer()
355{
356 clearCompositors();
357
358 delete(surface);
359
360 if (!sharedMpool) mpoolTerm(mpool);
361
362 --rendererCnt;
363
364 if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
365}
366
367
368bool SwRenderer::clear()
369{
370 for (auto task = tasks.data; task < tasks.end(); ++task) {
371 if ((*task)->disposed) {
372 delete(*task);
373 } else {
374 (*task)->done();
375 (*task)->pushed = false;
376 }
377 }
378 tasks.clear();
379
380 if (!sharedMpool) mpoolClear(mpool);
381
382 if (surface) {
383 vport.x = vport.y = 0;
384 vport.w = surface->w;
385 vport.h = surface->h;
386 }
387
388 return true;
389}
390
391
392bool SwRenderer::sync()
393{
394 return true;
395}
396
397
398RenderRegion SwRenderer::viewport()
399{
400 return vport;
401}
402
403
404bool SwRenderer::viewport(const RenderRegion& vp)
405{
406 vport = vp;
407 return true;
408}
409
410
411bool SwRenderer::target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs)
412{
413 if (!data || stride == 0 || w == 0 || h == 0 || w > stride) return false;
414
415 if (!surface) surface = new SwSurface;
416
417 surface->data = data;
418 surface->stride = stride;
419 surface->w = w;
420 surface->h = h;
421 surface->cs = cs;
422 surface->channelSize = CHANNEL_SIZE(cs);
423 surface->premultiplied = true;
424 surface->owner = true;
425
426 vport.x = vport.y = 0;
427 vport.w = surface->w;
428 vport.h = surface->h;
429
430 return rasterCompositor(surface);
431}
432
433
434bool SwRenderer::preRender()
435{
436 return rasterClear(surface, 0, 0, surface->w, surface->h);
437}
438
439
440void SwRenderer::clearCompositors()
441{
442 //Free Composite Caches
443 for (auto comp = compositors.data; comp < compositors.end(); ++comp) {
444 free((*comp)->compositor->image.data);
445 delete((*comp)->compositor);
446 delete(*comp);
447 }
448 compositors.reset();
449}
450
451
452bool SwRenderer::postRender()
453{
454 //Unmultiply alpha if needed
455 if (surface->cs == ColorSpace::ABGR8888S || surface->cs == ColorSpace::ARGB8888S) {
456 rasterUnpremultiply(surface);
457 }
458
459 for (auto task = tasks.data; task < tasks.end(); ++task) {
460 if ((*task)->disposed) delete(*task);
461 else (*task)->pushed = false;
462 }
463 tasks.clear();
464
465 clearCompositors();
466 return true;
467}
468
469
470bool SwRenderer::renderImage(RenderData data)
471{
472 auto task = static_cast<SwImageTask*>(data);
473 task->done();
474
475 if (task->opacity == 0) return true;
476
477 return rasterImage(surface, &task->image, task->mesh, task->transform, task->bbox, task->opacity);
478}
479
480
481bool SwRenderer::renderShape(RenderData data)
482{
483 auto task = static_cast<SwShapeTask*>(data);
484 if (!task) return false;
485
486 task->done();
487
488 if (task->opacity == 0) return true;
489
490 //Main raster stage
491 if (task->rshape->stroke && task->rshape->stroke->strokeFirst) {
492 _renderStroke(task, surface, task->opacity);
493 _renderFill(task, surface, task->opacity);
494 } else {
495 _renderFill(task, surface, task->opacity);
496 _renderStroke(task, surface, task->opacity);
497 }
498
499 return true;
500}
501
502
503bool SwRenderer::blend(BlendMethod method)
504{
505 if (surface->blendMethod == method) return true;
506 surface->blendMethod = method;
507
508 switch (method) {
509 case BlendMethod::Add:
510 surface->blender = opBlendAdd;
511 break;
512 case BlendMethod::Screen:
513 surface->blender = opBlendScreen;
514 break;
515 case BlendMethod::Multiply:
516 surface->blender = opBlendMultiply;
517 break;
518 case BlendMethod::Overlay:
519 surface->blender = opBlendOverlay;
520 break;
521 case BlendMethod::Difference:
522 surface->blender = opBlendDifference;
523 break;
524 case BlendMethod::Exclusion:
525 surface->blender = opBlendExclusion;
526 break;
527 case BlendMethod::SrcOver:
528 surface->blender = opBlendSrcOver;
529 break;
530 case BlendMethod::Darken:
531 surface->blender = opBlendDarken;
532 break;
533 case BlendMethod::Lighten:
534 surface->blender = opBlendLighten;
535 break;
536 case BlendMethod::ColorDodge:
537 surface->blender = opBlendColorDodge;
538 break;
539 case BlendMethod::ColorBurn:
540 surface->blender = opBlendColorBurn;
541 break;
542 case BlendMethod::HardLight:
543 surface->blender = opBlendHardLight;
544 break;
545 case BlendMethod::SoftLight:
546 surface->blender = opBlendSoftLight;
547 break;
548 default:
549 surface->blender = nullptr;
550 break;
551 }
552 return false;
553}
554
555
556RenderRegion SwRenderer::region(RenderData data)
557{
558 return static_cast<SwTask*>(data)->bounds();
559}
560
561
562bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity)
563{
564 if (!cmp) return false;
565 auto p = static_cast<SwCompositor*>(cmp);
566
567 p->method = method;
568 p->opacity = opacity;
569
570 //Current Context?
571 if (p->method != CompositeMethod::None) {
572 surface = p->recoverSfc;
573 surface->compositor = p;
574 }
575
576 return true;
577}
578
579
580bool SwRenderer::mempool(bool shared)
581{
582 if (shared == sharedMpool) return true;
583
584 if (shared) {
585 if (!sharedMpool) {
586 if (!mpoolTerm(mpool)) return false;
587 mpool = globalMpool;
588 }
589 } else {
590 if (sharedMpool) mpool = mpoolInit(threadsCnt);
591 }
592
593 sharedMpool = shared;
594
595 if (mpool) return true;
596 return false;
597}
598
599
600Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
601{
602 auto x = region.x;
603 auto y = region.y;
604 auto w = region.w;
605 auto h = region.h;
606 auto sw = static_cast<int32_t>(surface->w);
607 auto sh = static_cast<int32_t>(surface->h);
608
609 //Out of boundary
610 if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr;
611
612 SwSurface* cmp = nullptr;
613
614 auto reqChannelSize = CHANNEL_SIZE(cs);
615
616 //Use cached data
617 for (auto p = compositors.data; p < compositors.end(); ++p) {
618 if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == reqChannelSize) {
619 cmp = *p;
620 break;
621 }
622 }
623
624 //New Composition
625 if (!cmp) {
626 cmp = new SwSurface;
627
628 //Inherits attributes from main surface
629 *cmp = *surface;
630
631 cmp->compositor = new SwCompositor;
632
633 //TODO: We can optimize compositor surface size from (surface->stride x surface->h) to Parameter(w x h)
634 cmp->compositor->image.data = (pixel_t*)malloc(reqChannelSize * surface->stride * surface->h);
635 cmp->channelSize = cmp->compositor->image.channelSize = reqChannelSize;
636
637 compositors.push(cmp);
638 }
639
640 //Boundary Check
641 if (x + w > sw) w = (sw - x);
642 if (y + h > sh) h = (sh - y);
643
644 TVGLOG("SW_ENGINE", "Using intermediate composition [Region: %d %d %d %d]", x, y, w, h);
645
646 cmp->compositor->recoverSfc = surface;
647 cmp->compositor->recoverCmp = surface->compositor;
648 cmp->compositor->valid = false;
649 cmp->compositor->bbox.min.x = x;
650 cmp->compositor->bbox.min.y = y;
651 cmp->compositor->bbox.max.x = x + w;
652 cmp->compositor->bbox.max.y = y + h;
653 cmp->compositor->image.stride = surface->stride;
654 cmp->compositor->image.w = surface->w;
655 cmp->compositor->image.h = surface->h;
656 cmp->compositor->image.direct = true;
657
658 cmp->data = cmp->compositor->image.data;
659 cmp->w = cmp->compositor->image.w;
660 cmp->h = cmp->compositor->image.h;
661
662 rasterClear(cmp, x, y, w, h);
663
664 //Switch render target
665 surface = cmp;
666
667 return cmp->compositor;
668}
669
670
671bool SwRenderer::endComposite(Compositor* cmp)
672{
673 if (!cmp) return false;
674
675 auto p = static_cast<SwCompositor*>(cmp);
676 p->valid = true;
677
678 //Recover Context
679 surface = p->recoverSfc;
680 surface->compositor = p->recoverCmp;
681
682 //Default is alpha blending
683 if (p->method == CompositeMethod::None) {
684 return rasterImage(surface, &p->image, nullptr, nullptr, p->bbox, p->opacity);
685 }
686
687 return true;
688}
689
690
691ColorSpace SwRenderer::colorSpace()
692{
693 if (surface) return surface->cs;
694 else return ColorSpace::Unsupported;
695}
696
697
698bool SwRenderer::dispose(RenderData data)
699{
700 auto task = static_cast<SwTask*>(data);
701 if (!task) return true;
702 task->done();
703 task->dispose();
704
705 if (task->pushed) task->disposed = true;
706 else delete(task);
707
708 return true;
709}
710
711
712void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
713{
714 if (!surface) return task;
715 if (flags == RenderUpdateFlag::None) return task;
716
717 //Finish previous task if it has duplicated request.
718 task->done();
719
720 //TODO: Failed threading them. It would be better if it's possible.
721 //See: https://github.com/thorvg/thorvg/issues/1409
722 //Guarantee composition targets get ready.
723 for (auto clip = clips.data; clip < clips.end(); ++clip) {
724 static_cast<SwTask*>(*clip)->done();
725 }
726
727 task->clips = clips;
728
729 if (transform) {
730 if (!task->transform) task->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
731 *task->transform = transform->m;
732 } else {
733 if (task->transform) free(task->transform);
734 task->transform = nullptr;
735 }
736
737 task->opacity = opacity;
738 task->surface = surface;
739 task->mpool = mpool;
740 task->flags = flags;
741 task->bbox.min.x = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
742 task->bbox.min.y = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
743 task->bbox.max.x = mathMin(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
744 task->bbox.max.y = mathMin(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
745
746 if (!task->pushed) {
747 task->pushed = true;
748 tasks.push(task);
749 }
750
751 TaskScheduler::request(task);
752
753 return task;
754}
755
756
757RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
758{
759 //prepare task
760 auto task = static_cast<SwImageTask*>(data);
761 if (!task) task = new SwImageTask;
762 if (flags & RenderUpdateFlag::Image) {
763 task->source = surface;
764 task->mesh = mesh;
765 }
766 return prepareCommon(task, transform, clips, opacity, flags);
767}
768
769
770RenderData SwRenderer::prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
771{
772 //prepare task
773 auto task = static_cast<SwSceneTask*>(data);
774 if (!task) task = new SwSceneTask;
775 task->scene = scene;
776
777 //TODO: Failed threading them. It would be better if it's possible.
778 //See: https://github.com/thorvg/thorvg/issues/1409
779 //Guarantee composition targets get ready.
780 for (auto task = scene.data; task < scene.end(); ++task) {
781 static_cast<SwTask*>(*task)->done();
782 }
783 return prepareCommon(task, transform, clips, opacity, flags);
784}
785
786
787RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper)
788{
789 //prepare task
790 auto task = static_cast<SwShapeTask*>(data);
791 if (!task) {
792 task = new SwShapeTask;
793 task->rshape = &rshape;
794 }
795 task->clipper = clipper;
796
797 return prepareCommon(task, transform, clips, opacity, flags);
798}
799
800
801SwRenderer::SwRenderer():mpool(globalMpool)
802{
803}
804
805
806bool SwRenderer::init(uint32_t threads)
807{
808 if ((initEngineCnt++) > 0) return true;
809
810 threadsCnt = threads;
811
812 //Share the memory pool among the renderer
813 globalMpool = mpoolInit(threads);
814 if (!globalMpool) {
815 --initEngineCnt;
816 return false;
817 }
818
819 return true;
820}
821
822
823int32_t SwRenderer::init()
824{
825 return initEngineCnt;
826}
827
828
829bool SwRenderer::term()
830{
831 if ((--initEngineCnt) > 0) return true;
832
833 initEngineCnt = 0;
834
835 _termEngine();
836
837 return true;
838}
839
840SwRenderer* SwRenderer::gen()
841{
842 ++rendererCnt;
843 return new SwRenderer();
844}
845