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