src/video/SDL_surface.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 09 Jun 2006 07:06:12 +0000
branchSDL-1.3
changeset 1672 8e754b82cecc
parent 1670 eef792d31de8
child 1678 90bf530ced8e
permissions -rw-r--r--
Updated SDL_Surface code for software-only access, fixed some build errors
     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_VideoDevice *_this = SDL_GetVideoDevice();
    43     SDL_Surface *screen;
    44     SDL_Surface *surface;
    45 
    46     /* FIXME!! */
    47     /* Make sure the size requested doesn't overflow our datatypes */
    48     /* Next time I write a library like SDL, I'll use int for size. :) */
    49     if (width >= 16384 || height >= 65536) {
    50         SDL_SetError("Width or height is too large");
    51         return (NULL);
    52     }
    53 
    54     /* Allocate the surface */
    55     surface = (SDL_Surface *) SDL_malloc(sizeof(*surface));
    56     if (surface == NULL) {
    57         SDL_OutOfMemory();
    58         return (NULL);
    59     }
    60     surface->flags = 0;
    61     surface->format = SDL_AllocFormat(depth, Rmask, Gmask, Bmask, Amask);
    62     if (surface->format == NULL) {
    63         SDL_free(surface);
    64         return (NULL);
    65     }
    66     if (Amask) {
    67         surface->flags |= SDL_SRCALPHA;
    68     }
    69     surface->w = width;
    70     surface->h = height;
    71     surface->pitch = SDL_CalculatePitch(surface);
    72     surface->pixels = NULL;
    73     surface->locked = 0;
    74     surface->map = NULL;
    75     SDL_SetClipRect(surface, NULL);
    76     SDL_FormatChanged(surface);
    77 
    78     /* Get the pixels */
    79     if (surface->w && surface->h) {
    80         surface->pixels = SDL_malloc(surface->h * surface->pitch);
    81         if (surface->pixels == NULL) {
    82             SDL_FreeSurface(surface);
    83             SDL_OutOfMemory();
    84             return NULL;
    85         }
    86         /* This is important for bitmaps */
    87         SDL_memset(surface->pixels, 0, surface->h * surface->pitch);
    88     }
    89 
    90     /* Allocate an empty mapping */
    91     surface->map = SDL_AllocBlitMap();
    92     if (surface->map == NULL) {
    93         SDL_FreeSurface(surface);
    94         return (NULL);
    95     }
    96 
    97     /* The surface is ready to go */
    98     surface->refcount = 1;
    99 #ifdef CHECK_LEAKS
   100     ++surfaces_allocated;
   101 #endif
   102     return (surface);
   103 }
   104 
   105 /*
   106  * Create an RGB surface from an existing memory buffer
   107  */
   108 SDL_Surface *
   109 SDL_CreateRGBSurfaceFrom(void *pixels,
   110                          int width, int height, int depth, int pitch,
   111                          Uint32 Rmask, Uint32 Gmask, Uint32 Bmask,
   112                          Uint32 Amask)
   113 {
   114     SDL_Surface *surface;
   115 
   116     surface =
   117         SDL_CreateRGBSurface(0, 0, 0, depth, Rmask, Gmask, Bmask, Amask);
   118     if (surface != NULL) {
   119         surface->flags |= SDL_PREALLOC;
   120         surface->pixels = pixels;
   121         surface->w = width;
   122         surface->h = height;
   123         surface->pitch = pitch;
   124         SDL_SetClipRect(surface, NULL);
   125     }
   126     return surface;
   127 }
   128 
   129 SDL_Surface *
   130 SDL_CreateRGBSurfaceFromTexture(SDL_TextureID textureID)
   131 {
   132     SDL_Surface *surface;
   133     Uint32 format;
   134     int w, h;
   135     int bpp;
   136     Uint32 Rmask, Gmask, Bmask, Amask;
   137 
   138     if (SDL_QueryTexture(textureID, &format, NULL, &w, &h) < 0) {
   139         return NULL;
   140     }
   141 
   142     if (!SDL_PixelFormatEnumToMasks
   143         (format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
   144         SDL_SetError("Unknown texture format");
   145         return NULL;
   146     }
   147 
   148     surface = SDL_CreateRGBSurface(0, 0, 0, bpp, Rmask, Gmask, Bmask, Amask);
   149     if (surface != NULL) {
   150         surface->flags |= (SDL_HWSURFACE | SDL_PREALLOC);
   151         surface->w = w;
   152         surface->h = h;
   153         surface->lock_data = (void *) textureID;
   154         SDL_SetClipRect(surface, NULL);
   155     }
   156     return surface;
   157 }
   158 
   159 /*
   160  * Set the palette in a blittable surface
   161  */
   162 int
   163 SDL_SetColors(SDL_Surface * surface, SDL_Color * colors, int firstcolor,
   164               int ncolors)
   165 {
   166     SDL_Palette *pal;
   167     int gotall;
   168     int palsize;
   169 
   170     /* Verify the parameters */
   171     pal = surface->format->palette;
   172     if (!pal) {
   173         return 0;               /* not a palettized surface */
   174     }
   175     gotall = 1;
   176     palsize = 1 << surface->format->BitsPerPixel;
   177     if (ncolors > (palsize - firstcolor)) {
   178         ncolors = (palsize - firstcolor);
   179         gotall = 0;
   180     }
   181 
   182     if (colors != (pal->colors + firstcolor)) {
   183         SDL_memcpy(pal->colors + firstcolor, colors,
   184                    ncolors * sizeof(*colors));
   185     }
   186     SDL_FormatChanged(surface);
   187 
   188     if (surface->flags & (SDL_SHADOW_SURFACE | SDL_SCREEN_SURFACE)) {
   189         gotall &= SDL_SetScreenColors(surface, colors, firstcolor, ncolors);
   190     }
   191     return gotall;
   192 }
   193 
   194 /*
   195  * Set the color key in a blittable surface
   196  */
   197 int
   198 SDL_SetColorKey(SDL_Surface * surface, Uint32 flag, Uint32 key)
   199 {
   200     /* Sanity check the flag as it gets passed in */
   201     if (flag & SDL_SRCCOLORKEY) {
   202         if (flag & (SDL_RLEACCEL | SDL_RLEACCELOK)) {
   203             flag = (SDL_SRCCOLORKEY | SDL_RLEACCELOK);
   204         } else {
   205             flag = SDL_SRCCOLORKEY;
   206         }
   207     } else {
   208         flag = 0;
   209     }
   210 
   211     /* Optimize away operations that don't change anything */
   212     if ((flag == (surface->flags & (SDL_SRCCOLORKEY | SDL_RLEACCELOK))) &&
   213         (key == surface->format->colorkey)) {
   214         return (0);
   215     }
   216 
   217     /* UnRLE surfaces before we change the colorkey */
   218     if (surface->flags & SDL_RLEACCEL) {
   219         SDL_UnRLESurface(surface, 1);
   220     }
   221 
   222     if (flag) {
   223         surface->flags |= SDL_SRCCOLORKEY;
   224         surface->format->colorkey = key;
   225         if (flag & SDL_RLEACCELOK) {
   226             surface->flags |= SDL_RLEACCELOK;
   227         } else {
   228             surface->flags &= ~SDL_RLEACCELOK;
   229         }
   230     } else {
   231         surface->flags &= ~(SDL_SRCCOLORKEY | SDL_RLEACCELOK);
   232         surface->format->colorkey = 0;
   233     }
   234     SDL_InvalidateMap(surface->map);
   235     return (0);
   236 }
   237 
   238 /* This function sets the alpha channel of a surface */
   239 int
   240 SDL_SetAlpha(SDL_Surface * surface, Uint32 flag, Uint8 value)
   241 {
   242     Uint32 oldflags = surface->flags;
   243     Uint32 oldalpha = surface->format->alpha;
   244 
   245     /* Sanity check the flag as it gets passed in */
   246     if (flag & SDL_SRCALPHA) {
   247         if (flag & (SDL_RLEACCEL | SDL_RLEACCELOK)) {
   248             flag = (SDL_SRCALPHA | SDL_RLEACCELOK);
   249         } else {
   250             flag = SDL_SRCALPHA;
   251         }
   252     } else {
   253         flag = 0;
   254     }
   255 
   256     /* Optimize away operations that don't change anything */
   257     if ((flag == (surface->flags & (SDL_SRCALPHA | SDL_RLEACCELOK))) &&
   258         (!flag || value == oldalpha)) {
   259         return (0);
   260     }
   261 
   262     if (!(flag & SDL_RLEACCELOK) && (surface->flags & SDL_RLEACCEL))
   263         SDL_UnRLESurface(surface, 1);
   264 
   265     if (flag) {
   266         surface->flags |= SDL_SRCALPHA;
   267         surface->format->alpha = value;
   268         if (flag & SDL_RLEACCELOK) {
   269             surface->flags |= SDL_RLEACCELOK;
   270         } else {
   271             surface->flags &= ~SDL_RLEACCELOK;
   272         }
   273     } else {
   274         surface->flags &= ~SDL_SRCALPHA;
   275         surface->format->alpha = SDL_ALPHA_OPAQUE;
   276     }
   277     /*
   278      * The representation for software surfaces is independent of
   279      * per-surface alpha, so no need to invalidate the blit mapping
   280      * if just the alpha value was changed. (If either is 255, we still
   281      * need to invalidate.)
   282      */
   283     if (oldflags != surface->flags
   284         || (((oldalpha + 1) ^ (value + 1)) & 0x100)) {
   285         SDL_InvalidateMap(surface->map);
   286     }
   287     return (0);
   288 }
   289 
   290 int
   291 SDL_SetAlphaChannel(SDL_Surface * surface, Uint8 value)
   292 {
   293     int row, col;
   294     int offset;
   295     Uint8 *buf;
   296 
   297     if ((surface->format->Amask != 0xFF000000) &&
   298         (surface->format->Amask != 0x000000FF)) {
   299         SDL_SetError("Unsupported surface alpha mask format");
   300         return -1;
   301     }
   302 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   303     if (surface->format->Amask == 0xFF000000) {
   304         offset = 3;
   305     } else {
   306         offset = 0;
   307     }
   308 #else
   309     if (surface->format->Amask == 0xFF000000) {
   310         offset = 0;
   311     } else {
   312         offset = 3;
   313     }
   314 #endif /* Byte ordering */
   315 
   316     /* Quickly set the alpha channel of an RGBA or ARGB surface */
   317     if (SDL_MUSTLOCK(surface)) {
   318         if (SDL_LockSurface(surface) < 0) {
   319             return -1;
   320         }
   321     }
   322     row = surface->h;
   323     while (row--) {
   324         col = surface->w;
   325         buf = (Uint8 *) surface->pixels + row * surface->pitch + offset;
   326         while (col--) {
   327             *buf = value;
   328             buf += 4;
   329         }
   330     }
   331     if (SDL_MUSTLOCK(surface)) {
   332         SDL_UnlockSurface(surface);
   333     }
   334     return 0;
   335 }
   336 
   337 /*
   338  * A function to calculate the intersection of two rectangles:
   339  * return true if the rectangles intersect, false otherwise
   340  */
   341 static __inline__ SDL_bool
   342 SDL_IntersectRect(const SDL_Rect * A, const SDL_Rect * B,
   343                   SDL_Rect * intersection)
   344 {
   345     int Amin, Amax, Bmin, Bmax;
   346 
   347     /* Horizontal intersection */
   348     Amin = A->x;
   349     Amax = Amin + A->w;
   350     Bmin = B->x;
   351     Bmax = Bmin + B->w;
   352     if (Bmin > Amin)
   353         Amin = Bmin;
   354     intersection->x = Amin;
   355     if (Bmax < Amax)
   356         Amax = Bmax;
   357     intersection->w = Amax - Amin > 0 ? Amax - Amin : 0;
   358 
   359     /* Vertical intersection */
   360     Amin = A->y;
   361     Amax = Amin + A->h;
   362     Bmin = B->y;
   363     Bmax = Bmin + B->h;
   364     if (Bmin > Amin)
   365         Amin = Bmin;
   366     intersection->y = Amin;
   367     if (Bmax < Amax)
   368         Amax = Bmax;
   369     intersection->h = Amax - Amin > 0 ? Amax - Amin : 0;
   370 
   371     return (intersection->w && intersection->h);
   372 }
   373 
   374 /*
   375  * Set the clipping rectangle for a blittable surface
   376  */
   377 SDL_bool
   378 SDL_SetClipRect(SDL_Surface * surface, const SDL_Rect * rect)
   379 {
   380     SDL_Rect full_rect;
   381 
   382     /* Don't do anything if there's no surface to act on */
   383     if (!surface) {
   384         return SDL_FALSE;
   385     }
   386 
   387     /* Set up the full surface rectangle */
   388     full_rect.x = 0;
   389     full_rect.y = 0;
   390     full_rect.w = surface->w;
   391     full_rect.h = surface->h;
   392 
   393     /* Set the clipping rectangle */
   394     if (!rect) {
   395         surface->clip_rect = full_rect;
   396         return 1;
   397     }
   398     return SDL_IntersectRect(rect, &full_rect, &surface->clip_rect);
   399 }
   400 
   401 void
   402 SDL_GetClipRect(SDL_Surface * surface, SDL_Rect * rect)
   403 {
   404     if (surface && rect) {
   405         *rect = surface->clip_rect;
   406     }
   407 }
   408 
   409 /* 
   410  * Set up a blit between two surfaces -- split into three parts:
   411  * The upper part, SDL_UpperBlit(), performs clipping and rectangle 
   412  * verification.  The lower part is a pointer to a low level
   413  * accelerated blitting function.
   414  *
   415  * These parts are separated out and each used internally by this 
   416  * library in the optimimum places.  They are exported so that if
   417  * you know exactly what you are doing, you can optimize your code
   418  * by calling the one(s) you need.
   419  */
   420 int
   421 SDL_LowerBlit(SDL_Surface * src, SDL_Rect * srcrect,
   422               SDL_Surface * dst, SDL_Rect * dstrect)
   423 {
   424     /* Check to make sure the blit mapping is valid */
   425     if ((src->map->dst != dst) ||
   426         (src->map->dst->format_version != src->map->format_version)) {
   427         if (SDL_MapSurface(src, dst) < 0) {
   428             return (-1);
   429         }
   430     }
   431     return (src->map->sw_blit(src, srcrect, dst, dstrect));
   432 }
   433 
   434 
   435 int
   436 SDL_UpperBlit(SDL_Surface * src, SDL_Rect * srcrect,
   437               SDL_Surface * dst, SDL_Rect * dstrect)
   438 {
   439     SDL_Rect fulldst;
   440     int srcx, srcy, w, h;
   441 
   442     /* Make sure the surfaces aren't locked */
   443     if (!src || !dst) {
   444         SDL_SetError("SDL_UpperBlit: passed a NULL surface");
   445         return (-1);
   446     }
   447     if (src->locked || dst->locked) {
   448         SDL_SetError("Surfaces must not be locked during blit");
   449         return (-1);
   450     }
   451 
   452     /* If the destination rectangle is NULL, use the entire dest surface */
   453     if (dstrect == NULL) {
   454         fulldst.x = fulldst.y = 0;
   455         dstrect = &fulldst;
   456     }
   457 
   458     /* clip the source rectangle to the source surface */
   459     if (srcrect) {
   460         int maxw, maxh;
   461 
   462         srcx = srcrect->x;
   463         w = srcrect->w;
   464         if (srcx < 0) {
   465             w += srcx;
   466             dstrect->x -= srcx;
   467             srcx = 0;
   468         }
   469         maxw = src->w - srcx;
   470         if (maxw < w)
   471             w = maxw;
   472 
   473         srcy = srcrect->y;
   474         h = srcrect->h;
   475         if (srcy < 0) {
   476             h += srcy;
   477             dstrect->y -= srcy;
   478             srcy = 0;
   479         }
   480         maxh = src->h - srcy;
   481         if (maxh < h)
   482             h = maxh;
   483 
   484     } else {
   485         srcx = srcy = 0;
   486         w = src->w;
   487         h = src->h;
   488     }
   489 
   490     /* clip the destination rectangle against the clip rectangle */
   491     {
   492         SDL_Rect *clip = &dst->clip_rect;
   493         int dx, dy;
   494 
   495         dx = clip->x - dstrect->x;
   496         if (dx > 0) {
   497             w -= dx;
   498             dstrect->x += dx;
   499             srcx += dx;
   500         }
   501         dx = dstrect->x + w - clip->x - clip->w;
   502         if (dx > 0)
   503             w -= dx;
   504 
   505         dy = clip->y - dstrect->y;
   506         if (dy > 0) {
   507             h -= dy;
   508             dstrect->y += dy;
   509             srcy += dy;
   510         }
   511         dy = dstrect->y + h - clip->y - clip->h;
   512         if (dy > 0)
   513             h -= dy;
   514     }
   515 
   516     if (w > 0 && h > 0) {
   517         SDL_Rect sr;
   518         sr.x = srcx;
   519         sr.y = srcy;
   520         sr.w = dstrect->w = w;
   521         sr.h = dstrect->h = h;
   522         return SDL_LowerBlit(src, &sr, dst, dstrect);
   523     }
   524     dstrect->w = dstrect->h = 0;
   525     return 0;
   526 }
   527 
   528 static int
   529 SDL_FillRect1(SDL_Surface * dst, SDL_Rect * dstrect, Uint32 color)
   530 {
   531     /* FIXME: We have to worry about packing order.. *sigh* */
   532     SDL_SetError("1-bpp rect fill not yet implemented");
   533     return -1;
   534 }
   535 
   536 static int
   537 SDL_FillRect4(SDL_Surface * dst, SDL_Rect * dstrect, Uint32 color)
   538 {
   539     /* FIXME: We have to worry about packing order.. *sigh* */
   540     SDL_SetError("4-bpp rect fill not yet implemented");
   541     return -1;
   542 }
   543 
   544 /* 
   545  * This function performs a fast fill of the given rectangle with 'color'
   546  */
   547 int
   548 SDL_FillRect(SDL_Surface * dst, SDL_Rect * dstrect, Uint32 color)
   549 {
   550     SDL_VideoDevice *_this = SDL_GetVideoDevice();
   551     int x, y;
   552     Uint8 *row;
   553 
   554     /* This function doesn't work on surfaces < 8 bpp */
   555     if (dst->format->BitsPerPixel < 8) {
   556         switch (dst->format->BitsPerPixel) {
   557         case 1:
   558             return SDL_FillRect1(dst, dstrect, color);
   559             break;
   560         case 4:
   561             return SDL_FillRect4(dst, dstrect, color);
   562             break;
   563         default:
   564             SDL_SetError("Fill rect on unsupported surface format");
   565             return (-1);
   566             break;
   567         }
   568     }
   569 
   570     /* If 'dstrect' == NULL, then fill the whole surface */
   571     if (dstrect) {
   572         /* Perform clipping */
   573         if (!SDL_IntersectRect(dstrect, &dst->clip_rect, dstrect)) {
   574             return (0);
   575         }
   576     } else {
   577         dstrect = &dst->clip_rect;
   578     }
   579 
   580     /* Perform software fill */
   581     if (SDL_LockSurface(dst) != 0) {
   582         return (-1);
   583     }
   584     row = (Uint8 *) dst->pixels + dstrect->y * dst->pitch +
   585         dstrect->x * dst->format->BytesPerPixel;
   586     if (dst->format->palette || (color == 0)) {
   587         x = dstrect->w * dst->format->BytesPerPixel;
   588         if (!color && !((uintptr_t) row & 3) && !(x & 3)
   589             && !(dst->pitch & 3)) {
   590             int n = x >> 2;
   591             for (y = dstrect->h; y; --y) {
   592                 SDL_memset4(row, 0, n);
   593                 row += dst->pitch;
   594             }
   595         } else {
   596 #ifdef __powerpc__
   597             /*
   598              * SDL_memset() on PPC (both glibc and codewarrior) uses
   599              * the dcbz (Data Cache Block Zero) instruction, which
   600              * causes an alignment exception if the destination is
   601              * uncachable, so only use it on software surfaces
   602              */
   603             if (dst->flags & SDL_HWSURFACE) {
   604                 if (dstrect->w >= 8) {
   605                     /*
   606                      * 64-bit stores are probably most
   607                      * efficient to uncached video memory
   608                      */
   609                     double fill;
   610                     SDL_memset(&fill, color, (sizeof fill));
   611                     for (y = dstrect->h; y; y--) {
   612                         Uint8 *d = row;
   613                         unsigned n = x;
   614                         unsigned nn;
   615                         Uint8 c = color;
   616                         double f = fill;
   617                         while ((unsigned long) d & (sizeof(double) - 1)) {
   618                             *d++ = c;
   619                             n--;
   620                         }
   621                         nn = n / (sizeof(double) * 4);
   622                         while (nn) {
   623                             ((double *) d)[0] = f;
   624                             ((double *) d)[1] = f;
   625                             ((double *) d)[2] = f;
   626                             ((double *) d)[3] = f;
   627                             d += 4 * sizeof(double);
   628                             nn--;
   629                         }
   630                         n &= ~(sizeof(double) * 4 - 1);
   631                         nn = n / sizeof(double);
   632                         while (nn) {
   633                             *(double *) d = f;
   634                             d += sizeof(double);
   635                             nn--;
   636                         }
   637                         n &= ~(sizeof(double) - 1);
   638                         while (n) {
   639                             *d++ = c;
   640                             n--;
   641                         }
   642                         row += dst->pitch;
   643                     }
   644                 } else {
   645                     /* narrow boxes */
   646                     for (y = dstrect->h; y; y--) {
   647                         Uint8 *d = row;
   648                         Uint8 c = color;
   649                         int n = x;
   650                         while (n) {
   651                             *d++ = c;
   652                             n--;
   653                         }
   654                         row += dst->pitch;
   655                     }
   656                 }
   657             } else
   658 #endif /* __powerpc__ */
   659             {
   660                 for (y = dstrect->h; y; y--) {
   661                     SDL_memset(row, color, x);
   662                     row += dst->pitch;
   663                 }
   664             }
   665         }
   666     } else {
   667         switch (dst->format->BytesPerPixel) {
   668         case 2:
   669             for (y = dstrect->h; y; --y) {
   670                 Uint16 *pixels = (Uint16 *) row;
   671                 Uint16 c = (Uint16) color;
   672                 Uint32 cc = (Uint32) c << 16 | c;
   673                 int n = dstrect->w;
   674                 if ((uintptr_t) pixels & 3) {
   675                     *pixels++ = c;
   676                     n--;
   677                 }
   678                 if (n >> 1)
   679                     SDL_memset4(pixels, cc, n >> 1);
   680                 if (n & 1)
   681                     pixels[n - 1] = c;
   682                 row += dst->pitch;
   683             }
   684             break;
   685 
   686         case 3:
   687 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   688             color <<= 8;
   689 #endif
   690             for (y = dstrect->h; y; --y) {
   691                 Uint8 *pixels = row;
   692                 for (x = dstrect->w; x; --x) {
   693                     SDL_memcpy(pixels, &color, 3);
   694                     pixels += 3;
   695                 }
   696                 row += dst->pitch;
   697             }
   698             break;
   699 
   700         case 4:
   701             for (y = dstrect->h; y; --y) {
   702                 SDL_memset4(row, color, dstrect->w);
   703                 row += dst->pitch;
   704             }
   705             break;
   706         }
   707     }
   708     SDL_UnlockSurface(dst);
   709 
   710     /* We're done! */
   711     return (0);
   712 }
   713 
   714 /*
   715  * Lock a surface to directly access the pixels
   716  */
   717 int
   718 SDL_LockSurface(SDL_Surface * surface)
   719 {
   720     if (!surface->locked) {
   721         /* Perform the lock */
   722         if (surface->flags & SDL_HWSURFACE) {
   723             if (SDL_LockTexture
   724                 ((SDL_TextureID) surface->lock_data, NULL, 1,
   725                  &surface->pixels, &surface->pitch) < 0) {
   726                 return (-1);
   727             }
   728         }
   729         if (surface->flags & SDL_RLEACCEL) {
   730             SDL_UnRLESurface(surface, 1);
   731             surface->flags |= SDL_RLEACCEL;     /* save accel'd state */
   732         }
   733     }
   734 
   735     /* Increment the surface lock count, for recursive locks */
   736     ++surface->locked;
   737 
   738     /* Ready to go.. */
   739     return (0);
   740 }
   741 
   742 /*
   743  * Unlock a previously locked surface
   744  */
   745 void
   746 SDL_UnlockSurface(SDL_Surface * surface)
   747 {
   748     /* Only perform an unlock if we are locked */
   749     if (!surface->locked || (--surface->locked > 0)) {
   750         return;
   751     }
   752 
   753     /* Unlock hardware or accelerated surfaces */
   754     if (surface->flags & SDL_HWSURFACE) {
   755         SDL_UnlockTexture((SDL_TextureID) surface->lock_data);
   756     } else {
   757         /* Update RLE encoded surface with new data */
   758         if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) {
   759             surface->flags &= ~SDL_RLEACCEL;    /* stop lying */
   760             SDL_RLESurface(surface);
   761         }
   762     }
   763 }
   764 
   765 /* 
   766  * Convert a surface into the specified pixel format.
   767  */
   768 SDL_Surface *
   769 SDL_ConvertSurface(SDL_Surface * surface,
   770                    SDL_PixelFormat * format, Uint32 flags)
   771 {
   772     SDL_Surface *convert;
   773     Uint32 colorkey = 0;
   774     Uint8 alpha = 0;
   775     Uint32 surface_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 != 0) ||
   783                 (format->palette->colors[i].g != 0) ||
   784                 (format->palette->colors[i].b != 0))
   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,
   795                                    surface->w, surface->h,
   796                                    format->BitsPerPixel, format->Rmask,
   797                                    format->Gmask, format->Bmask,
   798                                    format->Amask);
   799     if (convert == NULL) {
   800         return (NULL);
   801     }
   802 
   803     /* Copy the palette if any */
   804     if (format->palette && convert->format->palette) {
   805         SDL_memcpy(convert->format->palette->colors,
   806                    format->palette->colors,
   807                    format->palette->ncolors * sizeof(SDL_Color));
   808         convert->format->palette->ncolors = format->palette->ncolors;
   809     }
   810 
   811     /* Save the original surface color key and alpha */
   812     surface_flags = surface->flags;
   813     if ((surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY) {
   814         /* Convert colourkeyed surfaces to RGBA if requested */
   815         if ((flags & SDL_SRCCOLORKEY) != SDL_SRCCOLORKEY && format->Amask) {
   816             surface_flags &= ~SDL_SRCCOLORKEY;
   817         } else {
   818             colorkey = surface->format->colorkey;
   819             SDL_SetColorKey(surface, 0, 0);
   820         }
   821     }
   822     if ((surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA) {
   823         /* Copy over the alpha channel to RGBA if requested */
   824         if (format->Amask) {
   825             surface->flags &= ~SDL_SRCALPHA;
   826         } else {
   827             alpha = surface->format->alpha;
   828             SDL_SetAlpha(surface, 0, 0);
   829         }
   830     }
   831 
   832     /* Copy over the image data */
   833     bounds.x = 0;
   834     bounds.y = 0;
   835     bounds.w = surface->w;
   836     bounds.h = surface->h;
   837     SDL_LowerBlit(surface, &bounds, convert, &bounds);
   838 
   839     /* Clean up the original surface, and update converted surface */
   840     if (convert != NULL) {
   841         SDL_SetClipRect(convert, &surface->clip_rect);
   842     }
   843     if ((surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY) {
   844         Uint32 cflags = surface_flags & (SDL_SRCCOLORKEY | SDL_RLEACCELOK);
   845         if (convert != NULL) {
   846             Uint8 keyR, keyG, keyB;
   847 
   848             SDL_GetRGB(colorkey, surface->format, &keyR, &keyG, &keyB);
   849             SDL_SetColorKey(convert, cflags | (flags & SDL_RLEACCELOK),
   850                             SDL_MapRGB(convert->format, keyR, keyG, keyB));
   851         }
   852         SDL_SetColorKey(surface, cflags, colorkey);
   853     }
   854     if ((surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA) {
   855         Uint32 aflags = surface_flags & (SDL_SRCALPHA | SDL_RLEACCELOK);
   856         if (convert != NULL) {
   857             SDL_SetAlpha(convert, aflags | (flags & SDL_RLEACCELOK), alpha);
   858         }
   859         if (format->Amask) {
   860             surface->flags |= SDL_SRCALPHA;
   861         } else {
   862             SDL_SetAlpha(surface, aflags, alpha);
   863         }
   864     }
   865 
   866     /* We're ready to go! */
   867     return (convert);
   868 }
   869 
   870 /*
   871  * Free a surface created by the above function.
   872  */
   873 void
   874 SDL_FreeSurface(SDL_Surface * surface)
   875 {
   876     if (surface == NULL) {
   877         return;
   878     }
   879     if (--surface->refcount > 0) {
   880         return;
   881     }
   882     while (surface->locked > 0) {
   883         SDL_UnlockSurface(surface);
   884     }
   885     if (surface->flags & SDL_RLEACCEL) {
   886         SDL_UnRLESurface(surface, 0);
   887     }
   888     if (surface->format) {
   889         SDL_FreeFormat(surface->format);
   890         surface->format = NULL;
   891     }
   892     if (surface->map != NULL) {
   893         SDL_FreeBlitMap(surface->map);
   894         surface->map = NULL;
   895     }
   896     /* Should we destroy the texture too?
   897        if (surface->flags & SDL_HWSURFACE) {
   898        SDL_DestroyTexture((SDL_TextureID)surface->lock_data);
   899        }
   900      */
   901     if (surface->pixels && ((surface->flags & SDL_PREALLOC) != SDL_PREALLOC)) {
   902         SDL_free(surface->pixels);
   903     }
   904     SDL_free(surface);
   905 #ifdef CHECK_LEAKS
   906     --surfaces_allocated;
   907 #endif
   908 }
   909 
   910 /* vi: set ts=4 sw=4 expandtab: */