updated copyright.
[platform/core/graphics/tizenvg.git] / src / lib / sw_engine / tvgSwRasterTexmap.h
1 /*
2  * Copyright (c) 2021 - 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 struct AALine
24 {
25    int32_t x[2];
26    int32_t coverage[2];
27    int32_t length[2];
28 };
29
30 struct AASpans
31 {
32    AALine *lines;
33    int32_t yStart;
34    int32_t yEnd;
35 };
36
37 static inline void _swap(float& a, float& b, float& tmp)
38 {
39     tmp = a;
40     a = b;
41     b = tmp;
42 }
43
44 //Careful! Shared resource, No support threading
45 static float dudx, dvdx;
46 static float dxdya, dxdyb, dudya, dvdya;
47 static float xa, xb, ua, va;
48
49
50 //Y Range exception handling
51 static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd)
52 {
53     int32_t regionTop, regionBottom;
54
55     if (region) {
56         regionTop = region->min.y;
57         regionBottom = region->max.y;
58     } else {
59         regionTop = image->rle->spans->y;
60         regionBottom = image->rle->spans[image->rle->size - 1].y;
61     }
62
63     if (yStart >= regionBottom) return false;
64
65     if (yStart < regionTop) yStart = regionTop;
66     if (yEnd > regionBottom) yEnd = regionBottom;
67
68     return true;
69 }
70
71
72 static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
73 {
74 #define TEXMAP_TRANSLUCENT
75 #define TEXMAP_MASKING
76       #include "tvgSwRasterTexmapInternal.h"
77 #undef TEXMAP_MASKING
78 #undef TEXMAP_TRANSLUCENT
79 }
80
81
82 static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
83 {
84 #define TEXMAP_MASKING
85     #include "tvgSwRasterTexmapInternal.h"
86 #undef TEXMAP_MASKING
87 }
88
89
90 static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, AASpans* aaSpans)
91 {
92 #define TEXMAP_TRANSLUCENT
93      #include "tvgSwRasterTexmapInternal.h"
94 #undef TEXMAP_TRANSLUCENT
95 }
96
97
98 static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans)
99 {
100     #include "tvgSwRasterTexmapInternal.h"
101 }
102
103
104 /* This mapping algorithm is based on Mikael Kalms's. */
105 static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, uint32_t opacity, Polygon& polygon, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
106 {
107     float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
108     float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
109     float u[3] = {polygon.vertex[0].uv.x, polygon.vertex[1].uv.x, polygon.vertex[2].uv.x};
110     float v[3] = {polygon.vertex[0].uv.y, polygon.vertex[1].uv.y, polygon.vertex[2].uv.y};
111
112     float off_y;
113     float dxdy[3] = {0.0f, 0.0f, 0.0f};
114     float tmp;
115
116     auto upper = false;
117
118     //Sort the vertices in ascending Y order
119     if (y[0] > y[1]) {
120         _swap(x[0], x[1], tmp);
121         _swap(y[0], y[1], tmp);
122         _swap(u[0], u[1], tmp);
123         _swap(v[0], v[1], tmp);
124     }
125     if (y[0] > y[2])  {
126         _swap(x[0], x[2], tmp);
127         _swap(y[0], y[2], tmp);
128         _swap(u[0], u[2], tmp);
129         _swap(v[0], v[2], tmp);
130     }
131     if (y[1] > y[2]) {
132         _swap(x[1], x[2], tmp);
133         _swap(y[1], y[2], tmp);
134         _swap(u[1], u[2], tmp);
135         _swap(v[1], v[2], tmp);
136     }
137
138     //Y indexes
139     int yi[3] = {(int)y[0], (int)y[1], (int)y[2]};
140
141     //Skip drawing if it's too thin to cover any pixels at all.
142     if ((yi[0] == yi[1] && yi[0] == yi[2]) || ((int) x[0] == (int) x[1] && (int) x[0] == (int) x[2])) return;
143
144     //Calculate horizontal and vertical increments for UV axes (these calcs are certainly not optimal, although they're stable (handles any dy being 0)
145     auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0]));
146
147     //Skip poly if it's an infinitely thin line
148     if (mathZero(denom)) return;
149
150     denom = 1 / denom;   //Reciprocal for speeding up
151     dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom;
152     dvdx = ((v[2] - v[0]) * (y[1] - y[0]) - (v[1] - v[0]) * (y[2] - y[0])) * denom;
153     auto dudy = ((u[1] - u[0]) * (x[2] - x[0]) - (u[2] - u[0]) * (x[1] - x[0])) * denom;
154     auto dvdy = ((v[1] - v[0]) * (x[2] - x[0]) - (v[2] - v[0]) * (x[1] - x[0])) * denom;
155
156     //Calculate X-slopes along the edges
157     if (y[1] > y[0]) dxdy[0] = (x[1] - x[0]) / (y[1] - y[0]);
158     if (y[2] > y[0]) dxdy[1] = (x[2] - x[0]) / (y[2] - y[0]);
159     if (y[2] > y[1]) dxdy[2] = (x[2] - x[1]) / (y[2] - y[1]);
160
161     //Determine which side of the polygon the longer edge is on
162     auto side = (dxdy[1] > dxdy[0]) ? true : false;
163
164     if (mathEqual(y[0], y[1])) side = x[0] > x[1];
165     if (mathEqual(y[1], y[2])) side = x[2] > x[1];
166
167     auto regionTop = region ? region->min.y : image->rle->spans->y;  //Normal Image or Rle Image?
168
169     //Longer edge is on the left side
170     if (!side) {
171         //Calculate slopes along left edge
172         dxdya = dxdy[1];
173         dudya = dxdya * dudx + dudy;
174         dvdya = dxdya * dvdx + dvdy;
175
176         //Perform subpixel pre-stepping along left edge
177         auto dy = 1.0f - (y[0] - yi[0]);
178         xa = x[0] + dy * dxdya;
179         ua = u[0] + dy * dudya;
180         va = v[0] + dy * dvdya;
181
182         //Draw upper segment if possibly visible
183         if (yi[0] < yi[1]) {
184             off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
185             xa += (off_y * dxdya);
186             ua += (off_y * dudya);
187             va += (off_y * dvdya);
188
189             // Set right edge X-slope and perform subpixel pre-stepping
190             dxdyb = dxdy[0];
191             xb = x[0] + dy * dxdyb + (off_y * dxdyb);
192
193             if (blendMethod) {
194                 if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans);
195                 else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans);
196             } else {
197                 if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans);
198                 else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans);
199             }
200
201             upper = true;
202         }
203         //Draw lower segment if possibly visible
204         if (yi[1] < yi[2]) {
205             off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
206             if (!upper) {
207                 xa += (off_y * dxdya);
208                 ua += (off_y * dudya);
209                 va += (off_y * dvdya);
210             }
211             // Set right edge X-slope and perform subpixel pre-stepping
212             dxdyb = dxdy[2];
213             xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
214             if (blendMethod) {
215                 if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans);
216                 else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans);
217             } else {
218                 if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans);
219                 else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans);
220             }
221         }
222     //Longer edge is on the right side
223     } else {
224         //Set right edge X-slope and perform subpixel pre-stepping
225         dxdyb = dxdy[1];
226         auto dy = 1.0f - (y[0] - yi[0]);
227         xb = x[0] + dy * dxdyb;
228
229         //Draw upper segment if possibly visible
230         if (yi[0] < yi[1]) {
231             off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
232             xb += (off_y *dxdyb);
233
234             // Set slopes along left edge and perform subpixel pre-stepping
235             dxdya = dxdy[0];
236             dudya = dxdya * dudx + dudy;
237             dvdya = dxdya * dvdx + dvdy;
238
239             xa = x[0] + dy * dxdya + (off_y * dxdya);
240             ua = u[0] + dy * dudya + (off_y * dudya);
241             va = v[0] + dy * dvdya + (off_y * dvdya);
242
243             if (blendMethod) {
244                 if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans);
245                 else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans);
246             } else {
247                 if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans);
248                 else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans);
249             }
250
251             upper = true;
252         }
253         //Draw lower segment if possibly visible
254         if (yi[1] < yi[2]) {
255             off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
256             if (!upper) xb += (off_y *dxdyb);
257
258             // Set slopes along left edge and perform subpixel pre-stepping
259             dxdya = dxdy[2];
260             dudya = dxdya * dudx + dudy;
261             dvdya = dxdya * dvdx + dvdy;
262             dy = 1 - (y[1] - yi[1]);
263             xa = x[1] + dy * dxdya + (off_y * dxdya);
264             ua = u[1] + dy * dudya + (off_y * dudya);
265             va = v[1] + dy * dvdya + (off_y * dvdya);
266
267             if (blendMethod) {
268                 if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans);
269                 else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans);
270             } else {
271                 if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans);
272                 else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans);
273             }
274         }
275     }
276 }
277
278
279 static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region)
280 {
281     auto yStart = static_cast<int32_t>(ymin);
282     auto yEnd = static_cast<int32_t>(ymax);
283
284     if (!_arrange(image, region, yStart, yEnd)) return nullptr;
285
286     auto aaSpans = static_cast<AASpans*>(malloc(sizeof(AASpans)));
287     aaSpans->yStart = yStart;
288     aaSpans->yEnd = yEnd;
289
290     //Initialize X range
291     auto height = yEnd - yStart;
292
293     aaSpans->lines = static_cast<AALine*>(calloc(height, sizeof(AALine)));
294
295     for (int32_t i = 0; i < height; i++) {
296         aaSpans->lines[i].x[0] = INT32_MAX;
297         aaSpans->lines[i].x[1] = INT32_MIN;
298     }
299     return aaSpans;
300 }
301
302
303 static void _calcIrregularCoverage(AALine* lines, int32_t eidx, int32_t y, int32_t diagonal, int32_t edgeDist, bool reverse)
304 {
305     if (eidx == 1) reverse = !reverse;
306     int32_t coverage = (255 / (diagonal + 2));
307     int32_t tmp;
308     for (int32_t ry = 0; ry < (diagonal + 2); ry++) {
309         tmp = y - ry - edgeDist;
310         if (tmp < 0) return;
311         lines[tmp].length[eidx] = 1;
312         if (reverse) lines[tmp].coverage[eidx] = 255 - (coverage * ry);
313         else lines[tmp].coverage[eidx] = (coverage * ry);
314     }
315 }
316
317
318 static void _calcVertCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t rewind, bool reverse)
319 {
320     if (eidx == 1) reverse = !reverse;
321     int32_t coverage = (255 / (rewind + 1));
322     int32_t tmp;
323     for (int ry = 1; ry < (rewind + 1); ry++) {
324         tmp = y - ry;
325         if (tmp < 0) return;
326         lines[tmp].length[eidx] = 1;
327         if (reverse) lines[tmp].coverage[eidx] = (255 - (coverage * ry));
328         else lines[tmp].coverage[eidx] = (coverage * ry);
329     }
330 }
331
332
333 static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2)
334 {
335     if (lines[y].length[eidx] < abs(x - x2)) {
336         lines[y].length[eidx] = abs(x - x2);
337         lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1));
338     }
339 }
340
341
342 /*
343  * This Anti-Aliasing mechanism is originated from Hermet Park's idea.
344  * To understand this AA logic, you can refer this page:
345  * www.hermet.pe.kr/122 (hermetpark@gmail.com)
346 */
347 static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
348 {
349 //Previous edge direction:
350 #define DirOutHor 0x0011
351 #define DirOutVer 0x0001
352 #define DirInHor  0x0010
353 #define DirInVer  0x0000
354 #define DirNone   0x1000
355
356 #define PUSH_VERTEX() \
357     do { \
358         pEdge.x = lines[y].x[eidx]; \
359         pEdge.y = y; \
360         ptx[0] = tx[0]; \
361         ptx[1] = tx[1]; \
362     } while (0)
363
364     int32_t y = 0;
365     SwPoint pEdge = {-1, -1};       //previous edge point
366     SwPoint edgeDiff = {0, 0};      //temporary used for point distance
367
368     /* store bigger to tx[0] between prev and current edge's x positions. */
369     int32_t tx[2] = {0, 0};
370     /* back up prev tx values */
371     int32_t ptx[2] = {0, 0};
372     int32_t diagonal = 0;           //straight diagonal pixels count
373
374     auto yStart = aaSpans->yStart;
375     auto yEnd = aaSpans->yEnd;
376     auto lines = aaSpans->lines;
377
378     int32_t prevDir = DirNone;
379     int32_t curDir = DirNone;
380
381     yEnd -= yStart;
382
383     //Start Edge
384     if (y < yEnd) {
385         pEdge.x = lines[y].x[eidx];
386         pEdge.y = y;
387     }
388
389     //Calculates AA Edges
390     for (y++; y < yEnd; y++) {
391         //Ready tx
392         if (eidx == 0) {
393             tx[0] = pEdge.x;
394             tx[1] = lines[y].x[0];
395         } else {
396             tx[0] = lines[y].x[1];
397             tx[1] = pEdge.x;
398         }
399         edgeDiff.x = (tx[0] - tx[1]);
400         edgeDiff.y = (y - pEdge.y);
401
402         //Confirm current edge direction
403         if (edgeDiff.x > 0) {
404             if (edgeDiff.y == 1) curDir = DirOutHor;
405             else curDir = DirOutVer;
406         } else if (edgeDiff.x < 0) {
407             if (edgeDiff.y == 1) curDir = DirInHor;
408             else curDir = DirInVer;
409         } else curDir = DirNone;
410
411         //straight diagonal increase
412         if ((curDir == prevDir) && (y < yEnd)) {
413             if ((abs(edgeDiff.x) == 1) && (edgeDiff.y == 1)) {
414                 ++diagonal;
415                 PUSH_VERTEX();
416                 continue;
417             }
418         }
419
420         switch (curDir) {
421             case DirOutHor: {
422                 _calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
423                 if (diagonal > 0) {
424                     _calcIrregularCoverage(lines, eidx, y, diagonal, 0, true);
425                     diagonal = 0;
426                 }
427                /* Increment direction is changed: Outside Vertical -> Outside Horizontal */
428                if (prevDir == DirOutVer) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
429
430                //Trick, but fine-tunning!
431                if (y == 1) _calcHorizCoverage(lines, eidx, pEdge.y, tx[0], tx[1]);
432                PUSH_VERTEX();
433             }
434             break;
435             case DirOutVer: {
436                 _calcVertCoverage(lines, eidx, y, edgeDiff.y, true);
437                 if (diagonal > 0) {
438                     _calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, false);
439                     diagonal = 0;
440                 }
441                /* Increment direction is changed: Outside Horizontal -> Outside Vertical */
442                if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
443                PUSH_VERTEX();
444             }
445             break;
446             case DirInHor: {
447                 _calcHorizCoverage(lines, eidx, (y - 1), tx[0], tx[1]);
448                 if (diagonal > 0) {
449                     _calcIrregularCoverage(lines, eidx, y, diagonal, 0, false);
450                     diagonal = 0;
451                 }
452                 /* Increment direction is changed: Outside Horizontal -> Inside Horizontal */
453                if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
454                PUSH_VERTEX();
455             }
456             break;
457             case DirInVer: {
458                 _calcVertCoverage(lines, eidx, y, edgeDiff.y, false);
459                 if (prevDir == DirOutHor) edgeDiff.y -= 1;      //Weird, fine tuning?????????????????????
460                 if (diagonal > 0) {
461                     _calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, true);
462                     diagonal = 0;
463                 }
464                 /* Increment direction is changed: Outside Horizontal -> Inside Vertical */
465                 if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
466                 PUSH_VERTEX();
467             }
468             break;
469         }
470         if (curDir != DirNone) prevDir = curDir;
471     }
472
473     //leftovers...?
474     if ((edgeDiff.y == 1) && (edgeDiff.x != 0)) {
475         if (y >= yEnd) y = (yEnd - 1);
476         _calcHorizCoverage(lines, eidx, y - 1, ptx[0], ptx[1]);
477         _calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
478     } else {
479         ++y;
480         if (y > yEnd) y = yEnd;
481         _calcVertCoverage(lines, eidx, y, (edgeDiff.y + 1), (prevDir & 0x00000001));
482     }
483 }
484
485
486 static bool _apply(SwSurface* surface, AASpans* aaSpans)
487 {
488     auto y = aaSpans->yStart;
489     uint32_t pixel;
490     uint32_t* dst;
491     int32_t pos;
492
493    //left side
494    _calcAAEdge(aaSpans, 0);
495    //right side
496    _calcAAEdge(aaSpans, 1);
497
498     while (y < aaSpans->yEnd) {
499         auto line = &aaSpans->lines[y - aaSpans->yStart];
500         auto width = line->x[1] - line->x[0];
501         if (width > 0) {
502             auto offset = y * surface->stride;
503
504             //Left edge
505             dst = surface->buffer + (offset + line->x[0]);
506             if (line->x[0] > 1) pixel = *(dst - 1);
507             else pixel = *dst;
508
509             pos = 1;
510             while (pos <= line->length[0]) {
511                 *dst = INTERPOLATE((line->coverage[0] * pos), *dst, pixel);
512                 ++dst;
513                 ++pos;
514             }
515
516             //Right edge
517             dst = surface->buffer + (offset + line->x[1] - 1);
518             if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
519             else pixel = *dst;
520
521             pos = width;
522             while ((int32_t)(width - line->length[1]) < pos) {
523                 *dst = INTERPOLATE(255 - (line->coverage[1] * (line->length[1] - (width - pos))), *dst, pixel);
524                 --dst;
525                 --pos;
526             }
527           }
528         y++;
529     }
530
531     free(aaSpans->lines);
532     free(aaSpans);
533
534     return true;
535 }
536
537
538 /*
539     2 triangles constructs 1 mesh.
540     below figure illustrates vert[4] index info.
541     If you need better quality, please divide a mesh by more number of triangles.
542
543     0 -- 1
544     |  / |
545     | /  |
546     3 -- 2
547 */
548 static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
549 {
550     //Exceptions: No dedicated drawing area?
551     if (!region && image->rle->size == 0) return false;
552
553    /* Prepare vertices.
554       shift XY coordinates to match the sub-pixeling technique. */
555     Vertex vertices[4];
556     vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
557     vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}};
558     vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}};
559     vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}};
560
561     float ys = FLT_MAX, ye = -1.0f;
562     for (int i = 0; i < 4; i++) {
563         mathMultiply(&vertices[i].pt, transform);
564
565         if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
566         if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
567     }
568
569     auto aaSpans = _AASpans(ys, ye, image, region);
570     if (!aaSpans) return true;
571
572     Polygon polygon;
573
574     //Draw the first polygon
575     polygon.vertex[0] = vertices[0];
576     polygon.vertex[1] = vertices[1];
577     polygon.vertex[2] = vertices[3];
578
579     _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans);
580
581     //Draw the second polygon
582     polygon.vertex[0] = vertices[1];
583     polygon.vertex[1] = vertices[2];
584     polygon.vertex[2] = vertices[3];
585
586     _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans);
587
588     return _apply(surface, aaSpans);
589 }
590
591
592 /*
593     Provide any number of triangles to draw a mesh using the supplied image.
594     Indexes are not used, so each triangle (Polygon) vertex has to be defined, even if they copy the previous one.
595     Example:
596
597       0 -- 1       0 -- 1   0
598       |  / |  -->  |  /   / |
599       | /  |       | /   /  |
600       2 -- 3       2   1 -- 2
601
602       Should provide two Polygons, one for each triangle.
603       // TODO: region?
604 */
605 static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const Polygon* triangles, const uint32_t triangleCount, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
606 {
607     //Exceptions: No dedicated drawing area?
608     if (!region && image->rle->size == 0) return false;
609
610     // Step polygons once to transform
611     auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * triangleCount);
612     float ys = FLT_MAX, ye = -1.0f;
613     for (uint32_t i = 0; i < triangleCount; i++) {
614         transformedTris[i] = triangles[i];
615         mathMultiply(&transformedTris[i].vertex[0].pt, transform);
616         mathMultiply(&transformedTris[i].vertex[1].pt, transform);
617         mathMultiply(&transformedTris[i].vertex[2].pt, transform);
618
619         if (transformedTris[i].vertex[0].pt.y < ys) ys = transformedTris[i].vertex[0].pt.y;
620         else if (transformedTris[i].vertex[0].pt.y > ye) ye = transformedTris[i].vertex[0].pt.y;
621         if (transformedTris[i].vertex[1].pt.y < ys) ys = transformedTris[i].vertex[1].pt.y;
622         else if (transformedTris[i].vertex[1].pt.y > ye) ye = transformedTris[i].vertex[1].pt.y;
623         if (transformedTris[i].vertex[2].pt.y < ys) ys = transformedTris[i].vertex[2].pt.y;
624         else if (transformedTris[i].vertex[2].pt.y > ye) ye = transformedTris[i].vertex[2].pt.y;
625
626         // Convert normalized UV coordinates to image coordinates
627         transformedTris[i].vertex[0].uv.x *= (float)image->w;
628         transformedTris[i].vertex[0].uv.y *= (float)image->h;
629         transformedTris[i].vertex[1].uv.x *= (float)image->w;
630         transformedTris[i].vertex[1].uv.y *= (float)image->h;
631         transformedTris[i].vertex[2].uv.x *= (float)image->w;
632         transformedTris[i].vertex[2].uv.y *= (float)image->h;
633     }
634
635     // Get AA spans and step polygons again to draw
636     auto aaSpans = _AASpans(ys, ye, image, region);
637     if (aaSpans) {
638         for (uint32_t i = 0; i < triangleCount; i++) {
639             _rasterPolygonImage(surface, image, region, opacity, transformedTris[i], blendMethod, aaSpans);
640         }
641         // Apply to surface (note: frees the AA spans)
642         _apply(surface, aaSpans);
643     }
644     free(transformedTris);
645
646     return true;
647 }