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