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