Atlas - SDL_surface.c
Home / ext / SDL / src / video Lines: 1 | Size: 96322 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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 return SDL_ConvertSurfaceAndColorspace(surface, surface->format, surface->palette, surface->colorspace, surface->props); 2215} 2216 2217SDL_Surface *SDL_ScaleSurface(SDL_Surface *surface, int width, int height, SDL_ScaleMode scaleMode) 2218{ 2219 SDL_Surface *convert = NULL; 2220 Uint32 copy_flags; 2221 SDL_Color copy_color; 2222 bool rc; 2223 2224 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 2225 SDL_InvalidParamError("surface"); 2226 goto error; 2227 } 2228 2229 if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2230 // We can't directly scale a YUV surface (yet!) 2231 SDL_Surface *tmp = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); 2232 if (!tmp) { 2233 return NULL; 2234 } 2235 2236 SDL_Surface *scaled = SDL_ScaleSurface(tmp, width, height, scaleMode); 2237 SDL_DestroySurface(tmp); 2238 if (!scaled) { 2239 return NULL; 2240 } 2241 2242 SDL_Surface *result = SDL_ConvertSurfaceAndColorspace(scaled, surface->format, NULL, surface->colorspace, surface->props); 2243 SDL_DestroySurface(scaled); 2244 return result; 2245 } 2246 2247 if (SDL_ISPIXELFORMAT_INDEXED(surface->format)) { 2248 // Linear scaling requires conversion to RGBA and then slow pixel color lookup 2249 scaleMode = SDL_SCALEMODE_NEAREST; 2250 } 2251 2252 // Create a new surface with the desired size 2253 if (surface->pixels || SDL_MUSTLOCK(surface)) { 2254 convert = SDL_CreateSurface(width, height, surface->format); 2255 } else { 2256 convert = SDL_CreateSurfaceFrom(width, height, surface->format, NULL, 0); 2257 } 2258 if (!convert) { 2259 goto error; 2260 } 2261 SDL_SetSurfacePalette(convert, surface->palette); 2262 SDL_SetSurfaceColorspace(convert, surface->colorspace); 2263 SDL_SetSurfaceRLE(convert, SDL_SurfaceHasRLE(surface)); 2264 2265 if (surface->pixels || SDL_MUSTLOCK(surface)) { 2266 // Save the original copy flags 2267 copy_flags = surface->map.info.flags; 2268 copy_color.r = surface->map.info.r; 2269 copy_color.g = surface->map.info.g; 2270 copy_color.b = surface->map.info.b; 2271 copy_color.a = surface->map.info.a; 2272 surface->map.info.r = 0xFF; 2273 surface->map.info.g = 0xFF; 2274 surface->map.info.b = 0xFF; 2275 surface->map.info.a = 0xFF; 2276 surface->map.info.flags = (copy_flags & (SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY)); 2277 SDL_InvalidateMap(&surface->map); 2278 2279 rc = SDL_BlitSurfaceScaled(surface, NULL, convert, NULL, scaleMode); 2280 2281 // Clean up the original surface, and update converted surface 2282 convert->map.info.r = copy_color.r; 2283 convert->map.info.g = copy_color.g; 2284 convert->map.info.b = copy_color.b; 2285 convert->map.info.a = copy_color.a; 2286 convert->map.info.flags = (copy_flags & ~(SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY)); 2287 surface->map.info.r = copy_color.r; 2288 surface->map.info.g = copy_color.g; 2289 surface->map.info.b = copy_color.b; 2290 surface->map.info.a = copy_color.a; 2291 surface->map.info.flags = copy_flags; 2292 SDL_InvalidateMap(&surface->map); 2293 } else { 2294 rc = true; 2295 } 2296 2297 // SDL_BlitSurfaceScaled failed, and so the conversion 2298 if (!rc) { 2299 goto error; 2300 } 2301 2302 // We're ready to go! 2303 return convert; 2304 2305error: 2306 if (convert) { 2307 SDL_DestroySurface(convert); 2308 } 2309 return NULL; 2310} 2311 2312SDL_Surface *SDL_ConvertSurfaceRect(SDL_Surface *surface, const SDL_Rect *rect, SDL_PixelFormat format) 2313{ 2314 return SDL_ConvertSurfaceRectAndColorspace(surface, NULL, format, NULL, SDL_GetDefaultColorspaceForFormat(format), surface->props); 2315} 2316 2317SDL_Surface *SDL_ConvertSurface(SDL_Surface *surface, SDL_PixelFormat format) 2318{ 2319 return SDL_ConvertSurfaceRect(surface, NULL, format); 2320} 2321 2322SDL_Surface *SDL_DuplicatePixels(int width, int height, SDL_PixelFormat format, SDL_Colorspace colorspace, void *pixels, int pitch) 2323{ 2324 SDL_Surface *surface; 2325 if (pixels) { 2326 surface = SDL_CreateSurface(width, height, format); 2327 } else { 2328 surface = SDL_CreateSurfaceFrom(width, height, format, NULL, 0); 2329 } 2330 if (!surface) { 2331 return NULL; 2332 } 2333 SDL_SetSurfaceColorspace(surface, colorspace); 2334 2335 if (surface->pixels) { 2336 int length = width * SDL_BYTESPERPIXEL(format); 2337 Uint8 *src = (Uint8 *)pixels; 2338 Uint8 *dst = (Uint8 *)surface->pixels; 2339 int rows = height; 2340 while (rows--) { 2341 SDL_memcpy(dst, src, length); 2342 dst += surface->pitch; 2343 src += pitch; 2344 } 2345 } 2346 return surface; 2347} 2348 2349bool SDL_ConvertPixelsAndColorspace(int width, int height, 2350 SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, 2351 SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch) 2352{ 2353 SDL_Surface src_surface; 2354 SDL_Surface dst_surface; 2355 SDL_Rect rect; 2356 void *nonconst_src = (void *)src; 2357 bool result; 2358 2359 CHECK_PARAM(!src) { 2360 return SDL_InvalidParamError("src"); 2361 } 2362 CHECK_PARAM(!src_pitch) { 2363 return SDL_InvalidParamError("src_pitch"); 2364 } 2365 CHECK_PARAM(!dst) { 2366 return SDL_InvalidParamError("dst"); 2367 } 2368 CHECK_PARAM(!dst_pitch) { 2369 return SDL_InvalidParamError("dst_pitch"); 2370 } 2371 2372 if (src_colorspace == SDL_COLORSPACE_UNKNOWN) { 2373 src_colorspace = SDL_GetDefaultColorspaceForFormat(src_format); 2374 } 2375 if (dst_colorspace == SDL_COLORSPACE_UNKNOWN) { 2376 dst_colorspace = SDL_GetDefaultColorspaceForFormat(dst_format); 2377 } 2378 2379 if (src_format == SDL_PIXELFORMAT_MJPG) { 2380 return SDL_ConvertPixels_STB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); 2381 } 2382 2383#ifdef SDL_HAVE_YUV 2384 if (SDL_ISPIXELFORMAT_FOURCC(src_format) && SDL_ISPIXELFORMAT_FOURCC(dst_format)) { 2385 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); 2386 } else if (SDL_ISPIXELFORMAT_FOURCC(src_format)) { 2387 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); 2388 } else if (SDL_ISPIXELFORMAT_FOURCC(dst_format)) { 2389 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); 2390 } 2391#else 2392 if (SDL_ISPIXELFORMAT_FOURCC(src_format) || SDL_ISPIXELFORMAT_FOURCC(dst_format)) { 2393 return SDL_SetError("SDL not built with YUV support"); 2394 } 2395#endif 2396 2397 // Fast path for same format copy 2398 if (src_format == dst_format && src_colorspace == dst_colorspace) { 2399 if (src_pitch == dst_pitch) { 2400 SDL_memcpy(dst, src, height * src_pitch); 2401 } else { 2402 int i; 2403 const int bpp = SDL_BYTESPERPIXEL(src_format); 2404 width *= bpp; 2405 for (i = height; i--;) { 2406 SDL_memcpy(dst, src, width); 2407 src = (const Uint8 *)src + src_pitch; 2408 dst = (Uint8 *)dst + dst_pitch; 2409 } 2410 } 2411 return true; 2412 } 2413 2414 if (!SDL_InitializeSurface(&src_surface, width, height, src_format, src_colorspace, src_properties, nonconst_src, src_pitch, true)) { 2415 return false; 2416 } 2417 SDL_SetSurfaceBlendMode(&src_surface, SDL_BLENDMODE_NONE); 2418 2419 if (!SDL_InitializeSurface(&dst_surface, width, height, dst_format, dst_colorspace, dst_properties, dst, dst_pitch, true)) { 2420 return false; 2421 } 2422 2423 // Set up the rect and go! 2424 rect.x = 0; 2425 rect.y = 0; 2426 rect.w = width; 2427 rect.h = height; 2428 result = SDL_BlitSurfaceUnchecked(&src_surface, &rect, &dst_surface, &rect); 2429 2430 SDL_DestroySurface(&src_surface); 2431 SDL_DestroySurface(&dst_surface); 2432 2433 return result; 2434} 2435 2436bool 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) 2437{ 2438 return SDL_ConvertPixelsAndColorspace(width, height, 2439 src_format, SDL_COLORSPACE_UNKNOWN, 0, src, src_pitch, 2440 dst_format, SDL_COLORSPACE_UNKNOWN, 0, dst, dst_pitch); 2441} 2442 2443/* 2444 * Premultiply the alpha on a block of pixels 2445 * 2446 * Here are some ideas for optimization: 2447 * https://github.com/Wizermil/premultiply_alpha/tree/master/premultiply_alpha 2448 * https://developer.arm.com/documentation/101964/0201/Pre-multiplied-alpha-channel-data 2449 */ 2450 2451static void SDL_PremultiplyAlpha_AXYZ8888(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) 2452{ 2453 int c; 2454 Uint32 srcpixel; 2455 Uint32 srcR, srcG, srcB, srcA; 2456 Uint32 dstpixel; 2457 Uint32 dstR, dstG, dstB, dstA; 2458 2459 while (height--) { 2460 const Uint32 *src_px = (const Uint32 *)src; 2461 Uint32 *dst_px = (Uint32 *)dst; 2462 for (c = width; c; --c) { 2463 // Component bytes extraction. 2464 srcpixel = *src_px++; 2465 RGBA_FROM_ARGB8888(srcpixel, srcR, srcG, srcB, srcA); 2466 2467 // Alpha pre-multiplication of each component. 2468 dstA = srcA; 2469 dstR = (srcA * srcR) / 255; 2470 dstG = (srcA * srcG) / 255; 2471 dstB = (srcA * srcB) / 255; 2472 2473 // ARGB8888 pixel recomposition. 2474 ARGB8888_FROM_RGBA(dstpixel, dstR, dstG, dstB, dstA); 2475 *dst_px++ = dstpixel; 2476 } 2477 src = (const Uint8 *)src + src_pitch; 2478 dst = (Uint8 *)dst + dst_pitch; 2479 } 2480} 2481 2482static void SDL_PremultiplyAlpha_XYZA8888(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) 2483{ 2484 int c; 2485 Uint32 srcpixel; 2486 Uint32 srcR, srcG, srcB, srcA; 2487 Uint32 dstpixel; 2488 Uint32 dstR, dstG, dstB, dstA; 2489 2490 while (height--) { 2491 const Uint32 *src_px = (const Uint32 *)src; 2492 Uint32 *dst_px = (Uint32 *)dst; 2493 for (c = width; c; --c) { 2494 // Component bytes extraction. 2495 srcpixel = *src_px++; 2496 RGBA_FROM_RGBA8888(srcpixel, srcR, srcG, srcB, srcA); 2497 2498 // Alpha pre-multiplication of each component. 2499 dstA = srcA; 2500 dstR = (srcA * srcR) / 255; 2501 dstG = (srcA * srcG) / 255; 2502 dstB = (srcA * srcB) / 255; 2503 2504 // RGBA8888 pixel recomposition. 2505 RGBA8888_FROM_RGBA(dstpixel, dstR, dstG, dstB, dstA); 2506 *dst_px++ = dstpixel; 2507 } 2508 src = (const Uint8 *)src + src_pitch; 2509 dst = (Uint8 *)dst + dst_pitch; 2510 } 2511} 2512 2513static void SDL_PremultiplyAlpha_AXYZ128(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) 2514{ 2515 int c; 2516 float flR, flG, flB, flA; 2517 2518 while (height--) { 2519 const float *src_px = (const float *)src; 2520 float *dst_px = (float *)dst; 2521 for (c = width; c; --c) { 2522 flA = *src_px++; 2523 flR = *src_px++; 2524 flG = *src_px++; 2525 flB = *src_px++; 2526 2527 // Alpha pre-multiplication of each component. 2528 flR *= flA; 2529 flG *= flA; 2530 flB *= flA; 2531 2532 *dst_px++ = flA; 2533 *dst_px++ = flR; 2534 *dst_px++ = flG; 2535 *dst_px++ = flB; 2536 } 2537 src = (const Uint8 *)src + src_pitch; 2538 dst = (Uint8 *)dst + dst_pitch; 2539 } 2540} 2541 2542static 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) 2543{ 2544 SDL_Surface *convert = NULL; 2545 void *final_dst = dst; 2546 int final_dst_pitch = dst_pitch; 2547 SDL_PixelFormat format; 2548 SDL_Colorspace colorspace; 2549 bool result = false; 2550 2551 CHECK_PARAM(!src) { 2552 return SDL_InvalidParamError("src"); 2553 } 2554 CHECK_PARAM(!src_pitch) { 2555 return SDL_InvalidParamError("src_pitch"); 2556 } 2557 CHECK_PARAM(!dst) { 2558 return SDL_InvalidParamError("dst"); 2559 } 2560 CHECK_PARAM(!dst_pitch) { 2561 return SDL_InvalidParamError("dst_pitch"); 2562 } 2563 2564 // Use a high precision format if we're converting to linear colorspace or using high precision pixel formats 2565 if (linear || 2566 SDL_ISPIXELFORMAT_10BIT(src_format) || SDL_BITSPERPIXEL(src_format) > 32 || 2567 SDL_ISPIXELFORMAT_10BIT(dst_format) || SDL_BITSPERPIXEL(dst_format) > 32) { 2568 if (src_format == SDL_PIXELFORMAT_ARGB128_FLOAT || 2569 src_format == SDL_PIXELFORMAT_ABGR128_FLOAT) { 2570 format = src_format; 2571 } else { 2572 format = SDL_PIXELFORMAT_ARGB128_FLOAT; 2573 } 2574 } else { 2575 if (src_format == SDL_PIXELFORMAT_ARGB8888 || 2576 src_format == SDL_PIXELFORMAT_ABGR8888 || 2577 src_format == SDL_PIXELFORMAT_RGBA8888 || 2578 src_format == SDL_PIXELFORMAT_BGRA8888) { 2579 format = src_format; 2580 } else { 2581 format = SDL_PIXELFORMAT_ARGB8888; 2582 } 2583 } 2584 if (linear) { 2585 colorspace = SDL_COLORSPACE_SRGB_LINEAR; 2586 } else { 2587 colorspace = SDL_COLORSPACE_SRGB; 2588 } 2589 2590 if (src_format != format || src_colorspace != colorspace) { 2591 convert = SDL_CreateSurface(width, height, format); 2592 if (!convert) { 2593 goto done; 2594 } 2595 if (!SDL_ConvertPixelsAndColorspace(width, height, src_format, src_colorspace, src_properties, src, src_pitch, format, colorspace, 0, convert->pixels, convert->pitch)) { 2596 goto done; 2597 } 2598 2599 src = convert->pixels; 2600 src_pitch = convert->pitch; 2601 dst = convert->pixels; 2602 dst_pitch = convert->pitch; 2603 2604 } else if (dst_format != format || dst_colorspace != colorspace) { 2605 convert = SDL_CreateSurface(width, height, format); 2606 if (!convert) { 2607 goto done; 2608 } 2609 dst = convert->pixels; 2610 dst_pitch = convert->pitch; 2611 } 2612 2613 switch (format) { 2614 case SDL_PIXELFORMAT_ARGB8888: 2615 case SDL_PIXELFORMAT_ABGR8888: 2616 SDL_PremultiplyAlpha_AXYZ8888(width, height, src, src_pitch, dst, dst_pitch); 2617 break; 2618 case SDL_PIXELFORMAT_RGBA8888: 2619 case SDL_PIXELFORMAT_BGRA8888: 2620 SDL_PremultiplyAlpha_XYZA8888(width, height, src, src_pitch, dst, dst_pitch); 2621 break; 2622 case SDL_PIXELFORMAT_ARGB128_FLOAT: 2623 case SDL_PIXELFORMAT_ABGR128_FLOAT: 2624 SDL_PremultiplyAlpha_AXYZ128(width, height, src, src_pitch, dst, dst_pitch); 2625 break; 2626 default: 2627 SDL_SetError("Unexpected internal pixel format"); 2628 goto done; 2629 } 2630 2631 if (dst != final_dst) { 2632 if (!SDL_ConvertPixelsAndColorspace(width, height, format, colorspace, 0, convert->pixels, convert->pitch, dst_format, dst_colorspace, dst_properties, final_dst, final_dst_pitch)) { 2633 goto done; 2634 } 2635 } 2636 result = true; 2637 2638done: 2639 if (convert) { 2640 SDL_DestroySurface(convert); 2641 } 2642 return result; 2643} 2644 2645bool SDL_PremultiplyAlpha(int width, int height, 2646 SDL_PixelFormat src_format, const void *src, int src_pitch, 2647 SDL_PixelFormat dst_format, void *dst, int dst_pitch, bool linear) 2648{ 2649 SDL_Colorspace src_colorspace = SDL_GetDefaultColorspaceForFormat(src_format); 2650 SDL_Colorspace dst_colorspace = SDL_GetDefaultColorspaceForFormat(dst_format); 2651 2652 return SDL_PremultiplyAlphaPixelsAndColorspace(width, height, src_format, src_colorspace, 0, src, src_pitch, dst_format, dst_colorspace, 0, dst, dst_pitch, linear); 2653} 2654 2655bool SDL_PremultiplySurfaceAlpha(SDL_Surface *surface, bool linear) 2656{ 2657 SDL_Colorspace colorspace; 2658 2659 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 2660 return SDL_InvalidParamError("surface"); 2661 } 2662 2663 colorspace = surface->colorspace; 2664 2665 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); 2666} 2667 2668bool SDL_ClearSurface(SDL_Surface *surface, float r, float g, float b, float a) 2669{ 2670 SDL_Rect clip_rect; 2671 bool result = false; 2672 2673 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 2674 return SDL_InvalidParamError("surface"); 2675 } 2676 2677 SDL_GetSurfaceClipRect(surface, &clip_rect); 2678 SDL_SetSurfaceClipRect(surface, NULL); 2679 2680 if (!SDL_ISPIXELFORMAT_FOURCC(surface->format) && 2681 SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32)) { 2682 Uint32 color; 2683 2684 color = SDL_MapSurfaceRGBA(surface, 2685 (Uint8)SDL_roundf(SDL_clamp(r, 0.0f, 1.0f) * 255.0f), 2686 (Uint8)SDL_roundf(SDL_clamp(g, 0.0f, 1.0f) * 255.0f), 2687 (Uint8)SDL_roundf(SDL_clamp(b, 0.0f, 1.0f) * 255.0f), 2688 (Uint8)SDL_roundf(SDL_clamp(a, 0.0f, 1.0f) * 255.0f)); 2689 result = SDL_FillSurfaceRect(surface, NULL, color); 2690 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2691 // We can't directly set an RGB value on a YUV surface 2692 SDL_Surface *tmp = SDL_CreateSurface(surface->w, surface->h, SDL_PIXELFORMAT_ARGB8888); 2693 if (!tmp) { 2694 goto done; 2695 } 2696 2697 if (SDL_ClearSurface(tmp, r, g, b, a)) { 2698 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); 2699 } 2700 SDL_DestroySurface(tmp); 2701 } else { 2702 // Take advantage of blit color conversion 2703 SDL_Surface *tmp = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_RGBA128_FLOAT); 2704 if (!tmp) { 2705 goto done; 2706 } 2707 SDL_SetSurfaceColorspace(tmp, surface->colorspace); 2708 SDL_SetSurfaceBlendMode(tmp, SDL_BLENDMODE_NONE); 2709 2710 float *pixels = (float *)tmp->pixels; 2711 pixels[0] = r; 2712 pixels[1] = g; 2713 pixels[2] = b; 2714 pixels[3] = a; 2715 2716 result = SDL_BlitSurfaceScaled(tmp, NULL, surface, NULL, SDL_SCALEMODE_NEAREST); 2717 SDL_DestroySurface(tmp); 2718 } 2719 2720done: 2721 SDL_SetSurfaceClipRect(surface, &clip_rect); 2722 2723 return result; 2724} 2725 2726Uint32 SDL_MapSurfaceRGB(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b) 2727{ 2728 return SDL_MapSurfaceRGBA(surface, r, g, b, SDL_ALPHA_OPAQUE); 2729} 2730 2731Uint32 SDL_MapSurfaceRGBA(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b, Uint8 a) 2732{ 2733 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 2734 SDL_InvalidParamError("surface"); 2735 return true; 2736 } 2737 return SDL_MapRGBA(surface->fmt, surface->palette, r, g, b, a); 2738} 2739 2740// This function Copyright 2023 Collabora Ltd., contributed to SDL under the ZLib license 2741bool SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a) 2742{ 2743 Uint32 pixel = 0; 2744 size_t bytes_per_pixel; 2745 Uint8 unused; 2746 Uint8 *p; 2747 bool result = false; 2748 2749 if (r) { 2750 *r = 0; 2751 } else { 2752 r = &unused; 2753 } 2754 2755 if (g) { 2756 *g = 0; 2757 } else { 2758 g = &unused; 2759 } 2760 2761 if (b) { 2762 *b = 0; 2763 } else { 2764 b = &unused; 2765 } 2766 2767 if (a) { 2768 *a = 0; 2769 } else { 2770 a = &unused; 2771 } 2772 2773 CHECK_PARAM(!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { 2774 return SDL_InvalidParamError("surface"); 2775 } 2776 2777 CHECK_PARAM(x < 0 || x >= surface->w) { 2778 return SDL_InvalidParamError("x"); 2779 } 2780 2781 CHECK_PARAM(y < 0 || y >= surface->h) { 2782 return SDL_InvalidParamError("y"); 2783 } 2784 2785 bytes_per_pixel = SDL_BYTESPERPIXEL(surface->format); 2786 2787 if (SDL_MUSTLOCK(surface)) { 2788 if (!SDL_LockSurface(surface)) { 2789 return false; 2790 } 2791 } 2792 2793 p = (Uint8 *)surface->pixels + y * surface->pitch + x * bytes_per_pixel; 2794 2795 if (bytes_per_pixel <= sizeof(pixel) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2796 /* Fill the appropriate number of least-significant bytes of pixel, 2797 * leaving the most-significant bytes set to zero */ 2798#if SDL_BYTEORDER == SDL_BIG_ENDIAN 2799 SDL_memcpy(((Uint8 *)&pixel) + (sizeof(pixel) - bytes_per_pixel), p, bytes_per_pixel); 2800#else 2801 SDL_memcpy(&pixel, p, bytes_per_pixel); 2802#endif 2803 SDL_GetRGBA(pixel, surface->fmt, surface->palette, r, g, b, a); 2804 result = true; 2805 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2806 // FIXME: We need code to extract a single macroblock from a YUV surface 2807 SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); 2808 if (converted) { 2809 result = SDL_ReadSurfacePixel(converted, x, y, r, g, b, a); 2810 SDL_DestroySurface(converted); 2811 } 2812 } else { 2813 // This is really slow, but it gets the job done 2814 Uint8 rgba[4]; 2815 2816 if (SDL_ConvertPixelsAndColorspace(1, 1, surface->format, surface->colorspace, surface->props, p, surface->pitch, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, rgba, sizeof(rgba))) { 2817 *r = rgba[0]; 2818 *g = rgba[1]; 2819 *b = rgba[2]; 2820 *a = rgba[3]; 2821 result = true; 2822 } 2823 } 2824 2825 if (SDL_MUSTLOCK(surface)) { 2826 SDL_UnlockSurface(surface); 2827 } 2828 return result; 2829} 2830 2831bool SDL_ReadSurfacePixelFloat(SDL_Surface *surface, int x, int y, float *r, float *g, float *b, float *a) 2832{ 2833 float unused; 2834 bool result = false; 2835 2836 if (r) { 2837 *r = 0.0f; 2838 } else { 2839 r = &unused; 2840 } 2841 2842 if (g) { 2843 *g = 0.0f; 2844 } else { 2845 g = &unused; 2846 } 2847 2848 if (b) { 2849 *b = 0.0f; 2850 } else { 2851 b = &unused; 2852 } 2853 2854 if (a) { 2855 *a = 0.0f; 2856 } else { 2857 a = &unused; 2858 } 2859 2860 CHECK_PARAM(!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { 2861 return SDL_InvalidParamError("surface"); 2862 } 2863 2864 CHECK_PARAM(x < 0 || x >= surface->w) { 2865 return SDL_InvalidParamError("x"); 2866 } 2867 2868 CHECK_PARAM(y < 0 || y >= surface->h) { 2869 return SDL_InvalidParamError("y"); 2870 } 2871 2872 if (SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2873 Uint8 r8, g8, b8, a8; 2874 2875 if (SDL_ReadSurfacePixel(surface, x, y, &r8, &g8, &b8, &a8)) { 2876 *r = (float)r8 / 255.0f; 2877 *g = (float)g8 / 255.0f; 2878 *b = (float)b8 / 255.0f; 2879 *a = (float)a8 / 255.0f; 2880 result = true; 2881 } 2882 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2883 // FIXME: We need code to extract a single macroblock from a YUV surface 2884 SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); 2885 if (converted) { 2886 result = SDL_ReadSurfacePixelFloat(converted, x, y, r, g, b, a); 2887 SDL_DestroySurface(converted); 2888 } 2889 } else { 2890 // This is really slow, but it gets the job done 2891 float rgba[4]; 2892 Uint8 *p; 2893 2894 if (SDL_MUSTLOCK(surface)) { 2895 if (!SDL_LockSurface(surface)) { 2896 return false; 2897 } 2898 } 2899 2900 p = (Uint8 *)surface->pixels + y * surface->pitch + x * SDL_BYTESPERPIXEL(surface->format); 2901 2902 if (surface->format == SDL_PIXELFORMAT_RGBA128_FLOAT) { 2903 SDL_memcpy(rgba, p, sizeof(rgba)); 2904 result = true; 2905 } else { 2906 SDL_Colorspace src_colorspace = surface->colorspace; 2907 SDL_Colorspace dst_colorspace = (src_colorspace == SDL_COLORSPACE_SRGB_LINEAR ? SDL_COLORSPACE_SRGB_LINEAR : SDL_COLORSPACE_SRGB); 2908 2909 if (SDL_ConvertPixelsAndColorspace(1, 1, surface->format, src_colorspace, surface->props, p, surface->pitch, SDL_PIXELFORMAT_RGBA128_FLOAT, dst_colorspace, 0, rgba, sizeof(rgba))) { 2910 result = true; 2911 } 2912 } 2913 2914 if (result) { 2915 *r = rgba[0]; 2916 *g = rgba[1]; 2917 *b = rgba[2]; 2918 *a = rgba[3]; 2919 } 2920 2921 if (SDL_MUSTLOCK(surface)) { 2922 SDL_UnlockSurface(surface); 2923 } 2924 } 2925 return result; 2926} 2927 2928bool SDL_WriteSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) 2929{ 2930 Uint32 pixel = 0; 2931 size_t bytes_per_pixel; 2932 Uint8 *p; 2933 bool result = false; 2934 2935 CHECK_PARAM(!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { 2936 return SDL_InvalidParamError("surface"); 2937 } 2938 2939 CHECK_PARAM(x < 0 || x >= surface->w) { 2940 return SDL_InvalidParamError("x"); 2941 } 2942 2943 CHECK_PARAM(y < 0 || y >= surface->h) { 2944 return SDL_InvalidParamError("y"); 2945 } 2946 2947 bytes_per_pixel = SDL_BYTESPERPIXEL(surface->format); 2948 2949 if (SDL_MUSTLOCK(surface)) { 2950 if (!SDL_LockSurface(surface)) { 2951 return false; 2952 } 2953 } 2954 2955 p = (Uint8 *)surface->pixels + y * surface->pitch + x * bytes_per_pixel; 2956 2957 if (bytes_per_pixel <= sizeof(pixel) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2958 pixel = SDL_MapRGBA(surface->fmt, surface->palette, r, g, b, a); 2959#if SDL_BYTEORDER == SDL_BIG_ENDIAN 2960 SDL_memcpy(p, ((Uint8 *)&pixel) + (sizeof(pixel) - bytes_per_pixel), bytes_per_pixel); 2961#else 2962 SDL_memcpy(p, &pixel, bytes_per_pixel); 2963#endif 2964 result = true; 2965 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 2966 result = SDL_Unsupported(); 2967 } else { 2968 // This is really slow, but it gets the job done 2969 Uint8 rgba[4]; 2970 2971 rgba[0] = r; 2972 rgba[1] = g; 2973 rgba[2] = b; 2974 rgba[3] = a; 2975 result = SDL_ConvertPixelsAndColorspace(1, 1, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, rgba, sizeof(rgba), surface->format, surface->colorspace, surface->props, p, surface->pitch); 2976 } 2977 2978 if (SDL_MUSTLOCK(surface)) { 2979 SDL_UnlockSurface(surface); 2980 } 2981 return result; 2982} 2983 2984bool SDL_WriteSurfacePixelFloat(SDL_Surface *surface, int x, int y, float r, float g, float b, float a) 2985{ 2986 bool result = false; 2987 2988 CHECK_PARAM(!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { 2989 return SDL_InvalidParamError("surface"); 2990 } 2991 2992 CHECK_PARAM(x < 0 || x >= surface->w) { 2993 return SDL_InvalidParamError("x"); 2994 } 2995 2996 CHECK_PARAM(y < 0 || y >= surface->h) { 2997 return SDL_InvalidParamError("y"); 2998 } 2999 3000 if (SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 3001 Uint8 r8, g8, b8, a8; 3002 3003 r8 = (Uint8)SDL_round(SDL_clamp(r, 0.0f, 1.0f) * 255.0f); 3004 g8 = (Uint8)SDL_round(SDL_clamp(g, 0.0f, 1.0f) * 255.0f); 3005 b8 = (Uint8)SDL_round(SDL_clamp(b, 0.0f, 1.0f) * 255.0f); 3006 a8 = (Uint8)SDL_round(SDL_clamp(a, 0.0f, 1.0f) * 255.0f); 3007 if (SDL_WriteSurfacePixel(surface, x, y, r8, g8, b8, a8)) { 3008 result = true; 3009 } 3010 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { 3011 result = SDL_Unsupported(); 3012 } else { 3013 // This is really slow, but it gets the job done 3014 float rgba[4]; 3015 Uint8 *p; 3016 3017 if (SDL_MUSTLOCK(surface)) { 3018 if (!SDL_LockSurface(surface)) { 3019 return false; 3020 } 3021 } 3022 3023 p = (Uint8 *)surface->pixels + y * surface->pitch + x * SDL_BYTESPERPIXEL(surface->format); 3024 3025 rgba[0] = r; 3026 rgba[1] = g; 3027 rgba[2] = b; 3028 rgba[3] = a; 3029 3030 if (surface->format == SDL_PIXELFORMAT_RGBA128_FLOAT) { 3031 SDL_memcpy(p, rgba, sizeof(rgba)); 3032 result = true; 3033 } else { 3034 SDL_Colorspace dst_colorspace = surface->colorspace; 3035 SDL_Colorspace src_colorspace = (dst_colorspace == SDL_COLORSPACE_SRGB_LINEAR ? SDL_COLORSPACE_SRGB_LINEAR : SDL_COLORSPACE_SRGB); 3036 3037 result = SDL_ConvertPixelsAndColorspace(1, 1, SDL_PIXELFORMAT_RGBA128_FLOAT, src_colorspace, 0, rgba, sizeof(rgba), surface->format, dst_colorspace, surface->props, p, surface->pitch); 3038 } 3039 3040 if (SDL_MUSTLOCK(surface)) { 3041 SDL_UnlockSurface(surface); 3042 } 3043 } 3044 return result; 3045} 3046 3047/* 3048 * Free a surface created by the above function. 3049 */ 3050void SDL_DestroySurface(SDL_Surface *surface) 3051{ 3052 if (!SDL_SurfaceValid(surface)) { 3053 return; 3054 } 3055 if (surface->internal_flags & SDL_INTERNAL_SURFACE_DONTFREE) { 3056 return; 3057 } 3058 if (--surface->refcount > 0) { 3059 return; 3060 } 3061 3062 SDL_RemoveSurfaceAlternateImages(surface); 3063 3064 SDL_DestroyProperties(surface->props); 3065 3066 SDL_InvalidateMap(&surface->map); 3067 3068 while (surface->locked > 0) { 3069 SDL_UnlockSurface(surface); 3070 } 3071#ifdef SDL_HAVE_RLE 3072 if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { 3073 SDL_UnRLESurface(surface); 3074 } 3075#endif 3076 SDL_SetSurfacePalette(surface, NULL); 3077 3078 if (surface->flags & SDL_SURFACE_PREALLOCATED) { 3079 // Don't free 3080 } else if (surface->flags & SDL_SURFACE_SIMD_ALIGNED) { 3081 // Free aligned 3082 SDL_aligned_free(surface->pixels); 3083 } else { 3084 // Normal 3085 SDL_free(surface->pixels); 3086 } 3087 3088 surface->reserved = NULL; 3089 3090 if (!(surface->internal_flags & SDL_INTERNAL_SURFACE_STACK)) { 3091 SDL_free(surface); 3092 } 3093} 3094 3095SDL_Surface *SDL_LoadSurface_IO(SDL_IOStream *src, bool closeio) 3096{ 3097 if (SDL_IsBMP(src)) { 3098 return SDL_LoadBMP_IO(src, closeio); 3099 } else if (SDL_IsPNG(src)) { 3100 return SDL_LoadPNG_IO(src, closeio); 3101 } else { 3102 if (closeio) { 3103 SDL_CloseIO(src); 3104 } 3105 SDL_SetError("Unsupported image format"); 3106 return NULL; 3107 } 3108} 3109 3110SDL_Surface *SDL_LoadSurface(const char *file) 3111{ 3112 SDL_IOStream *stream = SDL_IOFromFile(file, "rb"); 3113 if (!stream) { 3114 return NULL; 3115 } 3116 3117 return SDL_LoadSurface_IO(stream, true); 3118} 3119[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.