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