Simple Directmedia Layer
at main 30 kB view raw
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2024 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