Atlas - SDL_surface.c
Home / ext / SDL / src / video Lines: 1 | Size: 96878 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 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#include "SDL_sysvideo.h" 24#include "SDL_video_c.h" 25#include "SDL_RLEaccel_c.h" 26#include "SDL_pixels_c.h" 27#include "SDL_rotate.h" 28#include "SDL_stb_c.h" 29#include "SDL_yuv_c.h" 30#include "../render/SDL_sysrender.h" 31 32#include "SDL_surface_c.h" 33 34 35// Check to make sure we can safely check multiplication of surface w and pitch and it won't overflow size_t 36SDL_COMPILE_TIME_ASSERT(surface_size_assumptions, 37 sizeof(int) == sizeof(Sint32) && sizeof(size_t) >= sizeof(Sint32)); 38 39SDL_COMPILE_TIME_ASSERT(can_indicate_overflow, SDL_SIZE_MAX > SDL_MAX_SINT32); 40 41// Magic! 42static char SDL_surface_magic; 43 44// Public routines 45 46bool SDL_SurfaceValid(SDL_Surface *surface) 47{ 48 return (surface && surface->reserved == &SDL_surface_magic); 49} 50 51void SDL_UpdateSurfaceLockFlag(SDL_Surface *surface) 52{ 53 // We need to mark the surface as needing unlock while locked 54 if ((surface->flags & SDL_SURFACE_LOCKED) || 55 (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL)) { 56 surface->flags |= SDL_SURFACE_LOCK_NEEDED; 57 } else { 58 surface->flags &= ~SDL_SURFACE_LOCK_NEEDED; 59 } 60} 61 62/* 63 * Calculate the pad-aligned scanline width of a surface. 64 * 65 * for FOURCC, use SDL_CalculateYUVSize() 66 */ 67static bool SDL_CalculateRGBSize(Uint32 format, size_t width, size_t height, size_t *size, size_t *pitch, bool minimal) 68{ 69 if (SDL_BITSPERPIXEL(format) >= 8) { 70 if (!SDL_size_mul_check_overflow(width, SDL_BYTESPERPIXEL(format), pitch)) { 71 return SDL_SetError("width * bpp would overflow"); 72 } 73 } else { 74 if (!SDL_size_mul_check_overflow(width, SDL_BITSPERPIXEL(format), pitch)) { 75 return SDL_SetError("width * bpp would overflow"); 76 } 77 if (!SDL_size_add_check_overflow(*pitch, 7, pitch)) { 78 return SDL_SetError("aligning pitch would overflow"); 79 } 80 *pitch /= 8; 81 } 82 if (!minimal) { 83 // 4-byte aligning for speed 84 if (!SDL_size_add_check_overflow(*pitch, 3, pitch)) { 85 return SDL_SetError("aligning pitch would overflow"); 86 } 87 *pitch &= ~3; 88 } 89 90 if (!SDL_size_mul_check_overflow(height, *pitch, size)) { 91 return SDL_SetError("height * pitch would overflow"); 92 } 93 94 return true; 95} 96 97bool SDL_CalculateSurfaceSize(SDL_PixelFormat format, int width, int height, size_t *size, size_t *pitch, bool minimalPitch) 98{ 99 size_t p = 0, sz = 0; 100 101 if (size) { 102 *size = 0; 103 } 104 105 if (pitch) { 106 *pitch = 0; 107 } 108 109 if (SDL_ISPIXELFORMAT_FOURCC(format)) { 110 if (format == SDL_PIXELFORMAT_MJPG) { 111 // We don't know in advance what it will be, we'll figure it out later. 112 return true; 113 } 114 115 if (!SDL_CalculateYUVSize(format, width, height, &sz, &p)) { 116 // Overflow... 117 return false; 118 } 119 } else { 120 if (!SDL_CalculateRGBSize(format, width, height, &sz, &p, minimalPitch)) { 121 // Overflow... 122 return false; 123 } 124 } 125 126 if (size) { 127 *size = sz; 128 } 129 130 if (pitch) { 131 *pitch = p; 132 } 133 134 return true; 135} 136 137static bool SDL_InitializeSurface(SDL_Surface *surface, int width, int height, SDL_PixelFormat format, SDL_Colorspace colorspace, SDL_PropertiesID props, void *pixels, int pitch, bool onstack) 138{ 139 SDL_zerop(surface); 140 141 surface->flags = SDL_SURFACE_PREALLOCATED; 142 surface->format = format; 143 surface->w = width; 144 surface->h = height; 145 surface->pixels = pixels; 146 surface->pitch = pitch; 147 surface->reserved = &SDL_surface_magic; 148 149 if (onstack) { 150 surface->internal_flags |= SDL_INTERNAL_SURFACE_STACK; 151 } 152 153 surface->fmt = SDL_GetPixelFormatDetails(format); 154 if (!surface->fmt) { 155 SDL_DestroySurface(surface); 156 return false; 157 } 158 159 // Initialize the clip rect 160 surface->clip_rect.w = width; 161 surface->clip_rect.h = height; 162 163 // Allocate an empty mapping 164 surface->map.info.r = 0xFF; 165 surface->map.info.g = 0xFF; 166 surface->map.info.b = 0xFF; 167 surface->map.info.a = 0xFF; 168 169 if (colorspace == SDL_COLORSPACE_UNKNOWN) { 170 surface->colorspace = SDL_GetDefaultColorspaceForFormat(format); 171 } else { 172 surface->colorspace = colorspace; 173 } 174 175 if (props) { 176 if (!SDL_CopyProperties(props, SDL_GetSurfaceProperties(surface))) { 177 SDL_DestroySurface(surface); 178 return false; 179 } 180 } 181 182 // By default surfaces with an alpha mask are set up for blending 183 if (SDL_ISPIXELFORMAT_ALPHA(surface->format)) { 184 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); 185 } 186 187 // The surface is ready to go 188 surface->refcount = 1; 189 return true; 190} 191 192/* 193 * Create an empty surface of the appropriate depth using the given format 194 */ 195SDL_Surface *SDL_CreateSurface(int width, int height, SDL_PixelFormat format) 196{ 197 size_t pitch, size; 198 SDL_Surface *surface; 199 200 CHECK_PARAM(width < 0) { 201 SDL_InvalidParamError("width"); 202 return NULL; 203 } 204 205 CHECK_PARAM(height < 0) { 206 SDL_InvalidParamError("height"); 207 return NULL; 208 } 209 210 CHECK_PARAM(format == SDL_PIXELFORMAT_UNKNOWN) { 211 SDL_InvalidParamError("format"); 212 return NULL; 213 } 214 215 if (!SDL_CalculateSurfaceSize(format, width, height, &size, &pitch, false /* not minimal pitch */)) { 216 // Overflow... 217 return NULL; 218 } 219 220 // Allocate and initialize the surface 221 surface = (SDL_Surface *)SDL_malloc(sizeof(*surface)); 222 if (!surface) { 223 return NULL; 224 } 225 226 if (!SDL_InitializeSurface(surface, width, height, format, SDL_COLORSPACE_UNKNOWN, 0, NULL, (int)pitch, false)) { 227 return NULL; 228 } 229 230 if (surface->w && surface->h && format != SDL_PIXELFORMAT_MJPG) { 231 surface->flags &= ~SDL_SURFACE_PREALLOCATED; 232 if (SDL_GetHintBoolean("SDL_SURFACE_MALLOC", false)) { 233 surface->pixels = SDL_malloc(size); 234 } else { 235 surface->flags |= SDL_SURFACE_SIMD_ALIGNED; 236 surface->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), size); 237 } 238 if (!surface->pixels) { 239 SDL_DestroySurface(surface); 240 return NULL; 241 } 242 243 // This is important for bitmaps 244 SDL_memset(surface->pixels, 0, size); 245 } 246 return surface; 247} 248 249/* 250 * Create an RGB surface from an existing memory buffer using the given 251 * enum SDL_PIXELFORMAT_* format 252 */ 253SDL_Surface *SDL_CreateSurfaceFrom(int width, int height, SDL_PixelFormat format, void *pixels, int pitch) 254{ 255 CHECK_PARAM(width < 0) { 256 SDL_InvalidParamError("width"); 257 return NULL; 258 } 259 260 CHECK_PARAM(height < 0) { 261 SDL_InvalidParamError("height"); 262 return NULL; 263 } 264 265 CHECK_PARAM(format == SDL_PIXELFORMAT_UNKNOWN) { 266 SDL_InvalidParamError("format"); 267 return NULL; 268 } 269 270 if (pitch == 0 && !pixels) { 271 // The application will fill these in later with valid values 272 } else { 273 size_t minimalPitch; 274 275 if (!SDL_CalculateSurfaceSize(format, width, height, NULL, &minimalPitch, true /* minimal pitch */)) { 276 // Overflow... 277 return NULL; 278 } 279 280 CHECK_PARAM(pitch < 0 || (size_t)pitch < minimalPitch) { 281 SDL_InvalidParamError("pitch"); 282 return NULL; 283 } 284 } 285 286 // Allocate and initialize the surface 287 SDL_Surface *surface = (SDL_Surface *)SDL_malloc(sizeof(*surface)); 288 if (!surface || 289 !SDL_InitializeSurface(surface, width, height, format, SDL_COLORSPACE_UNKNOWN, 0, pixels, pitch, false)) { 290 return NULL; 291 } 292 return surface; 293} 294 295SDL_PropertiesID SDL_GetSurfaceProperties(SDL_Surface *surface) 296{ 297 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 298 SDL_InvalidParamError("surface"); 299 return 0; 300 } 301 302 if (!surface->props) { 303 surface->props = SDL_CreateProperties(); 304 } 305 return surface->props; 306} 307 308bool SDL_SetSurfaceColorspace(SDL_Surface *surface, SDL_Colorspace colorspace) 309{ 310 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 311 return SDL_InvalidParamError("surface"); 312 } 313 314 surface->colorspace = colorspace; 315 return true; 316} 317 318SDL_Colorspace SDL_GetSurfaceColorspace(SDL_Surface *surface) 319{ 320 if (!SDL_SurfaceValid(surface)) { 321 return SDL_COLORSPACE_UNKNOWN; 322 } 323 324 return surface->colorspace; 325} 326 327float SDL_GetDefaultSDRWhitePoint(SDL_Colorspace colorspace) 328{ 329 return SDL_GetSurfaceSDRWhitePoint(NULL, colorspace); 330} 331 332float SDL_GetSurfaceSDRWhitePoint(SDL_Surface *surface, SDL_Colorspace colorspace) 333{ 334 SDL_TransferCharacteristics transfer = SDL_COLORSPACETRANSFER(colorspace); 335 336 if (transfer == SDL_TRANSFER_CHARACTERISTICS_LINEAR || 337 transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) { 338 SDL_PropertiesID props; 339 float default_value = 1.0f; 340 341 if (SDL_SurfaceValid(surface)) { 342 props = surface->props; 343 } else { 344 props = 0; 345 } 346 if (transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) { 347 /* The older standards use an SDR white point of 100 nits. 348 * ITU-R BT.2408-6 recommends using an SDR white point of 203 nits. 349 * This is the default Chrome uses, and what a lot of game content 350 * assumes, so we'll go with that. 351 */ 352 const float DEFAULT_PQ_SDR_WHITE_POINT = 203.0f; 353 default_value = DEFAULT_PQ_SDR_WHITE_POINT; 354 } 355 return SDL_GetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, default_value); 356 } 357 return 1.0f; 358} 359 360float SDL_GetDefaultHDRHeadroom(SDL_Colorspace colorspace) 361{ 362 return SDL_GetSurfaceHDRHeadroom(NULL, colorspace); 363} 364 365float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colorspace) 366{ 367 SDL_TransferCharacteristics transfer = SDL_COLORSPACETRANSFER(colorspace); 368 369 if (transfer == SDL_TRANSFER_CHARACTERISTICS_LINEAR || 370 transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) { 371 SDL_PropertiesID props; 372 float default_value = 0.0f; 373 374 if (SDL_SurfaceValid(surface)) { 375 props = surface->props; 376 } else { 377 props = 0; 378 } 379 return SDL_GetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, default_value); 380 } 381 return 1.0f; 382} 383 384SDL_Palette *SDL_CreateSurfacePalette(SDL_Surface *surface) 385{ 386 SDL_Palette *palette; 387 388 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 389 SDL_InvalidParamError("surface"); 390 return NULL; 391 } 392 393 CHECK_PARAM(!SDL_ISPIXELFORMAT_INDEXED(surface->format)) { 394 SDL_SetError("The surface is not indexed format"); 395 return NULL; 396 } 397 398 palette = SDL_CreatePalette((1 << SDL_BITSPERPIXEL(surface->format))); 399 if (!palette) { 400 return NULL; 401 } 402 403 if (palette->ncolors == 2) { 404 // Create a black and white bitmap palette 405 palette->colors[0].r = 0xFF; 406 palette->colors[0].g = 0xFF; 407 palette->colors[0].b = 0xFF; 408 palette->colors[1].r = 0x00; 409 palette->colors[1].g = 0x00; 410 palette->colors[1].b = 0x00; 411 } 412 413 if (!SDL_SetSurfacePalette(surface, palette)) { 414 SDL_DestroyPalette(palette); 415 return NULL; 416 } 417 418 // The surface has retained the palette, we can remove the reference here 419 SDL_assert(palette->refcount == 2); 420 SDL_DestroyPalette(palette); 421 return palette; 422} 423 424bool SDL_SetSurfacePalette(SDL_Surface *surface, SDL_Palette *palette) 425{ 426 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 427 return SDL_InvalidParamError("surface"); 428 } 429 430 CHECK_PARAM(palette && palette->ncolors > (1 << SDL_BITSPERPIXEL(surface->format))) { 431 return SDL_SetError("Palette doesn't match surface format"); 432 } 433 434 if (palette != surface->palette) { 435 if (surface->palette) { 436 SDL_DestroyPalette(surface->palette); 437 } 438 439 surface->palette = palette; 440 441 if (surface->palette) { 442 ++surface->palette->refcount; 443 } 444 } 445 446 SDL_InvalidateMap(&surface->map); 447 448 return true; 449} 450 451SDL_Palette *SDL_GetSurfacePalette(SDL_Surface *surface) 452{ 453 if (!SDL_SurfaceValid(surface)) { 454 return NULL; 455 } 456 457 return surface->palette; 458} 459 460bool SDL_AddSurfaceAlternateImage(SDL_Surface *surface, SDL_Surface *image) 461{ 462 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 463 return SDL_InvalidParamError("surface"); 464 } 465 466 CHECK_PARAM(!SDL_SurfaceValid(image)) { 467 return SDL_InvalidParamError("image"); 468 } 469 470 SDL_Surface **images = (SDL_Surface **)SDL_realloc(surface->images, (surface->num_images + 1) * sizeof(*images)); 471 if (!images) { 472 return false; 473 } 474 images[surface->num_images] = image; 475 surface->images = images; 476 ++surface->num_images; 477 ++image->refcount; 478 return true; 479} 480 481bool SDL_SurfaceHasAlternateImages(SDL_Surface *surface) 482{ 483 if (!SDL_SurfaceValid(surface)) { 484 return false; 485 } 486 487 return (surface->num_images > 0); 488} 489 490SDL_Surface **SDL_GetSurfaceImages(SDL_Surface *surface, int *count) 491{ 492 if (count) { 493 *count = 0; 494 } 495 496 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 497 SDL_InvalidParamError("surface"); 498 return NULL; 499 } 500 501 int num_images = 1 + surface->num_images; 502 SDL_Surface **images = (SDL_Surface **)SDL_malloc((num_images + 1) * sizeof(*images)); 503 if (!images) { 504 return NULL; 505 } 506 images[0] = surface; 507 if (surface->num_images > 0) { 508 SDL_memcpy(&images[1], surface->images, (surface->num_images * sizeof(images[1]))); 509 } 510 images[num_images] = NULL; 511 512 if (count) { 513 *count = num_images; 514 } 515 return images; 516} 517 518SDL_Surface *SDL_GetSurfaceImage(SDL_Surface *surface, float display_scale) 519{ 520 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 521 SDL_InvalidParamError("surface"); 522 return NULL; 523 } 524 525 if (!SDL_SurfaceHasAlternateImages(surface)) { 526 ++surface->refcount; 527 return surface; 528 } 529 530 // This surface has high DPI images, pick the best one available, or scale one to the correct size 531 SDL_Surface **images = SDL_GetSurfaceImages(surface, NULL); 532 if (!images) { 533 // Failure, fall back to the existing surface 534 ++surface->refcount; 535 return surface; 536 } 537 538 // Find closest image. Images that are larger than the 539 // desired size are preferred over images that are smaller. 540 SDL_Surface *closest = NULL; 541 int desired_w = (int)SDL_round(surface->w * display_scale); 542 int desired_h = (int)SDL_round(surface->h * display_scale); 543 int desired_size = desired_w * desired_h; 544 int closest_distance = -1; 545 int closest_size = -1; 546 for (int i = 0; images[i]; ++i) { 547 SDL_Surface *candidate = images[i]; 548 int size = candidate->w * candidate->h; 549 int delta_w = (candidate->w - desired_w); 550 int delta_h = (candidate->h - desired_h); 551 int distance = (delta_w * delta_w) + (delta_h * delta_h); 552 if (closest_distance < 0 || distance < closest_distance || 553 (size > desired_size && closest_size < desired_size)) { 554 closest = candidate; 555 closest_distance = distance; 556 closest_size = size; 557 } 558 } 559 SDL_free(images); 560 SDL_assert(closest != NULL); // We should always have at least one surface 561 562 if (closest->w == desired_w && closest->h == desired_h) { 563 ++closest->refcount; 564 return closest; 565 } 566 567 // We need to scale the image to the correct size. To maintain good image quality, downscaling 568 // is done in steps, never reducing the width and height by more than half each time. 569 SDL_Surface *scaled = closest; 570 do { 571 int next_scaled_w = SDL_max(desired_w, (scaled->w + 1) / 2); 572 int next_scaled_h = SDL_max(desired_h, (scaled->h + 1) / 2); 573 SDL_Surface *next_scaled = SDL_ScaleSurface(scaled, next_scaled_w, next_scaled_h, SDL_SCALEMODE_LINEAR); 574 if (scaled != closest) { 575 SDL_DestroySurface(scaled); 576 } 577 scaled = next_scaled; 578 if (!scaled) { 579 // Failure, fall back to the closest surface 580 ++closest->refcount; 581 return closest; 582 } 583 } while (scaled->w != desired_w || scaled->h != desired_h); 584 585 return scaled; 586} 587 588void SDL_RemoveSurfaceAlternateImages(SDL_Surface *surface) 589{ 590 if (!SDL_SurfaceValid(surface)) { 591 return; 592 } 593 594 if (surface->num_images > 0) { 595 for (int i = 0; i < surface->num_images; ++i) { 596 SDL_DestroySurface(surface->images[i]); 597 } 598 SDL_free(surface->images); 599 surface->images = NULL; 600 surface->num_images = 0; 601 } 602} 603 604bool SDL_SetSurfaceRLE(SDL_Surface *surface, bool enabled) 605{ 606 int flags; 607 608 CHECK_PARAM(!SDL_SurfaceValid(surface) || SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 609 return SDL_InvalidParamError("surface"); 610 } 611 612 flags = surface->map.info.flags; 613 if (enabled) { 614 surface->map.info.flags |= SDL_COPY_RLE_DESIRED; 615 } else { 616 surface->map.info.flags &= ~SDL_COPY_RLE_DESIRED; 617 } 618 if (surface->map.info.flags != flags) { 619 SDL_InvalidateMap(&surface->map); 620 } 621 return true; 622} 623 624bool SDL_SurfaceHasRLE(SDL_Surface *surface) 625{ 626 if (!SDL_SurfaceValid(surface)) { 627 return false; 628 } 629 630 if (!(surface->map.info.flags & SDL_COPY_RLE_DESIRED)) { 631 return false; 632 } 633 634 return true; 635} 636 637bool SDL_SetSurfaceColorKey(SDL_Surface *surface, bool enabled, Uint32 key) 638{ 639 int flags; 640 641 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 642 return SDL_InvalidParamError("surface"); 643 } 644 645 CHECK_PARAM(surface->palette && key >= ((Uint32)surface->palette->ncolors)) { 646 return SDL_InvalidParamError("key"); 647 } 648 649 flags = surface->map.info.flags; 650 if (enabled) { 651 surface->map.info.flags |= SDL_COPY_COLORKEY; 652 surface->map.info.colorkey = key; 653 } else { 654 surface->map.info.flags &= ~SDL_COPY_COLORKEY; 655 } 656 if (surface->map.info.flags != flags) { 657 SDL_InvalidateMap(&surface->map); 658 } 659 660 return true; 661} 662 663bool SDL_SurfaceHasColorKey(SDL_Surface *surface) 664{ 665 if (!SDL_SurfaceValid(surface)) { 666 return false; 667 } 668 669 if (!(surface->map.info.flags & SDL_COPY_COLORKEY)) { 670 return false; 671 } 672 673 return true; 674} 675 676bool SDL_GetSurfaceColorKey(SDL_Surface *surface, Uint32 *key) 677{ 678 if (key) { 679 *key = 0; 680 } 681 682 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 683 return SDL_InvalidParamError("surface"); 684 } 685 686 CHECK_PARAM(!(surface->map.info.flags & SDL_COPY_COLORKEY)) { 687 return SDL_SetError("Surface doesn't have a colorkey"); 688 } 689 690 if (key) { 691 *key = surface->map.info.colorkey; 692 } 693 return true; 694} 695 696/* This is a fairly slow function to switch from colorkey to alpha 697 NB: it doesn't handle bpp 1 or 3, because they have no alpha channel */ 698static void SDL_ConvertColorkeyToAlpha(SDL_Surface *surface, bool ignore_alpha) 699{ 700 int x, y, bpp; 701 702 if (!SDL_SurfaceValid(surface)) { 703 return; 704 } 705 706 if (!(surface->map.info.flags & SDL_COPY_COLORKEY) || 707 !SDL_ISPIXELFORMAT_ALPHA(surface->format)) { 708 return; 709 } 710 711 bpp = SDL_BYTESPERPIXEL(surface->format); 712 713 SDL_LockSurface(surface); 714 715 if (bpp == 2) { 716 Uint16 *row, *spot; 717 Uint16 ckey = (Uint16)surface->map.info.colorkey; 718 Uint16 mask = (Uint16)(~surface->fmt->Amask); 719 720 // Ignore, or not, alpha in colorkey comparison 721 if (ignore_alpha) { 722 ckey &= mask; 723 row = (Uint16 *)surface->pixels; 724 for (y = surface->h; y--;) { 725 spot = row; 726 for (x = surface->w; x--;) { 727 if ((*spot & mask) == ckey) { 728 *spot &= mask; 729 } 730 ++spot; 731 } 732 row += surface->pitch / 2; 733 } 734 } else { 735 row = (Uint16 *)surface->pixels; 736 for (y = surface->h; y--;) { 737 spot = row; 738 for (x = surface->w; x--;) { 739 if (*spot == ckey) { 740 *spot &= mask; 741 } 742 ++spot; 743 } 744 row += surface->pitch / 2; 745 } 746 } 747 } else if (bpp == 4) { 748 Uint32 *row, *spot; 749 Uint32 ckey = surface->map.info.colorkey; 750 Uint32 mask = ~surface->fmt->Amask; 751 752 // Ignore, or not, alpha in colorkey comparison 753 if (ignore_alpha) { 754 ckey &= mask; 755 row = (Uint32 *)surface->pixels; 756 for (y = surface->h; y--;) { 757 spot = row; 758 for (x = surface->w; x--;) { 759 if ((*spot & mask) == ckey) { 760 *spot &= mask; 761 } 762 ++spot; 763 } 764 row += surface->pitch / 4; 765 } 766 } else { 767 row = (Uint32 *)surface->pixels; 768 for (y = surface->h; y--;) { 769 spot = row; 770 for (x = surface->w; x--;) { 771 if (*spot == ckey) { 772 *spot &= mask; 773 } 774 ++spot; 775 } 776 row += surface->pitch / 4; 777 } 778 } 779 } 780 781 SDL_UnlockSurface(surface); 782 783 SDL_SetSurfaceColorKey(surface, false, 0); 784 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); 785} 786 787bool SDL_SetSurfaceColorMod(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b) 788{ 789 int flags; 790 791 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 792 return SDL_InvalidParamError("surface"); 793 } 794 795 surface->map.info.r = r; 796 surface->map.info.g = g; 797 surface->map.info.b = b; 798 799 flags = surface->map.info.flags; 800 if (r != 0xFF || g != 0xFF || b != 0xFF) { 801 surface->map.info.flags |= SDL_COPY_MODULATE_COLOR; 802 } else { 803 surface->map.info.flags &= ~SDL_COPY_MODULATE_COLOR; 804 } 805 if (surface->map.info.flags != flags) { 806 SDL_InvalidateMap(&surface->map); 807 } 808 return true; 809} 810 811bool SDL_GetSurfaceColorMod(SDL_Surface *surface, Uint8 *r, Uint8 *g, Uint8 *b) 812{ 813 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 814 if (r) { 815 *r = 255; 816 } 817 if (g) { 818 *g = 255; 819 } 820 if (b) { 821 *b = 255; 822 } 823 return SDL_InvalidParamError("surface"); 824 } 825 826 if (r) { 827 *r = surface->map.info.r; 828 } 829 if (g) { 830 *g = surface->map.info.g; 831 } 832 if (b) { 833 *b = surface->map.info.b; 834 } 835 return true; 836} 837 838bool SDL_SetSurfaceAlphaMod(SDL_Surface *surface, Uint8 alpha) 839{ 840 int flags; 841 842 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 843 return SDL_InvalidParamError("surface"); 844 } 845 846 surface->map.info.a = alpha; 847 848 flags = surface->map.info.flags; 849 if (alpha != 0xFF) { 850 surface->map.info.flags |= SDL_COPY_MODULATE_ALPHA; 851 } else { 852 surface->map.info.flags &= ~SDL_COPY_MODULATE_ALPHA; 853 } 854 if (surface->map.info.flags != flags) { 855 SDL_InvalidateMap(&surface->map); 856 } 857 return true; 858} 859 860bool SDL_GetSurfaceAlphaMod(SDL_Surface *surface, Uint8 *alpha) 861{ 862 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 863 if (alpha) { 864 *alpha = 255; 865 } 866 return SDL_InvalidParamError("surface"); 867 } 868 869 if (alpha) { 870 *alpha = surface->map.info.a; 871 } 872 return true; 873} 874 875bool SDL_SetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode blendMode) 876{ 877 int flags; 878 bool result = true; 879 880 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 881 return SDL_InvalidParamError("surface"); 882 } 883 884 CHECK_PARAM(blendMode == SDL_BLENDMODE_INVALID) { 885 return SDL_InvalidParamError("blendMode"); 886 } 887 888 flags = surface->map.info.flags; 889 surface->map.info.flags &= ~SDL_COPY_BLEND_MASK; 890 switch (blendMode) { 891 case SDL_BLENDMODE_NONE: 892 break; 893 case SDL_BLENDMODE_BLEND: 894 surface->map.info.flags |= SDL_COPY_BLEND; 895 break; 896 case SDL_BLENDMODE_BLEND_PREMULTIPLIED: 897 surface->map.info.flags |= SDL_COPY_BLEND_PREMULTIPLIED; 898 break; 899 case SDL_BLENDMODE_ADD: 900 surface->map.info.flags |= SDL_COPY_ADD; 901 break; 902 case SDL_BLENDMODE_ADD_PREMULTIPLIED: 903 surface->map.info.flags |= SDL_COPY_ADD_PREMULTIPLIED; 904 break; 905 case SDL_BLENDMODE_MOD: 906 surface->map.info.flags |= SDL_COPY_MOD; 907 break; 908 case SDL_BLENDMODE_MUL: 909 surface->map.info.flags |= SDL_COPY_MUL; 910 break; 911 default: 912 result = SDL_Unsupported(); 913 break; 914 } 915 916 if (surface->map.info.flags != flags) { 917 SDL_InvalidateMap(&surface->map); 918 } 919 920 return result; 921} 922 923bool SDL_GetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode *blendMode) 924{ 925 if (blendMode) { 926 *blendMode = SDL_BLENDMODE_INVALID; 927 } 928 929 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 930 return SDL_InvalidParamError("surface"); 931 } 932 933 if (!blendMode) { 934 return true; 935 } 936 937 switch (surface->map.info.flags & SDL_COPY_BLEND_MASK) { 938 case SDL_COPY_BLEND: 939 *blendMode = SDL_BLENDMODE_BLEND; 940 break; 941 case SDL_COPY_BLEND_PREMULTIPLIED: 942 *blendMode = SDL_BLENDMODE_BLEND_PREMULTIPLIED; 943 break; 944 case SDL_COPY_ADD: 945 *blendMode = SDL_BLENDMODE_ADD; 946 break; 947 case SDL_COPY_ADD_PREMULTIPLIED: 948 *blendMode = SDL_BLENDMODE_ADD_PREMULTIPLIED; 949 break; 950 case SDL_COPY_MOD: 951 *blendMode = SDL_BLENDMODE_MOD; 952 break; 953 case SDL_COPY_MUL: 954 *blendMode = SDL_BLENDMODE_MUL; 955 break; 956 default: 957 *blendMode = SDL_BLENDMODE_NONE; 958 break; 959 } 960 return true; 961} 962 963bool SDL_SetSurfaceClipRect(SDL_Surface *surface, const SDL_Rect *rect) 964{ 965 SDL_Rect full_rect; 966 967 // Don't do anything if there's no surface to act on 968 if (!SDL_SurfaceValid(surface)) { 969 return false; 970 } 971 972 // Set up the full surface rectangle 973 full_rect.x = 0; 974 full_rect.y = 0; 975 full_rect.w = surface->w; 976 full_rect.h = surface->h; 977 978 // Set the clipping rectangle 979 if (!rect) { 980 surface->clip_rect = full_rect; 981 return true; 982 } 983 return SDL_GetRectIntersection(rect, &full_rect, &surface->clip_rect); 984} 985 986bool SDL_GetSurfaceClipRect(SDL_Surface *surface, SDL_Rect *rect) 987{ 988 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 989 if (rect) { 990 SDL_zerop(rect); 991 } 992 return SDL_InvalidParamError("surface"); 993 } 994 CHECK_PARAM(!rect) { 995 return SDL_InvalidParamError("rect"); 996 } 997 *rect = surface->clip_rect; 998 return true; 999} 1000 1001/* 1002 * Set up a blit between two surfaces -- split into three parts: 1003 * The upper part, SDL_BlitSurface(), performs clipping and rectangle 1004 * verification. The lower part is a pointer to a low level 1005 * accelerated blitting function. 1006 * 1007 * These parts are separated out and each used internally by this 1008 * library in the optimum places. They are exported so that if 1009 * you know exactly what you are doing, you can optimize your code 1010 * by calling the one(s) you need. 1011 */ 1012bool SDL_BlitSurfaceUnchecked(SDL_Surface *src, const SDL_Rect *srcrect, 1013 SDL_Surface *dst, const SDL_Rect *dstrect) 1014{ 1015 // Check to make sure the blit mapping is valid 1016 if (!SDL_ValidateMap(src, dst)) { 1017 return false; 1018 } 1019 return src->map.blit(src, srcrect, dst, dstrect); 1020} 1021 1022bool SDL_BlitSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect) 1023{ 1024 SDL_Rect r_src, r_dst; 1025 1026 // Make sure the surfaces aren't locked 1027 CHECK_PARAM(!SDL_SurfaceValid(src) || (!src->pixels && !SDL_MUSTLOCK(src))) { 1028 return SDL_InvalidParamError("src"); 1029 } 1030 CHECK_PARAM(!SDL_SurfaceValid(dst) || (!dst->pixels && !SDL_MUSTLOCK(dst))) { 1031 return SDL_InvalidParamError("dst"); 1032 } 1033 CHECK_PARAM((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) { 1034 return SDL_SetError("Surfaces must not be locked during blit"); 1035 } 1036 1037 // Full src surface 1038 r_src.x = 0; 1039 r_src.y = 0; 1040 r_src.w = src->w; 1041 r_src.h = src->h; 1042 1043 if (dstrect) { 1044 r_dst.x = dstrect->x; 1045 r_dst.y = dstrect->y; 1046 } else { 1047 r_dst.x = 0; 1048 r_dst.y = 0; 1049 } 1050 1051 // clip the source rectangle to the source surface 1052 if (srcrect) { 1053 SDL_Rect tmp; 1054 if (SDL_GetRectIntersection(srcrect, &r_src, &tmp) == false) { 1055 return true; 1056 } 1057 1058 // Shift dstrect, if srcrect origin has changed 1059 r_dst.x += tmp.x - srcrect->x; 1060 r_dst.y += tmp.y - srcrect->y; 1061 1062 // Update srcrect 1063 r_src = tmp; 1064 } 1065 1066 // There're no dstrect.w/h parameters. It's the same as srcrect 1067 r_dst.w = r_src.w; 1068 r_dst.h = r_src.h; 1069 1070 // clip the destination rectangle against the clip rectangle 1071 { 1072 SDL_Rect tmp; 1073 if (SDL_GetRectIntersection(&r_dst, &dst->clip_rect, &tmp) == false) { 1074 return true; 1075 } 1076 1077 // Shift srcrect, if dstrect has changed 1078 r_src.x += tmp.x - r_dst.x; 1079 r_src.y += tmp.y - r_dst.y; 1080 r_src.w = tmp.w; 1081 r_src.h = tmp.h; 1082 1083 // Update dstrect 1084 r_dst = tmp; 1085 } 1086 1087 if (r_dst.w <= 0 || r_dst.h <= 0) { 1088 // No-op. 1089 return true; 1090 } 1091 1092 // Switch back to a fast blit if we were previously stretching 1093 if (src->map.info.flags & SDL_COPY_NEAREST) { 1094 src->map.info.flags &= ~SDL_COPY_NEAREST; 1095 SDL_InvalidateMap(&src->map); 1096 } 1097 1098 return SDL_BlitSurfaceUnchecked(src, &r_src, dst, &r_dst); 1099} 1100 1101static bool SDL_BlitSurfaceClippedScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode) 1102{ 1103 // We need to scale first, then blit into dst because we're clipping in the destination surface pixel coordinates 1104 bool result; 1105 int saved_w = src->w; 1106 int saved_h = src->h; 1107 void *saved_pixels = src->pixels; 1108 src->w = srcrect->w; 1109 src->h = srcrect->h; 1110 src->pixels = (Uint8 *)src->pixels + srcrect->y * src->pitch + srcrect->x * SDL_BYTESPERPIXEL(src->format); 1111 SDL_Surface *scaled = SDL_ScaleSurface(src, dstrect->w, dstrect->h, scaleMode); 1112 if (scaled) { 1113 result = SDL_BlitSurface(scaled, NULL, dst, dstrect); 1114 SDL_DestroySurface(scaled); 1115 } else { 1116 result = false; 1117 } 1118 src->w = saved_w; 1119 src->h = saved_h; 1120 src->pixels = saved_pixels; 1121 return result; 1122} 1123 1124bool SDL_BlitSurfaceScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode) 1125{ 1126 SDL_Rect r_src, r_dst; 1127 bool result; 1128 1129 // Make sure the surfaces aren't locked 1130 CHECK_PARAM(!SDL_SurfaceValid(src) || (!src->pixels && !SDL_MUSTLOCK(src))) { 1131 return SDL_InvalidParamError("src"); 1132 } 1133 CHECK_PARAM(!SDL_SurfaceValid(dst) || (!dst->pixels && !SDL_MUSTLOCK(dst))) { 1134 return SDL_InvalidParamError("dst"); 1135 } 1136 CHECK_PARAM((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) { 1137 return SDL_SetError("Surfaces must not be locked during blit"); 1138 } 1139 1140 switch (scaleMode) { 1141 case SDL_SCALEMODE_NEAREST: 1142 break; 1143 case SDL_SCALEMODE_LINEAR: 1144 break; 1145 case SDL_SCALEMODE_PIXELART: 1146 scaleMode = SDL_SCALEMODE_NEAREST; 1147 break; 1148 default: 1149 return SDL_InvalidParamError("scaleMode"); 1150 } 1151 1152 int src_w = srcrect ? srcrect->w : src->w; 1153 int src_h = srcrect ? srcrect->h : src->h; 1154 int dst_w = dstrect ? dstrect->w : dst->w; 1155 int dst_h = dstrect ? dstrect->h : dst->h; 1156 if (src_w == dst_w && src_h == dst_h) { 1157 // No scaling, defer to regular blit 1158 return SDL_BlitSurface(src, srcrect, dst, dstrect); 1159 } 1160 1161 if (src->w == 0 || src->h == 0) { 1162 // Nothing to do 1163 return true; 1164 } 1165 1166 // Full src surface 1167 r_src.x = 0; 1168 r_src.y = 0; 1169 r_src.w = src->w; 1170 r_src.h = src->h; 1171 1172 if (dstrect) { 1173 r_dst.x = dstrect->x; 1174 r_dst.y = dstrect->y; 1175 r_dst.w = dstrect->w; 1176 r_dst.h = dstrect->h; 1177 } else { 1178 r_dst.x = 0; 1179 r_dst.y = 0; 1180 r_dst.w = dst->w; 1181 r_dst.h = dst->h; 1182 } 1183 1184 // clip the source rectangle to the source surface 1185 if (srcrect) { 1186 SDL_Rect desired, tmp; 1187 desired.x = srcrect->x; 1188 desired.y = srcrect->y; 1189 desired.w = SDL_max(srcrect->w, 1); 1190 desired.h = SDL_max(srcrect->h, 1); 1191 if (SDL_GetRectIntersection(&desired, &r_src, &tmp) == false) { 1192 return true; 1193 } 1194 1195 // Shift dstrect, if srcrect origin has changed 1196 r_dst.x += (tmp.x - desired.x) * r_dst.w / desired.w; 1197 r_dst.y += (tmp.y - desired.y) * r_dst.h / desired.h; 1198 r_dst.w += (tmp.w - desired.w) * r_dst.w / desired.w; 1199 r_dst.h += (tmp.h - desired.h) * r_dst.h / desired.h; 1200 1201 // Update srcrect 1202 r_src = tmp; 1203 } 1204 1205 SDL_Rect tmp; 1206 if (SDL_GetRectIntersection(&r_dst, &dst->clip_rect, &tmp) == false) { 1207 return true; 1208 } 1209 1210 if (tmp.x != r_dst.x || tmp.y != r_dst.y || tmp.w != r_dst.w || tmp.h != r_dst.h) { 1211 // Need to do a clipped and scaled blit 1212 return SDL_BlitSurfaceClippedScaled(src, &r_src, dst, &r_dst, scaleMode); 1213 } 1214 1215 if (SDL_MUSTLOCK(src)) { 1216 if (!SDL_LockSurface(src)) { 1217 return false; 1218 } 1219 } 1220 if (SDL_MUSTLOCK(dst)) { 1221 if (!SDL_LockSurface(dst)) { 1222 if (SDL_MUSTLOCK(src)) { 1223 SDL_UnlockSurface(src); 1224 } 1225 return false; 1226 } 1227 } 1228 1229 result = SDL_BlitSurfaceUncheckedScaled(src, &r_src, dst, &r_dst, scaleMode); 1230 1231 if (SDL_MUSTLOCK(src)) { 1232 SDL_UnlockSurface(src); 1233 } 1234 if (SDL_MUSTLOCK(dst)) { 1235 SDL_UnlockSurface(dst); 1236 } 1237 return result; 1238} 1239 1240/** 1241 * This is a semi-private blit function and it performs low-level surface 1242 * scaled blitting only. 1243 */ 1244bool SDL_BlitSurfaceUncheckedScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode) 1245{ 1246 static const Uint32 complex_copy_flags = (SDL_COPY_MODULATE_MASK | SDL_COPY_BLEND_MASK | SDL_COPY_COLORKEY); 1247 1248 if (srcrect->w > SDL_MAX_UINT16 || srcrect->h > SDL_MAX_UINT16 || 1249 dstrect->w > SDL_MAX_UINT16 || dstrect->h > SDL_MAX_UINT16) { 1250 return SDL_SetError("Size too large for scaling"); 1251 } 1252 1253 if (!(src->map.info.flags & SDL_COPY_NEAREST)) { 1254 src->map.info.flags |= SDL_COPY_NEAREST; 1255 SDL_InvalidateMap(&src->map); 1256 } 1257 1258 if (scaleMode == SDL_SCALEMODE_NEAREST || scaleMode == SDL_SCALEMODE_PIXELART) { 1259 if (!(src->map.info.flags & complex_copy_flags) && 1260 src->format == dst->format && 1261 (!SDL_ISPIXELFORMAT_INDEXED(src->format) || 1262 (SDL_BITSPERPIXEL(src->format) == 8 && SDL_IsSamePalette(src->palette, dst->palette))) && 1263 SDL_BYTESPERPIXEL(src->format) <= 4) { 1264 return SDL_StretchSurface(src, srcrect, dst, dstrect, SDL_SCALEMODE_NEAREST); 1265 } else if (SDL_BITSPERPIXEL(src->format) < 8) { 1266 // Scaling bitmap not yet supported, convert to RGBA for blit 1267 bool result = false; 1268 SDL_Surface *tmp = SDL_ConvertSurface(src, SDL_PIXELFORMAT_ARGB8888); 1269 if (tmp) { 1270 result = SDL_BlitSurfaceUncheckedScaled(tmp, srcrect, dst, dstrect, SDL_SCALEMODE_NEAREST); 1271 SDL_DestroySurface(tmp); 1272 } 1273 return result; 1274 } else { 1275 return SDL_BlitSurfaceUnchecked(src, srcrect, dst, dstrect); 1276 } 1277 } else { 1278 if (!(src->map.info.flags & complex_copy_flags) && 1279 src->format == dst->format && 1280 !SDL_ISPIXELFORMAT_INDEXED(src->format) && 1281 SDL_BYTESPERPIXEL(src->format) == 4 && 1282 src->format != SDL_PIXELFORMAT_ARGB2101010) { 1283 // fast path 1284 return SDL_StretchSurface(src, srcrect, dst, dstrect, SDL_SCALEMODE_LINEAR); 1285 } else if (SDL_BITSPERPIXEL(src->format) < 8) { 1286 // Scaling bitmap not yet supported, convert to RGBA for blit 1287 bool result = false; 1288 SDL_Surface *tmp = SDL_ConvertSurface(src, SDL_PIXELFORMAT_ARGB8888); 1289 if (tmp) { 1290 result = SDL_BlitSurfaceUncheckedScaled(tmp, srcrect, dst, dstrect, scaleMode); 1291 SDL_DestroySurface(tmp); 1292 } 1293 return result; 1294 } else { 1295 // Use intermediate surface(s) 1296 SDL_Surface *tmp1 = NULL; 1297 bool result; 1298 SDL_Rect srcrect2; 1299 int is_complex_copy_flags = (src->map.info.flags & complex_copy_flags); 1300 1301 Uint8 r, g, b; 1302 Uint8 alpha; 1303 SDL_BlendMode blendMode; 1304 1305 // Save source infos 1306 SDL_GetSurfaceColorMod(src, &r, &g, &b); 1307 SDL_GetSurfaceAlphaMod(src, &alpha); 1308 SDL_GetSurfaceBlendMode(src, &blendMode); 1309 srcrect2.x = srcrect->x; 1310 srcrect2.y = srcrect->y; 1311 srcrect2.w = srcrect->w; 1312 srcrect2.h = srcrect->h; 1313 1314 // Change source format if not appropriate for scaling 1315 if (SDL_BYTESPERPIXEL(src->format) != 4 || src->format == SDL_PIXELFORMAT_ARGB2101010) { 1316 SDL_PixelFormat fmt; 1317 if (SDL_BYTESPERPIXEL(dst->format) == 4 && dst->format != SDL_PIXELFORMAT_ARGB2101010 && 1318 (SDL_ISPIXELFORMAT_ALPHA(dst->format) || !is_complex_copy_flags)) { 1319 fmt = dst->format; 1320 } else { 1321 fmt = SDL_PIXELFORMAT_ARGB8888; 1322 } 1323 tmp1 = SDL_ConvertSurfaceRect(src, srcrect, fmt); 1324 if (!tmp1) { 1325 return false; 1326 } 1327 src = tmp1; 1328 srcrect2.x = 0; 1329 srcrect2.y = 0; 1330 } 1331 1332 // Intermediate scaling 1333 if (is_complex_copy_flags || src->format != dst->format) { 1334 SDL_Rect tmprect; 1335 SDL_Surface *tmp2 = SDL_CreateSurface(dstrect->w, dstrect->h, src->format); 1336 SDL_StretchSurface(src, &srcrect2, tmp2, NULL, SDL_SCALEMODE_LINEAR); 1337 1338 SDL_SetSurfaceColorMod(tmp2, r, g, b); 1339 SDL_SetSurfaceAlphaMod(tmp2, alpha); 1340 SDL_SetSurfaceBlendMode(tmp2, blendMode); 1341 1342 tmprect.x = 0; 1343 tmprect.y = 0; 1344 tmprect.w = dstrect->w; 1345 tmprect.h = dstrect->h; 1346 result = SDL_BlitSurfaceUnchecked(tmp2, &tmprect, dst, dstrect); 1347 SDL_DestroySurface(tmp2); 1348 } else { 1349 result = SDL_StretchSurface(src, &srcrect2, dst, dstrect, SDL_SCALEMODE_LINEAR); 1350 } 1351 1352 SDL_DestroySurface(tmp1); 1353 return result; 1354 } 1355 } 1356} 1357 1358bool SDL_BlitSurfaceTiled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect) 1359{ 1360 SDL_Rect r_src, r_dst; 1361 1362 // Make sure the surfaces aren't locked 1363 CHECK_PARAM(!SDL_SurfaceValid(src)) { 1364 return SDL_InvalidParamError("src"); 1365 } 1366 CHECK_PARAM(!SDL_SurfaceValid(dst)) { 1367 return SDL_InvalidParamError("dst"); 1368 } 1369 CHECK_PARAM((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) { 1370 return SDL_SetError("Surfaces must not be locked during blit"); 1371 } 1372 1373 // Full src surface 1374 r_src.x = 0; 1375 r_src.y = 0; 1376 r_src.w = src->w; 1377 r_src.h = src->h; 1378 1379 if (dstrect) { 1380 r_dst.x = dstrect->x; 1381 r_dst.y = dstrect->y; 1382 r_dst.w = dstrect->w; 1383 r_dst.h = dstrect->h; 1384 } else { 1385 r_dst.x = 0; 1386 r_dst.y = 0; 1387 r_dst.w = dst->w; 1388 r_dst.h = dst->h; 1389 } 1390 1391 // clip the source rectangle to the source surface 1392 if (srcrect) { 1393 if (SDL_GetRectIntersection(srcrect, &r_src, &r_src) == false) { 1394 return true; 1395 } 1396 1397 // For tiling we don't adjust the destination rectangle 1398 } 1399 1400 // clip the destination rectangle against the clip rectangle 1401 { 1402 if (SDL_GetRectIntersection(&r_dst, &dst->clip_rect, &r_dst) == false) { 1403 return true; 1404 } 1405 1406 // For tiling we don't adjust the source rectangle 1407 } 1408 1409 // Switch back to a fast blit if we were previously stretching 1410 if (src->map.info.flags & SDL_COPY_NEAREST) { 1411 src->map.info.flags &= ~SDL_COPY_NEAREST; 1412 SDL_InvalidateMap(&src->map); 1413 } 1414 1415 int rows = r_dst.h / r_src.h; 1416 int cols = r_dst.w / r_src.w; 1417 int remaining_w = r_dst.w % r_src.w; 1418 int remaining_h = r_dst.h % r_src.h; 1419 SDL_Rect curr_src, curr_dst; 1420 1421 SDL_copyp(&curr_src, &r_src); 1422 curr_dst.y = r_dst.y; 1423 curr_dst.w = r_src.w; 1424 curr_dst.h = r_src.h; 1425 for (int y = 0; y < rows; ++y) { 1426 curr_dst.x = r_dst.x; 1427 for (int x = 0; x < cols; ++x) { 1428 if (!SDL_BlitSurfaceUnchecked(src, &curr_src, dst, &curr_dst)) { 1429 return false; 1430 } 1431 curr_dst.x += curr_dst.w; 1432 } 1433 if (remaining_w) { 1434 curr_src.w = remaining_w; 1435 curr_dst.w = remaining_w; 1436 if (!SDL_BlitSurfaceUnchecked(src, &curr_src, dst, &curr_dst)) { 1437 return false; 1438 } 1439 curr_src.w = r_src.w; 1440 curr_dst.w = r_src.w; 1441 } 1442 curr_dst.y += curr_dst.h; 1443 } 1444 if (remaining_h) { 1445 curr_src.h = remaining_h; 1446 curr_dst.h = remaining_h; 1447 curr_dst.x = r_dst.x; 1448 for (int x = 0; x < cols; ++x) { 1449 if (!SDL_BlitSurfaceUnchecked(src, &curr_src, dst, &curr_dst)) { 1450 return false; 1451 } 1452 curr_dst.x += curr_dst.w; 1453 } 1454 if (remaining_w) { 1455 curr_src.w = remaining_w; 1456 curr_dst.w = remaining_w; 1457 if (!SDL_BlitSurfaceUnchecked(src, &curr_src, dst, &curr_dst)) { 1458 return false; 1459 } 1460 } 1461 } 1462 return true; 1463} 1464 1465bool SDL_BlitSurfaceTiledWithScale(SDL_Surface *src, const SDL_Rect *srcrect, float scale, SDL_ScaleMode scaleMode, SDL_Surface *dst, const SDL_Rect *dstrect) 1466{ 1467 SDL_Rect r_src, r_dst; 1468 1469 // Make sure the surfaces aren't locked 1470 CHECK_PARAM(!SDL_SurfaceValid(src)) { 1471 return SDL_InvalidParamError("src"); 1472 } 1473 CHECK_PARAM(!SDL_SurfaceValid(dst)) { 1474 return SDL_InvalidParamError("dst"); 1475 } 1476 CHECK_PARAM((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) { 1477 return SDL_SetError("Surfaces must not be locked during blit"); 1478 } 1479 CHECK_PARAM(scale < 0.0f) { 1480 return SDL_InvalidParamError("scale"); 1481 } 1482 1483 // Full src surface 1484 r_src.x = 0; 1485 r_src.y = 0; 1486 r_src.w = src->w; 1487 r_src.h = src->h; 1488 1489 if (dstrect) { 1490 r_dst.x = dstrect->x; 1491 r_dst.y = dstrect->y; 1492 r_dst.w = dstrect->w; 1493 r_dst.h = dstrect->h; 1494 } else { 1495 r_dst.x = 0; 1496 r_dst.y = 0; 1497 r_dst.w = dst->w; 1498 r_dst.h = dst->h; 1499 } 1500 1501 // clip the source rectangle to the source surface 1502 if (srcrect) { 1503 if (SDL_GetRectIntersection(srcrect, &r_src, &r_src) == false) { 1504 return true; 1505 } 1506 1507 // For tiling we don't adjust the destination rectangle 1508 } 1509 1510 // clip the destination rectangle against the clip rectangle 1511 { 1512 if (SDL_GetRectIntersection(&r_dst, &dst->clip_rect, &r_dst) == false) { 1513 return true; 1514 } 1515 1516 // For tiling we don't adjust the source rectangle 1517 } 1518 1519 // Switch back to a fast blit if we were previously stretching 1520 if (src->map.info.flags & SDL_COPY_NEAREST) { 1521 src->map.info.flags &= ~SDL_COPY_NEAREST; 1522 SDL_InvalidateMap(&src->map); 1523 } 1524 1525 int tile_width = (int)SDL_roundf(r_src.w * scale); 1526 int tile_height = (int)SDL_roundf(r_src.h * scale); 1527 if (tile_width <= 0 || tile_height <= 0) { 1528 // Nothing to do 1529 return true; 1530 } 1531 int rows = r_dst.h / tile_height; 1532 int cols = r_dst.w / tile_width; 1533 int remaining_dst_w = (r_dst.w - cols * tile_width); 1534 int remaining_dst_h = (r_dst.h - rows * tile_height); 1535 int remaining_src_w = (int)(remaining_dst_w / scale); 1536 int remaining_src_h = (int)(remaining_dst_h / scale); 1537 SDL_Rect curr_src, curr_dst; 1538 1539 SDL_copyp(&curr_src, &r_src); 1540 curr_dst.y = r_dst.y; 1541 curr_dst.w = tile_width; 1542 curr_dst.h = tile_height; 1543 for (int y = 0; y < rows; ++y) { 1544 curr_dst.x = r_dst.x; 1545 for (int x = 0; x < cols; ++x) { 1546 if (!SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1547 return false; 1548 } 1549 curr_dst.x += curr_dst.w; 1550 } 1551 if (remaining_dst_w > 0) { 1552 curr_src.w = remaining_src_w; 1553 curr_dst.w = remaining_dst_w; 1554 if (!SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1555 return false; 1556 } 1557 curr_src.w = r_src.w; 1558 curr_dst.w = tile_width; 1559 } 1560 curr_dst.y += curr_dst.h; 1561 } 1562 if (remaining_dst_h > 0) { 1563 curr_src.h = remaining_src_h; 1564 curr_dst.h = remaining_dst_h; 1565 curr_dst.x = r_dst.x; 1566 for (int x = 0; x < cols; ++x) { 1567 if (!SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1568 return false; 1569 } 1570 curr_dst.x += curr_dst.w; 1571 } 1572 if (remaining_dst_w > 0) { 1573 curr_src.w = remaining_src_w; 1574 curr_dst.w = remaining_dst_w; 1575 if (!SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1576 return false; 1577 } 1578 } 1579 } 1580 return true; 1581} 1582 1583bool SDL_BlitSurface9Grid(SDL_Surface *src, const SDL_Rect *srcrect, int left_width, int right_width, int top_height, int bottom_height, float scale, SDL_ScaleMode scaleMode, SDL_Surface *dst, const SDL_Rect *dstrect) 1584{ 1585 SDL_Rect full_src, full_dst; 1586 SDL_Rect curr_src, curr_dst; 1587 int dst_left_width; 1588 int dst_right_width; 1589 int dst_top_height; 1590 int dst_bottom_height; 1591 1592 // Make sure the surfaces aren't locked 1593 CHECK_PARAM(!SDL_SurfaceValid(src)) { 1594 return SDL_InvalidParamError("src"); 1595 } 1596 CHECK_PARAM(!SDL_SurfaceValid(dst)) { 1597 return SDL_InvalidParamError("dst"); 1598 } 1599 1600 if (!srcrect) { 1601 full_src.x = 0; 1602 full_src.y = 0; 1603 full_src.w = src->w; 1604 full_src.h = src->h; 1605 srcrect = &full_src; 1606 } 1607 1608 if (!dstrect) { 1609 full_dst.x = 0; 1610 full_dst.y = 0; 1611 full_dst.w = dst->w; 1612 full_dst.h = dst->h; 1613 dstrect = &full_dst; 1614 } 1615 1616 if (scale <= 0.0f || scale == 1.0f) { 1617 dst_left_width = left_width; 1618 dst_right_width = right_width; 1619 dst_top_height = top_height; 1620 dst_bottom_height = bottom_height; 1621 } else { 1622 dst_left_width = (int)SDL_roundf(left_width * scale); 1623 dst_right_width = (int)SDL_roundf(right_width * scale); 1624 dst_top_height = (int)SDL_roundf(top_height * scale); 1625 dst_bottom_height = (int)SDL_roundf(bottom_height * scale); 1626 } 1627 1628 // Upper-left corner 1629 curr_src.x = srcrect->x; 1630 curr_src.y = srcrect->y; 1631 curr_src.w = left_width; 1632 curr_src.h = top_height; 1633 curr_dst.x = dstrect->x; 1634 curr_dst.y = dstrect->y; 1635 curr_dst.w = dst_left_width; 1636 curr_dst.h = dst_top_height; 1637 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1638 return false; 1639 } 1640 1641 // Upper-right corner 1642 curr_src.x = srcrect->x + srcrect->w - right_width; 1643 curr_src.w = right_width; 1644 curr_dst.x = dstrect->x + dstrect->w - dst_right_width; 1645 curr_dst.w = dst_right_width; 1646 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1647 return false; 1648 } 1649 1650 // Lower-right corner 1651 curr_src.y = srcrect->y + srcrect->h - bottom_height; 1652 curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height; 1653 curr_dst.h = dst_bottom_height; 1654 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1655 return false; 1656 } 1657 1658 // Lower-left corner 1659 curr_src.x = srcrect->x; 1660 curr_src.w = left_width; 1661 curr_dst.x = dstrect->x; 1662 curr_dst.w = dst_left_width; 1663 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1664 return false; 1665 } 1666 1667 // Left 1668 curr_src.y = srcrect->y + top_height; 1669 curr_src.h = srcrect->h - top_height - bottom_height; 1670 curr_dst.y = dstrect->y + dst_top_height; 1671 curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; 1672 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1673 return false; 1674 } 1675 1676 // Right 1677 curr_src.x = srcrect->x + srcrect->w - right_width; 1678 curr_src.w = right_width; 1679 curr_dst.x = dstrect->x + dstrect->w - dst_right_width; 1680 curr_dst.w = dst_right_width; 1681 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1682 return false; 1683 } 1684 1685 // Top 1686 curr_src.x = srcrect->x + left_width; 1687 curr_src.y = srcrect->y; 1688 curr_src.w = srcrect->w - left_width - right_width; 1689 curr_src.h = top_height; 1690 curr_dst.x = dstrect->x + dst_left_width; 1691 curr_dst.y = dstrect->y; 1692 curr_dst.w = dstrect->w - dst_left_width - dst_right_width; 1693 curr_dst.h = dst_top_height; 1694 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1695 return false; 1696 } 1697 1698 // Bottom 1699 curr_src.y = srcrect->y + srcrect->h - bottom_height; 1700 curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height; 1701 curr_dst.h = dst_bottom_height; 1702 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1703 return false; 1704 } 1705 1706 // Center 1707 curr_src.x = srcrect->x + left_width; 1708 curr_src.y = srcrect->y + top_height; 1709 curr_src.w = srcrect->w - left_width - right_width; 1710 curr_src.h = srcrect->h - top_height - bottom_height; 1711 curr_dst.x = dstrect->x + dst_left_width; 1712 curr_dst.y = dstrect->y + dst_top_height; 1713 curr_dst.w = dstrect->w - dst_left_width - dst_right_width; 1714 curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; 1715 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { 1716 return false; 1717 } 1718 1719 return true; 1720} 1721 1722/* 1723 * Lock a surface to directly access the pixels 1724 */ 1725bool SDL_LockSurface(SDL_Surface *surface) 1726{ 1727 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 1728 return SDL_InvalidParamError("surface"); 1729 } 1730 1731 if (!surface->locked) { 1732#ifdef SDL_HAVE_RLE 1733 // Perform the lock 1734 if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { 1735 SDL_UnRLESurface(surface); 1736 } 1737#endif 1738 } 1739 1740 // Increment the surface lock count, for recursive locks 1741 ++surface->locked; 1742 surface->flags |= SDL_SURFACE_LOCKED; 1743 SDL_UpdateSurfaceLockFlag(surface); 1744 1745 // Ready to go.. 1746 return true; 1747} 1748 1749/* 1750 * Unlock a previously locked surface 1751 */ 1752void SDL_UnlockSurface(SDL_Surface *surface) 1753{ 1754 if (!SDL_SurfaceValid(surface)) { 1755 return; 1756 } 1757 1758 // Only perform an unlock if we are locked 1759 if (!surface->locked || (--surface->locked > 0)) { 1760 return; 1761 } 1762 1763 surface->flags &= ~SDL_SURFACE_LOCKED; 1764 SDL_UpdateSurfaceLockFlag(surface); 1765} 1766 1767static bool SDL_FlipSurfaceHorizontal(SDL_Surface *surface) 1768{ 1769 bool isstack; 1770 Uint8 *row, *a, *b, *tmp; 1771 int i, j, bpp; 1772 1773 if (SDL_BITSPERPIXEL(surface->format) < 8) { 1774 // We could implement this if needed, but we'd have to flip sets of bits within a byte 1775 return SDL_Unsupported(); 1776 } 1777 1778 if (surface->h <= 0) { 1779 return true; 1780 } 1781 1782 if (surface->w <= 1) { 1783 return true; 1784 } 1785 1786 bpp = SDL_BYTESPERPIXEL(surface->format); 1787 row = (Uint8 *)surface->pixels; 1788 tmp = SDL_small_alloc(Uint8, surface->pitch, &isstack); 1789 if (!tmp) { 1790 return false; 1791 } 1792 for (i = surface->h; i--; ) { 1793 a = row; 1794 b = a + (surface->w - 1) * bpp; 1795 for (j = surface->w / 2; j--; ) { 1796 SDL_memcpy(tmp, a, bpp); 1797 SDL_memcpy(a, b, bpp); 1798 SDL_memcpy(b, tmp, bpp); 1799 a += bpp; 1800 b -= bpp; 1801 } 1802 row += surface->pitch; 1803 } 1804 SDL_small_free(tmp, isstack); 1805 return true; 1806} 1807 1808static bool SDL_FlipSurfaceVertical(SDL_Surface *surface) 1809{ 1810 bool isstack; 1811 Uint8 *a, *b, *tmp; 1812 int i; 1813 1814 if (surface->h <= 1) { 1815 return true; 1816 } 1817 1818 a = (Uint8 *)surface->pixels; 1819 b = a + (surface->h - 1) * surface->pitch; 1820 tmp = SDL_small_alloc(Uint8, surface->pitch, &isstack); 1821 if (!tmp) { 1822 return false; 1823 } 1824 for (i = surface->h / 2; i--; ) { 1825 SDL_memcpy(tmp, a, surface->pitch); 1826 SDL_memcpy(a, b, surface->pitch); 1827 SDL_memcpy(b, tmp, surface->pitch); 1828 a += surface->pitch; 1829 b -= surface->pitch; 1830 } 1831 SDL_small_free(tmp, isstack); 1832 return true; 1833} 1834 1835bool SDL_FlipSurface(SDL_Surface *surface, SDL_FlipMode flip) 1836{ 1837 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 1838 return SDL_InvalidParamError("surface"); 1839 } 1840 if (!surface->pixels) { 1841 return true; 1842 } 1843 1844 bool result = true; 1845 switch (flip) { 1846 case SDL_FLIP_HORIZONTAL: 1847 result = SDL_FlipSurfaceHorizontal(surface); 1848 break; 1849 case SDL_FLIP_VERTICAL: 1850 result = SDL_FlipSurfaceVertical(surface); 1851 break; 1852 case SDL_FLIP_HORIZONTAL_AND_VERTICAL: 1853 result &= SDL_FlipSurfaceHorizontal(surface); 1854 result &= SDL_FlipSurfaceVertical(surface); 1855 break; 1856 default: 1857 result = SDL_InvalidParamError("flip"); 1858 break; 1859 } 1860 return result; 1861} 1862 1863static SDL_Surface *SDL_ConvertSurfaceRectAndColorspace(SDL_Surface *surface, const SDL_Rect *rect, SDL_PixelFormat format, SDL_Palette *palette, SDL_Colorspace colorspace, SDL_PropertiesID props) 1864{ 1865 SDL_Palette *temp_palette = NULL; 1866 SDL_Surface *convert = NULL; 1867 SDL_Colorspace src_colorspace; 1868 SDL_PropertiesID src_properties; 1869 Uint32 copy_flags; 1870 SDL_Color copy_color; 1871 SDL_Rect bounds; 1872 bool result; 1873 bool palette_ck_transform = false; 1874 Uint8 palette_ck_value = 0; 1875 Uint8 *palette_saved_alpha = NULL; 1876 int palette_saved_alpha_ncolors = 0; 1877 1878 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 1879 SDL_InvalidParamError("surface"); 1880 goto error; 1881 } 1882 1883 CHECK_PARAM(format == SDL_PIXELFORMAT_UNKNOWN) { 1884 SDL_InvalidParamError("format"); 1885 goto error; 1886 } 1887 1888 // Set the bounds of the new surface 1889 bounds.x = 0; 1890 bounds.y = 0; 1891 bounds.w = surface->w; 1892 bounds.h = surface->h; 1893 if (rect) { 1894 bounds.w = rect->w; 1895 bounds.h = rect->h; 1896 } else { 1897 rect = &bounds; 1898 } 1899 1900 // Check for empty destination palette! (results in empty image) 1901 if (palette) { 1902 int i; 1903 for (i = 0; i < palette->ncolors; ++i) { 1904 if ((palette->colors[i].r != 0xFF) || (palette->colors[i].g != 0xFF) || (palette->colors[i].b != 0xFF)) { 1905 break; 1906 } 1907 } 1908 if (i == palette->ncolors) { 1909 SDL_SetError("Empty destination palette"); 1910 goto error; 1911 } 1912 } else if (SDL_ISPIXELFORMAT_INDEXED(format)) { 1913 // Create a dither palette for conversion 1914 temp_palette = SDL_CreatePalette(1 << SDL_BITSPERPIXEL(format)); 1915 if (temp_palette) { 1916 SDL_DitherPalette(temp_palette); 1917 palette = temp_palette; 1918 } 1919 } 1920 1921 src_colorspace = surface->colorspace; 1922 src_properties = surface->props; 1923 1924 // Create a new surface with the desired format 1925 if (surface->pixels || SDL_MUSTLOCK(surface)) { 1926 convert = SDL_CreateSurface(rect->w, rect->h, format); 1927 } else { 1928 convert = SDL_CreateSurfaceFrom(rect->w, rect->h, format, NULL, 0); 1929 } 1930 if (!convert) { 1931 goto error; 1932 } 1933 if (SDL_ISPIXELFORMAT_INDEXED(format)) { 1934 SDL_SetSurfacePalette(convert, palette); 1935 } 1936 1937 if (colorspace == SDL_COLORSPACE_UNKNOWN) { 1938 colorspace = src_colorspace; 1939 } 1940 SDL_SetSurfaceColorspace(convert, colorspace); 1941 1942 if (SDL_ISPIXELFORMAT_FOURCC(format) || SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 1943 if (surface->format == SDL_PIXELFORMAT_MJPG && format == SDL_PIXELFORMAT_MJPG) { 1944 // Just do a straight pixel copy of the JPEG image 1945 size_t size = (size_t)surface->pitch; 1946 convert->pixels = SDL_malloc(size); 1947 if (!convert->pixels) { 1948 goto error; 1949 } 1950 convert->flags &= ~SDL_SURFACE_PREALLOCATED; 1951 convert->pitch = surface->pitch; 1952 SDL_memcpy(convert->pixels, surface->pixels, size); 1953 1954 } else if (!SDL_ConvertPixelsAndColorspace(surface->w, surface->h, surface->format, src_colorspace, src_properties, surface->pixels, surface->pitch, convert->format, colorspace, props, convert->pixels, convert->pitch)) { 1955 goto error; 1956 } 1957 1958 // Save the original copy flags 1959 copy_flags = surface->map.info.flags; 1960 1961 goto end; 1962 } 1963 1964 // Save the original copy flags 1965 copy_flags = surface->map.info.flags; 1966 copy_color.r = surface->map.info.r; 1967 copy_color.g = surface->map.info.g; 1968 copy_color.b = surface->map.info.b; 1969 copy_color.a = surface->map.info.a; 1970 surface->map.info.r = 0xFF; 1971 surface->map.info.g = 0xFF; 1972 surface->map.info.b = 0xFF; 1973 surface->map.info.a = 0xFF; 1974 surface->map.info.flags = (copy_flags & (SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY)); 1975 SDL_InvalidateMap(&surface->map); 1976 1977 /* Source surface has a palette with no real alpha (0 or OPAQUE). 1978 * Destination format has alpha. 1979 * -> set alpha channel to be opaque */ 1980 if (surface->palette && SDL_ISPIXELFORMAT_ALPHA(format)) { 1981 bool set_opaque = false; 1982 1983 bool is_opaque, has_alpha_channel; 1984 SDL_DetectPalette(surface->palette, &is_opaque, &has_alpha_channel); 1985 1986 if (is_opaque) { 1987 if (!has_alpha_channel) { 1988 set_opaque = true; 1989 } 1990 } 1991 1992 // Set opaque and backup palette alpha values 1993 if (set_opaque) { 1994 int i; 1995 palette_saved_alpha_ncolors = surface->palette->ncolors; 1996 if (palette_saved_alpha_ncolors > 0) { 1997 palette_saved_alpha = SDL_stack_alloc(Uint8, palette_saved_alpha_ncolors); 1998 for (i = 0; i < palette_saved_alpha_ncolors; i++) { 1999 palette_saved_alpha[i] = surface->palette->colors[i].a; 2000 surface->palette->colors[i].a = SDL_ALPHA_OPAQUE; 2001 } 2002 } 2003 } 2004 } 2005 2006 // Transform colorkey to alpha. for cases where source palette has duplicate values, and colorkey is one of them 2007 if (copy_flags & SDL_COPY_COLORKEY) { 2008 if (surface->palette && !palette) { 2009 palette_ck_transform = true; 2010 palette_ck_value = surface->palette->colors[surface->map.info.colorkey].a; 2011 surface->palette->colors[surface->map.info.colorkey].a = SDL_ALPHA_TRANSPARENT; 2012 } 2013 } 2014 2015 if (surface->pixels || SDL_MUSTLOCK(surface)) { 2016 result = SDL_BlitSurfaceUnchecked(surface, rect, convert, &bounds); 2017 } else { 2018 result = true; 2019 } 2020 2021 // Restore colorkey alpha value 2022 if (palette_ck_transform) { 2023 surface->palette->colors[surface->map.info.colorkey].a = palette_ck_value; 2024 } 2025 2026 // Restore palette alpha values 2027 if (palette_saved_alpha) { 2028 int i; 2029 for (i = 0; i < palette_saved_alpha_ncolors; i++) { 2030 surface->palette->colors[i].a = palette_saved_alpha[i]; 2031 } 2032 SDL_stack_free(palette_saved_alpha); 2033 } 2034 2035 // Clean up the original surface, and update converted surface 2036 convert->map.info.r = copy_color.r; 2037 convert->map.info.g = copy_color.g; 2038 convert->map.info.b = copy_color.b; 2039 convert->map.info.a = copy_color.a; 2040 convert->map.info.flags = 2041 (copy_flags & 2042 ~(SDL_COPY_COLORKEY | SDL_COPY_BLEND | SDL_COPY_RLE_DESIRED | SDL_COPY_RLE_COLORKEY | 2043 SDL_COPY_RLE_ALPHAKEY)); 2044 surface->map.info.r = copy_color.r; 2045 surface->map.info.g = copy_color.g; 2046 surface->map.info.b = copy_color.b; 2047 surface->map.info.a = copy_color.a; 2048 surface->map.info.flags = copy_flags; 2049 SDL_InvalidateMap(&surface->map); 2050 2051 // SDL_BlitSurfaceUnchecked failed, and so the conversion 2052 if (!result) { 2053 goto error; 2054 } 2055 2056 if (copy_flags & SDL_COPY_COLORKEY) { 2057 bool set_colorkey_by_color = false; 2058 bool convert_colorkey = true; 2059 2060 if (surface->palette) { 2061 if (palette && 2062 surface->palette->ncolors <= palette->ncolors && 2063 (SDL_memcmp(surface->palette->colors, palette->colors, 2064 surface->palette->ncolors * sizeof(SDL_Color)) == 0)) { 2065 // The palette is identical, just set the same colorkey 2066 SDL_SetSurfaceColorKey(convert, true, surface->map.info.colorkey); 2067 } else if (!palette) { 2068 if (SDL_ISPIXELFORMAT_ALPHA(format)) { 2069 // No need to add the colorkey, transparency is in the alpha channel 2070 } else { 2071 // Only set the colorkey information 2072 set_colorkey_by_color = true; 2073 convert_colorkey = false; 2074 } 2075 } else { 2076 set_colorkey_by_color = true; 2077 } 2078 } else { 2079 set_colorkey_by_color = true; 2080 } 2081 2082 if (set_colorkey_by_color) { 2083 SDL_Surface *tmp; 2084 SDL_Surface *tmp2; 2085 int converted_colorkey = 0; 2086 2087 // Create a dummy surface to get the colorkey converted 2088 tmp = SDL_CreateSurface(1, 1, surface->format); 2089 if (!tmp) { 2090 goto error; 2091 } 2092 2093 // Share the palette, if any 2094 if (surface->palette) { 2095 SDL_SetSurfacePalette(tmp, surface->palette); 2096 } 2097 2098 SDL_FillSurfaceRect(tmp, NULL, surface->map.info.colorkey); 2099 2100 tmp->map.info.flags &= ~SDL_COPY_COLORKEY; 2101 2102 // Conversion of the colorkey 2103 tmp2 = SDL_ConvertSurfaceAndColorspace(tmp, format, palette, colorspace, props); 2104 if (!tmp2) { 2105 SDL_DestroySurface(tmp); 2106 goto error; 2107 } 2108 2109 // Get the converted colorkey 2110 SDL_memcpy(&converted_colorkey, tmp2->pixels, tmp2->fmt->bytes_per_pixel); 2111 2112 SDL_DestroySurface(tmp); 2113 SDL_DestroySurface(tmp2); 2114 2115 // Set the converted colorkey on the new surface 2116 SDL_SetSurfaceColorKey(convert, true, converted_colorkey); 2117 2118 // This is needed when converting for 3D texture upload 2119 if (convert_colorkey) { 2120 SDL_ConvertColorkeyToAlpha(convert, true); 2121 } 2122 } 2123 } 2124 2125end: 2126 if (temp_palette) { 2127 SDL_DestroyPalette(temp_palette); 2128 } 2129 2130 SDL_SetSurfaceClipRect(convert, &surface->clip_rect); 2131 2132 /* Enable alpha blending by default if the new surface has an 2133 * alpha channel or alpha modulation */ 2134 if (SDL_ISPIXELFORMAT_ALPHA(format) || 2135 (copy_flags & SDL_COPY_MODULATE_ALPHA)) { 2136 SDL_SetSurfaceBlendMode(convert, SDL_BLENDMODE_BLEND); 2137 } 2138 if (copy_flags & SDL_COPY_RLE_DESIRED) { 2139 SDL_SetSurfaceRLE(convert, true); 2140 } 2141 2142 // Copy alternate images 2143 for (int i = 0; i < surface->num_images; ++i) { 2144 if (!SDL_AddSurfaceAlternateImage(convert, surface->images[i])) { 2145 goto error; 2146 } 2147 } 2148 2149 // Copy properties 2150 if (surface->props) { 2151 if (!SDL_CopyProperties(surface->props, SDL_GetSurfaceProperties(convert))) { 2152 goto error; 2153 } 2154 2155 // Make sure the new surface doesn't reference an old SDL2 surface. 2156 SDL_ClearProperty(SDL_GetSurfaceProperties(convert), "sdl2-compat.surface2"); 2157 } 2158 2159 // We're ready to go! 2160 return convert; 2161 2162error: 2163 if (temp_palette) { 2164 SDL_DestroyPalette(temp_palette); 2165 } 2166 if (convert) { 2167 SDL_DestroySurface(convert); 2168 } 2169 return NULL; 2170} 2171 2172SDL_Surface *SDL_ConvertSurfaceAndColorspace(SDL_Surface *surface, SDL_PixelFormat format, SDL_Palette *palette, SDL_Colorspace colorspace, SDL_PropertiesID props) 2173{ 2174 return SDL_ConvertSurfaceRectAndColorspace(surface, NULL, format, palette, colorspace, props); 2175} 2176 2177SDL_Surface *SDL_RotateSurface(SDL_Surface *surface, float angle) 2178{ 2179 SDL_Surface *rotated = NULL; 2180 2181 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 2182 SDL_InvalidParamError("surface"); 2183 return NULL; 2184 } 2185 2186 SDL_Rect rect_dest; 2187 double cangle, sangle; 2188 SDL_FPoint center = { surface->w * 0.5f, surface->h * 0.5f }; 2189 SDLgfx_rotozoomSurfaceSizeTrig(surface->w, surface->h, angle, ¢er, &rect_dest, &cangle, &sangle); 2190 2191 // This function requires a 32-bit surface or 8-bit surface with a colorkey 2192 if ((SDL_BITSPERPIXEL(surface->format) == 32 && SDL_PIXELLAYOUT(surface->format) == SDL_PACKEDLAYOUT_8888) || 2193 (surface->format == SDL_PIXELFORMAT_INDEX8 && SDL_SurfaceHasColorKey(surface))) { 2194 rotated = SDLgfx_rotateSurface(surface, angle, 1, 0, 0, &rect_dest, cangle, sangle, ¢er); 2195 } else { 2196 SDL_Surface *convert = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32); 2197 if (convert) { 2198 SDL_Surface *tmp = SDLgfx_rotateSurface(convert, angle, 1, 0, 0, &rect_dest, cangle, sangle, ¢er); 2199 if (tmp) { 2200 rotated = SDL_ConvertSurfaceAndColorspace(tmp, surface->format, surface->palette, surface->colorspace, surface->props); 2201 SDL_DestroySurface(tmp); 2202 } 2203 SDL_DestroySurface(convert); 2204 } 2205 } 2206 2207 if (rotated) { 2208 if (SDL_HasProperty(surface->props, SDL_PROP_SURFACE_ROTATION_FLOAT)) { 2209 const float rotation = (SDL_GetNumberProperty(surface->props, SDL_PROP_SURFACE_ROTATION_FLOAT, 0) - angle); 2210 SDL_SetFloatProperty(SDL_GetSurfaceProperties(rotated), SDL_PROP_SURFACE_ROTATION_FLOAT, rotation); 2211 } 2212 } 2213 2214 return rotated; 2215} 2216 2217SDL_Surface *SDL_DuplicateSurface(SDL_Surface *surface) 2218{ 2219 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 2220 SDL_InvalidParamError("surface"); 2221 return NULL; 2222 } 2223 2224 return SDL_ConvertSurfaceAndColorspace(surface, surface->format, surface->palette, surface->colorspace, surface->props); 2225} 2226 2227SDL_Surface *SDL_ScaleSurface(SDL_Surface *surface, int width, int height, SDL_ScaleMode scaleMode) 2228{ 2229 SDL_Surface *convert = NULL; 2230 Uint32 copy_flags; 2231 SDL_Color copy_color; 2232 bool rc; 2233 2234 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 2235 SDL_InvalidParamError("surface"); 2236 goto error; 2237 } 2238 2239 if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2240 // We can't directly scale a YUV surface (yet!) 2241 SDL_Surface *tmp = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); 2242 if (!tmp) { 2243 return NULL; 2244 } 2245 2246 SDL_Surface *scaled = SDL_ScaleSurface(tmp, width, height, scaleMode); 2247 SDL_DestroySurface(tmp); 2248 if (!scaled) { 2249 return NULL; 2250 } 2251 2252 SDL_Surface *result = SDL_ConvertSurfaceAndColorspace(scaled, surface->format, NULL, surface->colorspace, surface->props); 2253 SDL_DestroySurface(scaled); 2254 return result; 2255 } 2256 2257 if (SDL_ISPIXELFORMAT_INDEXED(surface->format)) { 2258 // Linear scaling requires conversion to RGBA and then slow pixel color lookup 2259 scaleMode = SDL_SCALEMODE_NEAREST; 2260 } 2261 2262 // Create a new surface with the desired size 2263 if (surface->pixels || SDL_MUSTLOCK(surface)) { 2264 convert = SDL_CreateSurface(width, height, surface->format); 2265 } else { 2266 convert = SDL_CreateSurfaceFrom(width, height, surface->format, NULL, 0); 2267 } 2268 if (!convert) { 2269 goto error; 2270 } 2271 SDL_SetSurfacePalette(convert, surface->palette); 2272 SDL_SetSurfaceColorspace(convert, surface->colorspace); 2273 SDL_SetSurfaceRLE(convert, SDL_SurfaceHasRLE(surface)); 2274 2275 if (surface->pixels || SDL_MUSTLOCK(surface)) { 2276 // Save the original copy flags 2277 copy_flags = surface->map.info.flags; 2278 copy_color.r = surface->map.info.r; 2279 copy_color.g = surface->map.info.g; 2280 copy_color.b = surface->map.info.b; 2281 copy_color.a = surface->map.info.a; 2282 surface->map.info.r = 0xFF; 2283 surface->map.info.g = 0xFF; 2284 surface->map.info.b = 0xFF; 2285 surface->map.info.a = 0xFF; 2286 surface->map.info.flags = (copy_flags & (SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY)); 2287 SDL_InvalidateMap(&surface->map); 2288 2289 rc = SDL_BlitSurfaceScaled(surface, NULL, convert, NULL, scaleMode); 2290 2291 // Clean up the original surface, and update converted surface 2292 convert->map.info.r = copy_color.r; 2293 convert->map.info.g = copy_color.g; 2294 convert->map.info.b = copy_color.b; 2295 convert->map.info.a = copy_color.a; 2296 convert->map.info.flags = (copy_flags & ~(SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY)); 2297 surface->map.info.r = copy_color.r; 2298 surface->map.info.g = copy_color.g; 2299 surface->map.info.b = copy_color.b; 2300 surface->map.info.a = copy_color.a; 2301 surface->map.info.flags = copy_flags; 2302 SDL_InvalidateMap(&surface->map); 2303 } else { 2304 rc = true; 2305 } 2306 2307 // SDL_BlitSurfaceScaled failed, and so the conversion 2308 if (!rc) { 2309 goto error; 2310 } 2311 2312 // We're ready to go! 2313 return convert; 2314 2315error: 2316 if (convert) { 2317 SDL_DestroySurface(convert); 2318 } 2319 return NULL; 2320} 2321 2322SDL_Surface *SDL_ConvertSurfaceRect(SDL_Surface *surface, const SDL_Rect *rect, SDL_PixelFormat format) 2323{ 2324 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 2325 SDL_InvalidParamError("surface"); 2326 return NULL; 2327 } 2328 2329 return SDL_ConvertSurfaceRectAndColorspace(surface, rect, format, NULL, SDL_GetDefaultColorspaceForFormat(format), surface->props); 2330} 2331 2332SDL_Surface *SDL_ConvertSurface(SDL_Surface *surface, SDL_PixelFormat format) 2333{ 2334 return SDL_ConvertSurfaceRect(surface, NULL, format); 2335} 2336 2337SDL_Surface *SDL_DuplicatePixels(int width, int height, SDL_PixelFormat format, SDL_Colorspace colorspace, void *pixels, int pitch) 2338{ 2339 SDL_Surface *surface; 2340 if (pixels) { 2341 surface = SDL_CreateSurface(width, height, format); 2342 } else { 2343 surface = SDL_CreateSurfaceFrom(width, height, format, NULL, 0); 2344 } 2345 if (!surface) { 2346 return NULL; 2347 } 2348 SDL_SetSurfaceColorspace(surface, colorspace); 2349 2350 if (surface->pixels) { 2351 int length = width * SDL_BYTESPERPIXEL(format); 2352 Uint8 *src = (Uint8 *)pixels; 2353 Uint8 *dst = (Uint8 *)surface->pixels; 2354 int rows = height; 2355 while (rows--) { 2356 SDL_memcpy(dst, src, length); 2357 dst += surface->pitch; 2358 src += pitch; 2359 } 2360 } 2361 return surface; 2362} 2363 2364bool SDL_ConvertPixelsAndColorspace(int width, int height, 2365 SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, 2366 SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch) 2367{ 2368 SDL_Surface src_surface; 2369 SDL_Surface dst_surface; 2370 SDL_Rect rect; 2371 void *nonconst_src = (void *)src; 2372 bool result; 2373 2374 CHECK_PARAM(!src) { 2375 return SDL_InvalidParamError("src"); 2376 } 2377 CHECK_PARAM(!src_pitch) { 2378 return SDL_InvalidParamError("src_pitch"); 2379 } 2380 CHECK_PARAM(!dst) { 2381 return SDL_InvalidParamError("dst"); 2382 } 2383 CHECK_PARAM(!dst_pitch) { 2384 return SDL_InvalidParamError("dst_pitch"); 2385 } 2386 2387 if (src_colorspace == SDL_COLORSPACE_UNKNOWN) { 2388 src_colorspace = SDL_GetDefaultColorspaceForFormat(src_format); 2389 } 2390 if (dst_colorspace == SDL_COLORSPACE_UNKNOWN) { 2391 dst_colorspace = SDL_GetDefaultColorspaceForFormat(dst_format); 2392 } 2393 2394 if (src_format == SDL_PIXELFORMAT_MJPG) { 2395 return SDL_ConvertPixels_STB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); 2396 } 2397 2398#ifdef SDL_HAVE_YUV 2399 if (SDL_ISPIXELFORMAT_FOURCC(src_format) && SDL_ISPIXELFORMAT_FOURCC(dst_format)) { 2400 return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); 2401 } else if (SDL_ISPIXELFORMAT_FOURCC(src_format)) { 2402 return SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); 2403 } else if (SDL_ISPIXELFORMAT_FOURCC(dst_format)) { 2404 return SDL_ConvertPixels_RGB_to_YUV(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); 2405 } 2406#else 2407 if (SDL_ISPIXELFORMAT_FOURCC(src_format) || SDL_ISPIXELFORMAT_FOURCC(dst_format)) { 2408 return SDL_SetError("SDL not built with YUV support"); 2409 } 2410#endif 2411 2412 // Fast path for same format copy 2413 if (src_format == dst_format && src_colorspace == dst_colorspace) { 2414 if (src_pitch == dst_pitch) { 2415 SDL_memcpy(dst, src, height * src_pitch); 2416 } else { 2417 int i; 2418 const int bpp = SDL_BYTESPERPIXEL(src_format); 2419 width *= bpp; 2420 for (i = height; i--;) { 2421 SDL_memcpy(dst, src, width); 2422 src = (const Uint8 *)src + src_pitch; 2423 dst = (Uint8 *)dst + dst_pitch; 2424 } 2425 } 2426 return true; 2427 } 2428 2429 if (!SDL_InitializeSurface(&src_surface, width, height, src_format, src_colorspace, src_properties, nonconst_src, src_pitch, true)) { 2430 return false; 2431 } 2432 SDL_SetSurfaceBlendMode(&src_surface, SDL_BLENDMODE_NONE); 2433 2434 if (!SDL_InitializeSurface(&dst_surface, width, height, dst_format, dst_colorspace, dst_properties, dst, dst_pitch, true)) { 2435 return false; 2436 } 2437 2438 // Set up the rect and go! 2439 rect.x = 0; 2440 rect.y = 0; 2441 rect.w = width; 2442 rect.h = height; 2443 result = SDL_BlitSurfaceUnchecked(&src_surface, &rect, &dst_surface, &rect); 2444 2445 SDL_DestroySurface(&src_surface); 2446 SDL_DestroySurface(&dst_surface); 2447 2448 return result; 2449} 2450 2451bool SDL_ConvertPixels(int width, int height, SDL_PixelFormat src_format, const void *src, int src_pitch, SDL_PixelFormat dst_format, void *dst, int dst_pitch) 2452{ 2453 return SDL_ConvertPixelsAndColorspace(width, height, 2454 src_format, SDL_COLORSPACE_UNKNOWN, 0, src, src_pitch, 2455 dst_format, SDL_COLORSPACE_UNKNOWN, 0, dst, dst_pitch); 2456} 2457 2458/* 2459 * Premultiply the alpha on a block of pixels 2460 * 2461 * Here are some ideas for optimization: 2462 * https://github.com/Wizermil/premultiply_alpha/tree/master/premultiply_alpha 2463 * https://developer.arm.com/documentation/101964/0201/Pre-multiplied-alpha-channel-data 2464 */ 2465 2466static void SDL_PremultiplyAlpha_AXYZ8888(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) 2467{ 2468 int c; 2469 Uint32 srcpixel; 2470 Uint32 srcR, srcG, srcB, srcA; 2471 Uint32 dstpixel; 2472 Uint32 dstR, dstG, dstB, dstA; 2473 2474 while (height--) { 2475 const Uint32 *src_px = (const Uint32 *)src; 2476 Uint32 *dst_px = (Uint32 *)dst; 2477 for (c = width; c; --c) { 2478 // Component bytes extraction. 2479 srcpixel = *src_px++; 2480 RGBA_FROM_ARGB8888(srcpixel, srcR, srcG, srcB, srcA); 2481 2482 // Alpha pre-multiplication of each component. 2483 dstA = srcA; 2484 dstR = (srcA * srcR) / 255; 2485 dstG = (srcA * srcG) / 255; 2486 dstB = (srcA * srcB) / 255; 2487 2488 // ARGB8888 pixel recomposition. 2489 ARGB8888_FROM_RGBA(dstpixel, dstR, dstG, dstB, dstA); 2490 *dst_px++ = dstpixel; 2491 } 2492 src = (const Uint8 *)src + src_pitch; 2493 dst = (Uint8 *)dst + dst_pitch; 2494 } 2495} 2496 2497static void SDL_PremultiplyAlpha_XYZA8888(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) 2498{ 2499 int c; 2500 Uint32 srcpixel; 2501 Uint32 srcR, srcG, srcB, srcA; 2502 Uint32 dstpixel; 2503 Uint32 dstR, dstG, dstB, dstA; 2504 2505 while (height--) { 2506 const Uint32 *src_px = (const Uint32 *)src; 2507 Uint32 *dst_px = (Uint32 *)dst; 2508 for (c = width; c; --c) { 2509 // Component bytes extraction. 2510 srcpixel = *src_px++; 2511 RGBA_FROM_RGBA8888(srcpixel, srcR, srcG, srcB, srcA); 2512 2513 // Alpha pre-multiplication of each component. 2514 dstA = srcA; 2515 dstR = (srcA * srcR) / 255; 2516 dstG = (srcA * srcG) / 255; 2517 dstB = (srcA * srcB) / 255; 2518 2519 // RGBA8888 pixel recomposition. 2520 RGBA8888_FROM_RGBA(dstpixel, dstR, dstG, dstB, dstA); 2521 *dst_px++ = dstpixel; 2522 } 2523 src = (const Uint8 *)src + src_pitch; 2524 dst = (Uint8 *)dst + dst_pitch; 2525 } 2526} 2527 2528static void SDL_PremultiplyAlpha_AXYZ128(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) 2529{ 2530 int c; 2531 float flR, flG, flB, flA; 2532 2533 while (height--) { 2534 const float *src_px = (const float *)src; 2535 float *dst_px = (float *)dst; 2536 for (c = width; c; --c) { 2537 flA = *src_px++; 2538 flR = *src_px++; 2539 flG = *src_px++; 2540 flB = *src_px++; 2541 2542 // Alpha pre-multiplication of each component. 2543 flR *= flA; 2544 flG *= flA; 2545 flB *= flA; 2546 2547 *dst_px++ = flA; 2548 *dst_px++ = flR; 2549 *dst_px++ = flG; 2550 *dst_px++ = flB; 2551 } 2552 src = (const Uint8 *)src + src_pitch; 2553 dst = (Uint8 *)dst + dst_pitch; 2554 } 2555} 2556 2557static bool SDL_PremultiplyAlphaPixelsAndColorspace(int width, int height, SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch, bool linear) 2558{ 2559 SDL_Surface *convert = NULL; 2560 void *final_dst = dst; 2561 int final_dst_pitch = dst_pitch; 2562 SDL_PixelFormat format; 2563 SDL_Colorspace colorspace; 2564 bool result = false; 2565 2566 CHECK_PARAM(!src) { 2567 return SDL_InvalidParamError("src"); 2568 } 2569 CHECK_PARAM(!src_pitch) { 2570 return SDL_InvalidParamError("src_pitch"); 2571 } 2572 CHECK_PARAM(!dst) { 2573 return SDL_InvalidParamError("dst"); 2574 } 2575 CHECK_PARAM(!dst_pitch) { 2576 return SDL_InvalidParamError("dst_pitch"); 2577 } 2578 2579 // Use a high precision format if we're converting to linear colorspace or using high precision pixel formats 2580 if (linear || 2581 SDL_ISPIXELFORMAT_10BIT(src_format) || SDL_BITSPERPIXEL(src_format) > 32 || 2582 SDL_ISPIXELFORMAT_10BIT(dst_format) || SDL_BITSPERPIXEL(dst_format) > 32) { 2583 if (src_format == SDL_PIXELFORMAT_ARGB128_FLOAT || 2584 src_format == SDL_PIXELFORMAT_ABGR128_FLOAT) { 2585 format = src_format; 2586 } else { 2587 format = SDL_PIXELFORMAT_ARGB128_FLOAT; 2588 } 2589 } else { 2590 if (src_format == SDL_PIXELFORMAT_ARGB8888 || 2591 src_format == SDL_PIXELFORMAT_ABGR8888 || 2592 src_format == SDL_PIXELFORMAT_RGBA8888 || 2593 src_format == SDL_PIXELFORMAT_BGRA8888) { 2594 format = src_format; 2595 } else { 2596 format = SDL_PIXELFORMAT_ARGB8888; 2597 } 2598 } 2599 if (linear) { 2600 colorspace = SDL_COLORSPACE_SRGB_LINEAR; 2601 } else { 2602 colorspace = SDL_COLORSPACE_SRGB; 2603 } 2604 2605 if (src_format != format || src_colorspace != colorspace) { 2606 convert = SDL_CreateSurface(width, height, format); 2607 if (!convert) { 2608 goto done; 2609 } 2610 if (!SDL_ConvertPixelsAndColorspace(width, height, src_format, src_colorspace, src_properties, src, src_pitch, format, colorspace, 0, convert->pixels, convert->pitch)) { 2611 goto done; 2612 } 2613 2614 src = convert->pixels; 2615 src_pitch = convert->pitch; 2616 dst = convert->pixels; 2617 dst_pitch = convert->pitch; 2618 2619 } else if (dst_format != format || dst_colorspace != colorspace) { 2620 convert = SDL_CreateSurface(width, height, format); 2621 if (!convert) { 2622 goto done; 2623 } 2624 dst = convert->pixels; 2625 dst_pitch = convert->pitch; 2626 } 2627 2628 switch (format) { 2629 case SDL_PIXELFORMAT_ARGB8888: 2630 case SDL_PIXELFORMAT_ABGR8888: 2631 SDL_PremultiplyAlpha_AXYZ8888(width, height, src, src_pitch, dst, dst_pitch); 2632 break; 2633 case SDL_PIXELFORMAT_RGBA8888: 2634 case SDL_PIXELFORMAT_BGRA8888: 2635 SDL_PremultiplyAlpha_XYZA8888(width, height, src, src_pitch, dst, dst_pitch); 2636 break; 2637 case SDL_PIXELFORMAT_ARGB128_FLOAT: 2638 case SDL_PIXELFORMAT_ABGR128_FLOAT: 2639 SDL_PremultiplyAlpha_AXYZ128(width, height, src, src_pitch, dst, dst_pitch); 2640 break; 2641 default: 2642 SDL_SetError("Unexpected internal pixel format"); 2643 goto done; 2644 } 2645 2646 if (dst != final_dst) { 2647 if (!SDL_ConvertPixelsAndColorspace(width, height, format, colorspace, 0, convert->pixels, convert->pitch, dst_format, dst_colorspace, dst_properties, final_dst, final_dst_pitch)) { 2648 goto done; 2649 } 2650 } 2651 result = true; 2652 2653done: 2654 if (convert) { 2655 SDL_DestroySurface(convert); 2656 } 2657 return result; 2658} 2659 2660bool SDL_PremultiplyAlpha(int width, int height, 2661 SDL_PixelFormat src_format, const void *src, int src_pitch, 2662 SDL_PixelFormat dst_format, void *dst, int dst_pitch, bool linear) 2663{ 2664 SDL_Colorspace src_colorspace = SDL_GetDefaultColorspaceForFormat(src_format); 2665 SDL_Colorspace dst_colorspace = SDL_GetDefaultColorspaceForFormat(dst_format); 2666 2667 return SDL_PremultiplyAlphaPixelsAndColorspace(width, height, src_format, src_colorspace, 0, src, src_pitch, dst_format, dst_colorspace, 0, dst, dst_pitch, linear); 2668} 2669 2670bool SDL_PremultiplySurfaceAlpha(SDL_Surface *surface, bool linear) 2671{ 2672 SDL_Colorspace colorspace; 2673 2674 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 2675 return SDL_InvalidParamError("surface"); 2676 } 2677 2678 colorspace = surface->colorspace; 2679 2680 return SDL_PremultiplyAlphaPixelsAndColorspace(surface->w, surface->h, surface->format, colorspace, surface->props, surface->pixels, surface->pitch, surface->format, colorspace, surface->props, surface->pixels, surface->pitch, linear); 2681} 2682 2683bool SDL_ClearSurface(SDL_Surface *surface, float r, float g, float b, float a) 2684{ 2685 SDL_Rect clip_rect; 2686 bool result = false; 2687 2688 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 2689 return SDL_InvalidParamError("surface"); 2690 } 2691 2692 SDL_GetSurfaceClipRect(surface, &clip_rect); 2693 SDL_SetSurfaceClipRect(surface, NULL); 2694 2695 if (!SDL_ISPIXELFORMAT_FOURCC(surface->format) && 2696 SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32)) { 2697 Uint32 color; 2698 2699 color = SDL_MapSurfaceRGBA(surface, 2700 (Uint8)SDL_roundf(SDL_clamp(r, 0.0f, 1.0f) * 255.0f), 2701 (Uint8)SDL_roundf(SDL_clamp(g, 0.0f, 1.0f) * 255.0f), 2702 (Uint8)SDL_roundf(SDL_clamp(b, 0.0f, 1.0f) * 255.0f), 2703 (Uint8)SDL_roundf(SDL_clamp(a, 0.0f, 1.0f) * 255.0f)); 2704 result = SDL_FillSurfaceRect(surface, NULL, color); 2705 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2706 // We can't directly set an RGB value on a YUV surface 2707 SDL_Surface *tmp = SDL_CreateSurface(surface->w, surface->h, SDL_PIXELFORMAT_ARGB8888); 2708 if (!tmp) { 2709 goto done; 2710 } 2711 2712 if (SDL_ClearSurface(tmp, r, g, b, a)) { 2713 result = SDL_ConvertPixelsAndColorspace(surface->w, surface->h, tmp->format, tmp->colorspace, tmp->props, tmp->pixels, tmp->pitch, surface->format, surface->colorspace, surface->props, surface->pixels, surface->pitch); 2714 } 2715 SDL_DestroySurface(tmp); 2716 } else { 2717 // Take advantage of blit color conversion 2718 SDL_Surface *tmp = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_RGBA128_FLOAT); 2719 if (!tmp) { 2720 goto done; 2721 } 2722 SDL_SetSurfaceColorspace(tmp, surface->colorspace); 2723 SDL_SetSurfaceBlendMode(tmp, SDL_BLENDMODE_NONE); 2724 2725 float *pixels = (float *)tmp->pixels; 2726 pixels[0] = r; 2727 pixels[1] = g; 2728 pixels[2] = b; 2729 pixels[3] = a; 2730 2731 result = SDL_BlitSurfaceScaled(tmp, NULL, surface, NULL, SDL_SCALEMODE_NEAREST); 2732 SDL_DestroySurface(tmp); 2733 } 2734 2735done: 2736 SDL_SetSurfaceClipRect(surface, &clip_rect); 2737 2738 return result; 2739} 2740 2741Uint32 SDL_MapSurfaceRGB(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b) 2742{ 2743 return SDL_MapSurfaceRGBA(surface, r, g, b, SDL_ALPHA_OPAQUE); 2744} 2745 2746Uint32 SDL_MapSurfaceRGBA(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b, Uint8 a) 2747{ 2748 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 2749 SDL_InvalidParamError("surface"); 2750 return true; 2751 } 2752 return SDL_MapRGBA(surface->fmt, surface->palette, r, g, b, a); 2753} 2754 2755// This function Copyright 2023 Collabora Ltd., contributed to SDL under the ZLib license 2756bool SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a) 2757{ 2758 Uint32 pixel = 0; 2759 size_t bytes_per_pixel; 2760 Uint8 unused; 2761 Uint8 *p; 2762 bool result = false; 2763 2764 if (r) { 2765 *r = 0; 2766 } else { 2767 r = &unused; 2768 } 2769 2770 if (g) { 2771 *g = 0; 2772 } else { 2773 g = &unused; 2774 } 2775 2776 if (b) { 2777 *b = 0; 2778 } else { 2779 b = &unused; 2780 } 2781 2782 if (a) { 2783 *a = 0; 2784 } else { 2785 a = &unused; 2786 } 2787 2788 CHECK_PARAM(!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { 2789 return SDL_InvalidParamError("surface"); 2790 } 2791 2792 CHECK_PARAM(x < 0 || x >= surface->w) { 2793 return SDL_InvalidParamError("x"); 2794 } 2795 2796 CHECK_PARAM(y < 0 || y >= surface->h) { 2797 return SDL_InvalidParamError("y"); 2798 } 2799 2800 bytes_per_pixel = SDL_BYTESPERPIXEL(surface->format); 2801 2802 if (SDL_MUSTLOCK(surface)) { 2803 if (!SDL_LockSurface(surface)) { 2804 return false; 2805 } 2806 } 2807 2808 p = (Uint8 *)surface->pixels + y * surface->pitch + x * bytes_per_pixel; 2809 2810 if (bytes_per_pixel <= sizeof(pixel) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2811 /* Fill the appropriate number of least-significant bytes of pixel, 2812 * leaving the most-significant bytes set to zero */ 2813#if SDL_BYTEORDER == SDL_BIG_ENDIAN 2814 SDL_memcpy(((Uint8 *)&pixel) + (sizeof(pixel) - bytes_per_pixel), p, bytes_per_pixel); 2815#else 2816 SDL_memcpy(&pixel, p, bytes_per_pixel); 2817#endif 2818 SDL_GetRGBA(pixel, surface->fmt, surface->palette, r, g, b, a); 2819 result = true; 2820 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2821 // FIXME: We need code to extract a single macroblock from a YUV surface 2822 SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); 2823 if (converted) { 2824 result = SDL_ReadSurfacePixel(converted, x, y, r, g, b, a); 2825 SDL_DestroySurface(converted); 2826 } 2827 } else { 2828 // This is really slow, but it gets the job done 2829 Uint8 rgba[4]; 2830 2831 if (SDL_ConvertPixelsAndColorspace(1, 1, surface->format, surface->colorspace, surface->props, p, surface->pitch, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, rgba, sizeof(rgba))) { 2832 *r = rgba[0]; 2833 *g = rgba[1]; 2834 *b = rgba[2]; 2835 *a = rgba[3]; 2836 result = true; 2837 } 2838 } 2839 2840 if (SDL_MUSTLOCK(surface)) { 2841 SDL_UnlockSurface(surface); 2842 } 2843 return result; 2844} 2845 2846bool SDL_ReadSurfacePixelFloat(SDL_Surface *surface, int x, int y, float *r, float *g, float *b, float *a) 2847{ 2848 float unused; 2849 bool result = false; 2850 2851 if (r) { 2852 *r = 0.0f; 2853 } else { 2854 r = &unused; 2855 } 2856 2857 if (g) { 2858 *g = 0.0f; 2859 } else { 2860 g = &unused; 2861 } 2862 2863 if (b) { 2864 *b = 0.0f; 2865 } else { 2866 b = &unused; 2867 } 2868 2869 if (a) { 2870 *a = 0.0f; 2871 } else { 2872 a = &unused; 2873 } 2874 2875 CHECK_PARAM(!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { 2876 return SDL_InvalidParamError("surface"); 2877 } 2878 2879 CHECK_PARAM(x < 0 || x >= surface->w) { 2880 return SDL_InvalidParamError("x"); 2881 } 2882 2883 CHECK_PARAM(y < 0 || y >= surface->h) { 2884 return SDL_InvalidParamError("y"); 2885 } 2886 2887 if (SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2888 Uint8 r8, g8, b8, a8; 2889 2890 if (SDL_ReadSurfacePixel(surface, x, y, &r8, &g8, &b8, &a8)) { 2891 *r = (float)r8 / 255.0f; 2892 *g = (float)g8 / 255.0f; 2893 *b = (float)b8 / 255.0f; 2894 *a = (float)a8 / 255.0f; 2895 result = true; 2896 } 2897 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2898 // FIXME: We need code to extract a single macroblock from a YUV surface 2899 SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); 2900 if (converted) { 2901 result = SDL_ReadSurfacePixelFloat(converted, x, y, r, g, b, a); 2902 SDL_DestroySurface(converted); 2903 } 2904 } else { 2905 // This is really slow, but it gets the job done 2906 float rgba[4]; 2907 Uint8 *p; 2908 2909 if (SDL_MUSTLOCK(surface)) { 2910 if (!SDL_LockSurface(surface)) { 2911 return false; 2912 } 2913 } 2914 2915 p = (Uint8 *)surface->pixels + y * surface->pitch + x * SDL_BYTESPERPIXEL(surface->format); 2916 2917 if (surface->format == SDL_PIXELFORMAT_RGBA128_FLOAT) { 2918 SDL_memcpy(rgba, p, sizeof(rgba)); 2919 result = true; 2920 } else { 2921 SDL_Colorspace src_colorspace = surface->colorspace; 2922 SDL_Colorspace dst_colorspace = (src_colorspace == SDL_COLORSPACE_SRGB_LINEAR ? SDL_COLORSPACE_SRGB_LINEAR : SDL_COLORSPACE_SRGB); 2923 2924 if (SDL_ConvertPixelsAndColorspace(1, 1, surface->format, src_colorspace, surface->props, p, surface->pitch, SDL_PIXELFORMAT_RGBA128_FLOAT, dst_colorspace, 0, rgba, sizeof(rgba))) { 2925 result = true; 2926 } 2927 } 2928 2929 if (result) { 2930 *r = rgba[0]; 2931 *g = rgba[1]; 2932 *b = rgba[2]; 2933 *a = rgba[3]; 2934 } 2935 2936 if (SDL_MUSTLOCK(surface)) { 2937 SDL_UnlockSurface(surface); 2938 } 2939 } 2940 return result; 2941} 2942 2943bool SDL_WriteSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) 2944{ 2945 Uint32 pixel = 0; 2946 size_t bytes_per_pixel; 2947 Uint8 *p; 2948 bool result = false; 2949 2950 CHECK_PARAM(!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { 2951 return SDL_InvalidParamError("surface"); 2952 } 2953 2954 CHECK_PARAM(x < 0 || x >= surface->w) { 2955 return SDL_InvalidParamError("x"); 2956 } 2957 2958 CHECK_PARAM(y < 0 || y >= surface->h) { 2959 return SDL_InvalidParamError("y"); 2960 } 2961 2962 bytes_per_pixel = SDL_BYTESPERPIXEL(surface->format); 2963 2964 if (SDL_MUSTLOCK(surface)) { 2965 if (!SDL_LockSurface(surface)) { 2966 return false; 2967 } 2968 } 2969 2970 p = (Uint8 *)surface->pixels + y * surface->pitch + x * bytes_per_pixel; 2971 2972 if (bytes_per_pixel <= sizeof(pixel) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2973 pixel = SDL_MapRGBA(surface->fmt, surface->palette, r, g, b, a); 2974#if SDL_BYTEORDER == SDL_BIG_ENDIAN 2975 SDL_memcpy(p, ((Uint8 *)&pixel) + (sizeof(pixel) - bytes_per_pixel), bytes_per_pixel); 2976#else 2977 SDL_memcpy(p, &pixel, bytes_per_pixel); 2978#endif 2979 result = true; 2980 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2981 result = SDL_Unsupported(); 2982 } else { 2983 // This is really slow, but it gets the job done 2984 Uint8 rgba[4]; 2985 2986 rgba[0] = r; 2987 rgba[1] = g; 2988 rgba[2] = b; 2989 rgba[3] = a; 2990 result = SDL_ConvertPixelsAndColorspace(1, 1, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, rgba, sizeof(rgba), surface->format, surface->colorspace, surface->props, p, surface->pitch); 2991 } 2992 2993 if (SDL_MUSTLOCK(surface)) { 2994 SDL_UnlockSurface(surface); 2995 } 2996 return result; 2997} 2998 2999bool SDL_WriteSurfacePixelFloat(SDL_Surface *surface, int x, int y, float r, float g, float b, float a) 3000{ 3001 bool result = false; 3002 3003 CHECK_PARAM(!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { 3004 return SDL_InvalidParamError("surface"); 3005 } 3006 3007 CHECK_PARAM(x < 0 || x >= surface->w) { 3008 return SDL_InvalidParamError("x"); 3009 } 3010 3011 CHECK_PARAM(y < 0 || y >= surface->h) { 3012 return SDL_InvalidParamError("y"); 3013 } 3014 3015 if (SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 3016 Uint8 r8, g8, b8, a8; 3017 3018 r8 = (Uint8)SDL_round(SDL_clamp(r, 0.0f, 1.0f) * 255.0f); 3019 g8 = (Uint8)SDL_round(SDL_clamp(g, 0.0f, 1.0f) * 255.0f); 3020 b8 = (Uint8)SDL_round(SDL_clamp(b, 0.0f, 1.0f) * 255.0f); 3021 a8 = (Uint8)SDL_round(SDL_clamp(a, 0.0f, 1.0f) * 255.0f); 3022 if (SDL_WriteSurfacePixel(surface, x, y, r8, g8, b8, a8)) { 3023 result = true; 3024 } 3025 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 3026 result = SDL_Unsupported(); 3027 } else { 3028 // This is really slow, but it gets the job done 3029 float rgba[4]; 3030 Uint8 *p; 3031 3032 if (SDL_MUSTLOCK(surface)) { 3033 if (!SDL_LockSurface(surface)) { 3034 return false; 3035 } 3036 } 3037 3038 p = (Uint8 *)surface->pixels + y * surface->pitch + x * SDL_BYTESPERPIXEL(surface->format); 3039 3040 rgba[0] = r; 3041 rgba[1] = g; 3042 rgba[2] = b; 3043 rgba[3] = a; 3044 3045 if (surface->format == SDL_PIXELFORMAT_RGBA128_FLOAT) { 3046 SDL_memcpy(p, rgba, sizeof(rgba)); 3047 result = true; 3048 } else { 3049 SDL_Colorspace dst_colorspace = surface->colorspace; 3050 SDL_Colorspace src_colorspace = (dst_colorspace == SDL_COLORSPACE_SRGB_LINEAR ? SDL_COLORSPACE_SRGB_LINEAR : SDL_COLORSPACE_SRGB); 3051 3052 result = SDL_ConvertPixelsAndColorspace(1, 1, SDL_PIXELFORMAT_RGBA128_FLOAT, src_colorspace, 0, rgba, sizeof(rgba), surface->format, dst_colorspace, surface->props, p, surface->pitch); 3053 } 3054 3055 if (SDL_MUSTLOCK(surface)) { 3056 SDL_UnlockSurface(surface); 3057 } 3058 } 3059 return result; 3060} 3061 3062/* 3063 * Free a surface created by the above function. 3064 */ 3065void SDL_DestroySurface(SDL_Surface *surface) 3066{ 3067 if (!SDL_SurfaceValid(surface)) { 3068 return; 3069 } 3070 if (surface->internal_flags & SDL_INTERNAL_SURFACE_DONTFREE) { 3071 return; 3072 } 3073 if (--surface->refcount > 0) { 3074 return; 3075 } 3076 3077 SDL_RemoveSurfaceAlternateImages(surface); 3078 3079 SDL_DestroyProperties(surface->props); 3080 3081 SDL_InvalidateMap(&surface->map); 3082 3083 while (surface->locked > 0) { 3084 SDL_UnlockSurface(surface); 3085 } 3086#ifdef SDL_HAVE_RLE 3087 if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { 3088 SDL_UnRLESurface(surface); 3089 } 3090#endif 3091 SDL_SetSurfacePalette(surface, NULL); 3092 3093 if (surface->flags & SDL_SURFACE_PREALLOCATED) { 3094 // Don't free 3095 } else if (surface->flags & SDL_SURFACE_SIMD_ALIGNED) { 3096 // Free aligned 3097 SDL_aligned_free(surface->pixels); 3098 } else { 3099 // Normal 3100 SDL_free(surface->pixels); 3101 } 3102 3103 surface->reserved = NULL; 3104 3105 if (!(surface->internal_flags & SDL_INTERNAL_SURFACE_STACK)) { 3106 SDL_free(surface); 3107 } 3108} 3109 3110SDL_Surface *SDL_LoadSurface_IO(SDL_IOStream *src, bool closeio) 3111{ 3112 CHECK_PARAM(!src) { 3113 SDL_InvalidParamError("src"); 3114 return NULL; 3115 } 3116 3117 if (SDL_IsBMP(src)) { 3118 return SDL_LoadBMP_IO(src, closeio); 3119 } else if (SDL_IsPNG(src)) { 3120 return SDL_LoadPNG_IO(src, closeio); 3121 } else { 3122 if (closeio) { 3123 SDL_CloseIO(src); 3124 } 3125 SDL_SetError("Unsupported image format"); 3126 return NULL; 3127 } 3128} 3129 3130SDL_Surface *SDL_LoadSurface(const char *file) 3131{ 3132 SDL_IOStream *stream = SDL_IOFromFile(file, "rb"); 3133 if (!stream) { 3134 return NULL; 3135 } 3136 3137 return SDL_LoadSurface_IO(stream, true); 3138} 3139[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.