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