2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2011 Sam Lantinga
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.
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.
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
22 #include "SDL_config.h"
24 #include "SDL_video.h"
25 #include "SDL_compat.h"
26 #include "SDL_sysvideo.h"
28 #include "SDL_RLEaccel_c.h"
29 #include "SDL_pixels_c.h"
34 * Create an empty RGB surface of the appropriate depth
37 SDL_CreateRGBSurface(Uint32 flags,
38 int width, int height, int depth,
39 Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
44 /* The flags are no longer used, make the compiler happy */
47 /* Get the pixel format */
48 format = SDL_MasksToPixelFormatEnum(depth, Rmask, Gmask, Bmask, Amask);
49 if (format == SDL_PIXELFORMAT_UNKNOWN) {
50 SDL_SetError("Unknown pixel format");
54 /* Allocate the surface */
55 surface = (SDL_Surface *) SDL_calloc(1, sizeof(*surface));
56 if (surface == NULL) {
61 surface->format = SDL_AllocFormat(format);
62 if (!surface->format) {
63 SDL_FreeSurface(surface);
68 surface->pitch = SDL_CalculatePitch(surface);
69 SDL_SetClipRect(surface, NULL);
71 if (SDL_ISPIXELFORMAT_INDEXED(surface->format->format)) {
72 SDL_Palette *palette =
73 SDL_AllocPalette((1 << surface->format->BitsPerPixel));
75 SDL_FreeSurface(surface);
78 if (Rmask || Bmask || Gmask) {
79 const SDL_PixelFormat *format = surface->format;
81 /* create palette according to masks */
83 int Rm = 0, Gm = 0, Bm = 0;
84 int Rw = 0, Gw = 0, Bw = 0;
87 Rw = 8 - format->Rloss;
88 for (i = format->Rloss; i > 0; i -= Rw)
92 Gw = 8 - format->Gloss;
93 for (i = format->Gloss; i > 0; i -= Gw)
97 Bw = 8 - format->Bloss;
98 for (i = format->Bloss; i > 0; i -= Bw)
101 for (i = 0; i < palette->ncolors; ++i) {
103 r = (i & Rmask) >> format->Rshift;
104 r = (r << format->Rloss) | ((r * Rm) >> Rw);
105 palette->colors[i].r = r;
107 g = (i & Gmask) >> format->Gshift;
108 g = (g << format->Gloss) | ((g * Gm) >> Gw);
109 palette->colors[i].g = g;
111 b = (i & Bmask) >> format->Bshift;
112 b = (b << format->Bloss) | ((b * Bm) >> Bw);
113 palette->colors[i].b = b;
115 } else if (palette->ncolors == 2) {
116 /* Create a black and white bitmap palette */
117 palette->colors[0].r = 0xFF;
118 palette->colors[0].g = 0xFF;
119 palette->colors[0].b = 0xFF;
120 palette->colors[1].r = 0x00;
121 palette->colors[1].g = 0x00;
122 palette->colors[1].b = 0x00;
124 SDL_SetSurfacePalette(surface, palette);
125 SDL_FreePalette(palette);
129 if (surface->w && surface->h) {
130 surface->pixels = SDL_malloc(surface->h * surface->pitch);
131 if (!surface->pixels) {
132 SDL_FreeSurface(surface);
136 /* This is important for bitmaps */
137 SDL_memset(surface->pixels, 0, surface->h * surface->pitch);
140 /* Allocate an empty mapping */
141 surface->map = SDL_AllocBlitMap();
143 SDL_FreeSurface(surface);
147 /* By default surface with an alpha mask are set up for blending */
149 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
152 /* The surface is ready to go */
153 surface->refcount = 1;
158 * Create an RGB surface from an existing memory buffer
161 SDL_CreateRGBSurfaceFrom(void *pixels,
162 int width, int height, int depth, int pitch,
163 Uint32 Rmask, Uint32 Gmask, Uint32 Bmask,
166 SDL_Surface *surface;
169 SDL_CreateRGBSurface(0, 0, 0, depth, Rmask, Gmask, Bmask, Amask);
170 if (surface != NULL) {
171 surface->flags |= SDL_PREALLOC;
172 surface->pixels = pixels;
175 surface->pitch = pitch;
176 SDL_SetClipRect(surface, NULL);
182 SDL_SetSurfacePalette(SDL_Surface * surface, SDL_Palette * palette)
185 SDL_SetError("SDL_SetSurfacePalette() passed a NULL surface");
188 return SDL_SetPixelFormatPalette(surface->format, palette);
192 SDL_SetSurfaceRLE(SDL_Surface * surface, int flag)
200 flags = surface->map->info.flags;
202 surface->map->info.flags |= SDL_COPY_RLE_DESIRED;
204 surface->map->info.flags &= ~SDL_COPY_RLE_DESIRED;
206 if (surface->map->info.flags != flags) {
207 SDL_InvalidateMap(surface->map);
213 SDL_SetColorKey(SDL_Surface * surface, int flag, Uint32 key)
221 if (flag & SDL_RLEACCEL) {
222 SDL_SetSurfaceRLE(surface, 1);
225 flags = surface->map->info.flags;
227 surface->map->info.flags |= SDL_COPY_COLORKEY;
228 surface->map->info.colorkey = key;
230 surface->map->info.flags &= ~SDL_COPY_COLORKEY;
232 if (surface->map->info.flags != flags) {
233 SDL_InvalidateMap(surface->map);
236 /* Compatibility mode */
237 if (surface->map->info.flags & SDL_COPY_COLORKEY) {
238 surface->flags |= SDL_SRCCOLORKEY;
240 surface->flags &= ~SDL_SRCCOLORKEY;
247 SDL_GetColorKey(SDL_Surface * surface, Uint32 * key)
253 if (!(surface->map->info.flags & SDL_COPY_COLORKEY)) {
258 *key = surface->map->info.colorkey;
263 /* This is a fairly slow function to switch from colorkey to alpha */
265 SDL_ConvertColorkeyToAlpha(SDL_Surface * surface)
273 if (!(surface->map->info.flags & SDL_COPY_COLORKEY) ||
274 !surface->format->Amask) {
278 SDL_LockSurface(surface);
280 switch (surface->format->BytesPerPixel) {
284 Uint16 ckey = (Uint16) surface->map->info.colorkey;
285 Uint16 mask = (Uint16) (~surface->format->Amask);
287 row = (Uint16 *) surface->pixels;
288 for (y = surface->h; y--;) {
290 for (x = surface->w; x--;) {
296 row += surface->pitch / 2;
306 Uint32 ckey = surface->map->info.colorkey;
307 Uint32 mask = ~surface->format->Amask;
309 row = (Uint32 *) surface->pixels;
310 for (y = surface->h; y--;) {
312 for (x = surface->w; x--;) {
318 row += surface->pitch / 4;
324 SDL_UnlockSurface(surface);
326 SDL_SetColorKey(surface, 0, 0);
327 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
331 SDL_SetSurfaceColorMod(SDL_Surface * surface, Uint8 r, Uint8 g, Uint8 b)
339 surface->map->info.r = r;
340 surface->map->info.g = g;
341 surface->map->info.b = b;
343 flags = surface->map->info.flags;
344 if (r != 0xFF || g != 0xFF || b != 0xFF) {
345 surface->map->info.flags |= SDL_COPY_MODULATE_COLOR;
347 surface->map->info.flags &= ~SDL_COPY_MODULATE_COLOR;
349 if (surface->map->info.flags != flags) {
350 SDL_InvalidateMap(surface->map);
357 SDL_GetSurfaceColorMod(SDL_Surface * surface, Uint8 * r, Uint8 * g, Uint8 * b)
364 *r = surface->map->info.r;
367 *g = surface->map->info.g;
370 *b = surface->map->info.b;
376 SDL_SetSurfaceAlphaMod(SDL_Surface * surface, Uint8 alpha)
384 surface->map->info.a = alpha;
386 flags = surface->map->info.flags;
388 surface->map->info.flags |= SDL_COPY_MODULATE_ALPHA;
390 surface->map->info.flags &= ~SDL_COPY_MODULATE_ALPHA;
392 if (surface->map->info.flags != flags) {
393 SDL_InvalidateMap(surface->map);
399 SDL_GetSurfaceAlphaMod(SDL_Surface * surface, Uint8 * alpha)
406 *alpha = surface->map->info.a;
412 SDL_SetSurfaceBlendMode(SDL_Surface * surface, SDL_BlendMode blendMode)
421 flags = surface->map->info.flags;
422 surface->map->info.flags &=
423 ~(SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD);
425 case SDL_BLENDMODE_NONE:
427 case SDL_BLENDMODE_BLEND:
428 surface->map->info.flags |= SDL_COPY_BLEND;
430 case SDL_BLENDMODE_ADD:
431 surface->map->info.flags |= SDL_COPY_ADD;
433 case SDL_BLENDMODE_MOD:
434 surface->map->info.flags |= SDL_COPY_MOD;
442 if (surface->map->info.flags != flags) {
443 SDL_InvalidateMap(surface->map);
446 /* Compatibility mode */
447 if (surface->map->info.flags & SDL_COPY_BLEND) {
448 surface->flags |= SDL_SRCALPHA;
450 surface->flags &= ~SDL_SRCALPHA;
457 SDL_GetSurfaceBlendMode(SDL_Surface * surface, SDL_BlendMode *blendMode)
467 switch (surface->map->
468 info.flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD)) {
470 *blendMode = SDL_BLENDMODE_BLEND;
473 *blendMode = SDL_BLENDMODE_ADD;
476 *blendMode = SDL_BLENDMODE_MOD;
479 *blendMode = SDL_BLENDMODE_NONE;
486 SDL_SetClipRect(SDL_Surface * surface, const SDL_Rect * rect)
490 /* Don't do anything if there's no surface to act on */
495 /* Set up the full surface rectangle */
498 full_rect.w = surface->w;
499 full_rect.h = surface->h;
501 /* Set the clipping rectangle */
503 surface->clip_rect = full_rect;
506 return SDL_IntersectRect(rect, &full_rect, &surface->clip_rect);
510 SDL_GetClipRect(SDL_Surface * surface, SDL_Rect * rect)
512 if (surface && rect) {
513 *rect = surface->clip_rect;
518 * Set up a blit between two surfaces -- split into three parts:
519 * The upper part, SDL_UpperBlit(), performs clipping and rectangle
520 * verification. The lower part is a pointer to a low level
521 * accelerated blitting function.
523 * These parts are separated out and each used internally by this
524 * library in the optimimum places. They are exported so that if
525 * you know exactly what you are doing, you can optimize your code
526 * by calling the one(s) you need.
529 SDL_LowerBlit(SDL_Surface * src, SDL_Rect * srcrect,
530 SDL_Surface * dst, SDL_Rect * dstrect)
532 /* Check to make sure the blit mapping is valid */
533 if ((src->map->dst != dst) ||
534 (dst->format->palette &&
535 src->map->palette_version != dst->format->palette->version)) {
536 if (SDL_MapSurface(src, dst) < 0) {
539 /* just here for debugging */
541 /* ("src = 0x%08X src->flags = %08X src->map->info.flags = %08x\ndst = 0x%08X dst->flags = %08X dst->map->info.flags = %08X\nsrc->map->blit = 0x%08x\n", */
542 /* src, dst->flags, src->map->info.flags, dst, dst->flags, */
543 /* dst->map->info.flags, src->map->blit); */
545 return (src->map->blit(src, srcrect, dst, dstrect));
550 SDL_UpperBlit(SDL_Surface * src, const SDL_Rect * srcrect,
551 SDL_Surface * dst, SDL_Rect * dstrect)
554 int srcx, srcy, w, h;
556 /* Make sure the surfaces aren't locked */
558 SDL_SetError("SDL_UpperBlit: passed a NULL surface");
561 if (src->locked || dst->locked) {
562 SDL_SetError("Surfaces must not be locked during blit");
566 /* If the destination rectangle is NULL, use the entire dest surface */
567 if (dstrect == NULL) {
568 fulldst.x = fulldst.y = 0;
572 /* clip the source rectangle to the source surface */
583 maxw = src->w - srcx;
594 maxh = src->h - srcy;
604 /* clip the destination rectangle against the clip rectangle */
606 SDL_Rect *clip = &dst->clip_rect;
609 dx = clip->x - dstrect->x;
615 dx = dstrect->x + w - clip->x - clip->w;
619 dy = clip->y - dstrect->y;
625 dy = dstrect->y + h - clip->y - clip->h;
630 if (w > 0 && h > 0) {
634 sr.w = dstrect->w = w;
635 sr.h = dstrect->h = h;
636 return SDL_LowerBlit(src, &sr, dst, dstrect);
638 dstrect->w = dstrect->h = 0;
643 * Scale and blit a surface
646 SDL_BlitScaled(SDL_Surface * src, const SDL_Rect * srcrect,
647 SDL_Surface * dst, const SDL_Rect * dstrect)
649 /* Save off the original dst width, height */
650 int dstW = dstrect->w;
651 int dstH = dstrect->h;
652 SDL_Rect final_dst = *dstrect;
653 SDL_Rect final_src = *srcrect;
655 /* Clip the dst surface to the dstrect */
656 SDL_SetClipRect( dst, &final_dst );
658 /* If the dest was clipped to a zero sized rect then exit */
659 if ( dst->clip_rect.w <= 0 || dst->clip_rect.h <= 0 ) {
663 /* Did the dst width change? */
664 if ( dstW != dst->clip_rect.w ) {
665 /* scale the src width appropriately */
666 final_src.w = final_src.w * dst->clip_rect.w / dstW;
669 /* Did the dst height change? */
670 if ( dstH != dst->clip_rect.h ) {
671 /* scale the src width appropriately */
672 final_src.h = final_src.h * dst->clip_rect.h / dstH;
675 /* Clip the src surface to the srcrect */
676 SDL_SetClipRect( src, &final_src );
678 src->map->info.flags |= SDL_COPY_NEAREST;
680 if ( src->format->format == dst->format->format && !SDL_ISPIXELFORMAT_INDEXED(src->format->format) ) {
681 return SDL_SoftStretch( src, &final_src, dst, &final_dst );
683 return SDL_LowerBlit( src, &final_src, dst, &final_dst );
688 * Lock a surface to directly access the pixels
691 SDL_LockSurface(SDL_Surface * surface)
693 if (!surface->locked) {
694 /* Perform the lock */
695 if (surface->flags & SDL_RLEACCEL) {
696 SDL_UnRLESurface(surface, 1);
697 surface->flags |= SDL_RLEACCEL; /* save accel'd state */
701 /* Increment the surface lock count, for recursive locks */
709 * Unlock a previously locked surface
712 SDL_UnlockSurface(SDL_Surface * surface)
714 /* Only perform an unlock if we are locked */
715 if (!surface->locked || (--surface->locked > 0)) {
719 /* Update RLE encoded surface with new data */
720 if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) {
721 surface->flags &= ~SDL_RLEACCEL; /* stop lying */
722 SDL_RLESurface(surface);
727 * Convert a surface into the specified pixel format.
730 SDL_ConvertSurface(SDL_Surface * surface, SDL_PixelFormat * format,
733 SDL_Surface *convert;
737 /* Check for empty destination palette! (results in empty image) */
738 if (format->palette != NULL) {
740 for (i = 0; i < format->palette->ncolors; ++i) {
741 if ((format->palette->colors[i].r != 0xFF) ||
742 (format->palette->colors[i].g != 0xFF) ||
743 (format->palette->colors[i].b != 0xFF))
746 if (i == format->palette->ncolors) {
747 SDL_SetError("Empty destination palette");
752 /* Create a new surface with the desired format */
753 convert = SDL_CreateRGBSurface(flags, surface->w, surface->h,
754 format->BitsPerPixel, format->Rmask,
755 format->Gmask, format->Bmask,
757 if (convert == NULL) {
761 /* Copy the palette if any */
762 if (format->palette && convert->format->palette) {
763 SDL_memcpy(convert->format->palette->colors,
764 format->palette->colors,
765 format->palette->ncolors * sizeof(SDL_Color));
766 convert->format->palette->ncolors = format->palette->ncolors;
769 /* Save the original copy flags */
770 copy_flags = surface->map->info.flags;
771 surface->map->info.flags = 0;
773 /* Copy over the image data */
776 bounds.w = surface->w;
777 bounds.h = surface->h;
778 SDL_LowerBlit(surface, &bounds, convert, &bounds);
780 /* Clean up the original surface, and update converted surface */
781 convert->map->info.r = surface->map->info.r;
782 convert->map->info.g = surface->map->info.g;
783 convert->map->info.b = surface->map->info.b;
784 convert->map->info.a = surface->map->info.a;
785 convert->map->info.flags =
787 ~(SDL_COPY_COLORKEY | SDL_COPY_BLEND
788 | SDL_COPY_RLE_DESIRED | SDL_COPY_RLE_COLORKEY |
789 SDL_COPY_RLE_ALPHAKEY));
790 surface->map->info.flags = copy_flags;
791 if (copy_flags & SDL_COPY_COLORKEY) {
792 Uint8 keyR, keyG, keyB, keyA;
794 SDL_GetRGBA(surface->map->info.colorkey, surface->format, &keyR,
795 &keyG, &keyB, &keyA);
796 SDL_SetColorKey(convert, 1,
797 SDL_MapRGBA(convert->format, keyR, keyG, keyB, keyA));
798 /* This is needed when converting for 3D texture upload */
799 SDL_ConvertColorkeyToAlpha(convert);
801 SDL_SetClipRect(convert, &surface->clip_rect);
803 /* Enable alpha blending by default if the new surface has an
804 * alpha channel or alpha modulation */
805 if ((surface->format->Amask && format->Amask) ||
806 (copy_flags & (SDL_COPY_COLORKEY|SDL_COPY_MODULATE_ALPHA))) {
807 SDL_SetSurfaceBlendMode(convert, SDL_BLENDMODE_BLEND);
809 if ((copy_flags & SDL_COPY_RLE_DESIRED) || (flags & SDL_RLEACCEL)) {
810 SDL_SetSurfaceRLE(convert, SDL_RLEACCEL);
813 /* We're ready to go! */
818 SDL_ConvertSurfaceFormat(SDL_Surface * surface, Uint32 pixel_format,
821 SDL_PixelFormat *fmt;
822 SDL_Surface *convert;
824 fmt = SDL_AllocFormat(pixel_format);
826 convert = SDL_ConvertSurface(surface, fmt, flags);
833 * Create a surface on the stack for quick blit operations
835 static __inline__ SDL_bool
836 SDL_CreateSurfaceOnStack(int width, int height, Uint32 pixel_format,
837 void * pixels, int pitch, SDL_Surface * surface,
838 SDL_PixelFormat * format, SDL_BlitMap * blitmap)
840 if (SDL_ISPIXELFORMAT_INDEXED(pixel_format)) {
841 SDL_SetError("Indexed pixel formats not supported");
844 if (SDL_InitFormat(format, pixel_format) < 0) {
849 surface->flags = SDL_PREALLOC;
850 surface->format = format;
851 surface->pixels = pixels;
854 surface->pitch = pitch;
855 /* We don't actually need to set up the clip rect for our purposes */
856 /*SDL_SetClipRect(surface, NULL);*/
858 /* Allocate an empty mapping */
860 blitmap->info.r = 0xFF;
861 blitmap->info.g = 0xFF;
862 blitmap->info.b = 0xFF;
863 blitmap->info.a = 0xFF;
864 surface->map = blitmap;
866 /* The surface is ready to go */
867 surface->refcount = 1;
872 * Copy a block of pixels of one format to another format
874 int SDL_ConvertPixels(int width, int height,
875 Uint32 src_format, const void * src, int src_pitch,
876 Uint32 dst_format, void * dst, int dst_pitch)
878 SDL_Surface src_surface, dst_surface;
879 SDL_PixelFormat src_fmt, dst_fmt;
880 SDL_BlitMap src_blitmap, dst_blitmap;
883 /* Fast path for same format copy */
884 if (src_format == dst_format) {
887 if (SDL_ISPIXELFORMAT_FOURCC(src_format)) {
888 switch (src_format) {
889 case SDL_PIXELFORMAT_YV12:
890 case SDL_PIXELFORMAT_IYUV:
891 case SDL_PIXELFORMAT_YUY2:
892 case SDL_PIXELFORMAT_UYVY:
893 case SDL_PIXELFORMAT_YVYU:
896 SDL_SetError("Unknown FOURCC pixel format");
900 bpp = SDL_BYTESPERPIXEL(src_format);
904 while (height-- > 0) {
905 SDL_memcpy(dst, src, width);
906 src = (Uint8*)src + src_pitch;
907 dst = (Uint8*)dst + dst_pitch;
912 if (!SDL_CreateSurfaceOnStack(width, height, src_format, (void*)src,
914 &src_surface, &src_fmt, &src_blitmap)) {
917 if (!SDL_CreateSurfaceOnStack(width, height, dst_format, dst, dst_pitch,
918 &dst_surface, &dst_fmt, &dst_blitmap)) {
922 /* Set up the rect and go! */
927 return SDL_LowerBlit(&src_surface, &rect, &dst_surface, &rect);
931 * Free a surface created by the above function.
934 SDL_FreeSurface(SDL_Surface * surface)
936 if (surface == NULL) {
939 if (surface->flags & SDL_DONTFREE) {
942 if (--surface->refcount > 0) {
945 while (surface->locked > 0) {
946 SDL_UnlockSurface(surface);
948 if (surface->flags & SDL_RLEACCEL) {
949 SDL_UnRLESurface(surface, 0);
951 if (surface->format) {
952 SDL_SetSurfacePalette(surface, NULL);
953 SDL_FreeFormat(surface->format);
954 surface->format = NULL;
956 if (surface->map != NULL) {
957 SDL_FreeBlitMap(surface->map);
960 if (surface->pixels && ((surface->flags & SDL_PREALLOC) != SDL_PREALLOC)) {
961 SDL_free(surface->pixels);
966 /* vi: set ts=4 sw=4 expandtab: */