updated copyright.
[platform/core/graphics/tizenvg.git] / src / lib / sw_engine / tvgSwRenderer.cpp
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 /************************************************************************/
31 static int32_t initEngineCnt = false;
32 static int32_t rendererCnt = 0;
33 static SwMpool* globalMpool = nullptr;
34 static uint32_t threadsCnt = 0;
35
36 struct SwTask : Task
37 {
38     Matrix* transform = nullptr;
39     SwSurface* surface = nullptr;
40     SwMpool* mpool = nullptr;
41     RenderUpdateFlag flags = RenderUpdateFlag::None;
42     Array<RenderData> clips;
43     uint32_t opacity;
44     SwBBox bbox = {{0, 0}, {0, 0}};       //Whole Rendering Region
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
65     virtual ~SwTask()
66     {
67         free(transform);
68     }
69 };
70
71
72 struct SwShapeTask : SwTask
73 {
74     SwShape shape;
75     const Shape* sdata = nullptr;
76     bool cmpStroking = false;
77
78     void run(unsigned tid) override
79     {
80         if (opacity == 0) return;  //Invisible
81
82         uint8_t strokeAlpha = 0;
83         auto visibleStroke = false;
84         bool visibleFill = false;
85         auto clipRegion = bbox;
86
87         if (HALF_STROKE(sdata->strokeWidth()) > 0) {
88             sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
89             visibleStroke = sdata->strokeFill() || (static_cast<uint32_t>(strokeAlpha * opacity / 255) > 0);
90         }
91
92         //This checks also for the case, if the invisible shape turned to visible by alpha.
93         auto prepareShape = false;
94         if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true;
95
96         //Shape
97         if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
98             uint8_t alpha = 0;
99             sdata->fillColor(nullptr, nullptr, nullptr, &alpha);
100             alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255);
101             visibleFill = (alpha > 0 || sdata->fill());
102             if (visibleFill || visibleStroke) {
103                 shapeReset(&shape);
104                 if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
105             }
106         }
107
108         //Decide Stroking Composition
109         if (visibleStroke && visibleFill && opacity < 255) cmpStroking = true;
110         else cmpStroking = false;
111
112         //Fill
113         if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
114             if (visibleFill) {
115                 /* We assume that if stroke width is bigger than 2,
116                    shape outline below stroke could be full covered by stroke drawing.
117                    Thus it turns off antialising in that condition.
118                    Also, it shouldn't be dash style. */
119                 auto antiAlias = (strokeAlpha == 255 && sdata->strokeWidth() > 2 && sdata->strokeDash(nullptr) == 0) ? false : true;
120
121                 if (!shapeGenRle(&shape, sdata, antiAlias)) goto err;
122             }
123             if (auto fill = sdata->fill()) {
124                 auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
125                 if (ctable) shapeResetFill(&shape);
126                 if (!shapeGenFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
127             } else {
128                 shapeDelFill(&shape);
129             }
130         }
131
132         //Stroke
133         if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
134             if (visibleStroke) {
135                 shapeResetStroke(&shape, sdata, transform);
136                 if (!shapeGenStrokeRle(&shape, sdata, transform, clipRegion, bbox, mpool, tid)) goto err;
137
138                 if (auto fill = sdata->strokeFill()) {
139                     auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
140                     if (ctable) shapeResetStrokeFill(&shape);
141                     if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
142                 } else {
143                     shapeDelStrokeFill(&shape);
144                 }
145             } else {
146                 shapeDelStroke(&shape);
147             }
148         }
149
150         //Clip Path
151         for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
152             auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
153             //Clip shape rle
154             if (shape.rle) {
155                 if (clipper->fastTrack) rleClipRect(shape.rle, &clipper->bbox);
156                 else if (clipper->rle) rleClipPath(shape.rle, clipper->rle);
157                 else goto err;
158             }
159             //Clip stroke rle
160             if (shape.strokeRle) {
161                 if (clipper->fastTrack) rleClipRect(shape.strokeRle, &clipper->bbox);
162                 else if (clipper->rle) rleClipPath(shape.strokeRle, clipper->rle);
163                 else goto err;
164             }
165         }
166         goto end;
167
168     err:
169         shapeReset(&shape);
170     end:
171         shapeDelOutline(&shape, mpool, tid);
172     }
173
174     bool dispose() override
175     {
176        shapeFree(&shape);
177        return true;
178     }
179 };
180
181
182 struct SwImageTask : SwTask
183 {
184     SwImage image;
185     Polygon* triangles;
186     uint32_t triangleCnt;
187
188     void run(unsigned tid) override
189     {
190         auto clipRegion = bbox;
191
192         //Invisible shape turned to visible by alpha.
193         if ((flags & (RenderUpdateFlag::Image | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) && (opacity > 0)) {
194             imageReset(&image);
195             if (!image.data || image.w == 0 || image.h == 0) goto end;
196
197             if (!imagePrepare(&image, triangles, triangleCnt, transform, clipRegion, bbox, mpool, tid)) goto end;
198
199             // TODO: How do we clip the triangle mesh? Only clip non-meshed images for now
200             if (triangleCnt == 0 && clips.count > 0) {
201                 if (!imageGenRle(&image, bbox, false)) goto end;
202                 if (image.rle) {
203                     for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
204                         auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
205                         if (clipper->fastTrack) rleClipRect(image.rle, &clipper->bbox);
206                         else if (clipper->rle) rleClipPath(image.rle, clipper->rle);
207                         else goto err;
208                     }
209                 }
210             }
211         }
212         goto end;
213
214     err:
215         rleReset(image.rle);
216     end:
217         imageDelOutline(&image, mpool, tid);
218     }
219
220     bool dispose() override
221     {
222        imageFree(&image);
223        return true;
224     }
225 };
226
227
228 static void _termEngine()
229 {
230     if (rendererCnt > 0) return;
231
232     mpoolTerm(globalMpool);
233     globalMpool = nullptr;
234 }
235
236
237 /************************************************************************/
238 /* External Class Implementation                                        */
239 /************************************************************************/
240
241 SwRenderer::~SwRenderer()
242 {
243     clearCompositors();
244
245     if (surface) delete(surface);
246
247     if (!sharedMpool) mpoolTerm(mpool);
248
249     --rendererCnt;
250
251     if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
252 }
253
254
255 bool SwRenderer::clear()
256 {
257     for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) {
258         if ((*task)->disposed) {
259             delete(*task);
260         } else {
261             (*task)->done();
262             (*task)->pushed = false;
263         }
264     }
265     tasks.clear();
266
267     if (!sharedMpool) mpoolClear(mpool);
268
269     if (surface) {
270         vport.x = vport.y = 0;
271         vport.w = surface->w;
272         vport.h = surface->h;
273     }
274
275     return true;
276 }
277
278
279 bool SwRenderer::sync()
280 {
281     return true;
282 }
283
284
285 RenderRegion SwRenderer::viewport()
286 {
287     return vport;
288 }
289
290
291 bool SwRenderer::viewport(const RenderRegion& vp)
292 {
293     vport = vp;
294     return true;
295 }
296
297
298 bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs)
299 {
300     if (!buffer || stride == 0 || w == 0 || h == 0 || w > stride) return false;
301
302     if (!surface) surface = new SwSurface;
303
304     surface->buffer = buffer;
305     surface->stride = stride;
306     surface->w = w;
307     surface->h = h;
308     surface->cs = cs;
309
310     vport.x = vport.y = 0;
311     vport.w = surface->w;
312     vport.h = surface->h;
313
314     return rasterCompositor(surface);
315 }
316
317
318 bool SwRenderer::preRender()
319 {
320     return rasterClear(surface);
321 }
322
323 void SwRenderer::clearCompositors()
324 {
325     //Free Composite Caches
326     for (auto comp = compositors.data; comp < (compositors.data + compositors.count); ++comp) {
327         free((*comp)->compositor->image.data);
328         delete((*comp)->compositor);
329         delete(*comp);
330     }
331     compositors.reset();
332 }
333
334
335 bool SwRenderer::postRender()
336 {
337     //Unmultiply alpha if needed
338     if (surface->cs == SwCanvas::ABGR8888_STRAIGHT || surface->cs == SwCanvas::ARGB8888_STRAIGHT) {
339         rasterUnpremultiply(surface);
340     }
341
342     for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) {
343         (*task)->pushed = false;
344     }
345     tasks.clear();
346
347     clearCompositors();
348     return true;
349 }
350
351
352 bool SwRenderer::renderImage(RenderData data)
353 {
354     auto task = static_cast<SwImageTask*>(data);
355     task->done();
356
357     if (task->opacity == 0) return true;
358
359     return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity);
360 }
361
362
363 bool SwRenderer::renderImageMesh(RenderData data)
364 {
365     auto task = static_cast<SwImageTask*>(data);
366     task->done();
367
368     if (task->opacity == 0) return true;
369
370     return rasterImageMesh(surface, &task->image, task->triangles, task->triangleCnt, task->transform, task->bbox, task->opacity);
371 }
372
373
374 bool SwRenderer::renderShape(RenderData data)
375 {
376     auto task = static_cast<SwShapeTask*>(data);
377     if (!task) return false;
378
379     task->done();
380
381     if (task->opacity == 0) return true;
382
383     uint32_t opacity;
384     Compositor* cmp = nullptr;
385
386     //Do Stroking Composition
387     if (task->cmpStroking) {
388         opacity = 255;
389         cmp = target(task->bounds());
390         beginComposite(cmp, CompositeMethod::None, task->opacity);
391     //No Stroking Composition
392     } else {
393         opacity = task->opacity;
394     }
395
396     //Main raster stage
397     uint8_t r, g, b, a;
398
399     if (auto fill = task->sdata->fill()) {
400         rasterGradientShape(surface, &task->shape, fill->identifier());
401     } else {
402         task->sdata->fillColor(&r, &g, &b, &a);
403         a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
404         if (a > 0) rasterShape(surface, &task->shape, r, g, b, a);
405     }
406
407     if (auto strokeFill = task->sdata->strokeFill()) {
408         rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
409     } else {
410         if (task->sdata->strokeColor(&r, &g, &b, &a) == Result::Success) {
411             a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
412             if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
413         }
414     }
415
416     if (task->cmpStroking) endComposite(cmp);
417
418     return true;
419 }
420
421
422 RenderRegion SwRenderer::region(RenderData data)
423 {
424     return static_cast<SwTask*>(data)->bounds();
425 }
426
427
428 bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity)
429 {
430     if (!cmp) return false;
431     auto p = static_cast<SwCompositor*>(cmp);
432
433     p->method = method;
434     p->opacity = opacity;
435
436     //Current Context?
437     if (p->method != CompositeMethod::None) {
438         surface = p->recoverSfc;
439         surface->compositor = p;
440     }
441
442     return true;
443 }
444
445
446 bool SwRenderer::mempool(bool shared)
447 {
448     if (shared == sharedMpool) return true;
449
450     if (shared) {
451         if (!sharedMpool) {
452             if (!mpoolTerm(mpool)) return false;
453             mpool = globalMpool;
454         }
455     } else {
456         if (sharedMpool) mpool = mpoolInit(threadsCnt);
457     }
458
459     sharedMpool = shared;
460
461     if (mpool) return true;
462     return false;
463 }
464
465
466 Compositor* SwRenderer::target(const RenderRegion& region)
467 {
468     auto x = region.x;
469     auto y = region.y;
470     auto w = region.w;
471     auto h = region.h;
472     auto sw = static_cast<int32_t>(surface->w);
473     auto sh = static_cast<int32_t>(surface->h);
474
475     //Out of boundary
476     if (x > sw || y > sh) return nullptr;
477
478     SwSurface* cmp = nullptr;
479
480     //Use cached data
481     for (auto p = compositors.data; p < (compositors.data + compositors.count); ++p) {
482         if ((*p)->compositor->valid) {
483             cmp = *p;
484             break;
485         }
486     }
487
488     //New Composition
489     if (!cmp) {
490         cmp = new SwSurface;
491         if (!cmp) goto err;
492
493         //Inherits attributes from main surface
494         *cmp = *surface;
495
496         cmp->compositor = new SwCompositor;
497         if (!cmp->compositor) goto err;
498
499         //SwImage, Optimize Me: Surface size from MainSurface(WxH) to Parameter W x H
500         cmp->compositor->image.data = (uint32_t*) malloc(sizeof(uint32_t) * surface->stride * surface->h);
501         if (!cmp->compositor->image.data) goto err;
502         compositors.push(cmp);
503     }
504
505     //Boundary Check
506     if (x + w > sw) w = (sw - x);
507     if (y + h > sh) h = (sh - y);
508
509     TVGLOG("SW_ENGINE", "Using intermediate composition [Region: %d %d %d %d]", x, y, w, h);
510
511     cmp->compositor->recoverSfc = surface;
512     cmp->compositor->recoverCmp = surface->compositor;
513     cmp->compositor->valid = false;
514     cmp->compositor->bbox.min.x = x;
515     cmp->compositor->bbox.min.y = y;
516     cmp->compositor->bbox.max.x = x + w;
517     cmp->compositor->bbox.max.y = y + h;
518     cmp->compositor->image.stride = surface->stride;
519     cmp->compositor->image.w = surface->w;
520     cmp->compositor->image.h = surface->h;
521     cmp->compositor->image.direct = true;
522
523     //We know partial clear region
524     cmp->buffer = cmp->compositor->image.data + (cmp->stride * y + x);
525     cmp->w = w;
526     cmp->h = h;
527
528     rasterClear(cmp);
529
530     //Recover context
531     cmp->buffer = cmp->compositor->image.data;
532     cmp->w = cmp->compositor->image.w;
533     cmp->h = cmp->compositor->image.h;
534
535     //Switch render target
536     surface = cmp;
537
538     return cmp->compositor;
539
540 err:
541     if (cmp) {
542         if (cmp->compositor) delete(cmp->compositor);
543         delete(cmp);
544     }
545
546     return nullptr;
547 }
548
549
550 bool SwRenderer::endComposite(Compositor* cmp)
551 {
552     if (!cmp) return false;
553
554     auto p = static_cast<SwCompositor*>(cmp);
555     p->valid = true;
556
557     //Recover Context
558     surface = p->recoverSfc;
559     surface->compositor = p->recoverCmp;
560
561     //Default is alpha blending
562     if (p->method == CompositeMethod::None) {
563         return rasterImage(surface, &p->image, nullptr, p->bbox, p->opacity);
564     }
565
566     return true;
567 }
568
569
570 bool SwRenderer::dispose(RenderData data)
571 {
572     auto task = static_cast<SwTask*>(data);
573     if (!task) return true;
574     task->done();
575     task->dispose();
576
577     if (task->pushed) task->disposed = true;
578     else delete(task);
579
580     return true;
581 }
582
583
584 void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array<RenderData>& clips, RenderUpdateFlag flags)
585 {
586     if (!surface) return task;
587     if (flags == RenderUpdateFlag::None) return task;
588
589     //Finish previous task if it has duplicated request.
590     task->done();
591
592     if (clips.count > 0) {
593         //Guarantee composition targets get ready.
594         for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
595             static_cast<SwShapeTask*>(*clip)->done();
596         }
597         task->clips = clips;
598     }
599
600     if (transform) {
601         if (!task->transform) task->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
602         *task->transform = transform->m;
603     } else {
604         if (task->transform) free(task->transform);
605         task->transform = nullptr;
606     }
607
608     task->opacity = opacity;
609     task->surface = surface;
610     task->mpool = mpool;
611     task->flags = flags;
612     task->bbox.min.x = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
613     task->bbox.min.y = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
614     task->bbox.max.x = mathMin(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
615     task->bbox.max.y = mathMin(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
616
617     if (!task->pushed) {
618         task->pushed = true;
619         tasks.push(task);
620     }
621
622     TaskScheduler::request(task);
623
624     return task;
625 }
626
627
628 RenderData SwRenderer::prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
629 {
630     //prepare task
631     auto task = static_cast<SwImageTask*>(data);
632     if (!task) task = new SwImageTask;
633     if (flags & RenderUpdateFlag::Image) {
634         task->image.data = image->buffer;
635         task->image.w = image->w;
636         task->image.h = image->h;
637         task->image.stride = image->stride;
638         task->triangles = triangles;
639         task->triangleCnt = triangleCnt;
640     }
641     return prepareCommon(task, transform, opacity, clips, flags);
642 }
643
644
645 RenderData SwRenderer::prepare(const Shape& sdata, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
646 {
647     //prepare task
648     auto task = static_cast<SwShapeTask*>(data);
649     if (!task) {
650         task = new SwShapeTask;
651         task->sdata = &sdata;
652     }
653     return prepareCommon(task, transform, opacity, clips, flags);
654 }
655
656
657 SwRenderer::SwRenderer():mpool(globalMpool)
658 {
659 }
660
661
662 bool SwRenderer::init(uint32_t threads)
663 {
664     if ((initEngineCnt++) > 0) return true;
665
666     threadsCnt = threads;
667
668     //Share the memory pool among the renderer
669     globalMpool = mpoolInit(threads);
670     if (!globalMpool) {
671         --initEngineCnt;
672         return false;
673     }
674
675     return true;
676 }
677
678
679 int32_t SwRenderer::init()
680 {
681     return initEngineCnt;
682 }
683
684
685 bool SwRenderer::term()
686 {
687     if ((--initEngineCnt) > 0) return true;
688
689     initEngineCnt = 0;
690
691    _termEngine();
692
693     return true;
694 }
695
696 SwRenderer* SwRenderer::gen()
697 {
698     ++rendererCnt;
699     return new SwRenderer();
700 }