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