SDL_surface.c (31763B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 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 29/* Public routines */ 30/* 31 * Create an empty RGB surface of the appropriate depth 32 */ 33SDL_Surface * 34SDL_CreateRGBSurface(Uint32 flags, 35 int width, int height, int depth, 36 Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) 37{ 38 SDL_Surface *surface; 39 Uint32 format; 40 41 /* The flags are no longer used, make the compiler happy */ 42 (void)flags; 43 44 /* Get the pixel format */ 45 format = SDL_MasksToPixelFormatEnum(depth, Rmask, Gmask, Bmask, Amask); 46 if (format == SDL_PIXELFORMAT_UNKNOWN) { 47 SDL_SetError("Unknown pixel format"); 48 return NULL; 49 } 50 51 /* Allocate the surface */ 52 surface = (SDL_Surface *) SDL_calloc(1, sizeof(*surface)); 53 if (surface == NULL) { 54 SDL_OutOfMemory(); 55 return NULL; 56 } 57 58 surface->format = SDL_AllocFormat(format); 59 if (!surface->format) { 60 SDL_FreeSurface(surface); 61 return NULL; 62 } 63 surface->w = width; 64 surface->h = height; 65 surface->pitch = SDL_CalculatePitch(surface); 66 SDL_SetClipRect(surface, NULL); 67 68 if (SDL_ISPIXELFORMAT_INDEXED(surface->format->format)) { 69 SDL_Palette *palette = 70 SDL_AllocPalette((1 << surface->format->BitsPerPixel)); 71 if (!palette) { 72 SDL_FreeSurface(surface); 73 return NULL; 74 } 75 if (palette->ncolors == 2) { 76 /* Create a black and white bitmap palette */ 77 palette->colors[0].r = 0xFF; 78 palette->colors[0].g = 0xFF; 79 palette->colors[0].b = 0xFF; 80 palette->colors[1].r = 0x00; 81 palette->colors[1].g = 0x00; 82 palette->colors[1].b = 0x00; 83 } 84 SDL_SetSurfacePalette(surface, palette); 85 SDL_FreePalette(palette); 86 } 87 88 /* Get the pixels */ 89 if (surface->w && surface->h) { 90 surface->pixels = SDL_malloc(surface->h * surface->pitch); 91 if (!surface->pixels) { 92 SDL_FreeSurface(surface); 93 SDL_OutOfMemory(); 94 return NULL; 95 } 96 /* This is important for bitmaps */ 97 SDL_memset(surface->pixels, 0, surface->h * surface->pitch); 98 } 99 100 /* Allocate an empty mapping */ 101 surface->map = SDL_AllocBlitMap(); 102 if (!surface->map) { 103 SDL_FreeSurface(surface); 104 return NULL; 105 } 106 107 /* By default surface with an alpha mask are set up for blending */ 108 if (Amask) { 109 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); 110 } 111 112 /* The surface is ready to go */ 113 surface->refcount = 1; 114 return surface; 115} 116 117/* 118 * Create an RGB surface from an existing memory buffer 119 */ 120SDL_Surface * 121SDL_CreateRGBSurfaceFrom(void *pixels, 122 int width, int height, int depth, int pitch, 123 Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, 124 Uint32 Amask) 125{ 126 SDL_Surface *surface; 127 128 surface = 129 SDL_CreateRGBSurface(0, 0, 0, depth, Rmask, Gmask, Bmask, Amask); 130 if (surface != NULL) { 131 surface->flags |= SDL_PREALLOC; 132 surface->pixels = pixels; 133 surface->w = width; 134 surface->h = height; 135 surface->pitch = pitch; 136 SDL_SetClipRect(surface, NULL); 137 } 138 return surface; 139} 140 141int 142SDL_SetSurfacePalette(SDL_Surface * surface, SDL_Palette * palette) 143{ 144 if (!surface) { 145 return SDL_SetError("SDL_SetSurfacePalette() passed a NULL surface"); 146 } 147 if (SDL_SetPixelFormatPalette(surface->format, palette) < 0) { 148 return -1; 149 } 150 SDL_InvalidateMap(surface->map); 151 152 return 0; 153} 154 155int 156SDL_SetSurfaceRLE(SDL_Surface * surface, int flag) 157{ 158 int flags; 159 160 if (!surface) { 161 return -1; 162 } 163 164 flags = surface->map->info.flags; 165 if (flag) { 166 surface->map->info.flags |= SDL_COPY_RLE_DESIRED; 167 } else { 168 surface->map->info.flags &= ~SDL_COPY_RLE_DESIRED; 169 } 170 if (surface->map->info.flags != flags) { 171 SDL_InvalidateMap(surface->map); 172 } 173 return 0; 174} 175 176int 177SDL_SetColorKey(SDL_Surface * surface, int flag, Uint32 key) 178{ 179 int flags; 180 181 if (!surface) { 182 return SDL_InvalidParamError("surface"); 183 } 184 185 if (surface->format->palette && key >= ((Uint32) surface->format->palette->ncolors)) { 186 return SDL_InvalidParamError("key"); 187 } 188 189 if (flag & SDL_RLEACCEL) { 190 SDL_SetSurfaceRLE(surface, 1); 191 } 192 193 flags = surface->map->info.flags; 194 if (flag) { 195 surface->map->info.flags |= SDL_COPY_COLORKEY; 196 surface->map->info.colorkey = key; 197 if (surface->format->palette) { 198 surface->format->palette->colors[surface->map->info.colorkey].a = SDL_ALPHA_TRANSPARENT; 199 ++surface->format->palette->version; 200 if (!surface->format->palette->version) { 201 surface->format->palette->version = 1; 202 } 203 } 204 } else { 205 if (surface->format->palette) { 206 surface->format->palette->colors[surface->map->info.colorkey].a = SDL_ALPHA_OPAQUE; 207 ++surface->format->palette->version; 208 if (!surface->format->palette->version) { 209 surface->format->palette->version = 1; 210 } 211 } 212 surface->map->info.flags &= ~SDL_COPY_COLORKEY; 213 } 214 if (surface->map->info.flags != flags) { 215 SDL_InvalidateMap(surface->map); 216 } 217 218 return 0; 219} 220 221int 222SDL_GetColorKey(SDL_Surface * surface, Uint32 * key) 223{ 224 if (!surface) { 225 return -1; 226 } 227 228 if (!(surface->map->info.flags & SDL_COPY_COLORKEY)) { 229 return -1; 230 } 231 232 if (key) { 233 *key = surface->map->info.colorkey; 234 } 235 return 0; 236} 237 238/* This is a fairly slow function to switch from colorkey to alpha */ 239static void 240SDL_ConvertColorkeyToAlpha(SDL_Surface * surface) 241{ 242 int x, y; 243 244 if (!surface) { 245 return; 246 } 247 248 if (!(surface->map->info.flags & SDL_COPY_COLORKEY) || 249 !surface->format->Amask) { 250 return; 251 } 252 253 SDL_LockSurface(surface); 254 255 switch (surface->format->BytesPerPixel) { 256 case 2: 257 { 258 Uint16 *row, *spot; 259 Uint16 ckey = (Uint16) surface->map->info.colorkey; 260 Uint16 mask = (Uint16) (~surface->format->Amask); 261 262 /* Ignore alpha in colorkey comparison */ 263 ckey &= mask; 264 row = (Uint16 *) surface->pixels; 265 for (y = surface->h; y--;) { 266 spot = row; 267 for (x = surface->w; x--;) { 268 if ((*spot & mask) == ckey) { 269 *spot &= mask; 270 } 271 ++spot; 272 } 273 row += surface->pitch / 2; 274 } 275 } 276 break; 277 case 3: 278 /* FIXME */ 279 break; 280 case 4: 281 { 282 Uint32 *row, *spot; 283 Uint32 ckey = surface->map->info.colorkey; 284 Uint32 mask = ~surface->format->Amask; 285 286 /* Ignore alpha in colorkey comparison */ 287 ckey &= mask; 288 row = (Uint32 *) surface->pixels; 289 for (y = surface->h; y--;) { 290 spot = row; 291 for (x = surface->w; x--;) { 292 if ((*spot & mask) == ckey) { 293 *spot &= mask; 294 } 295 ++spot; 296 } 297 row += surface->pitch / 4; 298 } 299 } 300 break; 301 } 302 303 SDL_UnlockSurface(surface); 304 305 SDL_SetColorKey(surface, 0, 0); 306 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); 307} 308 309int 310SDL_SetSurfaceColorMod(SDL_Surface * surface, Uint8 r, Uint8 g, Uint8 b) 311{ 312 int flags; 313 314 if (!surface) { 315 return -1; 316 } 317 318 surface->map->info.r = r; 319 surface->map->info.g = g; 320 surface->map->info.b = b; 321 322 flags = surface->map->info.flags; 323 if (r != 0xFF || g != 0xFF || b != 0xFF) { 324 surface->map->info.flags |= SDL_COPY_MODULATE_COLOR; 325 } else { 326 surface->map->info.flags &= ~SDL_COPY_MODULATE_COLOR; 327 } 328 if (surface->map->info.flags != flags) { 329 SDL_InvalidateMap(surface->map); 330 } 331 return 0; 332} 333 334 335int 336SDL_GetSurfaceColorMod(SDL_Surface * surface, Uint8 * r, Uint8 * g, Uint8 * b) 337{ 338 if (!surface) { 339 return -1; 340 } 341 342 if (r) { 343 *r = surface->map->info.r; 344 } 345 if (g) { 346 *g = surface->map->info.g; 347 } 348 if (b) { 349 *b = surface->map->info.b; 350 } 351 return 0; 352} 353 354int 355SDL_SetSurfaceAlphaMod(SDL_Surface * surface, Uint8 alpha) 356{ 357 int flags; 358 359 if (!surface) { 360 return -1; 361 } 362 363 surface->map->info.a = alpha; 364 365 flags = surface->map->info.flags; 366 if (alpha != 0xFF) { 367 surface->map->info.flags |= SDL_COPY_MODULATE_ALPHA; 368 } else { 369 surface->map->info.flags &= ~SDL_COPY_MODULATE_ALPHA; 370 } 371 if (surface->map->info.flags != flags) { 372 SDL_InvalidateMap(surface->map); 373 } 374 return 0; 375} 376 377int 378SDL_GetSurfaceAlphaMod(SDL_Surface * surface, Uint8 * alpha) 379{ 380 if (!surface) { 381 return -1; 382 } 383 384 if (alpha) { 385 *alpha = surface->map->info.a; 386 } 387 return 0; 388} 389 390int 391SDL_SetSurfaceBlendMode(SDL_Surface * surface, SDL_BlendMode blendMode) 392{ 393 int flags, status; 394 395 if (!surface) { 396 return -1; 397 } 398 399 status = 0; 400 flags = surface->map->info.flags; 401 surface->map->info.flags &= 402 ~(SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD); 403 switch (blendMode) { 404 case SDL_BLENDMODE_NONE: 405 break; 406 case SDL_BLENDMODE_BLEND: 407 surface->map->info.flags |= SDL_COPY_BLEND; 408 break; 409 case SDL_BLENDMODE_ADD: 410 surface->map->info.flags |= SDL_COPY_ADD; 411 break; 412 case SDL_BLENDMODE_MOD: 413 surface->map->info.flags |= SDL_COPY_MOD; 414 break; 415 default: 416 status = SDL_Unsupported(); 417 break; 418 } 419 420 if (surface->map->info.flags != flags) { 421 SDL_InvalidateMap(surface->map); 422 } 423 424 return status; 425} 426 427int 428SDL_GetSurfaceBlendMode(SDL_Surface * surface, SDL_BlendMode *blendMode) 429{ 430 if (!surface) { 431 return -1; 432 } 433 434 if (!blendMode) { 435 return 0; 436 } 437 438 switch (surface->map-> 439 info.flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD)) { 440 case SDL_COPY_BLEND: 441 *blendMode = SDL_BLENDMODE_BLEND; 442 break; 443 case SDL_COPY_ADD: 444 *blendMode = SDL_BLENDMODE_ADD; 445 break; 446 case SDL_COPY_MOD: 447 *blendMode = SDL_BLENDMODE_MOD; 448 break; 449 default: 450 *blendMode = SDL_BLENDMODE_NONE; 451 break; 452 } 453 return 0; 454} 455 456SDL_bool 457SDL_SetClipRect(SDL_Surface * surface, const SDL_Rect * rect) 458{ 459 SDL_Rect full_rect; 460 461 /* Don't do anything if there's no surface to act on */ 462 if (!surface) { 463 return SDL_FALSE; 464 } 465 466 /* Set up the full surface rectangle */ 467 full_rect.x = 0; 468 full_rect.y = 0; 469 full_rect.w = surface->w; 470 full_rect.h = surface->h; 471 472 /* Set the clipping rectangle */ 473 if (!rect) { 474 surface->clip_rect = full_rect; 475 return SDL_TRUE; 476 } 477 return SDL_IntersectRect(rect, &full_rect, &surface->clip_rect); 478} 479 480void 481SDL_GetClipRect(SDL_Surface * surface, SDL_Rect * rect) 482{ 483 if (surface && rect) { 484 *rect = surface->clip_rect; 485 } 486} 487 488/* 489 * Set up a blit between two surfaces -- split into three parts: 490 * The upper part, SDL_UpperBlit(), performs clipping and rectangle 491 * verification. The lower part is a pointer to a low level 492 * accelerated blitting function. 493 * 494 * These parts are separated out and each used internally by this 495 * library in the optimimum places. They are exported so that if 496 * you know exactly what you are doing, you can optimize your code 497 * by calling the one(s) you need. 498 */ 499int 500SDL_LowerBlit(SDL_Surface * src, SDL_Rect * srcrect, 501 SDL_Surface * dst, SDL_Rect * dstrect) 502{ 503 /* Check to make sure the blit mapping is valid */ 504 if ((src->map->dst != dst) || 505 (dst->format->palette && 506 src->map->dst_palette_version != dst->format->palette->version) || 507 (src->format->palette && 508 src->map->src_palette_version != src->format->palette->version)) { 509 if (SDL_MapSurface(src, dst) < 0) { 510 return (-1); 511 } 512 /* just here for debugging */ 513/* printf */ 514/* ("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", */ 515/* src, dst->flags, src->map->info.flags, dst, dst->flags, */ 516/* dst->map->info.flags, src->map->blit); */ 517 } 518 return (src->map->blit(src, srcrect, dst, dstrect)); 519} 520 521 522int 523SDL_UpperBlit(SDL_Surface * src, const SDL_Rect * srcrect, 524 SDL_Surface * dst, SDL_Rect * dstrect) 525{ 526 SDL_Rect fulldst; 527 int srcx, srcy, w, h; 528 529 /* Make sure the surfaces aren't locked */ 530 if (!src || !dst) { 531 return SDL_SetError("SDL_UpperBlit: passed a NULL surface"); 532 } 533 if (src->locked || dst->locked) { 534 return SDL_SetError("Surfaces must not be locked during blit"); 535 } 536 537 /* If the destination rectangle is NULL, use the entire dest surface */ 538 if (dstrect == NULL) { 539 fulldst.x = fulldst.y = 0; 540 fulldst.w = dst->w; 541 fulldst.h = dst->h; 542 dstrect = &fulldst; 543 } 544 545 /* clip the source rectangle to the source surface */ 546 if (srcrect) { 547 int maxw, maxh; 548 549 srcx = srcrect->x; 550 w = srcrect->w; 551 if (srcx < 0) { 552 w += srcx; 553 dstrect->x -= srcx; 554 srcx = 0; 555 } 556 maxw = src->w - srcx; 557 if (maxw < w) 558 w = maxw; 559 560 srcy = srcrect->y; 561 h = srcrect->h; 562 if (srcy < 0) { 563 h += srcy; 564 dstrect->y -= srcy; 565 srcy = 0; 566 } 567 maxh = src->h - srcy; 568 if (maxh < h) 569 h = maxh; 570 571 } else { 572 srcx = srcy = 0; 573 w = src->w; 574 h = src->h; 575 } 576 577 /* clip the destination rectangle against the clip rectangle */ 578 { 579 SDL_Rect *clip = &dst->clip_rect; 580 int dx, dy; 581 582 dx = clip->x - dstrect->x; 583 if (dx > 0) { 584 w -= dx; 585 dstrect->x += dx; 586 srcx += dx; 587 } 588 dx = dstrect->x + w - clip->x - clip->w; 589 if (dx > 0) 590 w -= dx; 591 592 dy = clip->y - dstrect->y; 593 if (dy > 0) { 594 h -= dy; 595 dstrect->y += dy; 596 srcy += dy; 597 } 598 dy = dstrect->y + h - clip->y - clip->h; 599 if (dy > 0) 600 h -= dy; 601 } 602 603 /* Switch back to a fast blit if we were previously stretching */ 604 if (src->map->info.flags & SDL_COPY_NEAREST) { 605 src->map->info.flags &= ~SDL_COPY_NEAREST; 606 SDL_InvalidateMap(src->map); 607 } 608 609 if (w > 0 && h > 0) { 610 SDL_Rect sr; 611 sr.x = srcx; 612 sr.y = srcy; 613 sr.w = dstrect->w = w; 614 sr.h = dstrect->h = h; 615 return SDL_LowerBlit(src, &sr, dst, dstrect); 616 } 617 dstrect->w = dstrect->h = 0; 618 return 0; 619} 620 621int 622SDL_UpperBlitScaled(SDL_Surface * src, const SDL_Rect * srcrect, 623 SDL_Surface * dst, SDL_Rect * dstrect) 624{ 625 double src_x0, src_y0, src_x1, src_y1; 626 double dst_x0, dst_y0, dst_x1, dst_y1; 627 SDL_Rect final_src, final_dst; 628 double scaling_w, scaling_h; 629 int src_w, src_h; 630 int dst_w, dst_h; 631 632 /* Make sure the surfaces aren't locked */ 633 if (!src || !dst) { 634 return SDL_SetError("SDL_UpperBlitScaled: passed a NULL surface"); 635 } 636 if (src->locked || dst->locked) { 637 return SDL_SetError("Surfaces must not be locked during blit"); 638 } 639 640 if (NULL == srcrect) { 641 src_w = src->w; 642 src_h = src->h; 643 } else { 644 src_w = srcrect->w; 645 src_h = srcrect->h; 646 } 647 648 if (NULL == dstrect) { 649 dst_w = dst->w; 650 dst_h = dst->h; 651 } else { 652 dst_w = dstrect->w; 653 dst_h = dstrect->h; 654 } 655 656 if (dst_w == src_w && dst_h == src_h) { 657 /* No scaling, defer to regular blit */ 658 return SDL_BlitSurface(src, srcrect, dst, dstrect); 659 } 660 661 scaling_w = (double)dst_w / src_w; 662 scaling_h = (double)dst_h / src_h; 663 664 if (NULL == dstrect) { 665 dst_x0 = 0; 666 dst_y0 = 0; 667 dst_x1 = dst_w - 1; 668 dst_y1 = dst_h - 1; 669 } else { 670 dst_x0 = dstrect->x; 671 dst_y0 = dstrect->y; 672 dst_x1 = dst_x0 + dst_w - 1; 673 dst_y1 = dst_y0 + dst_h - 1; 674 } 675 676 if (NULL == srcrect) { 677 src_x0 = 0; 678 src_y0 = 0; 679 src_x1 = src_w - 1; 680 src_y1 = src_h - 1; 681 } else { 682 src_x0 = srcrect->x; 683 src_y0 = srcrect->y; 684 src_x1 = src_x0 + src_w - 1; 685 src_y1 = src_y0 + src_h - 1; 686 687 /* Clip source rectangle to the source surface */ 688 689 if (src_x0 < 0) { 690 dst_x0 -= src_x0 * scaling_w; 691 src_x0 = 0; 692 } 693 694 if (src_x1 >= src->w) { 695 dst_x1 -= (src_x1 - src->w + 1) * scaling_w; 696 src_x1 = src->w - 1; 697 } 698 699 if (src_y0 < 0) { 700 dst_y0 -= src_y0 * scaling_h; 701 src_y0 = 0; 702 } 703 704 if (src_y1 >= src->h) { 705 dst_y1 -= (src_y1 - src->h + 1) * scaling_h; 706 src_y1 = src->h - 1; 707 } 708 } 709 710 /* Clip destination rectangle to the clip rectangle */ 711 712 /* Translate to clip space for easier calculations */ 713 dst_x0 -= dst->clip_rect.x; 714 dst_x1 -= dst->clip_rect.x; 715 dst_y0 -= dst->clip_rect.y; 716 dst_y1 -= dst->clip_rect.y; 717 718 if (dst_x0 < 0) { 719 src_x0 -= dst_x0 / scaling_w; 720 dst_x0 = 0; 721 } 722 723 if (dst_x1 >= dst->clip_rect.w) { 724 src_x1 -= (dst_x1 - dst->clip_rect.w + 1) / scaling_w; 725 dst_x1 = dst->clip_rect.w - 1; 726 } 727 728 if (dst_y0 < 0) { 729 src_y0 -= dst_y0 / scaling_h; 730 dst_y0 = 0; 731 } 732 733 if (dst_y1 >= dst->clip_rect.h) { 734 src_y1 -= (dst_y1 - dst->clip_rect.h + 1) / scaling_h; 735 dst_y1 = dst->clip_rect.h - 1; 736 } 737 738 /* Translate back to surface coordinates */ 739 dst_x0 += dst->clip_rect.x; 740 dst_x1 += dst->clip_rect.x; 741 dst_y0 += dst->clip_rect.y; 742 dst_y1 += dst->clip_rect.y; 743 744 final_src.x = SDL_floor(src_x0 + 0.5); 745 final_src.y = SDL_floor(src_y0 + 0.5); 746 final_src.w = SDL_floor(src_x1 - src_x0 + 1.5); 747 final_src.h = SDL_floor(src_y1 - src_y0 + 1.5); 748 749 final_dst.x = SDL_floor(dst_x0 + 0.5); 750 final_dst.y = SDL_floor(dst_y0 + 0.5); 751 final_dst.w = SDL_floor(dst_x1 - dst_x0 + 1.5); 752 final_dst.h = SDL_floor(dst_y1 - dst_y0 + 1.5); 753 754 if (final_dst.w < 0) 755 final_dst.w = 0; 756 if (final_dst.h < 0) 757 final_dst.h = 0; 758 759 if (dstrect) 760 *dstrect = final_dst; 761 762 if (final_dst.w == 0 || final_dst.h == 0 || 763 final_src.w <= 0 || final_src.h <= 0) { 764 /* No-op. */ 765 return 0; 766 } 767 768 return SDL_LowerBlitScaled(src, &final_src, dst, &final_dst); 769} 770 771/** 772 * This is a semi-private blit function and it performs low-level surface 773 * scaled blitting only. 774 */ 775int 776SDL_LowerBlitScaled(SDL_Surface * src, SDL_Rect * srcrect, 777 SDL_Surface * dst, SDL_Rect * dstrect) 778{ 779 static const Uint32 complex_copy_flags = ( 780 SDL_COPY_MODULATE_COLOR | SDL_COPY_MODULATE_ALPHA | 781 SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | 782 SDL_COPY_COLORKEY 783 ); 784 785 if (!(src->map->info.flags & SDL_COPY_NEAREST)) { 786 src->map->info.flags |= SDL_COPY_NEAREST; 787 SDL_InvalidateMap(src->map); 788 } 789 790 if ( !(src->map->info.flags & complex_copy_flags) && 791 src->format->format == dst->format->format && 792 !SDL_ISPIXELFORMAT_INDEXED(src->format->format) ) { 793 return SDL_SoftStretch( src, srcrect, dst, dstrect ); 794 } else { 795 return SDL_LowerBlit( src, srcrect, dst, dstrect ); 796 } 797} 798 799/* 800 * Lock a surface to directly access the pixels 801 */ 802int 803SDL_LockSurface(SDL_Surface * surface) 804{ 805 if (!surface->locked) { 806 /* Perform the lock */ 807 if (surface->flags & SDL_RLEACCEL) { 808 SDL_UnRLESurface(surface, 1); 809 surface->flags |= SDL_RLEACCEL; /* save accel'd state */ 810 } 811 } 812 813 /* Increment the surface lock count, for recursive locks */ 814 ++surface->locked; 815 816 /* Ready to go.. */ 817 return (0); 818} 819 820/* 821 * Unlock a previously locked surface 822 */ 823void 824SDL_UnlockSurface(SDL_Surface * surface) 825{ 826 /* Only perform an unlock if we are locked */ 827 if (!surface->locked || (--surface->locked > 0)) { 828 return; 829 } 830 831 /* Update RLE encoded surface with new data */ 832 if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) { 833 surface->flags &= ~SDL_RLEACCEL; /* stop lying */ 834 SDL_RLESurface(surface); 835 } 836} 837 838/* 839 * Convert a surface into the specified pixel format. 840 */ 841SDL_Surface * 842SDL_ConvertSurface(SDL_Surface * surface, const SDL_PixelFormat * format, 843 Uint32 flags) 844{ 845 SDL_Surface *convert; 846 Uint32 copy_flags; 847 SDL_Color copy_color; 848 SDL_Rect bounds; 849 850 /* Check for empty destination palette! (results in empty image) */ 851 if (format->palette != NULL) { 852 int i; 853 for (i = 0; i < format->palette->ncolors; ++i) { 854 if ((format->palette->colors[i].r != 0xFF) || 855 (format->palette->colors[i].g != 0xFF) || 856 (format->palette->colors[i].b != 0xFF)) 857 break; 858 } 859 if (i == format->palette->ncolors) { 860 SDL_SetError("Empty destination palette"); 861 return (NULL); 862 } 863 } 864 865 /* Create a new surface with the desired format */ 866 convert = SDL_CreateRGBSurface(flags, surface->w, surface->h, 867 format->BitsPerPixel, format->Rmask, 868 format->Gmask, format->Bmask, 869 format->Amask); 870 if (convert == NULL) { 871 return (NULL); 872 } 873 874 /* Copy the palette if any */ 875 if (format->palette && convert->format->palette) { 876 SDL_memcpy(convert->format->palette->colors, 877 format->palette->colors, 878 format->palette->ncolors * sizeof(SDL_Color)); 879 convert->format->palette->ncolors = format->palette->ncolors; 880 } 881 882 /* Save the original copy flags */ 883 copy_flags = surface->map->info.flags; 884 copy_color.r = surface->map->info.r; 885 copy_color.g = surface->map->info.g; 886 copy_color.b = surface->map->info.b; 887 copy_color.a = surface->map->info.a; 888 surface->map->info.r = 0xFF; 889 surface->map->info.g = 0xFF; 890 surface->map->info.b = 0xFF; 891 surface->map->info.a = 0xFF; 892 surface->map->info.flags = 0; 893 SDL_InvalidateMap(surface->map); 894 895 /* Copy over the image data */ 896 bounds.x = 0; 897 bounds.y = 0; 898 bounds.w = surface->w; 899 bounds.h = surface->h; 900 SDL_LowerBlit(surface, &bounds, convert, &bounds); 901 902 /* Clean up the original surface, and update converted surface */ 903 convert->map->info.r = copy_color.r; 904 convert->map->info.g = copy_color.g; 905 convert->map->info.b = copy_color.b; 906 convert->map->info.a = copy_color.a; 907 convert->map->info.flags = 908 (copy_flags & 909 ~(SDL_COPY_COLORKEY | SDL_COPY_BLEND 910 | SDL_COPY_RLE_DESIRED | SDL_COPY_RLE_COLORKEY | 911 SDL_COPY_RLE_ALPHAKEY)); 912 surface->map->info.r = copy_color.r; 913 surface->map->info.g = copy_color.g; 914 surface->map->info.b = copy_color.b; 915 surface->map->info.a = copy_color.a; 916 surface->map->info.flags = copy_flags; 917 SDL_InvalidateMap(surface->map); 918 if (copy_flags & SDL_COPY_COLORKEY) { 919 SDL_bool set_colorkey_by_color = SDL_FALSE; 920 921 if (surface->format->palette) { 922 if (format->palette && 923 surface->format->palette->ncolors <= format->palette->ncolors && 924 (SDL_memcmp(surface->format->palette->colors, format->palette->colors, 925 surface->format->palette->ncolors * sizeof(SDL_Color)) == 0)) { 926 /* The palette is identical, just set the same colorkey */ 927 SDL_SetColorKey(convert, 1, surface->map->info.colorkey); 928 } else if (format->Amask) { 929 /* The alpha was set in the destination from the palette */ 930 } else { 931 set_colorkey_by_color = SDL_TRUE; 932 } 933 } else { 934 set_colorkey_by_color = SDL_TRUE; 935 } 936 937 if (set_colorkey_by_color) { 938 /* Set the colorkey by color, which needs to be unique */ 939 Uint8 keyR, keyG, keyB, keyA; 940 941 SDL_GetRGBA(surface->map->info.colorkey, surface->format, &keyR, 942 &keyG, &keyB, &keyA); 943 SDL_SetColorKey(convert, 1, 944 SDL_MapRGBA(convert->format, keyR, keyG, keyB, keyA)); 945 /* This is needed when converting for 3D texture upload */ 946 SDL_ConvertColorkeyToAlpha(convert); 947 } 948 } 949 SDL_SetClipRect(convert, &surface->clip_rect); 950 951 /* Enable alpha blending by default if the new surface has an 952 * alpha channel or alpha modulation */ 953 if ((surface->format->Amask && format->Amask) || 954 (copy_flags & (SDL_COPY_COLORKEY|SDL_COPY_MODULATE_ALPHA))) { 955 SDL_SetSurfaceBlendMode(convert, SDL_BLENDMODE_BLEND); 956 } 957 if ((copy_flags & SDL_COPY_RLE_DESIRED) || (flags & SDL_RLEACCEL)) { 958 SDL_SetSurfaceRLE(convert, SDL_RLEACCEL); 959 } 960 961 /* We're ready to go! */ 962 return (convert); 963} 964 965SDL_Surface * 966SDL_ConvertSurfaceFormat(SDL_Surface * surface, Uint32 pixel_format, 967 Uint32 flags) 968{ 969 SDL_PixelFormat *fmt; 970 SDL_Surface *convert = NULL; 971 972 fmt = SDL_AllocFormat(pixel_format); 973 if (fmt) { 974 convert = SDL_ConvertSurface(surface, fmt, flags); 975 SDL_FreeFormat(fmt); 976 } 977 return convert; 978} 979 980/* 981 * Create a surface on the stack for quick blit operations 982 */ 983static SDL_INLINE SDL_bool 984SDL_CreateSurfaceOnStack(int width, int height, Uint32 pixel_format, 985 void * pixels, int pitch, SDL_Surface * surface, 986 SDL_PixelFormat * format, SDL_BlitMap * blitmap) 987{ 988 if (SDL_ISPIXELFORMAT_INDEXED(pixel_format)) { 989 SDL_SetError("Indexed pixel formats not supported"); 990 return SDL_FALSE; 991 } 992 if (SDL_InitFormat(format, pixel_format) < 0) { 993 return SDL_FALSE; 994 } 995 996 SDL_zerop(surface); 997 surface->flags = SDL_PREALLOC; 998 surface->format = format; 999 surface->pixels = pixels; 1000 surface->w = width; 1001 surface->h = height; 1002 surface->pitch = pitch; 1003 /* We don't actually need to set up the clip rect for our purposes */ 1004 /* SDL_SetClipRect(surface, NULL); */ 1005 1006 /* Allocate an empty mapping */ 1007 SDL_zerop(blitmap); 1008 blitmap->info.r = 0xFF; 1009 blitmap->info.g = 0xFF; 1010 blitmap->info.b = 0xFF; 1011 blitmap->info.a = 0xFF; 1012 surface->map = blitmap; 1013 1014 /* The surface is ready to go */ 1015 surface->refcount = 1; 1016 return SDL_TRUE; 1017} 1018 1019/* 1020 * Copy a block of pixels of one format to another format 1021 */ 1022int SDL_ConvertPixels(int width, int height, 1023 Uint32 src_format, const void * src, int src_pitch, 1024 Uint32 dst_format, void * dst, int dst_pitch) 1025{ 1026 SDL_Surface src_surface, dst_surface; 1027 SDL_PixelFormat src_fmt, dst_fmt; 1028 SDL_BlitMap src_blitmap, dst_blitmap; 1029 SDL_Rect rect; 1030 void *nonconst_src = (void *) src; 1031 1032 /* Check to make sure we are blitting somewhere, so we don't crash */ 1033 if (!dst) { 1034 return SDL_InvalidParamError("dst"); 1035 } 1036 if (!dst_pitch) { 1037 return SDL_InvalidParamError("dst_pitch"); 1038 } 1039 1040 /* Fast path for same format copy */ 1041 if (src_format == dst_format) { 1042 int bpp, i; 1043 1044 if (SDL_ISPIXELFORMAT_FOURCC(src_format)) { 1045 switch (src_format) { 1046 case SDL_PIXELFORMAT_YUY2: 1047 case SDL_PIXELFORMAT_UYVY: 1048 case SDL_PIXELFORMAT_YVYU: 1049 bpp = 2; 1050 break; 1051 case SDL_PIXELFORMAT_YV12: 1052 case SDL_PIXELFORMAT_IYUV: 1053 case SDL_PIXELFORMAT_NV12: 1054 case SDL_PIXELFORMAT_NV21: 1055 bpp = 1; 1056 break; 1057 default: 1058 return SDL_SetError("Unknown FOURCC pixel format"); 1059 } 1060 } else { 1061 bpp = SDL_BYTESPERPIXEL(src_format); 1062 } 1063 width *= bpp; 1064 1065 for (i = height; i--;) { 1066 SDL_memcpy(dst, src, width); 1067 src = (Uint8*)src + src_pitch; 1068 dst = (Uint8*)dst + dst_pitch; 1069 } 1070 1071 if (src_format == SDL_PIXELFORMAT_YV12 || src_format == SDL_PIXELFORMAT_IYUV) { 1072 /* U and V planes are a quarter the size of the Y plane */ 1073 width /= 2; 1074 height /= 2; 1075 src_pitch /= 2; 1076 dst_pitch /= 2; 1077 for (i = height * 2; i--;) { 1078 SDL_memcpy(dst, src, width); 1079 src = (Uint8*)src + src_pitch; 1080 dst = (Uint8*)dst + dst_pitch; 1081 } 1082 } else if (src_format == SDL_PIXELFORMAT_NV12 || src_format == SDL_PIXELFORMAT_NV21) { 1083 /* U/V plane is half the height of the Y plane */ 1084 height /= 2; 1085 for (i = height; i--;) { 1086 SDL_memcpy(dst, src, width); 1087 src = (Uint8*)src + src_pitch; 1088 dst = (Uint8*)dst + dst_pitch; 1089 } 1090 } 1091 return 0; 1092 } 1093 1094 if (!SDL_CreateSurfaceOnStack(width, height, src_format, nonconst_src, 1095 src_pitch, 1096 &src_surface, &src_fmt, &src_blitmap)) { 1097 return -1; 1098 } 1099 if (!SDL_CreateSurfaceOnStack(width, height, dst_format, dst, dst_pitch, 1100 &dst_surface, &dst_fmt, &dst_blitmap)) { 1101 return -1; 1102 } 1103 1104 /* Set up the rect and go! */ 1105 rect.x = 0; 1106 rect.y = 0; 1107 rect.w = width; 1108 rect.h = height; 1109 return SDL_LowerBlit(&src_surface, &rect, &dst_surface, &rect); 1110} 1111 1112/* 1113 * Free a surface created by the above function. 1114 */ 1115void 1116SDL_FreeSurface(SDL_Surface * surface) 1117{ 1118 if (surface == NULL) { 1119 return; 1120 } 1121 if (surface->flags & SDL_DONTFREE) { 1122 return; 1123 } 1124 if (--surface->refcount > 0) { 1125 return; 1126 } 1127 while (surface->locked > 0) { 1128 SDL_UnlockSurface(surface); 1129 } 1130 if (surface->flags & SDL_RLEACCEL) { 1131 SDL_UnRLESurface(surface, 0); 1132 } 1133 if (surface->format) { 1134 SDL_SetSurfacePalette(surface, NULL); 1135 SDL_FreeFormat(surface->format); 1136 surface->format = NULL; 1137 } 1138 if (surface->map != NULL) { 1139 SDL_FreeBlitMap(surface->map); 1140 surface->map = NULL; 1141 } 1142 if (!(surface->flags & SDL_PREALLOC)) { 1143 SDL_free(surface->pixels); 1144 } 1145 SDL_free(surface); 1146} 1147 1148/* vi: set ts=4 sw=4 expandtab: */