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