1 /*
2  * Copyright (c) 2021 - 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 struct Vertex
27 {
28    Point pt;
29    Point uv;
30 };
31 
32 struct Polygon
33 {
34    Vertex vertex[3];
35 };
36 
37 struct AALine
38 {
39    int32_t x[2];
40    int32_t coverage[2];
41    int32_t length[2];
42 };
43 
44 struct AASpans
45 {
46    AALine *lines;
47    int32_t yStart;
48    int32_t yEnd;
49 };
50 
51 //Careful! Shared resource, No support threading
52 static float dudx, dvdx;
53 static float dxdya, dxdyb, dudya, dvdya;
54 static float xa, xb, ua, va;
55 
56 
57 //Y Range exception handling
_arrange(const SwImage * image,const SwBBox * region,int & yStart,int & yEnd)58 static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd)
59 {
60     int32_t regionTop, regionBottom;
61 
62     if (region) {
63         regionTop = region->min.y;
64         regionBottom = region->max.y;
65     } else {
66         regionTop = image->rle->spans->y;
67         regionBottom = image->rle->spans[image->rle->size - 1].y;
68     }
69 
70     if (yStart >= regionBottom) return false;
71 
72     if (yStart < regionTop) yStart = regionTop;
73     if (yEnd > regionBottom) yEnd = regionBottom;
74 
75     return true;
76 }
77 
78 
79 static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
80 {
81     return false;
82 
83 #if 0 //Enable it when GRAYSCALE image is supported
84     auto maskOp = _getMaskOp(surface->compositor->method);
85     auto direct = _direct(surface->compositor->method);
86     float _dudx = dudx, _dvdx = dvdx;
87     float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
88     float _xa = xa, _xb = xb, _ua = ua, _va = va;
89     auto sbuf = image->buf8;
90     int32_t sw = static_cast<int32_t>(image->stride);
91     int32_t sh = image->h;
92     int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
93     int32_t vv = 0, uu = 0;
94     int32_t minx = INT32_MAX, maxx = 0;
95     float dx, u, v, iptr;
96     SwSpan* span = nullptr;         //used only when rle based.
97 
98     if (!_arrange(image, region, yStart, yEnd)) return false;
99 
100     //Loop through all lines in the segment
101     uint32_t spanIdx = 0;
102 
103     if (region) {
104         minx = region->min.x;
105         maxx = region->max.x;
106     } else {
107         span = image->rle->spans;
108         while (span->y < yStart) {
109             ++span;
110             ++spanIdx;
111         }
112     }
113 
114     y = yStart;
115 
116     while (y < yEnd) {
117         x1 = (int32_t)_xa;
118         x2 = (int32_t)_xb;
119 
120         if (!region) {
121             minx = INT32_MAX;
122             maxx = 0;
123             //one single row, could be consisted of multiple spans.
124             while (span->y == y && spanIdx < image->rle->size) {
125                 if (minx > span->x) minx = span->x;
126                 if (maxx < span->x + span->len) maxx = span->x + span->len;
127                 ++span;
128                 ++spanIdx;
129             }
130         }
131         if (x1 < minx) x1 = minx;
132         if (x2 > maxx) x2 = maxx;
133 
134         //Anti-Aliasing frames
135         ay = y - aaSpans->yStart;
136         if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
137         if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
138 
139         //Range allowed
140         if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
141 
142             //Perform subtexel pre-stepping on UV
143             dx = 1 - (_xa - x1);
144             u = _ua + dx * _dudx;
145             v = _va + dx * _dvdx;
146 
147             x = x1;
148 
149             auto cmp = &surface->compositor->image.buf8[y * surface->compositor->image.stride + x1];
150             auto dst = &surface->buf8[y * surface->stride + x1];
151 
152             if (opacity == 255) {
153                 //Draw horizontal line
154                 while (x++ < x2) {
155                     uu = (int) u;
156                     if (uu >= sw) continue;
157                     vv = (int) v;
158                     if (vv >= sh) continue;
159 
160                     ar = (int)(255 * (1 - modff(u, &iptr)));
161                     ab = (int)(255 * (1 - modff(v, &iptr)));
162                     iru = uu + 1;
163                     irv = vv + 1;
164 
165                     px = *(sbuf + (vv * sw) + uu);
166 
167                     /* horizontal interpolate */
168                     if (iru < sw) {
169                         /* right pixel */
170                         int px2 = *(sbuf + (vv * sw) + iru);
171                         px = INTERPOLATE(px, px2, ar);
172                     }
173                     /* vertical interpolate */
174                     if (irv < sh) {
175                         /* bottom pixel */
176                         int px2 = *(sbuf + (irv * sw) + uu);
177 
178                         /* horizontal interpolate */
179                         if (iru < sw) {
180                             /* bottom right pixel */
181                             int px3 = *(sbuf + (irv * sw) + iru);
182                             px2 = INTERPOLATE(px2, px3, ar);
183                         }
184                         px = INTERPOLATE(px, px2, ab);
185                     }
186                     if (direct) {
187                         auto tmp = maskOp(px, *cmp, 0);  //not use alpha
188                         *dst = tmp + MULTIPLY(*dst, ~tmp);
189                         ++dst;
190                     } else {
191                         *cmp = maskOp(px, *cmp, ~px);
192                     }
193                     ++cmp;
194 
195                     //Step UV horizontally
196                     u += _dudx;
197                     v += _dvdx;
198                     //range over?
199                     if ((uint32_t)v >= image->h) break;
200                 }
201             } else {
202                 //Draw horizontal line
203                 while (x++ < x2) {
204                     uu = (int) u;
205                     if (uu >= sw) continue;
206                     vv = (int) v;
207                     if (vv >= sh) continue;
208 
209                     ar = (int)(255 * (1 - modff(u, &iptr)));
210                     ab = (int)(255 * (1 - modff(v, &iptr)));
211                     iru = uu + 1;
212                     irv = vv + 1;
213 
214                     px = *(sbuf + (vv * sw) + uu);
215 
216                     /* horizontal interpolate */
217                     if (iru < sw) {
218                         /* right pixel */
219                         int px2 = *(sbuf + (vv * sw) + iru);
220                         px = INTERPOLATE(px, px2, ar);
221                     }
222                     /* vertical interpolate */
223                     if (irv < sh) {
224                         /* bottom pixel */
225                         int px2 = *(sbuf + (irv * sw) + uu);
226 
227                         /* horizontal interpolate */
228                         if (iru < sw) {
229                             /* bottom right pixel */
230                             int px3 = *(sbuf + (irv * sw) + iru);
231                             px2 = INTERPOLATE(px2, px3, ar);
232                         }
233                         px = INTERPOLATE(px, px2, ab);
234                     }
235 
236                     if (direct) {
237                         auto tmp = maskOp(MULTIPLY(px, opacity), *cmp, 0);
238                         *dst = tmp + MULTIPLY(*dst, ~tmp);
239                         ++dst;
240                     } else {
241                         auto tmp = MULTIPLY(px, opacity);
242                         *cmp = maskOp(tmp, *cmp, ~px);
243                     }
244                     ++cmp;
245 
246                     //Step UV horizontally
247                     u += _dudx;
248                     v += _dvdx;
249                     //range over?
250                     if ((uint32_t)v >= image->h) break;
251                 }
252             }
253         }
254 
255         //Step along both edges
256         _xa += _dxdya;
257         _xb += _dxdyb;
258         _ua += _dudya;
259         _va += _dvdya;
260 
261         if (!region && spanIdx >= image->rle->size) break;
262 
263         ++y;
264     }
265     xa = _xa;
266     xb = _xb;
267     ua = _ua;
268     va = _va;
269 
270     return true;
271 #endif
272 }
273 
274 
_rasterBlendingPolygonImageSegment(SwSurface * surface,const SwImage * image,const SwBBox * region,int yStart,int yEnd,AASpans * aaSpans,uint8_t opacity)275 static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
276 {
277     float _dudx = dudx, _dvdx = dvdx;
278     float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
279     float _xa = xa, _xb = xb, _ua = ua, _va = va;
280     auto sbuf = image->buf32;
281     auto dbuf = surface->buf32;
282     int32_t sw = static_cast<int32_t>(image->stride);
283     int32_t sh = image->h;
284     int32_t dw = surface->stride;
285     int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
286     int32_t vv = 0, uu = 0;
287     int32_t minx = INT32_MAX, maxx = 0;
288     float dx, u, v, iptr;
289     uint32_t* buf;
290     SwSpan* span = nullptr;         //used only when rle based.
291 
292     if (!_arrange(image, region, yStart, yEnd)) return;
293 
294     //Loop through all lines in the segment
295     uint32_t spanIdx = 0;
296 
297     if (region) {
298         minx = region->min.x;
299         maxx = region->max.x;
300     } else {
301         span = image->rle->spans;
302         while (span->y < yStart) {
303             ++span;
304             ++spanIdx;
305         }
306     }
307 
308     y = yStart;
309 
310     while (y < yEnd) {
311         x1 = (int32_t)_xa;
312         x2 = (int32_t)_xb;
313 
314         if (!region) {
315             minx = INT32_MAX;
316             maxx = 0;
317             //one single row, could be consisted of multiple spans.
318             while (span->y == y && spanIdx < image->rle->size) {
319                 if (minx > span->x) minx = span->x;
320                 if (maxx < span->x + span->len) maxx = span->x + span->len;
321                 ++span;
322                 ++spanIdx;
323             }
324         }
325         if (x1 < minx) x1 = minx;
326         if (x2 > maxx) x2 = maxx;
327 
328         //Anti-Aliasing frames
329         ay = y - aaSpans->yStart;
330         if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
331         if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
332 
333         //Range allowed
334         if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
335 
336             //Perform subtexel pre-stepping on UV
337             dx = 1 - (_xa - x1);
338             u = _ua + dx * _dudx;
339             v = _va + dx * _dvdx;
340 
341             buf = dbuf + ((y * dw) + x1);
342 
343             x = x1;
344 
345             if (opacity == 255) {
346                 //Draw horizontal line
347                 while (x++ < x2) {
348                     uu = (int) u;
349                     if (uu >= sw) continue;
350                     vv = (int) v;
351                     if (vv >= sh) continue;
352 
353                     ar = (int)(255 * (1 - modff(u, &iptr)));
354                     ab = (int)(255 * (1 - modff(v, &iptr)));
355                     iru = uu + 1;
356                     irv = vv + 1;
357 
358                     px = *(sbuf + (vv * sw) + uu);
359 
360                     /* horizontal interpolate */
361                     if (iru < sw) {
362                         /* right pixel */
363                         int px2 = *(sbuf + (vv * sw) + iru);
364                         px = INTERPOLATE(px, px2, ar);
365                     }
366                     /* vertical interpolate */
367                     if (irv < sh) {
368                         /* bottom pixel */
369                         int px2 = *(sbuf + (irv * sw) + uu);
370 
371                         /* horizontal interpolate */
372                         if (iru < sw) {
373                             /* bottom right pixel */
374                             int px3 = *(sbuf + (irv * sw) + iru);
375                             px2 = INTERPOLATE(px2, px3, ar);
376                         }
377                         px = INTERPOLATE(px, px2, ab);
378                     }
379                     *buf = surface->blender(px, *buf, IA(px));
380                     ++buf;
381 
382                     //Step UV horizontally
383                     u += _dudx;
384                     v += _dvdx;
385                     //range over?
386                     if ((uint32_t)v >= image->h) break;
387                 }
388             } else {
389                 //Draw horizontal line
390                 while (x++ < x2) {
391                     uu = (int) u;
392                     if (uu >= sw) continue;
393                     vv = (int) v;
394                     if (vv >= sh) continue;
395 
396                     ar = (int)(255 * (1 - modff(u, &iptr)));
397                     ab = (int)(255 * (1 - modff(v, &iptr)));
398                     iru = uu + 1;
399                     irv = vv + 1;
400 
401                     px = *(sbuf + (vv * sw) + uu);
402 
403                     /* horizontal interpolate */
404                     if (iru < sw) {
405                         /* right pixel */
406                         int px2 = *(sbuf + (vv * sw) + iru);
407                         px = INTERPOLATE(px, px2, ar);
408                     }
409                     /* vertical interpolate */
410                     if (irv < sh) {
411                         /* bottom pixel */
412                         int px2 = *(sbuf + (irv * sw) + uu);
413 
414                         /* horizontal interpolate */
415                         if (iru < sw) {
416                             /* bottom right pixel */
417                             int px3 = *(sbuf + (irv * sw) + iru);
418                             px2 = INTERPOLATE(px2, px3, ar);
419                         }
420                         px = INTERPOLATE(px, px2, ab);
421                     }
422                     auto src = ALPHA_BLEND(px, opacity);
423                     *buf = surface->blender(src, *buf, IA(src));
424                     ++buf;
425 
426                     //Step UV horizontally
427                     u += _dudx;
428                     v += _dvdx;
429                     //range over?
430                     if ((uint32_t)v >= image->h) break;
431                 }
432             }
433         }
434 
435         //Step along both edges
436         _xa += _dxdya;
437         _xb += _dxdyb;
438         _ua += _dudya;
439         _va += _dvdya;
440 
441         if (!region && spanIdx >= image->rle->size) break;
442 
443         ++y;
444     }
445     xa = _xa;
446     xb = _xb;
447     ua = _ua;
448     va = _va;
449 }
450 
451 
_rasterPolygonImageSegment(SwSurface * surface,const SwImage * image,const SwBBox * region,int yStart,int yEnd,AASpans * aaSpans,uint8_t opacity,bool matting)452 static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting)
453 {
454     float _dudx = dudx, _dvdx = dvdx;
455     float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
456     float _xa = xa, _xb = xb, _ua = ua, _va = va;
457     auto sbuf = image->buf32;
458     auto dbuf = surface->buf32;
459     int32_t sw = static_cast<int32_t>(image->stride);
460     int32_t sh = image->h;
461     int32_t dw = surface->stride;
462     int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
463     int32_t vv = 0, uu = 0;
464     int32_t minx = INT32_MAX, maxx = 0;
465     float dx, u, v, iptr;
466     uint32_t* buf;
467     SwSpan* span = nullptr;         //used only when rle based.
468 
469     //for matting(composition)
470     auto csize = matting ? surface->compositor->image.channelSize: 0;
471     auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr;
472     uint8_t* cmp = nullptr;
473 
474     if (!_arrange(image, region, yStart, yEnd)) return;
475 
476     //Loop through all lines in the segment
477     uint32_t spanIdx = 0;
478 
479     if (region) {
480         minx = region->min.x;
481         maxx = region->max.x;
482     } else {
483         span = image->rle->spans;
484         while (span->y < yStart) {
485             ++span;
486             ++spanIdx;
487         }
488     }
489 
490     y = yStart;
491 
492     while (y < yEnd) {
493         x1 = (int32_t)_xa;
494         x2 = (int32_t)_xb;
495 
496         if (!region) {
497             minx = INT32_MAX;
498             maxx = 0;
499             //one single row, could be consisted of multiple spans.
500             while (span->y == y && spanIdx < image->rle->size) {
501                 if (minx > span->x) minx = span->x;
502                 if (maxx < span->x + span->len) maxx = span->x + span->len;
503                 ++span;
504                 ++spanIdx;
505             }
506         }
507         if (x1 < minx) x1 = minx;
508         if (x2 > maxx) x2 = maxx;
509 
510         //Anti-Aliasing frames
511         ay = y - aaSpans->yStart;
512         if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
513         if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
514 
515         //Range allowed
516         if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
517 
518             //Perform subtexel pre-stepping on UV
519             dx = 1 - (_xa - x1);
520             u = _ua + dx * _dudx;
521             v = _va + dx * _dvdx;
522 
523             buf = dbuf + ((y * dw) + x1);
524 
525             x = x1;
526 
527             if (matting) cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize];
528 
529             if (opacity == 255) {
530                 //Draw horizontal line
531                 while (x++ < x2) {
532                     uu = (int) u;
533                     if (uu >= sw) continue;
534                     vv = (int) v;
535                     if (vv >= sh) continue;
536 
537                     ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
538                     ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
539                     iru = uu + 1;
540                     irv = vv + 1;
541 
542                     px = *(sbuf + (vv * sw) + uu);
543 
544                     /* horizontal interpolate */
545                     if (iru < sw) {
546                         /* right pixel */
547                         int px2 = *(sbuf + (vv * sw) + iru);
548                         px = INTERPOLATE(px, px2, ar);
549                     }
550                     /* vertical interpolate */
551                     if (irv < sh) {
552                         /* bottom pixel */
553                         int px2 = *(sbuf + (irv * sw) + uu);
554 
555                         /* horizontal interpolate */
556                         if (iru < sw) {
557                             /* bottom right pixel */
558                             int px3 = *(sbuf + (irv * sw) + iru);
559                             px2 = INTERPOLATE(px2, px3, ar);
560                         }
561                         px = INTERPOLATE(px, px2, ab);
562                     }
563                     uint32_t src;
564                     if (matting) {
565                         src = ALPHA_BLEND(px, alpha(cmp));
566                         cmp += csize;
567                     } else {
568                         src = px;
569                     }
570                     *buf = src + ALPHA_BLEND(*buf, IA(src));
571                     ++buf;
572 
573                     //Step UV horizontally
574                     u += _dudx;
575                     v += _dvdx;
576                     //range over?
577                     if ((uint32_t)v >= image->h) break;
578                 }
579             } else {
580                 //Draw horizontal line
581                 while (x++ < x2) {
582                     uu = (int) u;
583                     vv = (int) v;
584 
585                     ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
586                     ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
587                     iru = uu + 1;
588                     irv = vv + 1;
589 
590                     if (vv >= sh) continue;
591 
592                     px = *(sbuf + (vv * sw) + uu);
593 
594                     /* horizontal interpolate */
595                     if (iru < sw) {
596                         /* right pixel */
597                         int px2 = *(sbuf + (vv * sw) + iru);
598                         px = INTERPOLATE(px, px2, ar);
599                     }
600                     /* vertical interpolate */
601                     if (irv < sh) {
602                         /* bottom pixel */
603                         int px2 = *(sbuf + (irv * sw) + uu);
604 
605                         /* horizontal interpolate */
606                         if (iru < sw) {
607                             /* bottom right pixel */
608                             int px3 = *(sbuf + (irv * sw) + iru);
609                             px2 = INTERPOLATE(px2, px3, ar);
610                         }
611                         px = INTERPOLATE(px, px2, ab);
612                     }
613                     uint32_t src;
614                     if (matting) {
615                         src = ALPHA_BLEND(px, MULTIPLY(opacity, alpha(cmp)));
616                         cmp += csize;
617                     } else {
618                         src = ALPHA_BLEND(px, opacity);
619                     }
620                     *buf = src + ALPHA_BLEND(*buf, IA(src));
621                     ++buf;
622 
623                     //Step UV horizontally
624                     u += _dudx;
625                     v += _dvdx;
626                     //range over?
627                     if ((uint32_t)v >= image->h) break;
628                 }
629             }
630         }
631 
632         //Step along both edges
633         _xa += _dxdya;
634         _xb += _dxdyb;
635         _ua += _dudya;
636         _va += _dvdya;
637 
638         if (!region && spanIdx >= image->rle->size) break;
639 
640         ++y;
641     }
642     xa = _xa;
643     xb = _xb;
644     ua = _ua;
645     va = _va;
646 }
647 
648 
649 /* This mapping algorithm is based on Mikael Kalms's. */
_rasterPolygonImage(SwSurface * surface,const SwImage * image,const SwBBox * region,Polygon & polygon,AASpans * aaSpans,uint8_t opacity)650 static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, Polygon& polygon, AASpans* aaSpans, uint8_t opacity)
651 {
652     float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
653     float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
654     float u[3] = {polygon.vertex[0].uv.x, polygon.vertex[1].uv.x, polygon.vertex[2].uv.x};
655     float v[3] = {polygon.vertex[0].uv.y, polygon.vertex[1].uv.y, polygon.vertex[2].uv.y};
656 
657     float off_y;
658     float dxdy[3] = {0.0f, 0.0f, 0.0f};
659 
660     auto upper = false;
661 
662     //Sort the vertices in ascending Y order
663     if (y[0] > y[1]) {
664         std::swap(x[0], x[1]);
665         std::swap(y[0], y[1]);
666         std::swap(u[0], u[1]);
667         std::swap(v[0], v[1]);
668     }
669     if (y[0] > y[2])  {
670         std::swap(x[0], x[2]);
671         std::swap(y[0], y[2]);
672         std::swap(u[0], u[2]);
673         std::swap(v[0], v[2]);
674     }
675     if (y[1] > y[2]) {
676         std::swap(x[1], x[2]);
677         std::swap(y[1], y[2]);
678         std::swap(u[1], u[2]);
679         std::swap(v[1], v[2]);
680     }
681 
682     //Y indexes
683     int yi[3] = {(int)y[0], (int)y[1], (int)y[2]};
684 
685     //Skip drawing if it's too thin to cover any pixels at all.
686     if ((yi[0] == yi[1] && yi[0] == yi[2]) || ((int) x[0] == (int) x[1] && (int) x[0] == (int) x[2])) return;
687 
688     //Calculate horizontal and vertical increments for UV axes (these calcs are certainly not optimal, although they're stable (handles any dy being 0)
689     auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0]));
690 
691     //Skip poly if it's an infinitely thin line
692     if (tvg::zero(denom)) return;
693 
694     denom = 1 / denom;   //Reciprocal for speeding up
695     dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom;
696     dvdx = ((v[2] - v[0]) * (y[1] - y[0]) - (v[1] - v[0]) * (y[2] - y[0])) * denom;
697     auto dudy = ((u[1] - u[0]) * (x[2] - x[0]) - (u[2] - u[0]) * (x[1] - x[0])) * denom;
698     auto dvdy = ((v[1] - v[0]) * (x[2] - x[0]) - (v[2] - v[0]) * (x[1] - x[0])) * denom;
699 
700     //Calculate X-slopes along the edges
701     if (y[1] > y[0]) dxdy[0] = (x[1] - x[0]) / (y[1] - y[0]);
702     if (y[2] > y[0]) dxdy[1] = (x[2] - x[0]) / (y[2] - y[0]);
703     if (y[2] > y[1]) dxdy[2] = (x[2] - x[1]) / (y[2] - y[1]);
704 
705     //Determine which side of the polygon the longer edge is on
706     auto side = (dxdy[1] > dxdy[0]) ? true : false;
707 
708     if (tvg::equal(y[0], y[1])) side = x[0] > x[1];
709     if (tvg::equal(y[1], y[2])) side = x[2] > x[1];
710 
711     auto regionTop = region ? region->min.y : image->rle->spans->y;  //Normal Image or Rle Image?
712     auto compositing = _compositing(surface);   //Composition required
713     auto blending = _blending(surface);         //Blending required
714 
715     //Longer edge is on the left side
716     if (!side) {
717         //Calculate slopes along left edge
718         dxdya = dxdy[1];
719         dudya = dxdya * dudx + dudy;
720         dvdya = dxdya * dvdx + dvdy;
721 
722         //Perform subpixel pre-stepping along left edge
723         auto dy = 1.0f - (y[0] - yi[0]);
724         xa = x[0] + dy * dxdya;
725         ua = u[0] + dy * dudya;
726         va = v[0] + dy * dvdya;
727 
728         //Draw upper segment if possibly visible
729         if (yi[0] < yi[1]) {
730             off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
731             xa += (off_y * dxdya);
732             ua += (off_y * dudya);
733             va += (off_y * dvdya);
734 
735             // Set right edge X-slope and perform subpixel pre-stepping
736             dxdyb = dxdy[0];
737             xb = x[0] + dy * dxdyb + (off_y * dxdyb);
738 
739             if (compositing) {
740                 if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
741                 else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 1);
742             } else if (blending) {
743                 _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
744             } else {
745                 _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false);
746             }
747             upper = true;
748         }
749         //Draw lower segment if possibly visible
750         if (yi[1] < yi[2]) {
751             off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
752             if (!upper) {
753                 xa += (off_y * dxdya);
754                 ua += (off_y * dudya);
755                 va += (off_y * dvdya);
756             }
757             // Set right edge X-slope and perform subpixel pre-stepping
758             dxdyb = dxdy[2];
759             xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
760             if (compositing) {
761                 if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
762                 else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 2);
763             } else if (blending) {
764                  _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
765             } else {
766                 _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false);
767             }
768         }
769     //Longer edge is on the right side
770     } else {
771         //Set right edge X-slope and perform subpixel pre-stepping
772         dxdyb = dxdy[1];
773         auto dy = 1.0f - (y[0] - yi[0]);
774         xb = x[0] + dy * dxdyb;
775 
776         //Draw upper segment if possibly visible
777         if (yi[0] < yi[1]) {
778             off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
779             xb += (off_y *dxdyb);
780 
781             // Set slopes along left edge and perform subpixel pre-stepping
782             dxdya = dxdy[0];
783             dudya = dxdya * dudx + dudy;
784             dvdya = dxdya * dvdx + dvdy;
785 
786             xa = x[0] + dy * dxdya + (off_y * dxdya);
787             ua = u[0] + dy * dudya + (off_y * dudya);
788             va = v[0] + dy * dvdya + (off_y * dvdya);
789 
790             if (compositing) {
791                 if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
792                 else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 3);
793             } else if (blending) {
794                 _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
795             } else {
796                 _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false);
797             }
798             upper = true;
799         }
800         //Draw lower segment if possibly visible
801         if (yi[1] < yi[2]) {
802             off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
803             if (!upper) xb += (off_y *dxdyb);
804 
805             // Set slopes along left edge and perform subpixel pre-stepping
806             dxdya = dxdy[2];
807             dudya = dxdya * dudx + dudy;
808             dvdya = dxdya * dvdx + dvdy;
809             dy = 1 - (y[1] - yi[1]);
810             xa = x[1] + dy * dxdya + (off_y * dxdya);
811             ua = u[1] + dy * dudya + (off_y * dudya);
812             va = v[1] + dy * dvdya + (off_y * dvdya);
813 
814             if (compositing) {
815                 if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
816                 else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 4);
817             } else if (blending) {
818                 _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
819             } else {
820                 _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false);
821             }
822         }
823     }
824 }
825 
826 
_AASpans(float ymin,float ymax,const SwImage * image,const SwBBox * region)827 static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region)
828 {
829     auto yStart = static_cast<int>(ymin);
830     auto yEnd = static_cast<int>(ymax);
831 
832     if (!_arrange(image, region, yStart, yEnd)) return nullptr;
833 
834     auto aaSpans = static_cast<AASpans*>(malloc(sizeof(AASpans)));
835     aaSpans->yStart = yStart;
836     aaSpans->yEnd = yEnd;
837 
838     //Initialize X range
839     auto height = yEnd - yStart;
840 
841     aaSpans->lines = static_cast<AALine*>(malloc(height * sizeof(AALine)));
842 
843     for (int32_t i = 0; i < height; i++) {
844         aaSpans->lines[i].x[0] = INT32_MAX;
845         aaSpans->lines[i].x[1] = 0;
846         aaSpans->lines[i].length[0] = 0;
847         aaSpans->lines[i].length[1] = 0;
848     }
849     return aaSpans;
850 }
851 
852 
_calcIrregularCoverage(AALine * lines,int32_t eidx,int32_t y,int32_t diagonal,int32_t edgeDist,bool reverse)853 static void _calcIrregularCoverage(AALine* lines, int32_t eidx, int32_t y, int32_t diagonal, int32_t edgeDist, bool reverse)
854 {
855     if (eidx == 1) reverse = !reverse;
856     int32_t coverage = (255 / (diagonal + 2));
857     int32_t tmp;
858     for (int32_t ry = 0; ry < (diagonal + 2); ry++) {
859         tmp = y - ry - edgeDist;
860         if (tmp < 0) return;
861         lines[tmp].length[eidx] = 1;
862         if (reverse) lines[tmp].coverage[eidx] = 255 - (coverage * ry);
863         else lines[tmp].coverage[eidx] = (coverage * ry);
864     }
865 }
866 
867 
_calcVertCoverage(AALine * lines,int32_t eidx,int32_t y,int32_t rewind,bool reverse)868 static void _calcVertCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t rewind, bool reverse)
869 {
870     if (eidx == 1) reverse = !reverse;
871     int32_t coverage = (255 / (rewind + 1));
872     int32_t tmp;
873     for (int ry = 1; ry < (rewind + 1); ry++) {
874         tmp = y - ry;
875         if (tmp < 0) return;
876         lines[tmp].length[eidx] = 1;
877         if (reverse) lines[tmp].coverage[eidx] = (255 - (coverage * ry));
878         else lines[tmp].coverage[eidx] = (coverage * ry);
879     }
880 }
881 
882 
_calcHorizCoverage(AALine * lines,int32_t eidx,int32_t y,int32_t x,int32_t x2)883 static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2)
884 {
885     lines[y].length[eidx] = abs(x - x2);
886     lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1));
887 }
888 
889 
890 /*
891  * This Anti-Aliasing mechanism is originated from Hermet Park's idea.
892  * To understand this AA logic, you can refer this page:
893  * https://uigraphics.tistory.com/1
894 */
_calcAAEdge(AASpans * aaSpans,int32_t eidx)895 static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
896 {
897 //Previous edge direction:
898 #define DirOutHor 0x0011
899 #define DirOutVer 0x0001
900 #define DirInHor  0x0010
901 #define DirInVer  0x0000
902 #define DirNone   0x1000
903 
904 #define PUSH_VERTEX() \
905     do { \
906         pEdge.x = lines[y].x[eidx]; \
907         pEdge.y = y; \
908         ptx[0] = tx[0]; \
909         ptx[1] = tx[1]; \
910     } while (0)
911 
912     struct Point
913     {
914         int32_t x, y;
915     };
916 
917     int32_t y = 0;
918     Point pEdge = {-1, -1};       //previous edge point
919     Point edgeDiff = {0, 0};      //temporary used for point distance
920 
921     /* store bigger to tx[0] between prev and current edge's x positions. */
922     int32_t tx[2] = {0, 0};
923     /* back up prev tx values */
924     int32_t ptx[2] = {0, 0};
925     int32_t diagonal = 0;           //straight diagonal pixels count
926 
927     auto yStart = aaSpans->yStart;
928     auto yEnd = aaSpans->yEnd;
929     auto lines = aaSpans->lines;
930 
931     int32_t prevDir = DirNone;
932     int32_t curDir = DirNone;
933 
934     yEnd -= yStart;
935 
936     //Start Edge
937     if (y < yEnd) {
938         pEdge.x = lines[y].x[eidx];
939         pEdge.y = y;
940     }
941 
942     //Calculates AA Edges
943     for (y++; y < yEnd; y++) {
944 
945         if (lines[y].x[0] == INT32_MAX) continue;
946 
947         //Ready tx
948         if (eidx == 0) {
949             tx[0] = pEdge.x;
950             tx[1] = lines[y].x[0];
951         } else {
952             tx[0] = lines[y].x[1];
953             tx[1] = pEdge.x;
954         }
955         edgeDiff.x = (tx[0] - tx[1]);
956         edgeDiff.y = (y - pEdge.y);
957 
958         //Confirm current edge direction
959         if (edgeDiff.x > 0) {
960             if (edgeDiff.y == 1) curDir = DirOutHor;
961             else curDir = DirOutVer;
962         } else if (edgeDiff.x < 0) {
963             if (edgeDiff.y == 1) curDir = DirInHor;
964             else curDir = DirInVer;
965         } else curDir = DirNone;
966 
967         //straight diagonal increase
968         if ((curDir == prevDir) && (y < yEnd)) {
969             if ((abs(edgeDiff.x) == 1) && (edgeDiff.y == 1)) {
970                 ++diagonal;
971                 PUSH_VERTEX();
972                 continue;
973             }
974         }
975 
976         switch (curDir) {
977             case DirOutHor: {
978                 _calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
979                 if (diagonal > 0) {
980                     _calcIrregularCoverage(lines, eidx, y, diagonal, 0, true);
981                     diagonal = 0;
982                 }
983                /* Increment direction is changed: Outside Vertical -> Outside Horizontal */
984                if (prevDir == DirOutVer) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
985 
986                //Trick, but fine-tunning!
987                if (y == 1) _calcHorizCoverage(lines, eidx, pEdge.y, tx[0], tx[1]);
988                PUSH_VERTEX();
989             }
990             break;
991             case DirOutVer: {
992                 _calcVertCoverage(lines, eidx, y, edgeDiff.y, true);
993                 if (diagonal > 0) {
994                     _calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, false);
995                     diagonal = 0;
996                 }
997                /* Increment direction is changed: Outside Horizontal -> Outside Vertical */
998                if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
999                PUSH_VERTEX();
1000             }
1001             break;
1002             case DirInHor: {
1003                 _calcHorizCoverage(lines, eidx, (y - 1), tx[0], tx[1]);
1004                 if (diagonal > 0) {
1005                     _calcIrregularCoverage(lines, eidx, y, diagonal, 0, false);
1006                     diagonal = 0;
1007                 }
1008                 /* Increment direction is changed: Outside Horizontal -> Inside Horizontal */
1009                if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
1010                PUSH_VERTEX();
1011             }
1012             break;
1013             case DirInVer: {
1014                 _calcVertCoverage(lines, eidx, y, edgeDiff.y, false);
1015                 if (prevDir == DirOutHor) edgeDiff.y -= 1;      //Weird, fine tuning?????????????????????
1016                 if (diagonal > 0) {
1017                     _calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, true);
1018                     diagonal = 0;
1019                 }
1020                 /* Increment direction is changed: Outside Horizontal -> Inside Vertical */
1021                 if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
1022                 PUSH_VERTEX();
1023             }
1024             break;
1025         }
1026         if (curDir != DirNone) prevDir = curDir;
1027     }
1028 
1029     //leftovers...?
1030     if ((edgeDiff.y == 1) && (edgeDiff.x != 0)) {
1031         if (y >= yEnd) y = (yEnd - 1);
1032         _calcHorizCoverage(lines, eidx, y - 1, ptx[0], ptx[1]);
1033         _calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
1034     } else {
1035         ++y;
1036         if (y > yEnd) y = yEnd;
1037         _calcVertCoverage(lines, eidx, y, (edgeDiff.y + 1), (prevDir & 0x00000001));
1038     }
1039 }
1040 
1041 
_apply(SwSurface * surface,AASpans * aaSpans)1042 static bool _apply(SwSurface* surface, AASpans* aaSpans)
1043 {
1044     auto end = surface->buf32 + surface->h * surface->stride;
1045     auto y = aaSpans->yStart;
1046     uint32_t pixel;
1047     uint32_t* dst;
1048     int32_t pos;
1049 
1050    //left side
1051    _calcAAEdge(aaSpans, 0);
1052    //right side
1053    _calcAAEdge(aaSpans, 1);
1054 
1055     while (y < aaSpans->yEnd) {
1056         auto line = &aaSpans->lines[y - aaSpans->yStart];
1057         auto width = line->x[1] - line->x[0];
1058         if (width > 0) {
1059             auto offset = y * surface->stride;
1060 
1061             //Left edge
1062             dst = surface->buf32 + (offset + line->x[0]);
1063             if (line->x[0] > 1) pixel = *(dst - 1);
1064             else pixel = *dst;
1065             pos = 1;
1066 
1067             //exceptional handling. out of memory bound.
1068             if (dst + line->length[0] >= end) {
1069                 pos += (dst + line->length[0] - end);
1070             }
1071 
1072             while (pos <= line->length[0]) {
1073                 *dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos);
1074                 ++dst;
1075                 ++pos;
1076             }
1077 
1078             //Right edge
1079             dst = surface->buf32 + offset + line->x[1] - 1;
1080 
1081             if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
1082             else pixel = *dst;
1083             pos = line->length[1];
1084 
1085             //exceptional handling. out of memory bound.
1086             if (dst - pos < surface->buf32) --pos;
1087 
1088             while (pos > 0) {
1089                 *dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * pos));
1090                 --dst;
1091                 --pos;
1092             }
1093         }
1094         y++;
1095     }
1096 
1097     free(aaSpans->lines);
1098     free(aaSpans);
1099 
1100     return true;
1101 }
1102 
1103 
1104 /*
1105     2 triangles constructs 1 mesh.
1106     below figure illustrates vert[4] index info.
1107     If you need better quality, please divide a mesh by more number of triangles.
1108 
1109     0 -- 1
1110     |  / |
1111     | /  |
1112     3 -- 2
1113 */
_rasterTexmapPolygon(SwSurface * surface,const SwImage * image,const Matrix & transform,const SwBBox * region,uint8_t opacity)1114 static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox* region, uint8_t opacity)
1115 {
1116     if (surface->channelSize == sizeof(uint8_t)) {
1117         TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
1118         return false;
1119     }
1120 
1121     //Exceptions: No dedicated drawing area?
1122     if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return true;
1123 
1124    /* Prepare vertices.
1125       shift XY coordinates to match the sub-pixeling technique. */
1126     Vertex vertices[4];
1127     vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
1128     vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}};
1129     vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}};
1130     vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}};
1131 
1132     float ys = FLT_MAX, ye = -1.0f;
1133     for (int i = 0; i < 4; i++) {
1134         vertices[i].pt *= transform;
1135         if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
1136         if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
1137     }
1138 
1139     auto aaSpans = _AASpans(ys, ye, image, region);
1140     if (!aaSpans) return true;
1141 
1142     Polygon polygon;
1143 
1144     //Draw the first polygon
1145     polygon.vertex[0] = vertices[0];
1146     polygon.vertex[1] = vertices[1];
1147     polygon.vertex[2] = vertices[3];
1148 
1149     _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
1150 
1151     //Draw the second polygon
1152     polygon.vertex[0] = vertices[1];
1153     polygon.vertex[1] = vertices[2];
1154     polygon.vertex[2] = vertices[3];
1155 
1156     _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
1157 
1158 #if 0
1159     if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
1160         _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
1161     }
1162 #endif
1163     return _apply(surface, aaSpans);
1164 }
1165 
1166 #endif /* LV_USE_THORVG_INTERNAL */
1167 
1168