2 * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
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:
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
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
37 static inline void _swap(float& a, float& b, float& tmp)
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;
50 //Y Range exception handling
51 static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd)
53 int32_t regionTop, regionBottom;
56 regionTop = region->min.y;
57 regionBottom = region->max.y;
59 regionTop = image->rle->spans->y;
60 regionBottom = image->rle->spans[image->rle->size - 1].y;
63 if (yStart >= regionBottom) return false;
65 if (yStart < regionTop) yStart = regionTop;
66 if (yEnd > regionBottom) yEnd = regionBottom;
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)
74 #define TEXMAP_TRANSLUCENT
75 #define TEXMAP_MASKING
76 #include "tvgSwRasterTexmapInternal.h"
78 #undef TEXMAP_TRANSLUCENT
82 static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
84 #define TEXMAP_MASKING
85 #include "tvgSwRasterTexmapInternal.h"
90 static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, AASpans* aaSpans)
92 #define TEXMAP_TRANSLUCENT
93 #include "tvgSwRasterTexmapInternal.h"
94 #undef TEXMAP_TRANSLUCENT
98 static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans)
100 #include "tvgSwRasterTexmapInternal.h"
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)
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};
113 float dxdy[3] = {0.0f, 0.0f, 0.0f};
118 //Sort the vertices in ascending Y order
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);
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);
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);
139 int yi[3] = {(int)y[0], (int)y[1], (int)y[2]};
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;
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]));
147 //Skip poly if it's an infinitely thin line
148 if (mathZero(denom)) return;
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;
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]);
161 //Determine which side of the polygon the longer edge is on
162 auto side = (dxdy[1] > dxdy[0]) ? true : false;
164 if (mathEqual(y[0], y[1])) side = x[0] > x[1];
165 if (mathEqual(y[1], y[2])) side = x[2] > x[1];
167 auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image?
169 //Longer edge is on the left side
171 //Calculate slopes along left edge
173 dudya = dxdya * dudx + dudy;
174 dvdya = dxdya * dvdx + dvdy;
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;
182 //Draw upper segment if possibly visible
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);
189 // Set right edge X-slope and perform subpixel pre-stepping
191 xb = x[0] + dy * dxdyb + (off_y * dxdyb);
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);
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);
203 //Draw lower segment if possibly visible
205 off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
207 xa += (off_y * dxdya);
208 ua += (off_y * dudya);
209 va += (off_y * dvdya);
211 // Set right edge X-slope and perform subpixel pre-stepping
213 xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
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);
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);
222 //Longer edge is on the right side
224 //Set right edge X-slope and perform subpixel pre-stepping
226 auto dy = 1.0f - (y[0] - yi[0]);
227 xb = x[0] + dy * dxdyb;
229 //Draw upper segment if possibly visible
231 off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
232 xb += (off_y *dxdyb);
234 // Set slopes along left edge and perform subpixel pre-stepping
236 dudya = dxdya * dudx + dudy;
237 dvdya = dxdya * dvdx + dvdy;
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);
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);
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);
253 //Draw lower segment if possibly visible
255 off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
256 if (!upper) xb += (off_y *dxdyb);
258 // Set slopes along left edge and perform subpixel pre-stepping
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);
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);
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);
279 static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region)
281 auto yStart = static_cast<int32_t>(ymin);
282 auto yEnd = static_cast<int32_t>(ymax);
284 if (!_arrange(image, region, yStart, yEnd)) return nullptr;
286 auto aaSpans = static_cast<AASpans*>(malloc(sizeof(AASpans)));
287 aaSpans->yStart = yStart;
288 aaSpans->yEnd = yEnd;
291 auto height = yEnd - yStart;
293 aaSpans->lines = static_cast<AALine*>(calloc(height, sizeof(AALine)));
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;
303 static void _calcIrregularCoverage(AALine* lines, int32_t eidx, int32_t y, int32_t diagonal, int32_t edgeDist, bool reverse)
305 if (eidx == 1) reverse = !reverse;
306 int32_t coverage = (255 / (diagonal + 2));
308 for (int32_t ry = 0; ry < (diagonal + 2); ry++) {
309 tmp = y - ry - edgeDist;
311 lines[tmp].length[eidx] = 1;
312 if (reverse) lines[tmp].coverage[eidx] = 255 - (coverage * ry);
313 else lines[tmp].coverage[eidx] = (coverage * ry);
318 static void _calcVertCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t rewind, bool reverse)
320 if (eidx == 1) reverse = !reverse;
321 int32_t coverage = (255 / (rewind + 1));
323 for (int ry = 1; ry < (rewind + 1); ry++) {
326 lines[tmp].length[eidx] = 1;
327 if (reverse) lines[tmp].coverage[eidx] = (255 - (coverage * ry));
328 else lines[tmp].coverage[eidx] = (coverage * ry);
333 static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2)
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));
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)
347 static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
349 //Previous edge direction:
350 #define DirOutHor 0x0011
351 #define DirOutVer 0x0001
352 #define DirInHor 0x0010
353 #define DirInVer 0x0000
354 #define DirNone 0x1000
356 #define PUSH_VERTEX() \
358 pEdge.x = lines[y].x[eidx]; \
365 SwPoint pEdge = {-1, -1}; //previous edge point
366 SwPoint edgeDiff = {0, 0}; //temporary used for point distance
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
374 auto yStart = aaSpans->yStart;
375 auto yEnd = aaSpans->yEnd;
376 auto lines = aaSpans->lines;
378 int32_t prevDir = DirNone;
379 int32_t curDir = DirNone;
385 pEdge.x = lines[y].x[eidx];
389 //Calculates AA Edges
390 for (y++; y < yEnd; y++) {
394 tx[1] = lines[y].x[0];
396 tx[0] = lines[y].x[1];
399 edgeDiff.x = (tx[0] - tx[1]);
400 edgeDiff.y = (y - pEdge.y);
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;
411 //straight diagonal increase
412 if ((curDir == prevDir) && (y < yEnd)) {
413 if ((abs(edgeDiff.x) == 1) && (edgeDiff.y == 1)) {
422 _calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
424 _calcIrregularCoverage(lines, eidx, y, diagonal, 0, true);
427 /* Increment direction is changed: Outside Vertical -> Outside Horizontal */
428 if (prevDir == DirOutVer) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
430 //Trick, but fine-tunning!
431 if (y == 1) _calcHorizCoverage(lines, eidx, pEdge.y, tx[0], tx[1]);
436 _calcVertCoverage(lines, eidx, y, edgeDiff.y, true);
438 _calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, false);
441 /* Increment direction is changed: Outside Horizontal -> Outside Vertical */
442 if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
447 _calcHorizCoverage(lines, eidx, (y - 1), tx[0], tx[1]);
449 _calcIrregularCoverage(lines, eidx, y, diagonal, 0, false);
452 /* Increment direction is changed: Outside Horizontal -> Inside Horizontal */
453 if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
458 _calcVertCoverage(lines, eidx, y, edgeDiff.y, false);
459 if (prevDir == DirOutHor) edgeDiff.y -= 1; //Weird, fine tuning?????????????????????
461 _calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, true);
464 /* Increment direction is changed: Outside Horizontal -> Inside Vertical */
465 if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
470 if (curDir != DirNone) prevDir = curDir;
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]);
480 if (y > yEnd) y = yEnd;
481 _calcVertCoverage(lines, eidx, y, (edgeDiff.y + 1), (prevDir & 0x00000001));
486 static bool _apply(SwSurface* surface, AASpans* aaSpans)
488 auto y = aaSpans->yStart;
494 _calcAAEdge(aaSpans, 0);
496 _calcAAEdge(aaSpans, 1);
498 while (y < aaSpans->yEnd) {
499 auto line = &aaSpans->lines[y - aaSpans->yStart];
500 auto width = line->x[1] - line->x[0];
502 auto offset = y * surface->stride;
505 dst = surface->buffer + (offset + line->x[0]);
506 if (line->x[0] > 1) pixel = *(dst - 1);
510 while (pos <= line->length[0]) {
511 *dst = INTERPOLATE((line->coverage[0] * pos), *dst, pixel);
517 dst = surface->buffer + (offset + line->x[1] - 1);
518 if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
522 while ((int32_t)(width - line->length[1]) < pos) {
523 *dst = INTERPOLATE(255 - (line->coverage[1] * (line->length[1] - (width - pos))), *dst, pixel);
531 free(aaSpans->lines);
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.
548 static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
550 //Exceptions: No dedicated drawing area?
551 if (!region && image->rle->size == 0) return false;
554 shift XY coordinates to match the sub-pixeling technique. */
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)}};
561 float ys = FLT_MAX, ye = -1.0f;
562 for (int i = 0; i < 4; i++) {
563 mathMultiply(&vertices[i].pt, transform);
565 if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
566 if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
569 auto aaSpans = _AASpans(ys, ye, image, region);
570 if (!aaSpans) return true;
574 //Draw the first polygon
575 polygon.vertex[0] = vertices[0];
576 polygon.vertex[1] = vertices[1];
577 polygon.vertex[2] = vertices[3];
579 _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans);
581 //Draw the second polygon
582 polygon.vertex[0] = vertices[1];
583 polygon.vertex[1] = vertices[2];
584 polygon.vertex[2] = vertices[3];
586 _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans);
588 return _apply(surface, aaSpans);
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.
602 Should provide two Polygons, one for each triangle.
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))
607 //Exceptions: No dedicated drawing area?
608 if (!region && image->rle->size == 0) return false;
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);
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;
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;
635 // Get AA spans and step polygons again to draw
636 auto aaSpans = _AASpans(ys, ye, image, region);
638 for (uint32_t i = 0; i < triangleCount; i++) {
639 _rasterPolygonImage(surface, image, region, opacity, transformedTris[i], blendMethod, aaSpans);
641 // Apply to surface (note: frees the AA spans)
642 _apply(surface, aaSpans);
644 free(transformedTris);