src/video/SDL_surface.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 29 Nov 2008 11:26:01 +0000
changeset 2807 365fe1a2aad5
parent 2787 93764fe8601a
child 2824 4dba7aa7ea77
permissions -rw-r--r--
The SDL_RLEACCEL flag is respected in SDL_ConvertSurface(), per the docs.
Fixed saving BMP files of surfaces with an alpha channel.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #include "SDL_video.h"
    25 #include "SDL_compat.h"
    26 #include "SDL_sysvideo.h"
    27 #include "SDL_blit.h"
    28 #include "SDL_RLEaccel_c.h"
    29 #include "SDL_pixels_c.h"
    30 #include "SDL_leaks.h"
    31 
    32 
    33 /* Public routines */
    34 /*
    35  * Create an empty RGB surface of the appropriate depth
    36  */
    37 SDL_Surface *
    38 SDL_CreateRGBSurface(Uint32 flags,
    39                      int width, int height, int depth,
    40                      Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
    41 {
    42     SDL_Surface *surface;
    43 
    44     /* The flags are no longer used, make the compiler happy */
    45     flags;
    46 
    47     /* Allocate the surface */
    48     surface = (SDL_Surface *) SDL_calloc(1, sizeof(*surface));
    49     if (surface == NULL) {
    50         SDL_OutOfMemory();
    51         return NULL;
    52     }
    53 
    54     surface->format = SDL_AllocFormat(depth, Rmask, Gmask, Bmask, Amask);
    55     if (!surface->format) {
    56         SDL_FreeSurface(surface);
    57         return NULL;
    58     }
    59     surface->w = width;
    60     surface->h = height;
    61     surface->pitch = SDL_CalculatePitch(surface);
    62     SDL_SetClipRect(surface, NULL);
    63 
    64     if (surface->format->BitsPerPixel <= 8) {
    65         SDL_Palette *palette =
    66             SDL_AllocPalette((1 << surface->format->BitsPerPixel));
    67         if (!palette) {
    68             SDL_FreeSurface(surface);
    69             return NULL;
    70         }
    71         if (Rmask || Bmask || Gmask) {
    72             const SDL_PixelFormat *format = surface->format;
    73 
    74             /* create palette according to masks */
    75             int i;
    76             int Rm = 0, Gm = 0, Bm = 0;
    77             int Rw = 0, Gw = 0, Bw = 0;
    78 
    79             if (Rmask) {
    80                 Rw = 8 - format->Rloss;
    81                 for (i = format->Rloss; i > 0; i -= Rw)
    82                     Rm |= 1 << i;
    83             }
    84             if (Gmask) {
    85                 Gw = 8 - format->Gloss;
    86                 for (i = format->Gloss; i > 0; i -= Gw)
    87                     Gm |= 1 << i;
    88             }
    89             if (Bmask) {
    90                 Bw = 8 - format->Bloss;
    91                 for (i = format->Bloss; i > 0; i -= Bw)
    92                     Bm |= 1 << i;
    93             }
    94             for (i = 0; i < palette->ncolors; ++i) {
    95                 int r, g, b;
    96                 r = (i & Rmask) >> format->Rshift;
    97                 r = (r << format->Rloss) | ((r * Rm) >> Rw);
    98                 palette->colors[i].r = r;
    99 
   100                 g = (i & Gmask) >> format->Gshift;
   101                 g = (g << format->Gloss) | ((g * Gm) >> Gw);
   102                 palette->colors[i].g = g;
   103 
   104                 b = (i & Bmask) >> format->Bshift;
   105                 b = (b << format->Bloss) | ((b * Bm) >> Bw);
   106                 palette->colors[i].b = b;
   107             }
   108         } else if (palette->ncolors == 2) {
   109             /* Create a black and white bitmap palette */
   110             palette->colors[0].r = 0xFF;
   111             palette->colors[0].g = 0xFF;
   112             palette->colors[0].b = 0xFF;
   113             palette->colors[1].r = 0x00;
   114             palette->colors[1].g = 0x00;
   115             palette->colors[1].b = 0x00;
   116         }
   117         SDL_SetSurfacePalette(surface, palette);
   118         SDL_FreePalette(palette);
   119     }
   120 
   121     /* Get the pixels */
   122     if (surface->w && surface->h) {
   123         surface->pixels = SDL_malloc(surface->h * surface->pitch);
   124         if (!surface->pixels) {
   125             SDL_FreeSurface(surface);
   126             SDL_OutOfMemory();
   127             return NULL;
   128         }
   129         /* This is important for bitmaps */
   130         SDL_memset(surface->pixels, 0, surface->h * surface->pitch);
   131     }
   132 
   133     /* Allocate an empty mapping */
   134     surface->map = SDL_AllocBlitMap();
   135     if (!surface->map) {
   136         SDL_FreeSurface(surface);
   137         return NULL;
   138     }
   139     SDL_FormatChanged(surface);
   140 
   141     /* By default surface with an alpha mask are set up for blending */
   142     if (Amask) {
   143         SDL_SetSurfaceBlendMode(surface, SDL_TEXTUREBLENDMODE_BLEND);
   144     }
   145 
   146     /* The surface is ready to go */
   147     surface->refcount = 1;
   148 #ifdef CHECK_LEAKS
   149     ++surfaces_allocated;
   150 #endif
   151     return surface;
   152 }
   153 
   154 /*
   155  * Create an RGB surface from an existing memory buffer
   156  */
   157 SDL_Surface *
   158 SDL_CreateRGBSurfaceFrom(void *pixels,
   159                          int width, int height, int depth, int pitch,
   160                          Uint32 Rmask, Uint32 Gmask, Uint32 Bmask,
   161                          Uint32 Amask)
   162 {
   163     SDL_Surface *surface;
   164 
   165     surface =
   166         SDL_CreateRGBSurface(0, 0, 0, depth, Rmask, Gmask, Bmask, Amask);
   167     if (surface != NULL) {
   168         surface->flags |= SDL_PREALLOC;
   169         surface->pixels = pixels;
   170         surface->w = width;
   171         surface->h = height;
   172         surface->pitch = pitch;
   173         SDL_SetClipRect(surface, NULL);
   174     }
   175     return surface;
   176 }
   177 
   178 static int
   179 SDL_SurfacePaletteChanged(void *userdata, SDL_Palette * palette)
   180 {
   181     SDL_Surface *surface = (SDL_Surface *) userdata;
   182 
   183     SDL_FormatChanged(surface);
   184 
   185     return 0;
   186 }
   187 
   188 int
   189 SDL_SetSurfacePalette(SDL_Surface * surface, SDL_Palette * palette)
   190 {
   191     if (!surface || !surface->format) {
   192         SDL_SetError("SDL_SetSurfacePalette() passed a NULL surface");
   193         return -1;
   194     }
   195 
   196     if (palette && palette->ncolors != (1 << surface->format->BitsPerPixel)) {
   197         SDL_SetError
   198             ("SDL_SetSurfacePalette() passed a palette that doesn't match the surface format");
   199         return -1;
   200     }
   201 
   202     if (surface->format->palette == palette) {
   203         return 0;
   204     }
   205 
   206     if (surface->format->palette) {
   207         SDL_DelPaletteWatch(surface->format->palette,
   208                             SDL_SurfacePaletteChanged, surface);
   209     }
   210 
   211     surface->format->palette = palette;
   212 
   213     if (surface->format->palette) {
   214         SDL_AddPaletteWatch(surface->format->palette,
   215                             SDL_SurfacePaletteChanged, surface);
   216     }
   217     return 0;
   218 }
   219 
   220 int
   221 SDL_SetSurfaceRLE(SDL_Surface * surface, int flag)
   222 {
   223     int flags;
   224 
   225     if (!surface) {
   226         return -1;
   227     }
   228 
   229     flags = surface->map->info.flags;
   230     if (flag) {
   231         surface->map->info.flags |= SDL_COPY_RLE_DESIRED;
   232     } else {
   233         surface->map->info.flags &= ~SDL_COPY_RLE_DESIRED;
   234     }
   235     if (surface->map->info.flags != flags) {
   236         SDL_InvalidateMap(surface->map);
   237     }
   238     return 0;
   239 }
   240 
   241 int
   242 SDL_SetColorKey(SDL_Surface * surface, Uint32 flag, Uint32 key)
   243 {
   244     int flags;
   245 
   246     if (!surface) {
   247         return -1;
   248     }
   249 
   250     if (flag & SDL_RLEACCEL) {
   251         SDL_SetSurfaceRLE(surface, 1);
   252     }
   253 
   254     flags = surface->map->info.flags;
   255     if (flag) {
   256         surface->map->info.flags |= SDL_COPY_COLORKEY;
   257         surface->map->info.colorkey = key;
   258     } else {
   259         surface->map->info.flags &= ~SDL_COPY_COLORKEY;
   260     }
   261     if (surface->map->info.flags != flags) {
   262         SDL_InvalidateMap(surface->map);
   263     }
   264 
   265     /* Compatibility mode */
   266     if (surface->map->info.flags & SDL_COPY_COLORKEY) {
   267         surface->flags |= SDL_SRCCOLORKEY;
   268     } else {
   269         surface->flags &= ~SDL_SRCCOLORKEY;
   270     }
   271 
   272     return 0;
   273 }
   274 
   275 /* This is a fairly slow function to switch from colorkey to alpha */
   276 static void
   277 SDL_ConvertColorkeyToAlpha(SDL_Surface * surface)
   278 {
   279     int x, y;
   280 
   281     if (!surface) {
   282         return;
   283     }
   284 
   285     if (!(surface->map->info.flags & SDL_COPY_COLORKEY) ||
   286         !surface->format->Amask) {
   287         return;
   288     }
   289 
   290     SDL_LockSurface(surface);
   291 
   292     switch (surface->format->BytesPerPixel) {
   293     case 2:
   294         {
   295             Uint16 *row, *spot;
   296             Uint16 ckey = (Uint16) surface->map->info.colorkey;
   297             Uint16 mask = (Uint16) (~surface->format->Amask);
   298 
   299             row = (Uint16 *) surface->pixels;
   300             for (y = surface->h; y--;) {
   301                 spot = row;
   302                 for (x = surface->w; x--;) {
   303                     if (*spot == ckey) {
   304                         *spot &= mask;
   305                     }
   306                     ++spot;
   307                 }
   308                 row += surface->pitch / 2;
   309             }
   310         }
   311         break;
   312     case 3:
   313         /* FIXME */
   314         break;
   315     case 4:
   316         {
   317             Uint32 *row, *spot;
   318             Uint32 ckey = surface->map->info.colorkey;
   319             Uint32 mask = ~surface->format->Amask;
   320 
   321             row = (Uint32 *) surface->pixels;
   322             for (y = surface->h; y--;) {
   323                 spot = row;
   324                 for (x = surface->w; x--;) {
   325                     if (*spot == ckey) {
   326                         *spot &= mask;
   327                     }
   328                     ++spot;
   329                 }
   330                 row += surface->pitch / 4;
   331             }
   332         }
   333         break;
   334     }
   335 
   336     SDL_UnlockSurface(surface);
   337 
   338     SDL_SetColorKey(surface, 0, 0);
   339 }
   340 
   341 int
   342 SDL_SetSurfaceColorMod(SDL_Surface * surface, Uint8 r, Uint8 g, Uint8 b)
   343 {
   344     int flags;
   345 
   346     if (!surface) {
   347         return -1;
   348     }
   349 
   350     surface->map->info.r = r;
   351     surface->map->info.g = g;
   352     surface->map->info.b = b;
   353 
   354     flags = surface->map->info.flags;
   355     if (r != 0xFF || g != 0xFF || b != 0xFF) {
   356         surface->map->info.flags |= SDL_COPY_MODULATE_COLOR;
   357     } else {
   358         surface->map->info.flags &= ~SDL_COPY_MODULATE_COLOR;
   359     }
   360     if (surface->map->info.flags != flags) {
   361         SDL_InvalidateMap(surface->map);
   362     }
   363     return 0;
   364 }
   365 
   366 
   367 int
   368 SDL_GetSurfaceColorMod(SDL_Surface * surface, Uint8 * r, Uint8 * g, Uint8 * b)
   369 {
   370     if (!surface) {
   371         return -1;
   372     }
   373 
   374     if (r) {
   375         *r = surface->map->info.r;
   376     }
   377     if (g) {
   378         *g = surface->map->info.g;
   379     }
   380     if (b) {
   381         *b = surface->map->info.b;
   382     }
   383     return 0;
   384 }
   385 
   386 int
   387 SDL_SetSurfaceAlphaMod(SDL_Surface * surface, Uint8 alpha)
   388 {
   389     int flags;
   390 
   391     if (!surface) {
   392         return -1;
   393     }
   394 
   395     surface->map->info.a = alpha;
   396 
   397     flags = surface->map->info.flags;
   398     if (alpha != 0xFF) {
   399         surface->map->info.flags |= SDL_COPY_MODULATE_ALPHA;
   400     } else {
   401         surface->map->info.flags &= ~SDL_COPY_MODULATE_ALPHA;
   402     }
   403     if (surface->map->info.flags != flags) {
   404         SDL_InvalidateMap(surface->map);
   405     }
   406     return 0;
   407 }
   408 
   409 int
   410 SDL_GetSurfaceAlphaMod(SDL_Surface * surface, Uint8 * alpha)
   411 {
   412     if (!surface) {
   413         return -1;
   414     }
   415 
   416     if (alpha) {
   417         *alpha = surface->map->info.a;
   418     }
   419     return 0;
   420 }
   421 
   422 int
   423 SDL_SetSurfaceBlendMode(SDL_Surface * surface, int blendMode)
   424 {
   425     int flags, status;
   426 
   427     if (!surface) {
   428         return -1;
   429     }
   430 
   431     status = 0;
   432     flags = surface->map->info.flags;
   433     surface->map->info.flags &=
   434         ~(SDL_COPY_MASK | SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD);
   435     switch (blendMode) {
   436     case SDL_TEXTUREBLENDMODE_NONE:
   437         break;
   438     case SDL_TEXTUREBLENDMODE_MASK:
   439         surface->map->info.flags |= SDL_COPY_MASK;
   440         break;
   441     case SDL_TEXTUREBLENDMODE_BLEND:
   442         surface->map->info.flags |= SDL_COPY_BLEND;
   443         break;
   444     case SDL_TEXTUREBLENDMODE_ADD:
   445         surface->map->info.flags |= SDL_COPY_ADD;
   446         break;
   447     case SDL_TEXTUREBLENDMODE_MOD:
   448         surface->map->info.flags |= SDL_COPY_MOD;
   449         break;
   450     default:
   451         SDL_Unsupported();
   452         status = -1;
   453         break;
   454     }
   455 
   456     if (surface->map->info.flags != flags) {
   457         SDL_InvalidateMap(surface->map);
   458     }
   459 
   460     /* Compatibility mode */
   461     if (surface->map->info.flags & SDL_COPY_BLEND) {
   462         surface->flags |= SDL_SRCALPHA;
   463     } else {
   464         surface->flags &= ~SDL_SRCALPHA;
   465     }
   466 
   467     return status;
   468 }
   469 
   470 int
   471 SDL_GetSurfaceBlendMode(SDL_Surface * surface, int *blendMode)
   472 {
   473     if (!surface) {
   474         return -1;
   475     }
   476 
   477     if (!blendMode) {
   478         return 0;
   479     }
   480 
   481     switch (surface->map->
   482             info.flags & (SDL_COPY_MASK | SDL_COPY_BLEND | SDL_COPY_ADD |
   483                           SDL_COPY_MOD)) {
   484     case SDL_COPY_MASK:
   485         *blendMode = SDL_TEXTUREBLENDMODE_MASK;
   486         break;
   487     case SDL_COPY_BLEND:
   488         *blendMode = SDL_TEXTUREBLENDMODE_BLEND;
   489         break;
   490     case SDL_COPY_ADD:
   491         *blendMode = SDL_TEXTUREBLENDMODE_ADD;
   492         break;
   493     case SDL_COPY_MOD:
   494         *blendMode = SDL_TEXTUREBLENDMODE_MOD;
   495         break;
   496     default:
   497         *blendMode = SDL_TEXTUREBLENDMODE_NONE;
   498         break;
   499     }
   500     return 0;
   501 }
   502 
   503 int
   504 SDL_SetSurfaceScaleMode(SDL_Surface * surface, int scaleMode)
   505 {
   506     int flags, status;
   507 
   508     if (!surface) {
   509         return -1;
   510     }
   511 
   512     status = 0;
   513     flags = surface->map->info.flags;
   514     surface->map->info.flags &= ~(SDL_COPY_NEAREST);
   515     switch (scaleMode) {
   516     case SDL_TEXTURESCALEMODE_NONE:
   517         break;
   518     case SDL_TEXTURESCALEMODE_FAST:
   519         surface->map->info.flags |= SDL_COPY_NEAREST;
   520         break;
   521     case SDL_TEXTURESCALEMODE_SLOW:
   522     case SDL_TEXTURESCALEMODE_BEST:
   523         SDL_Unsupported();
   524         surface->map->info.flags |= SDL_COPY_NEAREST;
   525         status = -1;
   526         break;
   527     default:
   528         SDL_Unsupported();
   529         status = -1;
   530         break;
   531     }
   532 
   533     if (surface->map->info.flags != flags) {
   534         SDL_InvalidateMap(surface->map);
   535     }
   536     return status;
   537 }
   538 
   539 int
   540 SDL_GetSurfaceScaleMode(SDL_Surface * surface, int *scaleMode)
   541 {
   542     if (!surface) {
   543         return -1;
   544     }
   545 
   546     if (!scaleMode) {
   547         return 0;
   548     }
   549 
   550     switch (surface->map->info.flags & (SDL_COPY_NEAREST)) {
   551     case SDL_COPY_NEAREST:
   552         *scaleMode = SDL_TEXTURESCALEMODE_FAST;
   553         break;
   554     default:
   555         *scaleMode = SDL_TEXTURESCALEMODE_NONE;
   556         break;
   557     }
   558     return 0;
   559 }
   560 
   561 SDL_bool
   562 SDL_SetClipRect(SDL_Surface * surface, const SDL_Rect * rect)
   563 {
   564     SDL_Rect full_rect;
   565 
   566     /* Don't do anything if there's no surface to act on */
   567     if (!surface) {
   568         return SDL_FALSE;
   569     }
   570 
   571     /* Set up the full surface rectangle */
   572     full_rect.x = 0;
   573     full_rect.y = 0;
   574     full_rect.w = surface->w;
   575     full_rect.h = surface->h;
   576 
   577     /* Set the clipping rectangle */
   578     if (!rect) {
   579         surface->clip_rect = full_rect;
   580         return 1;
   581     }
   582     return SDL_IntersectRect(rect, &full_rect, &surface->clip_rect);
   583 }
   584 
   585 void
   586 SDL_GetClipRect(SDL_Surface * surface, SDL_Rect * rect)
   587 {
   588     if (surface && rect) {
   589         *rect = surface->clip_rect;
   590     }
   591 }
   592 
   593 /* 
   594  * Set up a blit between two surfaces -- split into three parts:
   595  * The upper part, SDL_UpperBlit(), performs clipping and rectangle 
   596  * verification.  The lower part is a pointer to a low level
   597  * accelerated blitting function.
   598  *
   599  * These parts are separated out and each used internally by this 
   600  * library in the optimimum places.  They are exported so that if
   601  * you know exactly what you are doing, you can optimize your code
   602  * by calling the one(s) you need.
   603  */
   604 int
   605 SDL_LowerBlit(SDL_Surface * src, SDL_Rect * srcrect,
   606               SDL_Surface * dst, SDL_Rect * dstrect)
   607 {
   608     /* Check to make sure the blit mapping is valid */
   609     if ((src->map->dst != dst) ||
   610         (src->map->dst->format_version != src->map->format_version)) {
   611         if (SDL_MapSurface(src, dst) < 0) {
   612             return (-1);
   613         }
   614         /* just here for debugging */
   615 /*         printf */
   616 /*             ("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", */
   617 /*              src, dst->flags, src->map->info.flags, dst, dst->flags, */
   618 /*              dst->map->info.flags, src->map->blit); */
   619     }
   620     return (src->map->blit(src, srcrect, dst, dstrect));
   621 }
   622 
   623 
   624 int
   625 SDL_UpperBlit(SDL_Surface * src, SDL_Rect * srcrect,
   626               SDL_Surface * dst, SDL_Rect * dstrect)
   627 {
   628     SDL_Rect fulldst;
   629     int srcx, srcy, w, h;
   630 
   631     /* Make sure the surfaces aren't locked */
   632     if (!src || !dst) {
   633         SDL_SetError("SDL_UpperBlit: passed a NULL surface");
   634         return (-1);
   635     }
   636     if (src->locked || dst->locked) {
   637         SDL_SetError("Surfaces must not be locked during blit");
   638         return (-1);
   639     }
   640 
   641     /* If the destination rectangle is NULL, use the entire dest surface */
   642     if (dstrect == NULL) {
   643         fulldst.x = fulldst.y = 0;
   644         dstrect = &fulldst;
   645     }
   646 
   647     /* clip the source rectangle to the source surface */
   648     if (srcrect) {
   649         int maxw, maxh;
   650 
   651         srcx = srcrect->x;
   652         w = srcrect->w;
   653         if (srcx < 0) {
   654             w += srcx;
   655             dstrect->x -= srcx;
   656             srcx = 0;
   657         }
   658         maxw = src->w - srcx;
   659         if (maxw < w)
   660             w = maxw;
   661 
   662         srcy = srcrect->y;
   663         h = srcrect->h;
   664         if (srcy < 0) {
   665             h += srcy;
   666             dstrect->y -= srcy;
   667             srcy = 0;
   668         }
   669         maxh = src->h - srcy;
   670         if (maxh < h)
   671             h = maxh;
   672 
   673     } else {
   674         srcx = srcy = 0;
   675         w = src->w;
   676         h = src->h;
   677     }
   678 
   679     /* clip the destination rectangle against the clip rectangle */
   680     {
   681         SDL_Rect *clip = &dst->clip_rect;
   682         int dx, dy;
   683 
   684         dx = clip->x - dstrect->x;
   685         if (dx > 0) {
   686             w -= dx;
   687             dstrect->x += dx;
   688             srcx += dx;
   689         }
   690         dx = dstrect->x + w - clip->x - clip->w;
   691         if (dx > 0)
   692             w -= dx;
   693 
   694         dy = clip->y - dstrect->y;
   695         if (dy > 0) {
   696             h -= dy;
   697             dstrect->y += dy;
   698             srcy += dy;
   699         }
   700         dy = dstrect->y + h - clip->y - clip->h;
   701         if (dy > 0)
   702             h -= dy;
   703     }
   704 
   705     if (w > 0 && h > 0) {
   706         SDL_Rect sr;
   707         sr.x = srcx;
   708         sr.y = srcy;
   709         sr.w = dstrect->w = w;
   710         sr.h = dstrect->h = h;
   711         return SDL_LowerBlit(src, &sr, dst, dstrect);
   712     }
   713     dstrect->w = dstrect->h = 0;
   714     return 0;
   715 }
   716 
   717 /*
   718  * Lock a surface to directly access the pixels
   719  */
   720 int
   721 SDL_LockSurface(SDL_Surface * surface)
   722 {
   723     if (!surface->locked) {
   724         /* Perform the lock */
   725         if (surface->flags & SDL_RLEACCEL) {
   726             SDL_UnRLESurface(surface, 1);
   727             surface->flags |= SDL_RLEACCEL;     /* save accel'd state */
   728         }
   729     }
   730 
   731     /* Increment the surface lock count, for recursive locks */
   732     ++surface->locked;
   733 
   734     /* Ready to go.. */
   735     return (0);
   736 }
   737 
   738 /*
   739  * Unlock a previously locked surface
   740  */
   741 void
   742 SDL_UnlockSurface(SDL_Surface * surface)
   743 {
   744     /* Only perform an unlock if we are locked */
   745     if (!surface->locked || (--surface->locked > 0)) {
   746         return;
   747     }
   748 
   749     /* Update RLE encoded surface with new data */
   750     if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) {
   751         surface->flags &= ~SDL_RLEACCEL;        /* stop lying */
   752         SDL_RLESurface(surface);
   753     }
   754 }
   755 
   756 /* 
   757  * Convert a surface into the specified pixel format.
   758  */
   759 SDL_Surface *
   760 SDL_ConvertSurface(SDL_Surface * surface, SDL_PixelFormat * format,
   761                    Uint32 flags)
   762 {
   763     SDL_Surface *convert;
   764     Uint32 copy_flags;
   765     SDL_Rect bounds;
   766 
   767     /* Check for empty destination palette! (results in empty image) */
   768     if (format->palette != NULL) {
   769         int i;
   770         for (i = 0; i < format->palette->ncolors; ++i) {
   771             if ((format->palette->colors[i].r != 0xFF) ||
   772                 (format->palette->colors[i].g != 0xFF) ||
   773                 (format->palette->colors[i].b != 0xFF))
   774                 break;
   775         }
   776         if (i == format->palette->ncolors) {
   777             SDL_SetError("Empty destination palette");
   778             return (NULL);
   779         }
   780     }
   781 
   782     /* Create a new surface with the desired format */
   783     convert = SDL_CreateRGBSurface(flags, surface->w, surface->h,
   784                                    format->BitsPerPixel, format->Rmask,
   785                                    format->Gmask, format->Bmask,
   786                                    format->Amask);
   787     if (convert == NULL) {
   788         return (NULL);
   789     }
   790 
   791     /* Copy the palette if any */
   792     if (format->palette && convert->format->palette) {
   793         SDL_memcpy(convert->format->palette->colors,
   794                    format->palette->colors,
   795                    format->palette->ncolors * sizeof(SDL_Color));
   796         convert->format->palette->ncolors = format->palette->ncolors;
   797     }
   798 
   799     /* Save the original copy flags */
   800     copy_flags = surface->map->info.flags;
   801     surface->map->info.flags = 0;
   802 
   803     /* Copy over the image data */
   804     bounds.x = 0;
   805     bounds.y = 0;
   806     bounds.w = surface->w;
   807     bounds.h = surface->h;
   808     SDL_LowerBlit(surface, &bounds, convert, &bounds);
   809 
   810     /* Clean up the original surface, and update converted surface */
   811     SDL_SetClipRect(convert, &surface->clip_rect);
   812     if (copy_flags & SDL_COPY_COLORKEY) {
   813         Uint8 keyR, keyG, keyB, keyA;
   814 
   815         SDL_GetRGBA(surface->map->info.colorkey, surface->format, &keyR,
   816                     &keyG, &keyB, &keyA);
   817         SDL_SetColorKey(convert, 1,
   818                         SDL_MapRGBA(convert->format, keyR, keyG, keyB, keyA));
   819         SDL_ConvertColorkeyToAlpha(convert);
   820     }
   821     convert->map->info.r = surface->map->info.r;
   822     convert->map->info.g = surface->map->info.g;
   823     convert->map->info.b = surface->map->info.b;
   824     convert->map->info.a = surface->map->info.a;
   825     convert->map->info.flags = copy_flags;
   826     surface->map->info.flags = copy_flags;
   827 
   828     /* Enable alpha blending by default if the new surface has an
   829      * alpha channel or alpha modulation */
   830     if (format->Amask || (copy_flags & SDL_COPY_MODULATE_ALPHA)) {
   831         SDL_SetSurfaceBlendMode(convert, SDL_TEXTUREBLENDMODE_BLEND);
   832     }
   833     SDL_SetSurfaceRLE(convert, (flags & SDL_RLEACCEL));
   834 
   835     /* We're ready to go! */
   836     return (convert);
   837 }
   838 
   839 /*
   840  * Free a surface created by the above function.
   841  */
   842 void
   843 SDL_FreeSurface(SDL_Surface * surface)
   844 {
   845     if (surface == NULL) {
   846         return;
   847     }
   848     if (--surface->refcount > 0) {
   849         return;
   850     }
   851     while (surface->locked > 0) {
   852         SDL_UnlockSurface(surface);
   853     }
   854     if (surface->flags & SDL_RLEACCEL) {
   855         SDL_UnRLESurface(surface, 0);
   856     }
   857     if (surface->format) {
   858         SDL_SetSurfacePalette(surface, NULL);
   859         SDL_FreeFormat(surface->format);
   860         surface->format = NULL;
   861     }
   862     if (surface->map != NULL) {
   863         SDL_FreeBlitMap(surface->map);
   864         surface->map = NULL;
   865     }
   866     if (surface->pixels && ((surface->flags & SDL_PREALLOC) != SDL_PREALLOC)) {
   867         SDL_free(surface->pixels);
   868     }
   869     SDL_free(surface);
   870 #ifdef CHECK_LEAKS
   871     --surfaces_allocated;
   872 #endif
   873 }
   874 
   875 /* vi: set ts=4 sw=4 expandtab: */