1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_RENDER_SW
24
25#include <limits.h>
26
27#include "SDL_triangle.h"
28
29#include "../../video/SDL_surface_c.h"
30
31/* fixed points bits precision
32 * Set to 1, so that it can start rendering with middle of a pixel precision.
33 * It doesn't need to be increased.
34 * But, if increased too much, it overflows (srcx, srcy) coordinates used for filling with texture.
35 * (which could be turned to int64).
36 */
37#define FP_BITS 1
38
39#define COLOR_EQ(c1, c2) ((c1).r == (c2).r && (c1).g == (c2).g && (c1).b == (c2).b && (c1).a == (c2).a)
40
41static void SDL_BlitTriangle_Slow(SDL_BlitInfo *info,
42 SDL_Point s2_x_area, SDL_Rect dstrect, int area, int bias_w0, int bias_w1, int bias_w2,
43 int d2d1_y, int d1d2_x, int d0d2_y, int d2d0_x, int d1d0_y, int d0d1_x,
44 int s2s0_x, int s2s1_x, int s2s0_y, int s2s1_y, int w0_row, int w1_row, int w2_row,
45 SDL_Color c0, SDL_Color c1, SDL_Color c2, bool is_uniform, SDL_TextureAddressMode texture_address_mode);
46
47#if 0
48bool SDL_BlitTriangle(SDL_Surface *src, const SDL_Point srcpoints[3], SDL_Surface *dst, const SDL_Point dstpoints[3])
49{
50 int i;
51 SDL_Point points[6];
52
53 if (src == NULL || dst == NULL) {
54 return false;
55 }
56
57 for (i = 0; i < 3; i++) {
58 if (srcpoints[i].x < 0 || srcpoints[i].y < 0 || srcpoints[i].x >= src->w || srcpoints[i].y >= src->h) {
59 return SDL_SetError("Values of 'srcpoints' out of bounds");
60 }
61 }
62
63 points[0] = srcpoints[0];
64 points[1] = dstpoints[0];
65 points[2] = srcpoints[1];
66 points[3] = dstpoints[1];
67 points[4] = srcpoints[2];
68 points[5] = dstpoints[2];
69 for (i = 0; i < 3; i++) {
70 trianglepoint_2_fixedpoint(&points[2 * i + 1]);
71 }
72 return SDL_SW_BlitTriangle(src, dst, points);
73}
74
75bool SDL_FillTriangle(SDL_Surface *dst, const SDL_Point points[3], Uint32 color)
76{
77 int i;
78 SDL_Point points_tmp[3];
79 if (dst == NULL) {
80 return false;
81 }
82 for (i = 0; i < 3; i++) {
83 points_tmp[i] = points[i];
84 trianglepoint_2_fixedpoint(&points_tmp[i]);
85 }
86 return SDL_SW_FillTriangle(dst, points_tmp, SDL_BLENDMODE_NONE, color);
87}
88#endif
89
90// cross product AB x AC
91static Sint64 cross_product(const SDL_Point *a, const SDL_Point *b, int c_x, int c_y)
92{
93 return ((Sint64)(b->x - a->x)) * ((Sint64)(c_y - a->y)) - ((Sint64)(b->y - a->y)) * ((Sint64)(c_x - a->x));
94}
95
96// check for top left rules
97static bool is_top_left(const SDL_Point *a, const SDL_Point *b, int is_clockwise)
98{
99 if (is_clockwise) {
100 if (a->y == b->y && a->x < b->x) {
101 return true;
102 }
103 if (b->y < a->y) {
104 return true;
105 }
106 } else {
107 if (a->y == b->y && b->x < a->x) {
108 return true;
109 }
110 if (a->y < b->y) {
111 return true;
112 }
113 }
114 return false;
115}
116
117// x = (y << FP_BITS)
118// prevent runtime error: left shift of negative value
119#define PRECOMP(x, y) \
120 val = y; \
121 if (val >= 0) { \
122 x = val << FP_BITS; \
123 } else { \
124 val *= -1; \
125 x = val << FP_BITS; \
126 x *= -1; \
127 }
128
129void trianglepoint_2_fixedpoint(SDL_Point *a)
130{
131 int val;
132 PRECOMP(a->x, a->x);
133 PRECOMP(a->y, a->y);
134}
135
136// bounding rect of three points (in fixed point)
137static void bounding_rect_fixedpoint(const SDL_Point *a, const SDL_Point *b, const SDL_Point *c, SDL_Rect *r)
138{
139 int min_x = SDL_min(a->x, SDL_min(b->x, c->x));
140 int max_x = SDL_max(a->x, SDL_max(b->x, c->x));
141 int min_y = SDL_min(a->y, SDL_min(b->y, c->y));
142 int max_y = SDL_max(a->y, SDL_max(b->y, c->y));
143 // points are in fixed point, shift back
144 r->x = min_x >> FP_BITS;
145 r->y = min_y >> FP_BITS;
146 r->w = (max_x - min_x) >> FP_BITS;
147 r->h = (max_y - min_y) >> FP_BITS;
148}
149
150// bounding rect of three points
151static void bounding_rect(const SDL_Point *a, const SDL_Point *b, const SDL_Point *c, SDL_Rect *r)
152{
153 int min_x = SDL_min(a->x, SDL_min(b->x, c->x));
154 int max_x = SDL_max(a->x, SDL_max(b->x, c->x));
155 int min_y = SDL_min(a->y, SDL_min(b->y, c->y));
156 int max_y = SDL_max(a->y, SDL_max(b->y, c->y));
157 r->x = min_x;
158 r->y = min_y;
159 r->w = (max_x - min_x);
160 r->h = (max_y - min_y);
161}
162
163/* Triangle rendering, using Barycentric coordinates (w0, w1, w2)
164 *
165 * The cross product isn't computed from scratch at each iteration,
166 * but optimized using constant step increments
167 *
168 */
169
170#define TRIANGLE_BEGIN_LOOP \
171 { \
172 int x, y; \
173 for (y = 0; y < dstrect.h; y++) { \
174 /* y start */ \
175 Sint64 w0 = w0_row; \
176 Sint64 w1 = w1_row; \
177 Sint64 w2 = w2_row; \
178 for (x = 0; x < dstrect.w; x++) { \
179 /* In triangle */ \
180 if (w0 + bias_w0 >= 0 && w1 + bias_w1 >= 0 && w2 + bias_w2 >= 0) { \
181 Uint8 *dptr = (Uint8 *)dst_ptr + x * dstbpp;
182
183// Use 64 bits precision to prevent overflow when interpolating color / texture with wide triangles
184#define TRIANGLE_GET_TEXTCOORD \
185 int srcx = (int)(((Sint64)w0 * s2s0_x + (Sint64)w1 * s2s1_x + s2_x_area.x) / area); \
186 int srcy = (int)(((Sint64)w0 * s2s0_y + (Sint64)w1 * s2s1_y + s2_x_area.y) / area); \
187 if (texture_address_mode == SDL_TEXTURE_ADDRESS_WRAP) { \
188 srcx %= src_surface->w; \
189 if (srcx < 0) { \
190 srcx += (src_surface->w - 1); \
191 } \
192 srcy %= src_surface->h; \
193 if (srcy < 0) { \
194 srcy += (src_surface->h - 1); \
195 } \
196 }
197
198#define TRIANGLE_GET_MAPPED_COLOR \
199 Uint8 r = (Uint8)(((Sint64)w0 * c0.r + (Sint64)w1 * c1.r + (Sint64)w2 * c2.r) / area); \
200 Uint8 g = (Uint8)(((Sint64)w0 * c0.g + (Sint64)w1 * c1.g + (Sint64)w2 * c2.g) / area); \
201 Uint8 b = (Uint8)(((Sint64)w0 * c0.b + (Sint64)w1 * c1.b + (Sint64)w2 * c2.b) / area); \
202 Uint8 a = (Uint8)(((Sint64)w0 * c0.a + (Sint64)w1 * c1.a + (Sint64)w2 * c2.a) / area); \
203 Uint32 color = SDL_MapRGBA(format, palette, r, g, b, a);
204
205#define TRIANGLE_GET_COLOR \
206 int r = (int)(((Sint64)w0 * c0.r + (Sint64)w1 * c1.r + (Sint64)w2 * c2.r) / area); \
207 int g = (int)(((Sint64)w0 * c0.g + (Sint64)w1 * c1.g + (Sint64)w2 * c2.g) / area); \
208 int b = (int)(((Sint64)w0 * c0.b + (Sint64)w1 * c1.b + (Sint64)w2 * c2.b) / area); \
209 int a = (int)(((Sint64)w0 * c0.a + (Sint64)w1 * c1.a + (Sint64)w2 * c2.a) / area);
210
211#define TRIANGLE_END_LOOP \
212 } \
213 /* x += 1 */ \
214 w0 += d2d1_y; \
215 w1 += d0d2_y; \
216 w2 += d1d0_y; \
217 } \
218 /* y += 1 */ \
219 w0_row += d1d2_x; \
220 w1_row += d2d0_x; \
221 w2_row += d0d1_x; \
222 dst_ptr += dst_pitch; \
223 } \
224 }
225
226bool SDL_SW_FillTriangle(SDL_Surface *dst, SDL_Point *d0, SDL_Point *d1, SDL_Point *d2, SDL_BlendMode blend, SDL_Color c0, SDL_Color c1, SDL_Color c2)
227{
228 bool result = true;
229 int dst_locked = 0;
230
231 SDL_Rect dstrect;
232
233 int dstbpp;
234 Uint8 *dst_ptr;
235 int dst_pitch;
236
237 Sint64 area;
238 int is_clockwise;
239
240 int d2d1_y, d1d2_x, d0d2_y, d2d0_x, d1d0_y, d0d1_x;
241 Sint64 w0_row, w1_row, w2_row;
242 int bias_w0, bias_w1, bias_w2;
243
244 bool is_uniform;
245
246 SDL_Surface *tmp = NULL;
247
248 if (!SDL_SurfaceValid(dst)) {
249 return false;
250 }
251
252 area = cross_product(d0, d1, d2->x, d2->y);
253
254 is_uniform = COLOR_EQ(c0, c1) && COLOR_EQ(c1, c2);
255
256 // Flat triangle
257 if (area == 0) {
258 return true;
259 }
260
261 // Lock the destination, if needed
262 if (SDL_MUSTLOCK(dst)) {
263 if (!SDL_LockSurface(dst)) {
264 result = false;
265 goto end;
266 } else {
267 dst_locked = 1;
268 }
269 }
270
271 bounding_rect_fixedpoint(d0, d1, d2, &dstrect);
272
273 {
274 // Clip triangle rect with surface rect
275 SDL_Rect rect;
276 rect.x = 0;
277 rect.y = 0;
278 rect.w = dst->w;
279 rect.h = dst->h;
280 SDL_GetRectIntersection(&dstrect, &rect, &dstrect);
281 }
282
283 {
284 // Clip triangle with surface clip rect
285 SDL_Rect rect;
286 SDL_GetSurfaceClipRect(dst, &rect);
287 SDL_GetRectIntersection(&dstrect, &rect, &dstrect);
288 }
289
290 if (blend != SDL_BLENDMODE_NONE) {
291 SDL_PixelFormat format = dst->format;
292
293 // need an alpha format
294 if (!SDL_ISPIXELFORMAT_ALPHA(format)) {
295 format = SDL_PIXELFORMAT_ARGB8888;
296 }
297
298 // Use an intermediate surface
299 tmp = SDL_CreateSurface(dstrect.w, dstrect.h, format);
300 if (!tmp) {
301 result = false;
302 goto end;
303 }
304
305 if (blend == SDL_BLENDMODE_MOD) {
306 Uint32 c = SDL_MapSurfaceRGBA(tmp, 255, 255, 255, 255);
307 SDL_FillSurfaceRect(tmp, NULL, c);
308 }
309
310 SDL_SetSurfaceBlendMode(tmp, blend);
311
312 dstbpp = tmp->fmt->bytes_per_pixel;
313 dst_ptr = (Uint8 *)tmp->pixels;
314 dst_pitch = tmp->pitch;
315
316 } else {
317 // Write directly to destination surface
318 dstbpp = dst->fmt->bytes_per_pixel;
319 dst_ptr = (Uint8 *)dst->pixels + dstrect.x * dstbpp + dstrect.y * dst->pitch;
320 dst_pitch = dst->pitch;
321 }
322
323 is_clockwise = area > 0;
324 if (area < 0) {
325 area = -area;
326 }
327
328 {
329 int val;
330 PRECOMP(d2d1_y, d1->y - d2->y)
331 PRECOMP(d0d2_y, d2->y - d0->y)
332 PRECOMP(d1d0_y, d0->y - d1->y)
333 PRECOMP(d1d2_x, d2->x - d1->x)
334 PRECOMP(d2d0_x, d0->x - d2->x)
335 PRECOMP(d0d1_x, d1->x - d0->x)
336 }
337
338 // Starting point for rendering, at the middle of a pixel
339 {
340 SDL_Point p;
341 p.x = dstrect.x;
342 p.y = dstrect.y;
343 trianglepoint_2_fixedpoint(&p);
344 p.x += (1 << FP_BITS) / 2;
345 p.y += (1 << FP_BITS) / 2;
346 w0_row = cross_product(d1, d2, p.x, p.y);
347 w1_row = cross_product(d2, d0, p.x, p.y);
348 w2_row = cross_product(d0, d1, p.x, p.y);
349 }
350
351 // Handle anti-clockwise triangles
352 if (!is_clockwise) {
353 d2d1_y *= -1;
354 d0d2_y *= -1;
355 d1d0_y *= -1;
356 d1d2_x *= -1;
357 d2d0_x *= -1;
358 d0d1_x *= -1;
359 w0_row *= -1;
360 w1_row *= -1;
361 w2_row *= -1;
362 }
363
364 // Add a bias to respect top-left rasterization rule
365 bias_w0 = (is_top_left(d1, d2, is_clockwise) ? 0 : -1);
366 bias_w1 = (is_top_left(d2, d0, is_clockwise) ? 0 : -1);
367 bias_w2 = (is_top_left(d0, d1, is_clockwise) ? 0 : -1);
368
369 if (is_uniform) {
370 Uint32 color;
371 if (tmp) {
372 color = SDL_MapSurfaceRGBA(tmp, c0.r, c0.g, c0.b, c0.a);
373 } else {
374 color = SDL_MapSurfaceRGBA(dst, c0.r, c0.g, c0.b, c0.a);
375 }
376
377 if (dstbpp == 4) {
378 TRIANGLE_BEGIN_LOOP
379 {
380 *(Uint32 *)dptr = color;
381 }
382 TRIANGLE_END_LOOP
383 } else if (dstbpp == 3) {
384 TRIANGLE_BEGIN_LOOP
385 {
386 Uint8 *s = (Uint8 *)&color;
387 dptr[0] = s[0];
388 dptr[1] = s[1];
389 dptr[2] = s[2];
390 }
391 TRIANGLE_END_LOOP
392 } else if (dstbpp == 2) {
393 TRIANGLE_BEGIN_LOOP
394 {
395 *(Uint16 *)dptr = (Uint16)color;
396 }
397 TRIANGLE_END_LOOP
398 } else if (dstbpp == 1) {
399 TRIANGLE_BEGIN_LOOP
400 {
401 *dptr = (Uint8)color;
402 }
403 TRIANGLE_END_LOOP
404 }
405 } else {
406 const SDL_PixelFormatDetails *format;
407 SDL_Palette *palette;
408 if (tmp) {
409 format = tmp->fmt;
410 palette = tmp->palette;
411 } else {
412 format = dst->fmt;
413 palette = dst->palette;
414 }
415 if (dstbpp == 4) {
416 TRIANGLE_BEGIN_LOOP
417 {
418 TRIANGLE_GET_MAPPED_COLOR
419 *(Uint32 *)dptr = color;
420 }
421 TRIANGLE_END_LOOP
422 } else if (dstbpp == 3) {
423 TRIANGLE_BEGIN_LOOP
424 {
425 TRIANGLE_GET_MAPPED_COLOR
426 Uint8 *s = (Uint8 *)&color;
427 dptr[0] = s[0];
428 dptr[1] = s[1];
429 dptr[2] = s[2];
430 }
431 TRIANGLE_END_LOOP
432 } else if (dstbpp == 2) {
433 TRIANGLE_BEGIN_LOOP
434 {
435 TRIANGLE_GET_MAPPED_COLOR
436 *(Uint16 *)dptr = (Uint16)color;
437 }
438 TRIANGLE_END_LOOP
439 } else if (dstbpp == 1) {
440 TRIANGLE_BEGIN_LOOP
441 {
442 TRIANGLE_GET_MAPPED_COLOR
443 *dptr = (Uint8)color;
444 }
445 TRIANGLE_END_LOOP
446 }
447 }
448
449 if (tmp) {
450 SDL_BlitSurface(tmp, NULL, dst, &dstrect);
451 SDL_DestroySurface(tmp);
452 }
453
454end:
455 if (dst_locked) {
456 SDL_UnlockSurface(dst);
457 }
458
459 return result;
460}
461
462bool SDL_SW_BlitTriangle(
463 SDL_Surface *src,
464 SDL_Point *s0, SDL_Point *s1, SDL_Point *s2,
465 SDL_Surface *dst,
466 SDL_Point *d0, SDL_Point *d1, SDL_Point *d2,
467 SDL_Color c0, SDL_Color c1, SDL_Color c2,
468 SDL_TextureAddressMode texture_address_mode)
469{
470 bool result = true;
471 SDL_Surface *src_surface = src;
472 int src_locked = 0;
473 int dst_locked = 0;
474
475 SDL_BlendMode blend;
476
477 SDL_Rect dstrect;
478
479 SDL_Point s2_x_area;
480
481 int dstbpp;
482 Uint8 *dst_ptr;
483 int dst_pitch;
484
485 const int *src_ptr;
486 int src_pitch;
487
488 Sint64 area, tmp64;
489 int is_clockwise;
490
491 int d2d1_y, d1d2_x, d0d2_y, d2d0_x, d1d0_y, d0d1_x;
492 int s2s0_x, s2s1_x, s2s0_y, s2s1_y;
493
494 Sint64 w0_row, w1_row, w2_row;
495 int bias_w0, bias_w1, bias_w2;
496
497 bool is_uniform;
498
499 bool has_modulation;
500
501 if (!SDL_SurfaceValid(src)) {
502 return SDL_InvalidParamError("src");
503 }
504 if (!SDL_SurfaceValid(dst)) {
505 return SDL_InvalidParamError("dst");
506 }
507
508 area = cross_product(d0, d1, d2->x, d2->y);
509
510 // Flat triangle
511 if (area == 0) {
512 return true;
513 }
514
515 // Lock the destination, if needed
516 if (SDL_MUSTLOCK(dst)) {
517 if (!SDL_LockSurface(dst)) {
518 result = false;
519 goto end;
520 } else {
521 dst_locked = 1;
522 }
523 }
524
525 // Lock the source, if needed
526 if (SDL_MUSTLOCK(src)) {
527 if (!SDL_LockSurface(src)) {
528 result = false;
529 goto end;
530 } else {
531 src_locked = 1;
532 }
533 }
534
535 is_uniform = COLOR_EQ(c0, c1) && COLOR_EQ(c1, c2);
536
537 bounding_rect_fixedpoint(d0, d1, d2, &dstrect);
538
539 SDL_GetSurfaceBlendMode(src, &blend);
540
541 // TRIANGLE_GET_TEXTCOORD interpolates up to the max values included, so reduce by 1
542 if (texture_address_mode == SDL_TEXTURE_ADDRESS_CLAMP) {
543 SDL_Rect srcrect;
544 int maxx, maxy;
545 bounding_rect(s0, s1, s2, &srcrect);
546 maxx = srcrect.x + srcrect.w;
547 maxy = srcrect.y + srcrect.h;
548 if (srcrect.w > 0) {
549 if (s0->x == maxx) {
550 s0->x--;
551 }
552 if (s1->x == maxx) {
553 s1->x--;
554 }
555 if (s2->x == maxx) {
556 s2->x--;
557 }
558 }
559 if (srcrect.h > 0) {
560 if (s0->y == maxy) {
561 s0->y--;
562 }
563 if (s1->y == maxy) {
564 s1->y--;
565 }
566 if (s2->y == maxy) {
567 s2->y--;
568 }
569 }
570 }
571
572 if (is_uniform) {
573 // SDL_GetSurfaceColorMod(src, &r, &g, &b);
574 has_modulation = c0.r != 255 || c0.g != 255 || c0.b != 255 || c0.a != 255;
575 } else {
576 has_modulation = true;
577 }
578
579 {
580 // Clip triangle with surface clip rect
581 SDL_Rect rect;
582 SDL_GetSurfaceClipRect(dst, &rect);
583 SDL_GetRectIntersection(&dstrect, &rect, &dstrect);
584 }
585
586 // Set destination pointer
587 dstbpp = dst->fmt->bytes_per_pixel;
588 dst_ptr = (Uint8 *)dst->pixels + dstrect.x * dstbpp + dstrect.y * dst->pitch;
589 dst_pitch = dst->pitch;
590
591 // Set source pointer
592 src_ptr = (const int *)src->pixels;
593 src_pitch = src->pitch;
594
595 is_clockwise = area > 0;
596 if (area < 0) {
597 area = -area;
598 }
599
600 {
601 int val;
602 PRECOMP(d2d1_y, d1->y - d2->y)
603 PRECOMP(d0d2_y, d2->y - d0->y)
604 PRECOMP(d1d0_y, d0->y - d1->y)
605 PRECOMP(d1d2_x, d2->x - d1->x)
606 PRECOMP(d2d0_x, d0->x - d2->x)
607 PRECOMP(d0d1_x, d1->x - d0->x)
608 }
609
610 s2s0_x = s0->x - s2->x;
611 s2s1_x = s1->x - s2->x;
612 s2s0_y = s0->y - s2->y;
613 s2s1_y = s1->y - s2->y;
614
615 // Starting point for rendering, at the middle of a pixel
616 {
617 SDL_Point p;
618 p.x = dstrect.x;
619 p.y = dstrect.y;
620 trianglepoint_2_fixedpoint(&p);
621 p.x += (1 << FP_BITS) / 2;
622 p.y += (1 << FP_BITS) / 2;
623 w0_row = cross_product(d1, d2, p.x, p.y);
624 w1_row = cross_product(d2, d0, p.x, p.y);
625 w2_row = cross_product(d0, d1, p.x, p.y);
626 }
627
628 // Handle anti-clockwise triangles
629 if (!is_clockwise) {
630 d2d1_y *= -1;
631 d0d2_y *= -1;
632 d1d0_y *= -1;
633 d1d2_x *= -1;
634 d2d0_x *= -1;
635 d0d1_x *= -1;
636 w0_row *= -1;
637 w1_row *= -1;
638 w2_row *= -1;
639 }
640
641 // Add a bias to respect top-left rasterization rule
642 bias_w0 = (is_top_left(d1, d2, is_clockwise) ? 0 : -1);
643 bias_w1 = (is_top_left(d2, d0, is_clockwise) ? 0 : -1);
644 bias_w2 = (is_top_left(d0, d1, is_clockwise) ? 0 : -1);
645
646 /* precompute constant 's2->x * area' used in TRIANGLE_GET_TEXTCOORD */
647 tmp64 = s2->x * area;
648 if (tmp64 >= INT_MIN && tmp64 <= INT_MAX) {
649 s2_x_area.x = (int)tmp64;
650 } else {
651 result = SDL_SetError("triangle area overflow");
652 goto end;
653 }
654 tmp64 = s2->y * area;
655 if (tmp64 >= INT_MIN && tmp64 <= INT_MAX) {
656 s2_x_area.y = (int)tmp64;
657 } else {
658 result = SDL_SetError("triangle area overflow");
659 goto end;
660 }
661
662 if (blend != SDL_BLENDMODE_NONE || src->format != dst->format || has_modulation || !is_uniform) {
663 // Use SDL_BlitTriangle_Slow
664
665 SDL_BlitInfo *info = &src->map.info;
666 SDL_BlitInfo tmp_info;
667
668 SDL_zero(tmp_info);
669
670 tmp_info.src_fmt = src->fmt;
671 tmp_info.dst_fmt = dst->fmt;
672 tmp_info.flags = info->flags;
673 /*
674 tmp_info.r = info->r;
675 tmp_info.g = info->g;
676 tmp_info.b = info->b;
677 tmp_info.a = info->a;
678 */
679 tmp_info.r = c0.r;
680 tmp_info.g = c0.g;
681 tmp_info.b = c0.b;
682 tmp_info.a = c0.a;
683
684 tmp_info.flags &= ~(SDL_COPY_MODULATE_COLOR | SDL_COPY_MODULATE_ALPHA);
685
686 if (c0.r != 255 || c1.r != 255 || c2.r != 255 ||
687 c0.g != 255 || c1.g != 255 || c2.g != 255 ||
688 c0.b != 255 || c1.b != 255 || c2.b != 255) {
689 tmp_info.flags |= SDL_COPY_MODULATE_COLOR;
690 }
691
692 if (c0.a != 255 || c1.a != 255 || c2.a != 255) {
693 tmp_info.flags |= SDL_COPY_MODULATE_ALPHA;
694 }
695
696 tmp_info.colorkey = info->colorkey;
697
698 // src
699 tmp_info.src_surface = src_surface;
700 tmp_info.src = (Uint8 *)src_ptr;
701 tmp_info.src_pitch = src_pitch;
702
703 // dst
704 tmp_info.dst = dst_ptr;
705 tmp_info.dst_pitch = dst_pitch;
706
707#define CHECK_INT_RANGE(X) \
708 if ((X) < INT_MIN || (X) > INT_MAX) { \
709 result = SDL_SetError("integer overflow (%s = %" SDL_PRIs64 ")", #X, X); \
710 goto end; \
711 }
712 CHECK_INT_RANGE(area);
713 CHECK_INT_RANGE(w0_row);
714 CHECK_INT_RANGE(w1_row);
715 CHECK_INT_RANGE(w2_row);
716 SDL_BlitTriangle_Slow(&tmp_info, s2_x_area, dstrect, (int)area, bias_w0, bias_w1, bias_w2,
717 d2d1_y, d1d2_x, d0d2_y, d2d0_x, d1d0_y, d0d1_x,
718 s2s0_x, s2s1_x, s2s0_y, s2s1_y, (int)w0_row, (int)w1_row, (int)w2_row,
719 c0, c1, c2, is_uniform, texture_address_mode);
720
721 goto end;
722 }
723
724 if (dstbpp == 4) {
725 TRIANGLE_BEGIN_LOOP
726 {
727 TRIANGLE_GET_TEXTCOORD
728 Uint32 *sptr = (Uint32 *)((Uint8 *)src_ptr + srcy * src_pitch);
729 *(Uint32 *)dptr = sptr[srcx];
730 }
731 TRIANGLE_END_LOOP
732 } else if (dstbpp == 3) {
733 TRIANGLE_BEGIN_LOOP
734 {
735 TRIANGLE_GET_TEXTCOORD
736 Uint8 *sptr = (Uint8 *)src_ptr + srcy * src_pitch;
737 dptr[0] = sptr[3 * srcx];
738 dptr[1] = sptr[3 * srcx + 1];
739 dptr[2] = sptr[3 * srcx + 2];
740 }
741 TRIANGLE_END_LOOP
742 } else if (dstbpp == 2) {
743 TRIANGLE_BEGIN_LOOP
744 {
745 TRIANGLE_GET_TEXTCOORD
746 Uint16 *sptr = (Uint16 *)((Uint8 *)src_ptr + srcy * src_pitch);
747 *(Uint16 *)dptr = sptr[srcx];
748 }
749 TRIANGLE_END_LOOP
750 } else if (dstbpp == 1) {
751 TRIANGLE_BEGIN_LOOP
752 {
753 TRIANGLE_GET_TEXTCOORD
754 Uint8 *sptr = (Uint8 *)src_ptr + srcy * src_pitch;
755 *dptr = sptr[srcx];
756 }
757 TRIANGLE_END_LOOP
758 }
759
760end:
761 if (dst_locked) {
762 SDL_UnlockSurface(dst);
763 }
764 if (src_locked) {
765 SDL_UnlockSurface(src);
766 }
767
768 return result;
769}
770
771#define FORMAT_ALPHA 0
772#define FORMAT_NO_ALPHA -1
773#define FORMAT_2101010 1
774#define FORMAT_HAS_ALPHA(format) format == 0
775#define FORMAT_HAS_NO_ALPHA(format) format < 0
776static int detect_format(const SDL_PixelFormatDetails *pf)
777{
778 if (pf->format == SDL_PIXELFORMAT_ARGB2101010) {
779 return FORMAT_2101010;
780 } else if (pf->Amask) {
781 return FORMAT_ALPHA;
782 } else {
783 return FORMAT_NO_ALPHA;
784 }
785}
786
787static void SDL_BlitTriangle_Slow(SDL_BlitInfo *info,
788 SDL_Point s2_x_area, SDL_Rect dstrect, int area, int bias_w0, int bias_w1, int bias_w2,
789 int d2d1_y, int d1d2_x, int d0d2_y, int d2d0_x, int d1d0_y, int d0d1_x,
790 int s2s0_x, int s2s1_x, int s2s0_y, int s2s1_y, int w0_row, int w1_row, int w2_row,
791 SDL_Color c0, SDL_Color c1, SDL_Color c2, bool is_uniform, SDL_TextureAddressMode texture_address_mode)
792{
793 SDL_Surface *src_surface = info->src_surface;
794 const int flags = info->flags;
795 Uint32 modulateR = info->r;
796 Uint32 modulateG = info->g;
797 Uint32 modulateB = info->b;
798 Uint32 modulateA = info->a;
799 Uint32 srcpixel;
800 Uint32 srcR, srcG, srcB, srcA;
801 Uint32 dstpixel;
802 Uint32 dstR, dstG, dstB, dstA;
803 const SDL_PixelFormatDetails *src_fmt = info->src_fmt;
804 const SDL_PixelFormatDetails *dst_fmt = info->dst_fmt;
805 int srcbpp = src_fmt->bytes_per_pixel;
806 int dstbpp = dst_fmt->bytes_per_pixel;
807 int srcfmt_val;
808 int dstfmt_val;
809 Uint32 rgbmask = ~src_fmt->Amask;
810 Uint32 ckey = info->colorkey & rgbmask;
811
812 Uint8 *dst_ptr = info->dst;
813 int dst_pitch = info->dst_pitch;
814
815 srcfmt_val = detect_format(src_fmt);
816 dstfmt_val = detect_format(dst_fmt);
817
818 TRIANGLE_BEGIN_LOOP
819 {
820 Uint8 *src;
821 Uint8 *dst = dptr;
822 TRIANGLE_GET_TEXTCOORD
823 src = (info->src + (srcy * info->src_pitch) + (srcx * srcbpp));
824 if (FORMAT_HAS_ALPHA(srcfmt_val)) {
825 DISEMBLE_RGBA(src, srcbpp, src_fmt, srcpixel, srcR, srcG, srcB, srcA);
826 } else if (FORMAT_HAS_NO_ALPHA(srcfmt_val)) {
827 DISEMBLE_RGB(src, srcbpp, src_fmt, srcpixel, srcR, srcG, srcB);
828 srcA = 0xFF;
829 } else {
830 // SDL_PIXELFORMAT_ARGB2101010
831 srcpixel = *((Uint32 *)(src));
832 RGBA_FROM_ARGB2101010(srcpixel, srcR, srcG, srcB, srcA);
833 }
834 if (flags & SDL_COPY_COLORKEY) {
835 // srcpixel isn't set for 24 bpp
836 if (srcbpp == 3) {
837 srcpixel = (srcR << src_fmt->Rshift) |
838 (srcG << src_fmt->Gshift) | (srcB << src_fmt->Bshift);
839 }
840 if ((srcpixel & rgbmask) == ckey) {
841 continue;
842 }
843 }
844 if ((flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL))) {
845 if (FORMAT_HAS_ALPHA(dstfmt_val)) {
846 DISEMBLE_RGBA(dst, dstbpp, dst_fmt, dstpixel, dstR, dstG, dstB, dstA);
847 } else if (FORMAT_HAS_NO_ALPHA(dstfmt_val)) {
848 DISEMBLE_RGB(dst, dstbpp, dst_fmt, dstpixel, dstR, dstG, dstB);
849 dstA = 0xFF;
850 } else {
851 // SDL_PIXELFORMAT_ARGB2101010
852 dstpixel = *((Uint32 *) (dst));
853 RGBA_FROM_ARGB2101010(dstpixel, dstR, dstG, dstB, dstA);
854 }
855 } else {
856 // don't care
857 dstR = dstG = dstB = dstA = 0;
858 }
859
860 if (!is_uniform) {
861 TRIANGLE_GET_COLOR
862 modulateR = r;
863 modulateG = g;
864 modulateB = b;
865 modulateA = a;
866 }
867
868 if (flags & SDL_COPY_MODULATE_COLOR) {
869 srcR = (srcR * modulateR) / 255;
870 srcG = (srcG * modulateG) / 255;
871 srcB = (srcB * modulateB) / 255;
872 }
873 if (flags & SDL_COPY_MODULATE_ALPHA) {
874 srcA = (srcA * modulateA) / 255;
875 }
876 if (flags & (SDL_COPY_BLEND | SDL_COPY_ADD)) {
877 // This goes away if we ever use premultiplied alpha
878 if (srcA < 255) {
879 srcR = (srcR * srcA) / 255;
880 srcG = (srcG * srcA) / 255;
881 srcB = (srcB * srcA) / 255;
882 }
883 }
884 switch (flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL)) {
885 case 0:
886 dstR = srcR;
887 dstG = srcG;
888 dstB = srcB;
889 dstA = srcA;
890 break;
891 case SDL_COPY_BLEND:
892 dstR = srcR + ((255 - srcA) * dstR) / 255;
893 dstG = srcG + ((255 - srcA) * dstG) / 255;
894 dstB = srcB + ((255 - srcA) * dstB) / 255;
895 dstA = srcA + ((255 - srcA) * dstA) / 255;
896 break;
897 case SDL_COPY_ADD:
898 dstR = srcR + dstR;
899 if (dstR > 255) {
900 dstR = 255;
901 }
902 dstG = srcG + dstG;
903 if (dstG > 255) {
904 dstG = 255;
905 }
906 dstB = srcB + dstB;
907 if (dstB > 255) {
908 dstB = 255;
909 }
910 break;
911 case SDL_COPY_MOD:
912 dstR = (srcR * dstR) / 255;
913 dstG = (srcG * dstG) / 255;
914 dstB = (srcB * dstB) / 255;
915 break;
916 case SDL_COPY_MUL:
917 dstR = ((srcR * dstR) + (dstR * (255 - srcA))) / 255;
918 if (dstR > 255) {
919 dstR = 255;
920 }
921 dstG = ((srcG * dstG) + (dstG * (255 - srcA))) / 255;
922 if (dstG > 255) {
923 dstG = 255;
924 }
925 dstB = ((srcB * dstB) + (dstB * (255 - srcA))) / 255;
926 if (dstB > 255) {
927 dstB = 255;
928 }
929 break;
930 }
931 if (FORMAT_HAS_ALPHA(dstfmt_val)) {
932 ASSEMBLE_RGBA(dst, dstbpp, dst_fmt, dstR, dstG, dstB, dstA);
933 } else if (FORMAT_HAS_NO_ALPHA(dstfmt_val)) {
934 ASSEMBLE_RGB(dst, dstbpp, dst_fmt, dstR, dstG, dstB);
935 } else {
936 // SDL_PIXELFORMAT_ARGB2101010
937 Uint32 pixel;
938 ARGB2101010_FROM_RGBA(pixel, dstR, dstG, dstB, dstA);
939 *(Uint32 *)dst = pixel;
940 }
941 }
942 TRIANGLE_END_LOOP
943}
944
945#endif // SDL_VIDEO_RENDER_SW
946