SDL_surface.c (39016B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20 */ 21 #include "../SDL_internal.h" 22 23 #include "SDL_video.h" 24 #include "SDL_sysvideo.h" 25 #include "SDL_blit.h" 26 #include "SDL_RLEaccel_c.h" 27 #include "SDL_pixels_c.h" 28 #include "SDL_yuv_c.h" 29 30 31 /* Check to make sure we can safely check multiplication of surface w and pitch and it won't overflow size_t */ 32 SDL_COMPILE_TIME_ASSERT(surface_size_assumptions, 33 sizeof(int) == sizeof(Sint32) && sizeof(size_t) >= sizeof(Sint32)); 34 35 /* Public routines */ 36 37 /* 38 * Calculate the pad-aligned scanline width of a surface 39 */ 40 static Sint64 41 SDL_CalculatePitch(Uint32 format, int width) 42 { 43 Sint64 pitch; 44 45 if (SDL_ISPIXELFORMAT_FOURCC(format) || SDL_BITSPERPIXEL(format) >= 8) { 46 pitch = ((Sint64)width * SDL_BYTESPERPIXEL(format)); 47 } else { 48 pitch = (((Sint64)width * SDL_BITSPERPIXEL(format)) + 7) / 8; 49 } 50 pitch = (pitch + 3) & ~3; /* 4-byte aligning for speed */ 51 return pitch; 52 } 53 54 /* 55 * Create an empty RGB surface of the appropriate depth using the given 56 * enum SDL_PIXELFORMAT_* format 57 */ 58 SDL_Surface * 59 SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, 60 Uint32 format) 61 { 62 Sint64 pitch; 63 SDL_Surface *surface; 64 65 /* The flags are no longer used, make the compiler happy */ 66 (void)flags; 67 68 pitch = SDL_CalculatePitch(format, width); 69 if (pitch < 0 || pitch > SDL_MAX_SINT32) { 70 /* Overflow... */ 71 SDL_OutOfMemory(); 72 return NULL; 73 } 74 75 /* Allocate the surface */ 76 surface = (SDL_Surface *) SDL_calloc(1, sizeof(*surface)); 77 if (surface == NULL) { 78 SDL_OutOfMemory(); 79 return NULL; 80 } 81 82 surface->format = SDL_AllocFormat(format); 83 if (!surface->format) { 84 SDL_FreeSurface(surface); 85 return NULL; 86 } 87 surface->w = width; 88 surface->h = height; 89 surface->pitch = (int)pitch; 90 SDL_SetClipRect(surface, NULL); 91 92 if (SDL_ISPIXELFORMAT_INDEXED(surface->format->format)) { 93 SDL_Palette *palette = 94 SDL_AllocPalette((1 << surface->format->BitsPerPixel)); 95 if (!palette) { 96 SDL_FreeSurface(surface); 97 return NULL; 98 } 99 if (palette->ncolors == 2) { 100 /* Create a black and white bitmap palette */ 101 palette->colors[0].r = 0xFF; 102 palette->colors[0].g = 0xFF; 103 palette->colors[0].b = 0xFF; 104 palette->colors[1].r = 0x00; 105 palette->colors[1].g = 0x00; 106 palette->colors[1].b = 0x00; 107 } 108 SDL_SetSurfacePalette(surface, palette); 109 SDL_FreePalette(palette); 110 } 111 112 /* Get the pixels */ 113 if (surface->w && surface->h) { 114 /* Assumptions checked in surface_size_assumptions assert above */ 115 Sint64 size = ((Sint64)surface->h * surface->pitch); 116 if (size < 0 || size > SDL_MAX_SINT32) { 117 /* Overflow... */ 118 SDL_FreeSurface(surface); 119 SDL_OutOfMemory(); 120 return NULL; 121 } 122 123 surface->pixels = SDL_SIMDAlloc((size_t)size); 124 if (!surface->pixels) { 125 SDL_FreeSurface(surface); 126 SDL_OutOfMemory(); 127 return NULL; 128 } 129 surface->flags |= SDL_SIMD_ALIGNED; 130 /* This is important for bitmaps */ 131 SDL_memset(surface->pixels, 0, surface->h * surface->pitch); 132 } 133 134 /* Allocate an empty mapping */ 135 surface->map = SDL_AllocBlitMap(); 136 if (!surface->map) { 137 SDL_FreeSurface(surface); 138 return NULL; 139 } 140 141 /* By default surface with an alpha mask are set up for blending */ 142 if (surface->format->Amask) { 143 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); 144 } 145 146 /* The surface is ready to go */ 147 surface->refcount = 1; 148 return surface; 149 } 150 151 /* 152 * Create an empty RGB surface of the appropriate depth 153 */ 154 SDL_Surface * 155 SDL_CreateRGBSurface(Uint32 flags, 156 int width, int height, int depth, 157 Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) 158 { 159 Uint32 format; 160 161 /* Get the pixel format */ 162 format = SDL_MasksToPixelFormatEnum(depth, Rmask, Gmask, Bmask, Amask); 163 if (format == SDL_PIXELFORMAT_UNKNOWN) { 164 SDL_SetError("Unknown pixel format"); 165 return NULL; 166 } 167 168 return SDL_CreateRGBSurfaceWithFormat(flags, width, height, depth, format); 169 } 170 171 /* 172 * Create an RGB surface from an existing memory buffer 173 */ 174 SDL_Surface * 175 SDL_CreateRGBSurfaceFrom(void *pixels, 176 int width, int height, int depth, int pitch, 177 Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, 178 Uint32 Amask) 179 { 180 SDL_Surface *surface; 181 182 surface = SDL_CreateRGBSurface(0, 0, 0, depth, Rmask, Gmask, Bmask, Amask); 183 if (surface != NULL) { 184 surface->flags |= SDL_PREALLOC; 185 surface->pixels = pixels; 186 surface->w = width; 187 surface->h = height; 188 surface->pitch = pitch; 189 SDL_SetClipRect(surface, NULL); 190 } 191 return surface; 192 } 193 194 /* 195 * Create an RGB surface from an existing memory buffer using the given given 196 * enum SDL_PIXELFORMAT_* format 197 */ 198 SDL_Surface * 199 SDL_CreateRGBSurfaceWithFormatFrom(void *pixels, 200 int width, int height, int depth, int pitch, 201 Uint32 format) 202 { 203 SDL_Surface *surface; 204 205 surface = SDL_CreateRGBSurfaceWithFormat(0, 0, 0, depth, format); 206 if (surface != NULL) { 207 surface->flags |= SDL_PREALLOC; 208 surface->pixels = pixels; 209 surface->w = width; 210 surface->h = height; 211 surface->pitch = pitch; 212 SDL_SetClipRect(surface, NULL); 213 } 214 return surface; 215 } 216 217 int 218 SDL_SetSurfacePalette(SDL_Surface * surface, SDL_Palette * palette) 219 { 220 if (!surface) { 221 return SDL_SetError("SDL_SetSurfacePalette() passed a NULL surface"); 222 } 223 if (SDL_SetPixelFormatPalette(surface->format, palette) < 0) { 224 return -1; 225 } 226 SDL_InvalidateMap(surface->map); 227 228 return 0; 229 } 230 231 int 232 SDL_SetSurfaceRLE(SDL_Surface * surface, int flag) 233 { 234 int flags; 235 236 if (!surface) { 237 return -1; 238 } 239 240 flags = surface->map->info.flags; 241 if (flag) { 242 surface->map->info.flags |= SDL_COPY_RLE_DESIRED; 243 } else { 244 surface->map->info.flags &= ~SDL_COPY_RLE_DESIRED; 245 } 246 if (surface->map->info.flags != flags) { 247 SDL_InvalidateMap(surface->map); 248 } 249 return 0; 250 } 251 252 SDL_bool 253 SDL_HasSurfaceRLE(SDL_Surface * surface) 254 { 255 if (!surface) { 256 return SDL_FALSE; 257 } 258 259 if (!(surface->map->info.flags & SDL_COPY_RLE_DESIRED)) { 260 return SDL_FALSE; 261 } 262 263 return SDL_TRUE; 264 } 265 266 int 267 SDL_SetColorKey(SDL_Surface * surface, int flag, Uint32 key) 268 { 269 int flags; 270 271 if (!surface) { 272 return SDL_InvalidParamError("surface"); 273 } 274 275 if (surface->format->palette && key >= ((Uint32) surface->format->palette->ncolors)) { 276 return SDL_InvalidParamError("key"); 277 } 278 279 if (flag & SDL_RLEACCEL) { 280 SDL_SetSurfaceRLE(surface, 1); 281 } 282 283 flags = surface->map->info.flags; 284 if (flag) { 285 surface->map->info.flags |= SDL_COPY_COLORKEY; 286 surface->map->info.colorkey = key; 287 } else { 288 surface->map->info.flags &= ~SDL_COPY_COLORKEY; 289 } 290 if (surface->map->info.flags != flags) { 291 SDL_InvalidateMap(surface->map); 292 } 293 294 return 0; 295 } 296 297 SDL_bool 298 SDL_HasColorKey(SDL_Surface * surface) 299 { 300 if (!surface) { 301 return SDL_FALSE; 302 } 303 304 if (!(surface->map->info.flags & SDL_COPY_COLORKEY)) { 305 return SDL_FALSE; 306 } 307 308 return SDL_TRUE; 309 } 310 311 int 312 SDL_GetColorKey(SDL_Surface * surface, Uint32 * key) 313 { 314 if (!surface) { 315 return SDL_InvalidParamError("surface"); 316 } 317 318 if (!(surface->map->info.flags & SDL_COPY_COLORKEY)) { 319 return SDL_SetError("Surface doesn't have a colorkey"); 320 } 321 322 if (key) { 323 *key = surface->map->info.colorkey; 324 } 325 return 0; 326 } 327 328 /* This is a fairly slow function to switch from colorkey to alpha */ 329 static void 330 SDL_ConvertColorkeyToAlpha(SDL_Surface * surface, SDL_bool ignore_alpha) 331 { 332 int x, y; 333 334 if (!surface) { 335 return; 336 } 337 338 if (!(surface->map->info.flags & SDL_COPY_COLORKEY) || 339 !surface->format->Amask) { 340 return; 341 } 342 343 SDL_LockSurface(surface); 344 345 switch (surface->format->BytesPerPixel) { 346 case 2: 347 { 348 Uint16 *row, *spot; 349 Uint16 ckey = (Uint16) surface->map->info.colorkey; 350 Uint16 mask = (Uint16) (~surface->format->Amask); 351 352 /* Ignore, or not, alpha in colorkey comparison */ 353 if (ignore_alpha) { 354 ckey &= mask; 355 row = (Uint16 *) surface->pixels; 356 for (y = surface->h; y--;) { 357 spot = row; 358 for (x = surface->w; x--;) { 359 if ((*spot & mask) == ckey) { 360 *spot &= mask; 361 } 362 ++spot; 363 } 364 row += surface->pitch / 2; 365 } 366 } else { 367 row = (Uint16 *) surface->pixels; 368 for (y = surface->h; y--;) { 369 spot = row; 370 for (x = surface->w; x--;) { 371 if (*spot == ckey) { 372 *spot &= mask; 373 } 374 ++spot; 375 } 376 row += surface->pitch / 2; 377 } 378 } 379 } 380 break; 381 case 3: 382 /* FIXME */ 383 break; 384 case 4: 385 { 386 Uint32 *row, *spot; 387 Uint32 ckey = surface->map->info.colorkey; 388 Uint32 mask = ~surface->format->Amask; 389 390 /* Ignore, or not, alpha in colorkey comparison */ 391 if (ignore_alpha) { 392 ckey &= mask; 393 row = (Uint32 *) surface->pixels; 394 for (y = surface->h; y--;) { 395 spot = row; 396 for (x = surface->w; x--;) { 397 if ((*spot & mask) == ckey) { 398 *spot &= mask; 399 } 400 ++spot; 401 } 402 row += surface->pitch / 4; 403 } 404 } else { 405 row = (Uint32 *) surface->pixels; 406 for (y = surface->h; y--;) { 407 spot = row; 408 for (x = surface->w; x--;) { 409 if (*spot == ckey) { 410 *spot &= mask; 411 } 412 ++spot; 413 } 414 row += surface->pitch / 4; 415 } 416 } 417 } 418 break; 419 } 420 421 SDL_UnlockSurface(surface); 422 423 SDL_SetColorKey(surface, 0, 0); 424 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); 425 } 426 427 int 428 SDL_SetSurfaceColorMod(SDL_Surface * surface, Uint8 r, Uint8 g, Uint8 b) 429 { 430 int flags; 431 432 if (!surface) { 433 return -1; 434 } 435 436 surface->map->info.r = r; 437 surface->map->info.g = g; 438 surface->map->info.b = b; 439 440 flags = surface->map->info.flags; 441 if (r != 0xFF || g != 0xFF || b != 0xFF) { 442 surface->map->info.flags |= SDL_COPY_MODULATE_COLOR; 443 } else { 444 surface->map->info.flags &= ~SDL_COPY_MODULATE_COLOR; 445 } 446 if (surface->map->info.flags != flags) { 447 SDL_InvalidateMap(surface->map); 448 } 449 return 0; 450 } 451 452 453 int 454 SDL_GetSurfaceColorMod(SDL_Surface * surface, Uint8 * r, Uint8 * g, Uint8 * b) 455 { 456 if (!surface) { 457 return -1; 458 } 459 460 if (r) { 461 *r = surface->map->info.r; 462 } 463 if (g) { 464 *g = surface->map->info.g; 465 } 466 if (b) { 467 *b = surface->map->info.b; 468 } 469 return 0; 470 } 471 472 int 473 SDL_SetSurfaceAlphaMod(SDL_Surface * surface, Uint8 alpha) 474 { 475 int flags; 476 477 if (!surface) { 478 return -1; 479 } 480 481 surface->map->info.a = alpha; 482 483 flags = surface->map->info.flags; 484 if (alpha != 0xFF) { 485 surface->map->info.flags |= SDL_COPY_MODULATE_ALPHA; 486 } else { 487 surface->map->info.flags &= ~SDL_COPY_MODULATE_ALPHA; 488 } 489 if (surface->map->info.flags != flags) { 490 SDL_InvalidateMap(surface->map); 491 } 492 return 0; 493 } 494 495 int 496 SDL_GetSurfaceAlphaMod(SDL_Surface * surface, Uint8 * alpha) 497 { 498 if (!surface) { 499 return -1; 500 } 501 502 if (alpha) { 503 *alpha = surface->map->info.a; 504 } 505 return 0; 506 } 507 508 int 509 SDL_SetSurfaceBlendMode(SDL_Surface * surface, SDL_BlendMode blendMode) 510 { 511 int flags, status; 512 513 if (!surface) { 514 return -1; 515 } 516 517 status = 0; 518 flags = surface->map->info.flags; 519 surface->map->info.flags &= 520 ~(SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL); 521 switch (blendMode) { 522 case SDL_BLENDMODE_NONE: 523 break; 524 case SDL_BLENDMODE_BLEND: 525 surface->map->info.flags |= SDL_COPY_BLEND; 526 break; 527 case SDL_BLENDMODE_ADD: 528 surface->map->info.flags |= SDL_COPY_ADD; 529 break; 530 case SDL_BLENDMODE_MOD: 531 surface->map->info.flags |= SDL_COPY_MOD; 532 break; 533 case SDL_BLENDMODE_MUL: 534 surface->map->info.flags |= SDL_COPY_MUL; 535 break; 536 default: 537 status = SDL_Unsupported(); 538 break; 539 } 540 541 if (surface->map->info.flags != flags) { 542 SDL_InvalidateMap(surface->map); 543 } 544 545 return status; 546 } 547 548 int 549 SDL_GetSurfaceBlendMode(SDL_Surface * surface, SDL_BlendMode *blendMode) 550 { 551 if (!surface) { 552 return -1; 553 } 554 555 if (!blendMode) { 556 return 0; 557 } 558 559 switch (surface->map-> 560 info.flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL)) { 561 case SDL_COPY_BLEND: 562 *blendMode = SDL_BLENDMODE_BLEND; 563 break; 564 case SDL_COPY_ADD: 565 *blendMode = SDL_BLENDMODE_ADD; 566 break; 567 case SDL_COPY_MOD: 568 *blendMode = SDL_BLENDMODE_MOD; 569 break; 570 case SDL_COPY_MUL: 571 *blendMode = SDL_BLENDMODE_MUL; 572 break; 573 default: 574 *blendMode = SDL_BLENDMODE_NONE; 575 break; 576 } 577 return 0; 578 } 579 580 SDL_bool 581 SDL_SetClipRect(SDL_Surface * surface, const SDL_Rect * rect) 582 { 583 SDL_Rect full_rect; 584 585 /* Don't do anything if there's no surface to act on */ 586 if (!surface) { 587 return SDL_FALSE; 588 } 589 590 /* Set up the full surface rectangle */ 591 full_rect.x = 0; 592 full_rect.y = 0; 593 full_rect.w = surface->w; 594 full_rect.h = surface->h; 595 596 /* Set the clipping rectangle */ 597 if (!rect) { 598 surface->clip_rect = full_rect; 599 return SDL_TRUE; 600 } 601 return SDL_IntersectRect(rect, &full_rect, &surface->clip_rect); 602 } 603 604 void 605 SDL_GetClipRect(SDL_Surface * surface, SDL_Rect * rect) 606 { 607 if (surface && rect) { 608 *rect = surface->clip_rect; 609 } 610 } 611 612 /* 613 * Set up a blit between two surfaces -- split into three parts: 614 * The upper part, SDL_UpperBlit(), performs clipping and rectangle 615 * verification. The lower part is a pointer to a low level 616 * accelerated blitting function. 617 * 618 * These parts are separated out and each used internally by this 619 * library in the optimimum places. They are exported so that if 620 * you know exactly what you are doing, you can optimize your code 621 * by calling the one(s) you need. 622 */ 623 int 624 SDL_LowerBlit(SDL_Surface * src, SDL_Rect * srcrect, 625 SDL_Surface * dst, SDL_Rect * dstrect) 626 { 627 /* Check to make sure the blit mapping is valid */ 628 if ((src->map->dst != dst) || 629 (dst->format->palette && 630 src->map->dst_palette_version != dst->format->palette->version) || 631 (src->format->palette && 632 src->map->src_palette_version != src->format->palette->version)) { 633 if (SDL_MapSurface(src, dst) < 0) { 634 return (-1); 635 } 636 /* just here for debugging */ 637 /* printf */ 638 /* ("src = 0x%08X src->flags = %08X src->map->info.flags = %08x\ndst = 0x%08X dst->flags = %08X dst->map->info.flags = %08X\nsrc->map->blit = 0x%08x\n", */ 639 /* src, dst->flags, src->map->info.flags, dst, dst->flags, */ 640 /* dst->map->info.flags, src->map->blit); */ 641 } 642 return (src->map->blit(src, srcrect, dst, dstrect)); 643 } 644 645 646 int 647 SDL_UpperBlit(SDL_Surface * src, const SDL_Rect * srcrect, 648 SDL_Surface * dst, SDL_Rect * dstrect) 649 { 650 SDL_Rect fulldst; 651 int srcx, srcy, w, h; 652 653 /* Make sure the surfaces aren't locked */ 654 if (!src || !dst) { 655 return SDL_SetError("SDL_UpperBlit: passed a NULL surface"); 656 } 657 if (src->locked || dst->locked) { 658 return SDL_SetError("Surfaces must not be locked during blit"); 659 } 660 661 /* If the destination rectangle is NULL, use the entire dest surface */ 662 if (dstrect == NULL) { 663 fulldst.x = fulldst.y = 0; 664 fulldst.w = dst->w; 665 fulldst.h = dst->h; 666 dstrect = &fulldst; 667 } 668 669 /* clip the source rectangle to the source surface */ 670 if (srcrect) { 671 int maxw, maxh; 672 673 srcx = srcrect->x; 674 w = srcrect->w; 675 if (srcx < 0) { 676 w += srcx; 677 dstrect->x -= srcx; 678 srcx = 0; 679 } 680 maxw = src->w - srcx; 681 if (maxw < w) 682 w = maxw; 683 684 srcy = srcrect->y; 685 h = srcrect->h; 686 if (srcy < 0) { 687 h += srcy; 688 dstrect->y -= srcy; 689 srcy = 0; 690 } 691 maxh = src->h - srcy; 692 if (maxh < h) 693 h = maxh; 694 695 } else { 696 srcx = srcy = 0; 697 w = src->w; 698 h = src->h; 699 } 700 701 /* clip the destination rectangle against the clip rectangle */ 702 { 703 SDL_Rect *clip = &dst->clip_rect; 704 int dx, dy; 705 706 dx = clip->x - dstrect->x; 707 if (dx > 0) { 708 w -= dx; 709 dstrect->x += dx; 710 srcx += dx; 711 } 712 dx = dstrect->x + w - clip->x - clip->w; 713 if (dx > 0) 714 w -= dx; 715 716 dy = clip->y - dstrect->y; 717 if (dy > 0) { 718 h -= dy; 719 dstrect->y += dy; 720 srcy += dy; 721 } 722 dy = dstrect->y + h - clip->y - clip->h; 723 if (dy > 0) 724 h -= dy; 725 } 726 727 /* Switch back to a fast blit if we were previously stretching */ 728 if (src->map->info.flags & SDL_COPY_NEAREST) { 729 src->map->info.flags &= ~SDL_COPY_NEAREST; 730 SDL_InvalidateMap(src->map); 731 } 732 733 if (w > 0 && h > 0) { 734 SDL_Rect sr; 735 sr.x = srcx; 736 sr.y = srcy; 737 sr.w = dstrect->w = w; 738 sr.h = dstrect->h = h; 739 return SDL_LowerBlit(src, &sr, dst, dstrect); 740 } 741 dstrect->w = dstrect->h = 0; 742 return 0; 743 } 744 745 int 746 SDL_UpperBlitScaled(SDL_Surface * src, const SDL_Rect * srcrect, 747 SDL_Surface * dst, SDL_Rect * dstrect) 748 { 749 double src_x0, src_y0, src_x1, src_y1; 750 double dst_x0, dst_y0, dst_x1, dst_y1; 751 SDL_Rect final_src, final_dst; 752 double scaling_w, scaling_h; 753 int src_w, src_h; 754 int dst_w, dst_h; 755 756 /* Make sure the surfaces aren't locked */ 757 if (!src || !dst) { 758 return SDL_SetError("SDL_UpperBlitScaled: passed a NULL surface"); 759 } 760 if (src->locked || dst->locked) { 761 return SDL_SetError("Surfaces must not be locked during blit"); 762 } 763 764 if (NULL == srcrect) { 765 src_w = src->w; 766 src_h = src->h; 767 } else { 768 src_w = srcrect->w; 769 src_h = srcrect->h; 770 } 771 772 if (NULL == dstrect) { 773 dst_w = dst->w; 774 dst_h = dst->h; 775 } else { 776 dst_w = dstrect->w; 777 dst_h = dstrect->h; 778 } 779 780 if (dst_w == src_w && dst_h == src_h) { 781 /* No scaling, defer to regular blit */ 782 return SDL_BlitSurface(src, srcrect, dst, dstrect); 783 } 784 785 scaling_w = (double)dst_w / src_w; 786 scaling_h = (double)dst_h / src_h; 787 788 if (NULL == dstrect) { 789 dst_x0 = 0; 790 dst_y0 = 0; 791 dst_x1 = dst_w - 1; 792 dst_y1 = dst_h - 1; 793 } else { 794 dst_x0 = dstrect->x; 795 dst_y0 = dstrect->y; 796 dst_x1 = dst_x0 + dst_w - 1; 797 dst_y1 = dst_y0 + dst_h - 1; 798 } 799 800 if (NULL == srcrect) { 801 src_x0 = 0; 802 src_y0 = 0; 803 src_x1 = src_w - 1; 804 src_y1 = src_h - 1; 805 } else { 806 src_x0 = srcrect->x; 807 src_y0 = srcrect->y; 808 src_x1 = src_x0 + src_w - 1; 809 src_y1 = src_y0 + src_h - 1; 810 811 /* Clip source rectangle to the source surface */ 812 813 if (src_x0 < 0) { 814 dst_x0 -= src_x0 * scaling_w; 815 src_x0 = 0; 816 } 817 818 if (src_x1 >= src->w) { 819 dst_x1 -= (src_x1 - src->w + 1) * scaling_w; 820 src_x1 = src->w - 1; 821 } 822 823 if (src_y0 < 0) { 824 dst_y0 -= src_y0 * scaling_h; 825 src_y0 = 0; 826 } 827 828 if (src_y1 >= src->h) { 829 dst_y1 -= (src_y1 - src->h + 1) * scaling_h; 830 src_y1 = src->h - 1; 831 } 832 } 833 834 /* Clip destination rectangle to the clip rectangle */ 835 836 /* Translate to clip space for easier calculations */ 837 dst_x0 -= dst->clip_rect.x; 838 dst_x1 -= dst->clip_rect.x; 839 dst_y0 -= dst->clip_rect.y; 840 dst_y1 -= dst->clip_rect.y; 841 842 if (dst_x0 < 0) { 843 src_x0 -= dst_x0 / scaling_w; 844 dst_x0 = 0; 845 } 846 847 if (dst_x1 >= dst->clip_rect.w) { 848 src_x1 -= (dst_x1 - dst->clip_rect.w + 1) / scaling_w; 849 dst_x1 = dst->clip_rect.w - 1; 850 } 851 852 if (dst_y0 < 0) { 853 src_y0 -= dst_y0 / scaling_h; 854 dst_y0 = 0; 855 } 856 857 if (dst_y1 >= dst->clip_rect.h) { 858 src_y1 -= (dst_y1 - dst->clip_rect.h + 1) / scaling_h; 859 dst_y1 = dst->clip_rect.h - 1; 860 } 861 862 /* Translate back to surface coordinates */ 863 dst_x0 += dst->clip_rect.x; 864 dst_x1 += dst->clip_rect.x; 865 dst_y0 += dst->clip_rect.y; 866 dst_y1 += dst->clip_rect.y; 867 868 final_src.x = (int)SDL_floor(src_x0 + 0.5); 869 final_src.y = (int)SDL_floor(src_y0 + 0.5); 870 final_src.w = (int)SDL_floor(src_x1 + 1 + 0.5) - (int)SDL_floor(src_x0 + 0.5); 871 final_src.h = (int)SDL_floor(src_y1 + 1 + 0.5) - (int)SDL_floor(src_y0 + 0.5); 872 873 final_dst.x = (int)SDL_floor(dst_x0 + 0.5); 874 final_dst.y = (int)SDL_floor(dst_y0 + 0.5); 875 final_dst.w = (int)SDL_floor(dst_x1 - dst_x0 + 1.5); 876 final_dst.h = (int)SDL_floor(dst_y1 - dst_y0 + 1.5); 877 878 if (final_dst.w < 0) 879 final_dst.w = 0; 880 if (final_dst.h < 0) 881 final_dst.h = 0; 882 883 if (dstrect) 884 *dstrect = final_dst; 885 886 if (final_dst.w == 0 || final_dst.h == 0 || 887 final_src.w <= 0 || final_src.h <= 0) { 888 /* No-op. */ 889 return 0; 890 } 891 892 return SDL_LowerBlitScaled(src, &final_src, dst, &final_dst); 893 } 894 895 /** 896 * This is a semi-private blit function and it performs low-level surface 897 * scaled blitting only. 898 */ 899 int 900 SDL_LowerBlitScaled(SDL_Surface * src, SDL_Rect * srcrect, 901 SDL_Surface * dst, SDL_Rect * dstrect) 902 { 903 static const Uint32 complex_copy_flags = ( 904 SDL_COPY_MODULATE_COLOR | SDL_COPY_MODULATE_ALPHA | 905 SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL | 906 SDL_COPY_COLORKEY 907 ); 908 909 if (!(src->map->info.flags & SDL_COPY_NEAREST)) { 910 src->map->info.flags |= SDL_COPY_NEAREST; 911 SDL_InvalidateMap(src->map); 912 } 913 914 if ( !(src->map->info.flags & complex_copy_flags) && 915 src->format->format == dst->format->format && 916 !SDL_ISPIXELFORMAT_INDEXED(src->format->format) ) { 917 return SDL_SoftStretch( src, srcrect, dst, dstrect ); 918 } else { 919 return SDL_LowerBlit( src, srcrect, dst, dstrect ); 920 } 921 } 922 923 /* 924 * Lock a surface to directly access the pixels 925 */ 926 int 927 SDL_LockSurface(SDL_Surface * surface) 928 { 929 if (!surface->locked) { 930 #if SDL_HAVE_RLE 931 /* Perform the lock */ 932 if (surface->flags & SDL_RLEACCEL) { 933 SDL_UnRLESurface(surface, 1); 934 surface->flags |= SDL_RLEACCEL; /* save accel'd state */ 935 } 936 #endif 937 } 938 939 /* Increment the surface lock count, for recursive locks */ 940 ++surface->locked; 941 942 /* Ready to go.. */ 943 return (0); 944 } 945 946 /* 947 * Unlock a previously locked surface 948 */ 949 void 950 SDL_UnlockSurface(SDL_Surface * surface) 951 { 952 /* Only perform an unlock if we are locked */ 953 if (!surface->locked || (--surface->locked > 0)) { 954 return; 955 } 956 957 #if SDL_HAVE_RLE 958 /* Update RLE encoded surface with new data */ 959 if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) { 960 surface->flags &= ~SDL_RLEACCEL; /* stop lying */ 961 SDL_RLESurface(surface); 962 } 963 #endif 964 } 965 966 /* 967 * Creates a new surface identical to the existing surface 968 */ 969 SDL_Surface * 970 SDL_DuplicateSurface(SDL_Surface * surface) 971 { 972 return SDL_ConvertSurface(surface, surface->format, surface->flags); 973 } 974 975 /* 976 * Convert a surface into the specified pixel format. 977 */ 978 SDL_Surface * 979 SDL_ConvertSurface(SDL_Surface * surface, const SDL_PixelFormat * format, 980 Uint32 flags) 981 { 982 SDL_Surface *convert; 983 Uint32 copy_flags; 984 SDL_Color copy_color; 985 SDL_Rect bounds; 986 int ret; 987 SDL_bool palette_ck_transform = SDL_FALSE; 988 int palette_ck_value = 0; 989 SDL_bool palette_has_alpha = SDL_FALSE; 990 Uint8 *palette_saved_alpha = NULL; 991 992 if (!surface) { 993 SDL_InvalidParamError("surface"); 994 return NULL; 995 } 996 if (!format) { 997 SDL_InvalidParamError("format"); 998 return NULL; 999 } 1000 1001 /* Check for empty destination palette! (results in empty image) */ 1002 if (format->palette != NULL) { 1003 int i; 1004 for (i = 0; i < format->palette->ncolors; ++i) { 1005 if ((format->palette->colors[i].r != 0xFF) || 1006 (format->palette->colors[i].g != 0xFF) || 1007 (format->palette->colors[i].b != 0xFF)) 1008 break; 1009 } 1010 if (i == format->palette->ncolors) { 1011 SDL_SetError("Empty destination palette"); 1012 return (NULL); 1013 } 1014 } 1015 1016 /* Create a new surface with the desired format */ 1017 convert = SDL_CreateRGBSurface(flags, surface->w, surface->h, 1018 format->BitsPerPixel, format->Rmask, 1019 format->Gmask, format->Bmask, 1020 format->Amask); 1021 if (convert == NULL) { 1022 return (NULL); 1023 } 1024 1025 /* Copy the palette if any */ 1026 if (format->palette && convert->format->palette) { 1027 SDL_memcpy(convert->format->palette->colors, 1028 format->palette->colors, 1029 format->palette->ncolors * sizeof(SDL_Color)); 1030 convert->format->palette->ncolors = format->palette->ncolors; 1031 } 1032 1033 /* Save the original copy flags */ 1034 copy_flags = surface->map->info.flags; 1035 copy_color.r = surface->map->info.r; 1036 copy_color.g = surface->map->info.g; 1037 copy_color.b = surface->map->info.b; 1038 copy_color.a = surface->map->info.a; 1039 surface->map->info.r = 0xFF; 1040 surface->map->info.g = 0xFF; 1041 surface->map->info.b = 0xFF; 1042 surface->map->info.a = 0xFF; 1043 surface->map->info.flags = (copy_flags & (SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY)); 1044 SDL_InvalidateMap(surface->map); 1045 1046 /* Copy over the image data */ 1047 bounds.x = 0; 1048 bounds.y = 0; 1049 bounds.w = surface->w; 1050 bounds.h = surface->h; 1051 1052 /* Source surface has a palette with no real alpha (0 or OPAQUE). 1053 * Destination format has alpha. 1054 * -> set alpha channel to be opaque */ 1055 if (surface->format->palette && format->Amask) { 1056 SDL_bool set_opaque = SDL_FALSE; 1057 1058 SDL_bool is_opaque, has_alpha_channel; 1059 SDL_DetectPalette(surface->format->palette, &is_opaque, &has_alpha_channel); 1060 1061 if (is_opaque) { 1062 if (!has_alpha_channel) { 1063 set_opaque = SDL_TRUE; 1064 } 1065 } else { 1066 palette_has_alpha = SDL_TRUE; 1067 } 1068 1069 /* Set opaque and backup palette alpha values */ 1070 if (set_opaque) { 1071 int i; 1072 palette_saved_alpha = SDL_stack_alloc(Uint8, surface->format->palette->ncolors); 1073 for (i = 0; i < surface->format->palette->ncolors; i++) { 1074 palette_saved_alpha[i] = surface->format->palette->colors[i].a; 1075 surface->format->palette->colors[i].a = SDL_ALPHA_OPAQUE; 1076 } 1077 } 1078 } 1079 1080 /* Transform colorkey to alpha. for cases where source palette has duplicate values, and colorkey is one of them */ 1081 if (copy_flags & SDL_COPY_COLORKEY) { 1082 if (surface->format->palette && !format->palette) { 1083 palette_ck_transform = SDL_TRUE; 1084 palette_has_alpha = SDL_TRUE; 1085 palette_ck_value = surface->format->palette->colors[surface->map->info.colorkey].a; 1086 surface->format->palette->colors[surface->map->info.colorkey].a = SDL_ALPHA_TRANSPARENT; 1087 } 1088 } 1089 1090 ret = SDL_LowerBlit(surface, &bounds, convert, &bounds); 1091 1092 /* Restore colorkey alpha value */ 1093 if (palette_ck_transform) { 1094 surface->format->palette->colors[surface->map->info.colorkey].a = palette_ck_value; 1095 } 1096 1097 /* Restore palette alpha values */ 1098 if (palette_saved_alpha) { 1099 int i; 1100 for (i = 0; i < surface->format->palette->ncolors; i++) { 1101 surface->format->palette->colors[i].a = palette_saved_alpha[i]; 1102 } 1103 SDL_stack_free(palette_saved_alpha); 1104 } 1105 1106 /* Clean up the original surface, and update converted surface */ 1107 convert->map->info.r = copy_color.r; 1108 convert->map->info.g = copy_color.g; 1109 convert->map->info.b = copy_color.b; 1110 convert->map->info.a = copy_color.a; 1111 convert->map->info.flags = 1112 (copy_flags & 1113 ~(SDL_COPY_COLORKEY | SDL_COPY_BLEND 1114 | SDL_COPY_RLE_DESIRED | SDL_COPY_RLE_COLORKEY | 1115 SDL_COPY_RLE_ALPHAKEY)); 1116 surface->map->info.r = copy_color.r; 1117 surface->map->info.g = copy_color.g; 1118 surface->map->info.b = copy_color.b; 1119 surface->map->info.a = copy_color.a; 1120 surface->map->info.flags = copy_flags; 1121 SDL_InvalidateMap(surface->map); 1122 1123 /* SDL_LowerBlit failed, and so the conversion */ 1124 if (ret < 0) { 1125 SDL_FreeSurface(convert); 1126 return NULL; 1127 } 1128 1129 if (copy_flags & SDL_COPY_COLORKEY) { 1130 SDL_bool set_colorkey_by_color = SDL_FALSE; 1131 SDL_bool convert_colorkey = SDL_TRUE; 1132 1133 if (surface->format->palette) { 1134 if (format->palette && 1135 surface->format->palette->ncolors <= format->palette->ncolors && 1136 (SDL_memcmp(surface->format->palette->colors, format->palette->colors, 1137 surface->format->palette->ncolors * sizeof(SDL_Color)) == 0)) { 1138 /* The palette is identical, just set the same colorkey */ 1139 SDL_SetColorKey(convert, 1, surface->map->info.colorkey); 1140 } else if (!format->palette) { 1141 if (format->Amask) { 1142 /* No need to add the colorkey, transparency is in the alpha channel*/ 1143 } else { 1144 /* Only set the colorkey information */ 1145 set_colorkey_by_color = SDL_TRUE; 1146 convert_colorkey = SDL_FALSE; 1147 } 1148 } else { 1149 set_colorkey_by_color = SDL_TRUE; 1150 } 1151 } else { 1152 set_colorkey_by_color = SDL_TRUE; 1153 } 1154 1155 if (set_colorkey_by_color) { 1156 SDL_Surface *tmp; 1157 SDL_Surface *tmp2; 1158 int converted_colorkey = 0; 1159 1160 /* Create a dummy surface to get the colorkey converted */ 1161 tmp = SDL_CreateRGBSurface(0, 1, 1, 1162 surface->format->BitsPerPixel, surface->format->Rmask, 1163 surface->format->Gmask, surface->format->Bmask, 1164 surface->format->Amask); 1165 1166 /* Share the palette, if any */ 1167 if (surface->format->palette) { 1168 SDL_SetSurfacePalette(tmp, surface->format->palette); 1169 } 1170 1171 SDL_FillRect(tmp, NULL, surface->map->info.colorkey); 1172 1173 tmp->map->info.flags &= ~SDL_COPY_COLORKEY; 1174 1175 /* Convertion of the colorkey */ 1176 tmp2 = SDL_ConvertSurface(tmp, format, 0); 1177 1178 /* Get the converted colorkey */ 1179 SDL_memcpy(&converted_colorkey, tmp2->pixels, tmp2->format->BytesPerPixel); 1180 1181 SDL_FreeSurface(tmp); 1182 SDL_FreeSurface(tmp2); 1183 1184 /* Set the converted colorkey on the new surface */ 1185 SDL_SetColorKey(convert, 1, converted_colorkey); 1186 1187 /* This is needed when converting for 3D texture upload */ 1188 if (convert_colorkey) { 1189 SDL_ConvertColorkeyToAlpha(convert, SDL_TRUE); 1190 } 1191 } 1192 } 1193 SDL_SetClipRect(convert, &surface->clip_rect); 1194 1195 /* Enable alpha blending by default if the new surface has an 1196 * alpha channel or alpha modulation */ 1197 if ((surface->format->Amask && format->Amask) || 1198 (palette_has_alpha && format->Amask) || 1199 (copy_flags & SDL_COPY_MODULATE_ALPHA)) { 1200 SDL_SetSurfaceBlendMode(convert, SDL_BLENDMODE_BLEND); 1201 } 1202 if ((copy_flags & SDL_COPY_RLE_DESIRED) || (flags & SDL_RLEACCEL)) { 1203 SDL_SetSurfaceRLE(convert, SDL_RLEACCEL); 1204 } 1205 1206 /* We're ready to go! */ 1207 return (convert); 1208 } 1209 1210 SDL_Surface * 1211 SDL_ConvertSurfaceFormat(SDL_Surface * surface, Uint32 pixel_format, 1212 Uint32 flags) 1213 { 1214 SDL_PixelFormat *fmt; 1215 SDL_Surface *convert = NULL; 1216 1217 fmt = SDL_AllocFormat(pixel_format); 1218 if (fmt) { 1219 convert = SDL_ConvertSurface(surface, fmt, flags); 1220 SDL_FreeFormat(fmt); 1221 } 1222 return convert; 1223 } 1224 1225 /* 1226 * Create a surface on the stack for quick blit operations 1227 */ 1228 static SDL_INLINE SDL_bool 1229 SDL_CreateSurfaceOnStack(int width, int height, Uint32 pixel_format, 1230 void * pixels, int pitch, SDL_Surface * surface, 1231 SDL_PixelFormat * format, SDL_BlitMap * blitmap) 1232 { 1233 if (SDL_ISPIXELFORMAT_INDEXED(pixel_format)) { 1234 SDL_SetError("Indexed pixel formats not supported"); 1235 return SDL_FALSE; 1236 } 1237 if (SDL_InitFormat(format, pixel_format) < 0) { 1238 return SDL_FALSE; 1239 } 1240 1241 SDL_zerop(surface); 1242 surface->flags = SDL_PREALLOC; 1243 surface->format = format; 1244 surface->pixels = pixels; 1245 surface->w = width; 1246 surface->h = height; 1247 surface->pitch = pitch; 1248 /* We don't actually need to set up the clip rect for our purposes */ 1249 /* SDL_SetClipRect(surface, NULL); */ 1250 1251 /* Allocate an empty mapping */ 1252 SDL_zerop(blitmap); 1253 blitmap->info.r = 0xFF; 1254 blitmap->info.g = 0xFF; 1255 blitmap->info.b = 0xFF; 1256 blitmap->info.a = 0xFF; 1257 surface->map = blitmap; 1258 1259 /* The surface is ready to go */ 1260 surface->refcount = 1; 1261 return SDL_TRUE; 1262 } 1263 1264 /* 1265 * Copy a block of pixels of one format to another format 1266 */ 1267 int SDL_ConvertPixels(int width, int height, 1268 Uint32 src_format, const void * src, int src_pitch, 1269 Uint32 dst_format, void * dst, int dst_pitch) 1270 { 1271 SDL_Surface src_surface, dst_surface; 1272 SDL_PixelFormat src_fmt, dst_fmt; 1273 SDL_BlitMap src_blitmap, dst_blitmap; 1274 SDL_Rect rect; 1275 void *nonconst_src = (void *) src; 1276 int ret; 1277 1278 /* Check to make sure we are blitting somewhere, so we don't crash */ 1279 if (!dst) { 1280 return SDL_InvalidParamError("dst"); 1281 } 1282 if (!dst_pitch) { 1283 return SDL_InvalidParamError("dst_pitch"); 1284 } 1285 1286 #if SDL_HAVE_YUV 1287 if (SDL_ISPIXELFORMAT_FOURCC(src_format) && SDL_ISPIXELFORMAT_FOURCC(dst_format)) { 1288 return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch); 1289 } else if (SDL_ISPIXELFORMAT_FOURCC(src_format)) { 1290 return SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch); 1291 } else if (SDL_ISPIXELFORMAT_FOURCC(dst_format)) { 1292 return SDL_ConvertPixels_RGB_to_YUV(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch); 1293 } 1294 #else 1295 if (SDL_ISPIXELFORMAT_FOURCC(src_format) || SDL_ISPIXELFORMAT_FOURCC(dst_format)) { 1296 SDL_SetError("SDL not built with YUV support"); 1297 return -1; 1298 } 1299 #endif 1300 1301 /* Fast path for same format copy */ 1302 if (src_format == dst_format) { 1303 int i; 1304 const int bpp = SDL_BYTESPERPIXEL(src_format); 1305 width *= bpp; 1306 for (i = height; i--;) { 1307 SDL_memcpy(dst, src, width); 1308 src = (const Uint8*)src + src_pitch; 1309 dst = (Uint8*)dst + dst_pitch; 1310 } 1311 return 0; 1312 } 1313 1314 if (!SDL_CreateSurfaceOnStack(width, height, src_format, nonconst_src, 1315 src_pitch, 1316 &src_surface, &src_fmt, &src_blitmap)) { 1317 return -1; 1318 } 1319 if (!SDL_CreateSurfaceOnStack(width, height, dst_format, dst, dst_pitch, 1320 &dst_surface, &dst_fmt, &dst_blitmap)) { 1321 return -1; 1322 } 1323 1324 /* Set up the rect and go! */ 1325 rect.x = 0; 1326 rect.y = 0; 1327 rect.w = width; 1328 rect.h = height; 1329 ret = SDL_LowerBlit(&src_surface, &rect, &dst_surface, &rect); 1330 1331 /* Free blitmap reference, after blitting between stack'ed surfaces */ 1332 SDL_InvalidateMap(src_surface.map); 1333 1334 return ret; 1335 } 1336 1337 /* 1338 * Free a surface created by the above function. 1339 */ 1340 void 1341 SDL_FreeSurface(SDL_Surface * surface) 1342 { 1343 if (surface == NULL) { 1344 return; 1345 } 1346 if (surface->flags & SDL_DONTFREE) { 1347 return; 1348 } 1349 SDL_InvalidateMap(surface->map); 1350 1351 SDL_InvalidateAllBlitMap(surface); 1352 1353 if (--surface->refcount > 0) { 1354 return; 1355 } 1356 while (surface->locked > 0) { 1357 SDL_UnlockSurface(surface); 1358 } 1359 #if SDL_HAVE_RLE 1360 if (surface->flags & SDL_RLEACCEL) { 1361 SDL_UnRLESurface(surface, 0); 1362 } 1363 #endif 1364 if (surface->format) { 1365 SDL_SetSurfacePalette(surface, NULL); 1366 SDL_FreeFormat(surface->format); 1367 surface->format = NULL; 1368 } 1369 if (surface->flags & SDL_PREALLOC) { 1370 /* Don't free */ 1371 } else if (surface->flags & SDL_SIMD_ALIGNED) { 1372 /* Free aligned */ 1373 SDL_SIMDFree(surface->pixels); 1374 } else { 1375 /* Normal */ 1376 SDL_free(surface->pixels); 1377 } 1378 if (surface->map) { 1379 SDL_FreeBlitMap(surface->map); 1380 } 1381 SDL_free(surface); 1382 } 1383 1384 /* vi: set ts=4 sw=4 expandtab: */