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