| /* |
| SDL - Simple DirectMedia Layer |
| Copyright (C) 1997-2006 Sam Lantinga |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with this library; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| |
| Sam Lantinga |
| slouken@libsdl.org |
| */ |
| #include "SDL_config.h" |
| |
| #include "SDL_video.h" |
| #include "SDL_compat.h" |
| #include "SDL_sysvideo.h" |
| #include "SDL_blit.h" |
| #include "SDL_RLEaccel_c.h" |
| #include "SDL_pixels_c.h" |
| #include "SDL_leaks.h" |
| |
| |
| /* Public routines */ |
| /* |
| * Create an empty RGB surface of the appropriate depth |
| */ |
| SDL_Surface * |
| SDL_CreateRGBSurface(Uint32 flags, |
| int width, int height, int depth, |
| Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) |
| { |
| SDL_Surface *surface; |
| |
| /* Allocate the surface */ |
| surface = (SDL_Surface *) SDL_malloc(sizeof(*surface)); |
| if (surface == NULL) { |
| SDL_OutOfMemory(); |
| return NULL; |
| } |
| SDL_zerop(surface); |
| |
| surface->format = SDL_AllocFormat(depth, Rmask, Gmask, Bmask, Amask); |
| if (!surface->format) { |
| SDL_FreeSurface(surface); |
| return NULL; |
| } |
| if (Amask) { |
| surface->flags |= SDL_SRCALPHA; |
| } |
| surface->w = width; |
| surface->h = height; |
| surface->pitch = SDL_CalculatePitch(surface); |
| SDL_SetClipRect(surface, NULL); |
| |
| if (surface->format->BitsPerPixel <= 8) { |
| SDL_Palette *palette = |
| SDL_AllocPalette((1 << surface->format->BitsPerPixel)); |
| if (!palette) { |
| SDL_FreeSurface(surface); |
| return NULL; |
| } |
| if (Rmask || Bmask || Gmask) { |
| const SDL_PixelFormat *format = surface->format; |
| |
| /* create palette according to masks */ |
| int i; |
| int Rm = 0, Gm = 0, Bm = 0; |
| int Rw = 0, Gw = 0, Bw = 0; |
| |
| if (Rmask) { |
| Rw = 8 - format->Rloss; |
| for (i = format->Rloss; i > 0; i -= Rw) |
| Rm |= 1 << i; |
| } |
| if (Gmask) { |
| Gw = 8 - format->Gloss; |
| for (i = format->Gloss; i > 0; i -= Gw) |
| Gm |= 1 << i; |
| } |
| if (Bmask) { |
| Bw = 8 - format->Bloss; |
| for (i = format->Bloss; i > 0; i -= Bw) |
| Bm |= 1 << i; |
| } |
| for (i = 0; i < palette->ncolors; ++i) { |
| int r, g, b; |
| r = (i & Rmask) >> format->Rshift; |
| r = (r << format->Rloss) | ((r * Rm) >> Rw); |
| palette->colors[i].r = r; |
| |
| g = (i & Gmask) >> format->Gshift; |
| g = (g << format->Gloss) | ((g * Gm) >> Gw); |
| palette->colors[i].g = g; |
| |
| b = (i & Bmask) >> format->Bshift; |
| b = (b << format->Bloss) | ((b * Bm) >> Bw); |
| palette->colors[i].b = b; |
| } |
| } else if (palette->ncolors == 2) { |
| /* Create a black and white bitmap palette */ |
| palette->colors[0].r = 0xFF; |
| palette->colors[0].g = 0xFF; |
| palette->colors[0].b = 0xFF; |
| palette->colors[1].r = 0x00; |
| palette->colors[1].g = 0x00; |
| palette->colors[1].b = 0x00; |
| } |
| SDL_SetSurfacePalette(surface, palette); |
| SDL_FreePalette(palette); |
| } |
| |
| /* Get the pixels */ |
| if (surface->w && surface->h) { |
| surface->pixels = SDL_malloc(surface->h * surface->pitch); |
| if (!surface->pixels) { |
| SDL_FreeSurface(surface); |
| SDL_OutOfMemory(); |
| return NULL; |
| } |
| /* This is important for bitmaps */ |
| SDL_memset(surface->pixels, 0, surface->h * surface->pitch); |
| } |
| |
| /* Allocate an empty mapping */ |
| surface->map = SDL_AllocBlitMap(); |
| if (!surface->map) { |
| SDL_FreeSurface(surface); |
| return NULL; |
| } |
| SDL_FormatChanged(surface); |
| |
| /* The surface is ready to go */ |
| surface->refcount = 1; |
| #ifdef CHECK_LEAKS |
| ++surfaces_allocated; |
| #endif |
| return surface; |
| } |
| |
| /* |
| * Create an RGB surface from an existing memory buffer |
| */ |
| SDL_Surface * |
| SDL_CreateRGBSurfaceFrom(void *pixels, |
| int width, int height, int depth, int pitch, |
| Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, |
| Uint32 Amask) |
| { |
| SDL_Surface *surface; |
| |
| surface = |
| SDL_CreateRGBSurface(0, 0, 0, depth, Rmask, Gmask, Bmask, Amask); |
| if (surface != NULL) { |
| surface->flags |= SDL_PREALLOC; |
| surface->pixels = pixels; |
| surface->w = width; |
| surface->h = height; |
| surface->pitch = pitch; |
| SDL_SetClipRect(surface, NULL); |
| } |
| return surface; |
| } |
| |
| SDL_Surface * |
| SDL_CreateRGBSurfaceFromTexture(SDL_TextureID textureID) |
| { |
| SDL_Surface *surface; |
| Uint32 format; |
| int w, h; |
| int bpp; |
| Uint32 Rmask, Gmask, Bmask, Amask; |
| void *pixels; |
| int pitch; |
| |
| if (SDL_QueryTexture(textureID, &format, NULL, &w, &h) < 0) { |
| return NULL; |
| } |
| |
| if (!SDL_PixelFormatEnumToMasks |
| (format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) { |
| SDL_SetError("Unknown texture format"); |
| return NULL; |
| } |
| |
| if (SDL_QueryTexturePixels(textureID, &pixels, &pitch) == 0) { |
| surface = |
| SDL_CreateRGBSurfaceFrom(pixels, w, h, bpp, pitch, Rmask, Gmask, |
| Bmask, Amask); |
| } else { |
| surface = |
| SDL_CreateRGBSurface(0, 0, 0, bpp, Rmask, Gmask, Bmask, Amask); |
| if (surface) { |
| surface->flags |= SDL_HWSURFACE; |
| surface->w = w; |
| surface->h = h; |
| surface->pitch = SDL_CalculatePitch(surface); |
| SDL_SetClipRect(surface, NULL); |
| } |
| } |
| if (surface) { |
| surface->textureID = textureID; |
| } |
| |
| return surface; |
| } |
| |
| static int |
| SDL_SurfacePaletteChanged(void *userdata, SDL_Palette * palette) |
| { |
| SDL_Surface *surface = (SDL_Surface *) userdata; |
| |
| if (surface->textureID) { |
| if (SDL_SetTexturePalette |
| (surface->textureID, palette->colors, 0, palette->ncolors) < 0) { |
| SDL_GetTexturePalette(surface->textureID, palette->colors, 0, |
| palette->ncolors); |
| return -1; |
| } |
| } |
| SDL_FormatChanged(surface); |
| |
| return 0; |
| } |
| |
| int |
| SDL_SetSurfacePalette(SDL_Surface * surface, SDL_Palette * palette) |
| { |
| if (!surface || !surface->format) { |
| SDL_SetError("SDL_SetSurfacePalette() passed a NULL surface"); |
| return -1; |
| } |
| |
| if (palette && palette->ncolors != (1 << surface->format->BitsPerPixel)) { |
| SDL_SetError |
| ("SDL_SetSurfacePalette() passed a palette that doesn't match the surface format"); |
| return -1; |
| } |
| |
| if (surface->format->palette == palette) { |
| return 0; |
| } |
| |
| if (surface->format->palette) { |
| SDL_DelPaletteWatch(surface->format->palette, |
| SDL_SurfacePaletteChanged, surface); |
| } |
| |
| surface->format->palette = palette; |
| |
| if (surface->format->palette) { |
| SDL_AddPaletteWatch(surface->format->palette, |
| SDL_SurfacePaletteChanged, surface); |
| } |
| return 0; |
| } |
| |
| /* |
| * Set the color key in a blittable surface |
| */ |
| int |
| SDL_SetColorKey(SDL_Surface * surface, Uint32 flag, Uint32 key) |
| { |
| /* Sanity check the flag as it gets passed in */ |
| if (flag & SDL_SRCCOLORKEY) { |
| if (flag & (SDL_RLEACCEL | SDL_RLEACCELOK)) { |
| flag = (SDL_SRCCOLORKEY | SDL_RLEACCELOK); |
| } else { |
| flag = SDL_SRCCOLORKEY; |
| } |
| } else { |
| flag = 0; |
| } |
| |
| /* Optimize away operations that don't change anything */ |
| if ((flag == (surface->flags & (SDL_SRCCOLORKEY | SDL_RLEACCELOK))) && |
| (key == surface->format->colorkey)) { |
| return (0); |
| } |
| |
| /* UnRLE surfaces before we change the colorkey */ |
| if (surface->flags & SDL_RLEACCEL) { |
| SDL_UnRLESurface(surface, 1); |
| } |
| |
| if (flag) { |
| surface->flags |= SDL_SRCCOLORKEY; |
| surface->format->colorkey = key; |
| if (flag & SDL_RLEACCELOK) { |
| surface->flags |= SDL_RLEACCELOK; |
| } else { |
| surface->flags &= ~SDL_RLEACCELOK; |
| } |
| } else { |
| surface->flags &= ~(SDL_SRCCOLORKEY | SDL_RLEACCELOK); |
| surface->format->colorkey = 0; |
| } |
| SDL_InvalidateMap(surface->map); |
| return (0); |
| } |
| |
| /* This function sets the alpha channel of a surface */ |
| int |
| SDL_SetAlpha(SDL_Surface * surface, Uint32 flag, Uint8 value) |
| { |
| Uint32 oldflags = surface->flags; |
| Uint32 oldalpha = surface->format->alpha; |
| |
| /* Sanity check the flag as it gets passed in */ |
| if (flag & SDL_SRCALPHA) { |
| if (flag & (SDL_RLEACCEL | SDL_RLEACCELOK)) { |
| flag = (SDL_SRCALPHA | SDL_RLEACCELOK); |
| } else { |
| flag = SDL_SRCALPHA; |
| } |
| } else { |
| flag = 0; |
| } |
| |
| /* Optimize away operations that don't change anything */ |
| if ((flag == (surface->flags & (SDL_SRCALPHA | SDL_RLEACCELOK))) && |
| (!flag || value == oldalpha)) { |
| return (0); |
| } |
| |
| if (!(flag & SDL_RLEACCELOK) && (surface->flags & SDL_RLEACCEL)) |
| SDL_UnRLESurface(surface, 1); |
| |
| if (flag) { |
| surface->flags |= SDL_SRCALPHA; |
| surface->format->alpha = value; |
| if (flag & SDL_RLEACCELOK) { |
| surface->flags |= SDL_RLEACCELOK; |
| } else { |
| surface->flags &= ~SDL_RLEACCELOK; |
| } |
| } else { |
| surface->flags &= ~SDL_SRCALPHA; |
| surface->format->alpha = SDL_ALPHA_OPAQUE; |
| } |
| /* |
| * The representation for software surfaces is independent of |
| * per-surface alpha, so no need to invalidate the blit mapping |
| * if just the alpha value was changed. (If either is 255, we still |
| * need to invalidate.) |
| */ |
| if (oldflags != surface->flags |
| || (((oldalpha + 1) ^ (value + 1)) & 0x100)) { |
| SDL_InvalidateMap(surface->map); |
| } |
| return (0); |
| } |
| |
| int |
| SDL_SetAlphaChannel(SDL_Surface * surface, Uint8 value) |
| { |
| int row, col; |
| int offset; |
| Uint8 *buf; |
| |
| if ((surface->format->Amask != 0xFF000000) && |
| (surface->format->Amask != 0x000000FF)) { |
| SDL_SetError("Unsupported surface alpha mask format"); |
| return -1; |
| } |
| #if SDL_BYTEORDER == SDL_LIL_ENDIAN |
| if (surface->format->Amask == 0xFF000000) { |
| offset = 3; |
| } else { |
| offset = 0; |
| } |
| #else |
| if (surface->format->Amask == 0xFF000000) { |
| offset = 0; |
| } else { |
| offset = 3; |
| } |
| #endif /* Byte ordering */ |
| |
| /* Quickly set the alpha channel of an RGBA or ARGB surface */ |
| if (SDL_MUSTLOCK(surface)) { |
| if (SDL_LockSurface(surface) < 0) { |
| return -1; |
| } |
| } |
| row = surface->h; |
| while (row--) { |
| col = surface->w; |
| buf = (Uint8 *) surface->pixels + row * surface->pitch + offset; |
| while (col--) { |
| *buf = value; |
| buf += 4; |
| } |
| } |
| if (SDL_MUSTLOCK(surface)) { |
| SDL_UnlockSurface(surface); |
| } |
| return 0; |
| } |
| |
| /* |
| * Set the clipping rectangle for a blittable surface |
| */ |
| SDL_bool |
| SDL_SetClipRect(SDL_Surface * surface, const SDL_Rect * rect) |
| { |
| SDL_Rect full_rect; |
| |
| /* Don't do anything if there's no surface to act on */ |
| if (!surface) { |
| return SDL_FALSE; |
| } |
| |
| /* Set up the full surface rectangle */ |
| full_rect.x = 0; |
| full_rect.y = 0; |
| full_rect.w = surface->w; |
| full_rect.h = surface->h; |
| |
| /* Set the clipping rectangle */ |
| if (!rect) { |
| surface->clip_rect = full_rect; |
| return 1; |
| } |
| return SDL_IntersectRect(rect, &full_rect, &surface->clip_rect); |
| } |
| |
| void |
| SDL_GetClipRect(SDL_Surface * surface, SDL_Rect * rect) |
| { |
| if (surface && rect) { |
| *rect = surface->clip_rect; |
| } |
| } |
| |
| /* |
| * Set up a blit between two surfaces -- split into three parts: |
| * The upper part, SDL_UpperBlit(), performs clipping and rectangle |
| * verification. The lower part is a pointer to a low level |
| * accelerated blitting function. |
| * |
| * These parts are separated out and each used internally by this |
| * library in the optimimum places. They are exported so that if |
| * you know exactly what you are doing, you can optimize your code |
| * by calling the one(s) you need. |
| */ |
| int |
| SDL_LowerBlit(SDL_Surface * src, SDL_Rect * srcrect, |
| SDL_Surface * dst, SDL_Rect * dstrect) |
| { |
| /* Check to make sure the blit mapping is valid */ |
| if ((src->map->dst != dst) || |
| (src->map->dst->format_version != src->map->format_version)) { |
| if (SDL_MapSurface(src, dst) < 0) { |
| return (-1); |
| } |
| } |
| return (src->map->sw_blit(src, srcrect, dst, dstrect)); |
| } |
| |
| |
| int |
| SDL_UpperBlit(SDL_Surface * src, SDL_Rect * srcrect, |
| SDL_Surface * dst, SDL_Rect * dstrect) |
| { |
| SDL_Rect fulldst; |
| int srcx, srcy, w, h; |
| |
| /* Make sure the surfaces aren't locked */ |
| if (!src || !dst) { |
| SDL_SetError("SDL_UpperBlit: passed a NULL surface"); |
| return (-1); |
| } |
| if (src->locked || dst->locked) { |
| SDL_SetError("Surfaces must not be locked during blit"); |
| return (-1); |
| } |
| |
| /* If the destination rectangle is NULL, use the entire dest surface */ |
| if (dstrect == NULL) { |
| fulldst.x = fulldst.y = 0; |
| dstrect = &fulldst; |
| } |
| |
| /* clip the source rectangle to the source surface */ |
| if (srcrect) { |
| int maxw, maxh; |
| |
| srcx = srcrect->x; |
| w = srcrect->w; |
| if (srcx < 0) { |
| w += srcx; |
| dstrect->x -= srcx; |
| srcx = 0; |
| } |
| maxw = src->w - srcx; |
| if (maxw < w) |
| w = maxw; |
| |
| srcy = srcrect->y; |
| h = srcrect->h; |
| if (srcy < 0) { |
| h += srcy; |
| dstrect->y -= srcy; |
| srcy = 0; |
| } |
| maxh = src->h - srcy; |
| if (maxh < h) |
| h = maxh; |
| |
| } else { |
| srcx = srcy = 0; |
| w = src->w; |
| h = src->h; |
| } |
| |
| /* clip the destination rectangle against the clip rectangle */ |
| { |
| SDL_Rect *clip = &dst->clip_rect; |
| int dx, dy; |
| |
| dx = clip->x - dstrect->x; |
| if (dx > 0) { |
| w -= dx; |
| dstrect->x += dx; |
| srcx += dx; |
| } |
| dx = dstrect->x + w - clip->x - clip->w; |
| if (dx > 0) |
| w -= dx; |
| |
| dy = clip->y - dstrect->y; |
| if (dy > 0) { |
| h -= dy; |
| dstrect->y += dy; |
| srcy += dy; |
| } |
| dy = dstrect->y + h - clip->y - clip->h; |
| if (dy > 0) |
| h -= dy; |
| } |
| |
| if (w > 0 && h > 0) { |
| SDL_Rect sr; |
| sr.x = srcx; |
| sr.y = srcy; |
| sr.w = dstrect->w = w; |
| sr.h = dstrect->h = h; |
| return SDL_LowerBlit(src, &sr, dst, dstrect); |
| } |
| dstrect->w = dstrect->h = 0; |
| return 0; |
| } |
| |
| static int |
| SDL_FillRect1(SDL_Surface * dst, SDL_Rect * dstrect, Uint32 color) |
| { |
| /* FIXME: We have to worry about packing order.. *sigh* */ |
| SDL_SetError("1-bpp rect fill not yet implemented"); |
| return -1; |
| } |
| |
| static int |
| SDL_FillRect4(SDL_Surface * dst, SDL_Rect * dstrect, Uint32 color) |
| { |
| /* FIXME: We have to worry about packing order.. *sigh* */ |
| SDL_SetError("4-bpp rect fill not yet implemented"); |
| return -1; |
| } |
| |
| /* |
| * This function performs a fast fill of the given rectangle with 'color' |
| */ |
| int |
| SDL_FillRect(SDL_Surface * dst, SDL_Rect * dstrect, Uint32 color) |
| { |
| int x, y; |
| Uint8 *row; |
| |
| /* This function doesn't work on surfaces < 8 bpp */ |
| if (dst->format->BitsPerPixel < 8) { |
| switch (dst->format->BitsPerPixel) { |
| case 1: |
| return SDL_FillRect1(dst, dstrect, color); |
| break; |
| case 4: |
| return SDL_FillRect4(dst, dstrect, color); |
| break; |
| default: |
| SDL_SetError("Fill rect on unsupported surface format"); |
| return (-1); |
| break; |
| } |
| } |
| |
| /* If 'dstrect' == NULL, then fill the whole surface */ |
| if (dstrect) { |
| /* Perform clipping */ |
| if (!SDL_IntersectRect(dstrect, &dst->clip_rect, dstrect)) { |
| return (0); |
| } |
| } else { |
| dstrect = &dst->clip_rect; |
| } |
| |
| /* Perform software fill */ |
| if (SDL_LockSurface(dst) != 0) { |
| return (-1); |
| } |
| row = (Uint8 *) dst->pixels + dstrect->y * dst->pitch + |
| dstrect->x * dst->format->BytesPerPixel; |
| if (dst->format->palette || (color == 0)) { |
| x = dstrect->w * dst->format->BytesPerPixel; |
| if (!color && !((uintptr_t) row & 3) && !(x & 3) |
| && !(dst->pitch & 3)) { |
| int n = x >> 2; |
| for (y = dstrect->h; y; --y) { |
| SDL_memset4(row, 0, n); |
| row += dst->pitch; |
| } |
| } else { |
| #ifdef __powerpc__ |
| /* |
| * SDL_memset() on PPC (both glibc and codewarrior) uses |
| * the dcbz (Data Cache Block Zero) instruction, which |
| * causes an alignment exception if the destination is |
| * uncachable, so only use it on software surfaces |
| */ |
| if (dst->flags & SDL_HWSURFACE) { |
| if (dstrect->w >= 8) { |
| /* |
| * 64-bit stores are probably most |
| * efficient to uncached video memory |
| */ |
| double fill; |
| SDL_memset(&fill, color, (sizeof fill)); |
| for (y = dstrect->h; y; y--) { |
| Uint8 *d = row; |
| unsigned n = x; |
| unsigned nn; |
| Uint8 c = color; |
| double f = fill; |
| while ((unsigned long) d & (sizeof(double) - 1)) { |
| *d++ = c; |
| n--; |
| } |
| nn = n / (sizeof(double) * 4); |
| while (nn) { |
| ((double *) d)[0] = f; |
| ((double *) d)[1] = f; |
| ((double *) d)[2] = f; |
| ((double *) d)[3] = f; |
| d += 4 * sizeof(double); |
| nn--; |
| } |
| n &= ~(sizeof(double) * 4 - 1); |
| nn = n / sizeof(double); |
| while (nn) { |
| *(double *) d = f; |
| d += sizeof(double); |
| nn--; |
| } |
| n &= ~(sizeof(double) - 1); |
| while (n) { |
| *d++ = c; |
| n--; |
| } |
| row += dst->pitch; |
| } |
| } else { |
| /* narrow boxes */ |
| for (y = dstrect->h; y; y--) { |
| Uint8 *d = row; |
| Uint8 c = color; |
| int n = x; |
| while (n) { |
| *d++ = c; |
| n--; |
| } |
| row += dst->pitch; |
| } |
| } |
| } else |
| #endif /* __powerpc__ */ |
| { |
| for (y = dstrect->h; y; y--) { |
| SDL_memset(row, color, x); |
| row += dst->pitch; |
| } |
| } |
| } |
| } else { |
| switch (dst->format->BytesPerPixel) { |
| case 2: |
| for (y = dstrect->h; y; --y) { |
| Uint16 *pixels = (Uint16 *) row; |
| Uint16 c = (Uint16) color; |
| Uint32 cc = (Uint32) c << 16 | c; |
| int n = dstrect->w; |
| if ((uintptr_t) pixels & 3) { |
| *pixels++ = c; |
| n--; |
| } |
| if (n >> 1) |
| SDL_memset4(pixels, cc, n >> 1); |
| if (n & 1) |
| pixels[n - 1] = c; |
| row += dst->pitch; |
| } |
| break; |
| |
| case 3: |
| #if SDL_BYTEORDER == SDL_BIG_ENDIAN |
| color <<= 8; |
| #endif |
| for (y = dstrect->h; y; --y) { |
| Uint8 *pixels = row; |
| for (x = dstrect->w; x; --x) { |
| SDL_memcpy(pixels, &color, 3); |
| pixels += 3; |
| } |
| row += dst->pitch; |
| } |
| break; |
| |
| case 4: |
| for (y = dstrect->h; y; --y) { |
| SDL_memset4(row, color, dstrect->w); |
| row += dst->pitch; |
| } |
| break; |
| } |
| } |
| SDL_UnlockSurface(dst); |
| |
| /* We're done! */ |
| return (0); |
| } |
| |
| /* |
| * Lock a surface to directly access the pixels |
| */ |
| int |
| SDL_LockSurface(SDL_Surface * surface) |
| { |
| if (!surface->locked) { |
| /* Perform the lock */ |
| if (surface->flags & SDL_HWSURFACE) { |
| if (SDL_LockTexture |
| (surface->textureID, NULL, 1, &surface->pixels, |
| &surface->pitch) < 0) { |
| return (-1); |
| } |
| } |
| if (surface->flags & SDL_RLEACCEL) { |
| SDL_UnRLESurface(surface, 1); |
| surface->flags |= SDL_RLEACCEL; /* save accel'd state */ |
| } |
| } |
| |
| /* Increment the surface lock count, for recursive locks */ |
| ++surface->locked; |
| |
| /* Ready to go.. */ |
| return (0); |
| } |
| |
| /* |
| * Unlock a previously locked surface |
| */ |
| void |
| SDL_UnlockSurface(SDL_Surface * surface) |
| { |
| /* Only perform an unlock if we are locked */ |
| if (!surface->locked || (--surface->locked > 0)) { |
| return; |
| } |
| |
| /* Unlock hardware or accelerated surfaces */ |
| if (surface->flags & SDL_HWSURFACE) { |
| SDL_UnlockTexture(surface->textureID); |
| } |
| |
| /* Update RLE encoded surface with new data */ |
| if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) { |
| surface->flags &= ~SDL_RLEACCEL; /* stop lying */ |
| SDL_RLESurface(surface); |
| } |
| } |
| |
| /* |
| * Convert a surface into the specified pixel format. |
| */ |
| SDL_Surface * |
| SDL_ConvertSurface(SDL_Surface * surface, |
| SDL_PixelFormat * format, Uint32 flags) |
| { |
| SDL_Surface *convert; |
| Uint32 colorkey = 0; |
| Uint8 alpha = 0; |
| Uint32 surface_flags; |
| SDL_Rect bounds; |
| |
| /* Check for empty destination palette! (results in empty image) */ |
| if (format->palette != NULL) { |
| int i; |
| for (i = 0; i < format->palette->ncolors; ++i) { |
| if ((format->palette->colors[i].r != 0xFF) || |
| (format->palette->colors[i].g != 0xFF) || |
| (format->palette->colors[i].b != 0xFF)) |
| break; |
| } |
| if (i == format->palette->ncolors) { |
| SDL_SetError("Empty destination palette"); |
| return (NULL); |
| } |
| } |
| |
| /* Create a new surface with the desired format */ |
| convert = SDL_CreateRGBSurface(flags, |
| surface->w, surface->h, |
| format->BitsPerPixel, format->Rmask, |
| format->Gmask, format->Bmask, |
| format->Amask); |
| if (convert == NULL) { |
| return (NULL); |
| } |
| |
| /* Copy the palette if any */ |
| if (format->palette && convert->format->palette) { |
| SDL_memcpy(convert->format->palette->colors, |
| format->palette->colors, |
| format->palette->ncolors * sizeof(SDL_Color)); |
| convert->format->palette->ncolors = format->palette->ncolors; |
| } |
| |
| /* Save the original surface color key and alpha */ |
| surface_flags = surface->flags; |
| if ((surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY) { |
| /* Convert colourkeyed surfaces to RGBA if requested */ |
| if ((flags & SDL_SRCCOLORKEY) != SDL_SRCCOLORKEY && format->Amask) { |
| surface_flags &= ~SDL_SRCCOLORKEY; |
| } else { |
| colorkey = surface->format->colorkey; |
| SDL_SetColorKey(surface, 0, 0); |
| } |
| } |
| if ((surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA) { |
| /* Copy over the alpha channel to RGBA if requested */ |
| if (format->Amask) { |
| surface->flags &= ~SDL_SRCALPHA; |
| } else { |
| alpha = surface->format->alpha; |
| SDL_SetAlpha(surface, 0, 0); |
| } |
| } |
| |
| /* Copy over the image data */ |
| bounds.x = 0; |
| bounds.y = 0; |
| bounds.w = surface->w; |
| bounds.h = surface->h; |
| SDL_LowerBlit(surface, &bounds, convert, &bounds); |
| |
| /* Clean up the original surface, and update converted surface */ |
| if (convert != NULL) { |
| SDL_SetClipRect(convert, &surface->clip_rect); |
| } |
| if ((surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY) { |
| Uint32 cflags = surface_flags & (SDL_SRCCOLORKEY | SDL_RLEACCELOK); |
| if (convert != NULL) { |
| Uint8 keyR, keyG, keyB; |
| |
| SDL_GetRGB(colorkey, surface->format, &keyR, &keyG, &keyB); |
| SDL_SetColorKey(convert, cflags | (flags & SDL_RLEACCELOK), |
| SDL_MapRGB(convert->format, keyR, keyG, keyB)); |
| } |
| SDL_SetColorKey(surface, cflags, colorkey); |
| } |
| if ((surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA) { |
| Uint32 aflags = surface_flags & (SDL_SRCALPHA | SDL_RLEACCELOK); |
| if (convert != NULL) { |
| SDL_SetAlpha(convert, aflags | (flags & SDL_RLEACCELOK), alpha); |
| } |
| if (format->Amask) { |
| surface->flags |= SDL_SRCALPHA; |
| } else { |
| SDL_SetAlpha(surface, aflags, alpha); |
| } |
| } |
| |
| /* We're ready to go! */ |
| return (convert); |
| } |
| |
| /* |
| * Free a surface created by the above function. |
| */ |
| void |
| SDL_FreeSurface(SDL_Surface * surface) |
| { |
| if (surface == NULL) { |
| return; |
| } |
| if (--surface->refcount > 0) { |
| return; |
| } |
| while (surface->locked > 0) { |
| SDL_UnlockSurface(surface); |
| } |
| if (surface->flags & SDL_RLEACCEL) { |
| SDL_UnRLESurface(surface, 0); |
| } |
| if (surface->format) { |
| SDL_SetSurfacePalette(surface, NULL); |
| SDL_FreeFormat(surface->format); |
| surface->format = NULL; |
| } |
| if (surface->map != NULL) { |
| SDL_FreeBlitMap(surface->map); |
| surface->map = NULL; |
| } |
| /* Should we destroy the texture too? |
| if (surface->textureID) { |
| SDL_DestroyTexture(surface->textureID); |
| } |
| */ |
| if (surface->pixels && ((surface->flags & SDL_PREALLOC) != SDL_PREALLOC)) { |
| SDL_free(surface->pixels); |
| } |
| SDL_free(surface); |
| #ifdef CHECK_LEAKS |
| --surfaces_allocated; |
| #endif |
| } |
| |
| /* vi: set ts=4 sw=4 expandtab: */ |