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