src/video/SDL_surface.c
author Sylvain Becker <sylvain.becker@gmail.com>
Wed, 30 Jan 2019 16:36:47 +0100
changeset 12584 233b2a61cad1
parent 12566 884f99b039f0
child 12587 b2d4dcb4ba33
permissions -rw-r--r--
Fixed failing SDL_ConvertSurface() when blit has failed.

Some blit combination are not supported (eg ARGB8888 -> SDL_PIXELFORMAT_INDEX1MSB)
So prevent SDL_ConvertSurface from creating a broken surface, which cannot be blitted
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../SDL_internal.h"
    22 
    23 #include "SDL_video.h"
    24 #include "SDL_sysvideo.h"
    25 #include "SDL_blit.h"
    26 #include "SDL_RLEaccel_c.h"
    27 #include "SDL_pixels_c.h"
    28 #include "SDL_yuv_c.h"
    29 
    30 
    31 /* Check to make sure we can safely check multiplication of surface w and pitch and it won't overflow size_t */
    32 SDL_COMPILE_TIME_ASSERT(surface_size_assumptions,
    33     sizeof(int) == sizeof(Sint32) && sizeof(size_t) >= sizeof(Sint32));
    34 
    35 /* Public routines */
    36 
    37 /*
    38  * Calculate the pad-aligned scanline width of a surface
    39  */
    40 static int
    41 SDL_CalculatePitch(Uint32 format, int width)
    42 {
    43     int pitch;
    44 
    45     /* Surface should be 4-byte aligned for speed */
    46     pitch = width * SDL_BYTESPERPIXEL(format);
    47     switch (SDL_BITSPERPIXEL(format)) {
    48     case 1:
    49         pitch = (pitch + 7) / 8;
    50         break;
    51     case 4:
    52         pitch = (pitch + 1) / 2;
    53         break;
    54     default:
    55         break;
    56     }
    57     pitch = (pitch + 3) & ~3;   /* 4-byte aligning */
    58     return pitch;
    59 }
    60 
    61 /*
    62  * Create an empty RGB surface of the appropriate depth using the given
    63  * enum SDL_PIXELFORMAT_* format
    64  */
    65 SDL_Surface *
    66 SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth,
    67                                Uint32 format)
    68 {
    69     SDL_Surface *surface;
    70 
    71     /* The flags are no longer used, make the compiler happy */
    72     (void)flags;
    73 
    74     /* Allocate the surface */
    75     surface = (SDL_Surface *) SDL_calloc(1, sizeof(*surface));
    76     if (surface == NULL) {
    77         SDL_OutOfMemory();
    78         return NULL;
    79     }
    80 
    81     surface->format = SDL_AllocFormat(format);
    82     if (!surface->format) {
    83         SDL_FreeSurface(surface);
    84         return NULL;
    85     }
    86     surface->w = width;
    87     surface->h = height;
    88     surface->pitch = SDL_CalculatePitch(format, width);
    89     SDL_SetClipRect(surface, NULL);
    90 
    91     if (SDL_ISPIXELFORMAT_INDEXED(surface->format->format)) {
    92         SDL_Palette *palette =
    93             SDL_AllocPalette((1 << surface->format->BitsPerPixel));
    94         if (!palette) {
    95             SDL_FreeSurface(surface);
    96             return NULL;
    97         }
    98         if (palette->ncolors == 2) {
    99             /* Create a black and white bitmap palette */
   100             palette->colors[0].r = 0xFF;
   101             palette->colors[0].g = 0xFF;
   102             palette->colors[0].b = 0xFF;
   103             palette->colors[1].r = 0x00;
   104             palette->colors[1].g = 0x00;
   105             palette->colors[1].b = 0x00;
   106         }
   107         SDL_SetSurfacePalette(surface, palette);
   108         SDL_FreePalette(palette);
   109     }
   110 
   111     /* Get the pixels */
   112     if (surface->w && surface->h) {
   113         /* Assumptions checked in surface_size_assumptions assert above */
   114         Sint64 size = ((Sint64)surface->h * surface->pitch);
   115         if (size < 0 || size > SDL_MAX_SINT32) {
   116             /* Overflow... */
   117             SDL_FreeSurface(surface);
   118             SDL_OutOfMemory();
   119             return NULL;
   120         }
   121 
   122         surface->pixels = SDL_malloc((size_t)size);
   123         if (!surface->pixels) {
   124             SDL_FreeSurface(surface);
   125             SDL_OutOfMemory();
   126             return NULL;
   127         }
   128         /* This is important for bitmaps */
   129         SDL_memset(surface->pixels, 0, surface->h * surface->pitch);
   130     }
   131 
   132     /* Allocate an empty mapping */
   133     surface->map = SDL_AllocBlitMap();
   134     if (!surface->map) {
   135         SDL_FreeSurface(surface);
   136         return NULL;
   137     }
   138 
   139     /* By default surface with an alpha mask are set up for blending */
   140     if (surface->format->Amask) {
   141         SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
   142     }
   143 
   144     /* The surface is ready to go */
   145     surface->refcount = 1;
   146     return surface;
   147 }
   148 
   149 /*
   150  * Create an empty RGB surface of the appropriate depth
   151  */
   152 SDL_Surface *
   153 SDL_CreateRGBSurface(Uint32 flags,
   154                      int width, int height, int depth,
   155                      Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
   156 {
   157     Uint32 format;
   158 
   159     /* Get the pixel format */
   160     format = SDL_MasksToPixelFormatEnum(depth, Rmask, Gmask, Bmask, Amask);
   161     if (format == SDL_PIXELFORMAT_UNKNOWN) {
   162         SDL_SetError("Unknown pixel format");
   163         return NULL;
   164     }
   165 
   166     return SDL_CreateRGBSurfaceWithFormat(flags, width, height, depth, format);
   167 }
   168 
   169 /*
   170  * Create an RGB surface from an existing memory buffer
   171  */
   172 SDL_Surface *
   173 SDL_CreateRGBSurfaceFrom(void *pixels,
   174                          int width, int height, int depth, int pitch,
   175                          Uint32 Rmask, Uint32 Gmask, Uint32 Bmask,
   176                          Uint32 Amask)
   177 {
   178     SDL_Surface *surface;
   179 
   180     surface = SDL_CreateRGBSurface(0, 0, 0, depth, Rmask, Gmask, Bmask, Amask);
   181     if (surface != NULL) {
   182         surface->flags |= SDL_PREALLOC;
   183         surface->pixels = pixels;
   184         surface->w = width;
   185         surface->h = height;
   186         surface->pitch = pitch;
   187         SDL_SetClipRect(surface, NULL);
   188     }
   189     return surface;
   190 }
   191 
   192 /*
   193  * Create an RGB surface from an existing memory buffer using the given given
   194  * enum SDL_PIXELFORMAT_* format
   195  */
   196 SDL_Surface *
   197 SDL_CreateRGBSurfaceWithFormatFrom(void *pixels,
   198                          int width, int height, int depth, int pitch,
   199                          Uint32 format)
   200 {
   201     SDL_Surface *surface;
   202 
   203     surface = SDL_CreateRGBSurfaceWithFormat(0, 0, 0, depth, format);
   204     if (surface != NULL) {
   205         surface->flags |= SDL_PREALLOC;
   206         surface->pixels = pixels;
   207         surface->w = width;
   208         surface->h = height;
   209         surface->pitch = pitch;
   210         SDL_SetClipRect(surface, NULL);
   211     }
   212     return surface;
   213 }
   214 
   215 int
   216 SDL_SetSurfacePalette(SDL_Surface * surface, SDL_Palette * palette)
   217 {
   218     if (!surface) {
   219         return SDL_SetError("SDL_SetSurfacePalette() passed a NULL surface");
   220     }
   221     if (SDL_SetPixelFormatPalette(surface->format, palette) < 0) {
   222         return -1;
   223     }
   224     SDL_InvalidateMap(surface->map);
   225 
   226     return 0;
   227 }
   228 
   229 int
   230 SDL_SetSurfaceRLE(SDL_Surface * surface, int flag)
   231 {
   232     int flags;
   233 
   234     if (!surface) {
   235         return -1;
   236     }
   237 
   238     flags = surface->map->info.flags;
   239     if (flag) {
   240         surface->map->info.flags |= SDL_COPY_RLE_DESIRED;
   241     } else {
   242         surface->map->info.flags &= ~SDL_COPY_RLE_DESIRED;
   243     }
   244     if (surface->map->info.flags != flags) {
   245         SDL_InvalidateMap(surface->map);
   246     }
   247     return 0;
   248 }
   249 
   250 int
   251 SDL_SetColorKey(SDL_Surface * surface, int flag, Uint32 key)
   252 {
   253     int flags;
   254 
   255     if (!surface) {
   256         return SDL_InvalidParamError("surface");
   257     }
   258 
   259     if (surface->format->palette && key >= ((Uint32) surface->format->palette->ncolors)) {
   260         return SDL_InvalidParamError("key");
   261     }
   262 
   263     if (flag & SDL_RLEACCEL) {
   264         SDL_SetSurfaceRLE(surface, 1);
   265     }
   266 
   267     flags = surface->map->info.flags;
   268     if (flag) {
   269         surface->map->info.flags |= SDL_COPY_COLORKEY;
   270         surface->map->info.colorkey = key;
   271     } else {
   272         surface->map->info.flags &= ~SDL_COPY_COLORKEY;
   273     }
   274     if (surface->map->info.flags != flags) {
   275         SDL_InvalidateMap(surface->map);
   276     }
   277 
   278     return 0;
   279 }
   280 
   281 SDL_bool
   282 SDL_HasColorKey(SDL_Surface * surface)
   283 {
   284     if (!surface) {
   285         return SDL_FALSE;
   286     }
   287 
   288     if (!(surface->map->info.flags & SDL_COPY_COLORKEY)) {
   289         return SDL_FALSE;
   290     }
   291 
   292 	return SDL_TRUE;
   293 }
   294 
   295 int
   296 SDL_GetColorKey(SDL_Surface * surface, Uint32 * key)
   297 {
   298     if (!surface) {
   299         return SDL_InvalidParamError("surface");
   300     }
   301 
   302     if (!(surface->map->info.flags & SDL_COPY_COLORKEY)) {
   303         return SDL_SetError("Surface doesn't have a colorkey");
   304     }
   305 
   306     if (key) {
   307         *key = surface->map->info.colorkey;
   308     }
   309     return 0;
   310 }
   311 
   312 /* This is a fairly slow function to switch from colorkey to alpha */
   313 static void
   314 SDL_ConvertColorkeyToAlpha(SDL_Surface * surface, SDL_bool ignore_alpha)
   315 {
   316     int x, y;
   317 
   318     if (!surface) {
   319         return;
   320     }
   321 
   322     if (!(surface->map->info.flags & SDL_COPY_COLORKEY) ||
   323         !surface->format->Amask) {
   324         return;
   325     }
   326 
   327     SDL_LockSurface(surface);
   328 
   329     switch (surface->format->BytesPerPixel) {
   330     case 2:
   331         {
   332             Uint16 *row, *spot;
   333             Uint16 ckey = (Uint16) surface->map->info.colorkey;
   334             Uint16 mask = (Uint16) (~surface->format->Amask);
   335 
   336             /* Ignore, or not, alpha in colorkey comparison */
   337             if (ignore_alpha) {
   338                 ckey &= mask;
   339                 row = (Uint16 *) surface->pixels;
   340                 for (y = surface->h; y--;) {
   341                     spot = row;
   342                     for (x = surface->w; x--;) {
   343                         if ((*spot & mask) == ckey) {
   344                             *spot &= mask;
   345                         }
   346                         ++spot;
   347                     }
   348                     row += surface->pitch / 2;
   349                 }
   350             } else {
   351                 row = (Uint16 *) surface->pixels;
   352                 for (y = surface->h; y--;) {
   353                     spot = row;
   354                     for (x = surface->w; x--;) {
   355                         if (*spot == ckey) {
   356                             *spot &= mask;
   357                         }
   358                         ++spot;
   359                     }
   360                     row += surface->pitch / 2;
   361                 }
   362             }
   363         }
   364         break;
   365     case 3:
   366         /* FIXME */
   367         break;
   368     case 4:
   369         {
   370             Uint32 *row, *spot;
   371             Uint32 ckey = surface->map->info.colorkey;
   372             Uint32 mask = ~surface->format->Amask;
   373 
   374             /* Ignore, or not, alpha in colorkey comparison */
   375             if (ignore_alpha) {
   376                 ckey &= mask;
   377                 row = (Uint32 *) surface->pixels;
   378                 for (y = surface->h; y--;) {
   379                     spot = row;
   380                     for (x = surface->w; x--;) {
   381                         if ((*spot & mask) == ckey) {
   382                             *spot &= mask;
   383                         }
   384                         ++spot;
   385                     }
   386                     row += surface->pitch / 4;
   387                 }
   388             } else {
   389                 row = (Uint32 *) surface->pixels;
   390                 for (y = surface->h; y--;) {
   391                     spot = row;
   392                     for (x = surface->w; x--;) {
   393                         if (*spot == ckey) {
   394                             *spot &= mask;
   395                         }
   396                         ++spot;
   397                     }
   398                     row += surface->pitch / 4;
   399                 }
   400             }
   401         }
   402         break;
   403     }
   404 
   405     SDL_UnlockSurface(surface);
   406 
   407     SDL_SetColorKey(surface, 0, 0);
   408     SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
   409 }
   410 
   411 int
   412 SDL_SetSurfaceColorMod(SDL_Surface * surface, Uint8 r, Uint8 g, Uint8 b)
   413 {
   414     int flags;
   415 
   416     if (!surface) {
   417         return -1;
   418     }
   419 
   420     surface->map->info.r = r;
   421     surface->map->info.g = g;
   422     surface->map->info.b = b;
   423 
   424     flags = surface->map->info.flags;
   425     if (r != 0xFF || g != 0xFF || b != 0xFF) {
   426         surface->map->info.flags |= SDL_COPY_MODULATE_COLOR;
   427     } else {
   428         surface->map->info.flags &= ~SDL_COPY_MODULATE_COLOR;
   429     }
   430     if (surface->map->info.flags != flags) {
   431         SDL_InvalidateMap(surface->map);
   432     }
   433     return 0;
   434 }
   435 
   436 
   437 int
   438 SDL_GetSurfaceColorMod(SDL_Surface * surface, Uint8 * r, Uint8 * g, Uint8 * b)
   439 {
   440     if (!surface) {
   441         return -1;
   442     }
   443 
   444     if (r) {
   445         *r = surface->map->info.r;
   446     }
   447     if (g) {
   448         *g = surface->map->info.g;
   449     }
   450     if (b) {
   451         *b = surface->map->info.b;
   452     }
   453     return 0;
   454 }
   455 
   456 int
   457 SDL_SetSurfaceAlphaMod(SDL_Surface * surface, Uint8 alpha)
   458 {
   459     int flags;
   460 
   461     if (!surface) {
   462         return -1;
   463     }
   464 
   465     surface->map->info.a = alpha;
   466 
   467     flags = surface->map->info.flags;
   468     if (alpha != 0xFF) {
   469         surface->map->info.flags |= SDL_COPY_MODULATE_ALPHA;
   470     } else {
   471         surface->map->info.flags &= ~SDL_COPY_MODULATE_ALPHA;
   472     }
   473     if (surface->map->info.flags != flags) {
   474         SDL_InvalidateMap(surface->map);
   475     }
   476     return 0;
   477 }
   478 
   479 int
   480 SDL_GetSurfaceAlphaMod(SDL_Surface * surface, Uint8 * alpha)
   481 {
   482     if (!surface) {
   483         return -1;
   484     }
   485 
   486     if (alpha) {
   487         *alpha = surface->map->info.a;
   488     }
   489     return 0;
   490 }
   491 
   492 int
   493 SDL_SetSurfaceBlendMode(SDL_Surface * surface, SDL_BlendMode blendMode)
   494 {
   495     int flags, status;
   496 
   497     if (!surface) {
   498         return -1;
   499     }
   500 
   501     status = 0;
   502     flags = surface->map->info.flags;
   503     surface->map->info.flags &=
   504         ~(SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD);
   505     switch (blendMode) {
   506     case SDL_BLENDMODE_NONE:
   507         break;
   508     case SDL_BLENDMODE_BLEND:
   509         surface->map->info.flags |= SDL_COPY_BLEND;
   510         break;
   511     case SDL_BLENDMODE_ADD:
   512         surface->map->info.flags |= SDL_COPY_ADD;
   513         break;
   514     case SDL_BLENDMODE_MOD:
   515         surface->map->info.flags |= SDL_COPY_MOD;
   516         break;
   517     default:
   518         status = SDL_Unsupported();
   519         break;
   520     }
   521 
   522     if (surface->map->info.flags != flags) {
   523         SDL_InvalidateMap(surface->map);
   524     }
   525 
   526     return status;
   527 }
   528 
   529 int
   530 SDL_GetSurfaceBlendMode(SDL_Surface * surface, SDL_BlendMode *blendMode)
   531 {
   532     if (!surface) {
   533         return -1;
   534     }
   535 
   536     if (!blendMode) {
   537         return 0;
   538     }
   539 
   540     switch (surface->map->
   541             info.flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD)) {
   542     case SDL_COPY_BLEND:
   543         *blendMode = SDL_BLENDMODE_BLEND;
   544         break;
   545     case SDL_COPY_ADD:
   546         *blendMode = SDL_BLENDMODE_ADD;
   547         break;
   548     case SDL_COPY_MOD:
   549         *blendMode = SDL_BLENDMODE_MOD;
   550         break;
   551     default:
   552         *blendMode = SDL_BLENDMODE_NONE;
   553         break;
   554     }
   555     return 0;
   556 }
   557 
   558 SDL_bool
   559 SDL_SetClipRect(SDL_Surface * surface, const SDL_Rect * rect)
   560 {
   561     SDL_Rect full_rect;
   562 
   563     /* Don't do anything if there's no surface to act on */
   564     if (!surface) {
   565         return SDL_FALSE;
   566     }
   567 
   568     /* Set up the full surface rectangle */
   569     full_rect.x = 0;
   570     full_rect.y = 0;
   571     full_rect.w = surface->w;
   572     full_rect.h = surface->h;
   573 
   574     /* Set the clipping rectangle */
   575     if (!rect) {
   576         surface->clip_rect = full_rect;
   577         return SDL_TRUE;
   578     }
   579     return SDL_IntersectRect(rect, &full_rect, &surface->clip_rect);
   580 }
   581 
   582 void
   583 SDL_GetClipRect(SDL_Surface * surface, SDL_Rect * rect)
   584 {
   585     if (surface && rect) {
   586         *rect = surface->clip_rect;
   587     }
   588 }
   589 
   590 /*
   591  * Set up a blit between two surfaces -- split into three parts:
   592  * The upper part, SDL_UpperBlit(), performs clipping and rectangle
   593  * verification.  The lower part is a pointer to a low level
   594  * accelerated blitting function.
   595  *
   596  * These parts are separated out and each used internally by this
   597  * library in the optimimum places.  They are exported so that if
   598  * you know exactly what you are doing, you can optimize your code
   599  * by calling the one(s) you need.
   600  */
   601 int
   602 SDL_LowerBlit(SDL_Surface * src, SDL_Rect * srcrect,
   603               SDL_Surface * dst, SDL_Rect * dstrect)
   604 {
   605     /* Check to make sure the blit mapping is valid */
   606     if ((src->map->dst != dst) ||
   607         (dst->format->palette &&
   608          src->map->dst_palette_version != dst->format->palette->version) ||
   609         (src->format->palette &&
   610          src->map->src_palette_version != src->format->palette->version)) {
   611         if (SDL_MapSurface(src, dst) < 0) {
   612             return (-1);
   613         }
   614         /* just here for debugging */
   615 /*         printf */
   616 /*             ("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", */
   617 /*              src, dst->flags, src->map->info.flags, dst, dst->flags, */
   618 /*              dst->map->info.flags, src->map->blit); */
   619     }
   620     return (src->map->blit(src, srcrect, dst, dstrect));
   621 }
   622 
   623 
   624 int
   625 SDL_UpperBlit(SDL_Surface * src, const SDL_Rect * srcrect,
   626               SDL_Surface * dst, SDL_Rect * dstrect)
   627 {
   628     SDL_Rect fulldst;
   629     int srcx, srcy, w, h;
   630 
   631     /* Make sure the surfaces aren't locked */
   632     if (!src || !dst) {
   633         return SDL_SetError("SDL_UpperBlit: passed a NULL surface");
   634     }
   635     if (src->locked || dst->locked) {
   636         return SDL_SetError("Surfaces must not be locked during blit");
   637     }
   638 
   639     /* If the destination rectangle is NULL, use the entire dest surface */
   640     if (dstrect == NULL) {
   641         fulldst.x = fulldst.y = 0;
   642         fulldst.w = dst->w;
   643         fulldst.h = dst->h;
   644         dstrect = &fulldst;
   645     }
   646 
   647     /* clip the source rectangle to the source surface */
   648     if (srcrect) {
   649         int maxw, maxh;
   650 
   651         srcx = srcrect->x;
   652         w = srcrect->w;
   653         if (srcx < 0) {
   654             w += srcx;
   655             dstrect->x -= srcx;
   656             srcx = 0;
   657         }
   658         maxw = src->w - srcx;
   659         if (maxw < w)
   660             w = maxw;
   661 
   662         srcy = srcrect->y;
   663         h = srcrect->h;
   664         if (srcy < 0) {
   665             h += srcy;
   666             dstrect->y -= srcy;
   667             srcy = 0;
   668         }
   669         maxh = src->h - srcy;
   670         if (maxh < h)
   671             h = maxh;
   672 
   673     } else {
   674         srcx = srcy = 0;
   675         w = src->w;
   676         h = src->h;
   677     }
   678 
   679     /* clip the destination rectangle against the clip rectangle */
   680     {
   681         SDL_Rect *clip = &dst->clip_rect;
   682         int dx, dy;
   683 
   684         dx = clip->x - dstrect->x;
   685         if (dx > 0) {
   686             w -= dx;
   687             dstrect->x += dx;
   688             srcx += dx;
   689         }
   690         dx = dstrect->x + w - clip->x - clip->w;
   691         if (dx > 0)
   692             w -= dx;
   693 
   694         dy = clip->y - dstrect->y;
   695         if (dy > 0) {
   696             h -= dy;
   697             dstrect->y += dy;
   698             srcy += dy;
   699         }
   700         dy = dstrect->y + h - clip->y - clip->h;
   701         if (dy > 0)
   702             h -= dy;
   703     }
   704 
   705     /* Switch back to a fast blit if we were previously stretching */
   706     if (src->map->info.flags & SDL_COPY_NEAREST) {
   707         src->map->info.flags &= ~SDL_COPY_NEAREST;
   708         SDL_InvalidateMap(src->map);
   709     }
   710 
   711     if (w > 0 && h > 0) {
   712         SDL_Rect sr;
   713         sr.x = srcx;
   714         sr.y = srcy;
   715         sr.w = dstrect->w = w;
   716         sr.h = dstrect->h = h;
   717         return SDL_LowerBlit(src, &sr, dst, dstrect);
   718     }
   719     dstrect->w = dstrect->h = 0;
   720     return 0;
   721 }
   722 
   723 int
   724 SDL_UpperBlitScaled(SDL_Surface * src, const SDL_Rect * srcrect,
   725               SDL_Surface * dst, SDL_Rect * dstrect)
   726 {
   727     double src_x0, src_y0, src_x1, src_y1;
   728     double dst_x0, dst_y0, dst_x1, dst_y1;
   729     SDL_Rect final_src, final_dst;
   730     double scaling_w, scaling_h;
   731     int src_w, src_h;
   732     int dst_w, dst_h;
   733 
   734     /* Make sure the surfaces aren't locked */
   735     if (!src || !dst) {
   736         return SDL_SetError("SDL_UpperBlitScaled: passed a NULL surface");
   737     }
   738     if (src->locked || dst->locked) {
   739         return SDL_SetError("Surfaces must not be locked during blit");
   740     }
   741 
   742     if (NULL == srcrect) {
   743         src_w = src->w;
   744         src_h = src->h;
   745     } else {
   746         src_w = srcrect->w;
   747         src_h = srcrect->h;
   748     }
   749 
   750     if (NULL == dstrect) {
   751         dst_w = dst->w;
   752         dst_h = dst->h;
   753     } else {
   754         dst_w = dstrect->w;
   755         dst_h = dstrect->h;
   756     }
   757 
   758     if (dst_w == src_w && dst_h == src_h) {
   759         /* No scaling, defer to regular blit */
   760         return SDL_BlitSurface(src, srcrect, dst, dstrect);
   761     }
   762 
   763     scaling_w = (double)dst_w / src_w;
   764     scaling_h = (double)dst_h / src_h;
   765 
   766     if (NULL == dstrect) {
   767         dst_x0 = 0;
   768         dst_y0 = 0;
   769         dst_x1 = dst_w - 1;
   770         dst_y1 = dst_h - 1;
   771     } else {
   772         dst_x0 = dstrect->x;
   773         dst_y0 = dstrect->y;
   774         dst_x1 = dst_x0 + dst_w - 1;
   775         dst_y1 = dst_y0 + dst_h - 1;
   776     }
   777 
   778     if (NULL == srcrect) {
   779         src_x0 = 0;
   780         src_y0 = 0;
   781         src_x1 = src_w - 1;
   782         src_y1 = src_h - 1;
   783     } else {
   784         src_x0 = srcrect->x;
   785         src_y0 = srcrect->y;
   786         src_x1 = src_x0 + src_w - 1;
   787         src_y1 = src_y0 + src_h - 1;
   788 
   789         /* Clip source rectangle to the source surface */
   790 
   791         if (src_x0 < 0) {
   792             dst_x0 -= src_x0 * scaling_w;
   793             src_x0 = 0;
   794         }
   795 
   796         if (src_x1 >= src->w) {
   797             dst_x1 -= (src_x1 - src->w + 1) * scaling_w;
   798             src_x1 = src->w - 1;
   799         }
   800 
   801         if (src_y0 < 0) {
   802             dst_y0 -= src_y0 * scaling_h;
   803             src_y0 = 0;
   804         }
   805 
   806         if (src_y1 >= src->h) {
   807             dst_y1 -= (src_y1 - src->h + 1) * scaling_h;
   808             src_y1 = src->h - 1;
   809         }
   810     }
   811 
   812     /* Clip destination rectangle to the clip rectangle */
   813 
   814     /* Translate to clip space for easier calculations */
   815     dst_x0 -= dst->clip_rect.x;
   816     dst_x1 -= dst->clip_rect.x;
   817     dst_y0 -= dst->clip_rect.y;
   818     dst_y1 -= dst->clip_rect.y;
   819 
   820     if (dst_x0 < 0) {
   821         src_x0 -= dst_x0 / scaling_w;
   822         dst_x0 = 0;
   823     }
   824 
   825     if (dst_x1 >= dst->clip_rect.w) {
   826         src_x1 -= (dst_x1 - dst->clip_rect.w + 1) / scaling_w;
   827         dst_x1 = dst->clip_rect.w - 1;
   828     }
   829 
   830     if (dst_y0 < 0) {
   831         src_y0 -= dst_y0 / scaling_h;
   832         dst_y0 = 0;
   833     }
   834 
   835     if (dst_y1 >= dst->clip_rect.h) {
   836         src_y1 -= (dst_y1 - dst->clip_rect.h + 1) / scaling_h;
   837         dst_y1 = dst->clip_rect.h - 1;
   838     }
   839 
   840     /* Translate back to surface coordinates */
   841     dst_x0 += dst->clip_rect.x;
   842     dst_x1 += dst->clip_rect.x;
   843     dst_y0 += dst->clip_rect.y;
   844     dst_y1 += dst->clip_rect.y;
   845 
   846     final_src.x = (int)SDL_floor(src_x0 + 0.5);
   847     final_src.y = (int)SDL_floor(src_y0 + 0.5);
   848     final_src.w = (int)SDL_floor(src_x1 + 1 + 0.5) - (int)SDL_floor(src_x0 + 0.5);
   849     final_src.h = (int)SDL_floor(src_y1 + 1 + 0.5) - (int)SDL_floor(src_y0 + 0.5);
   850 
   851     final_dst.x = (int)SDL_floor(dst_x0 + 0.5);
   852     final_dst.y = (int)SDL_floor(dst_y0 + 0.5);
   853     final_dst.w = (int)SDL_floor(dst_x1 - dst_x0 + 1.5);
   854     final_dst.h = (int)SDL_floor(dst_y1 - dst_y0 + 1.5);
   855 
   856     if (final_dst.w < 0)
   857         final_dst.w = 0;
   858     if (final_dst.h < 0)
   859         final_dst.h = 0;
   860 
   861     if (dstrect)
   862         *dstrect = final_dst;
   863 
   864     if (final_dst.w == 0 || final_dst.h == 0 ||
   865         final_src.w <= 0 || final_src.h <= 0) {
   866         /* No-op. */
   867         return 0;
   868     }
   869 
   870     return SDL_LowerBlitScaled(src, &final_src, dst, &final_dst);
   871 }
   872 
   873 /**
   874  *  This is a semi-private blit function and it performs low-level surface
   875  *  scaled blitting only.
   876  */
   877 int
   878 SDL_LowerBlitScaled(SDL_Surface * src, SDL_Rect * srcrect,
   879                 SDL_Surface * dst, SDL_Rect * dstrect)
   880 {
   881     static const Uint32 complex_copy_flags = (
   882         SDL_COPY_MODULATE_COLOR | SDL_COPY_MODULATE_ALPHA |
   883         SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD |
   884         SDL_COPY_COLORKEY
   885     );
   886 
   887     if (!(src->map->info.flags & SDL_COPY_NEAREST)) {
   888         src->map->info.flags |= SDL_COPY_NEAREST;
   889         SDL_InvalidateMap(src->map);
   890     }
   891 
   892     if ( !(src->map->info.flags & complex_copy_flags) &&
   893          src->format->format == dst->format->format &&
   894          !SDL_ISPIXELFORMAT_INDEXED(src->format->format) ) {
   895         return SDL_SoftStretch( src, srcrect, dst, dstrect );
   896     } else {
   897         return SDL_LowerBlit( src, srcrect, dst, dstrect );
   898     }
   899 }
   900 
   901 /*
   902  * Lock a surface to directly access the pixels
   903  */
   904 int
   905 SDL_LockSurface(SDL_Surface * surface)
   906 {
   907     if (!surface->locked) {
   908         /* Perform the lock */
   909         if (surface->flags & SDL_RLEACCEL) {
   910             SDL_UnRLESurface(surface, 1);
   911             surface->flags |= SDL_RLEACCEL;     /* save accel'd state */
   912         }
   913     }
   914 
   915     /* Increment the surface lock count, for recursive locks */
   916     ++surface->locked;
   917 
   918     /* Ready to go.. */
   919     return (0);
   920 }
   921 
   922 /*
   923  * Unlock a previously locked surface
   924  */
   925 void
   926 SDL_UnlockSurface(SDL_Surface * surface)
   927 {
   928     /* Only perform an unlock if we are locked */
   929     if (!surface->locked || (--surface->locked > 0)) {
   930         return;
   931     }
   932 
   933     /* Update RLE encoded surface with new data */
   934     if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) {
   935         surface->flags &= ~SDL_RLEACCEL;        /* stop lying */
   936         SDL_RLESurface(surface);
   937     }
   938 }
   939 
   940 /*
   941  * Creates a new surface identical to the existing surface
   942  */
   943 SDL_Surface *
   944 SDL_DuplicateSurface(SDL_Surface * surface)
   945 {
   946     return SDL_ConvertSurface(surface, surface->format, surface->flags);
   947 }
   948 
   949 /*
   950  * Convert a surface into the specified pixel format.
   951  */
   952 SDL_Surface *
   953 SDL_ConvertSurface(SDL_Surface * surface, const SDL_PixelFormat * format,
   954                    Uint32 flags)
   955 {
   956     SDL_Surface *convert;
   957     Uint32 copy_flags;
   958     SDL_Color copy_color;
   959     SDL_Rect bounds;
   960     int ret;
   961 
   962     if (!surface) {
   963         SDL_InvalidParamError("surface");
   964         return NULL;
   965     }
   966     if (!format) {
   967         SDL_InvalidParamError("format");
   968         return NULL;
   969     }
   970 
   971     /* Check for empty destination palette! (results in empty image) */
   972     if (format->palette != NULL) {
   973         int i;
   974         for (i = 0; i < format->palette->ncolors; ++i) {
   975             if ((format->palette->colors[i].r != 0xFF) ||
   976                 (format->palette->colors[i].g != 0xFF) ||
   977                 (format->palette->colors[i].b != 0xFF))
   978                 break;
   979         }
   980         if (i == format->palette->ncolors) {
   981             SDL_SetError("Empty destination palette");
   982             return (NULL);
   983         }
   984     }
   985 
   986     /* Create a new surface with the desired format */
   987     convert = SDL_CreateRGBSurface(flags, surface->w, surface->h,
   988                                    format->BitsPerPixel, format->Rmask,
   989                                    format->Gmask, format->Bmask,
   990                                    format->Amask);
   991     if (convert == NULL) {
   992         return (NULL);
   993     }
   994 
   995     /* Copy the palette if any */
   996     if (format->palette && convert->format->palette) {
   997         SDL_memcpy(convert->format->palette->colors,
   998                    format->palette->colors,
   999                    format->palette->ncolors * sizeof(SDL_Color));
  1000         convert->format->palette->ncolors = format->palette->ncolors;
  1001     }
  1002 
  1003     /* Save the original copy flags */
  1004     copy_flags = surface->map->info.flags;
  1005     copy_color.r = surface->map->info.r;
  1006     copy_color.g = surface->map->info.g;
  1007     copy_color.b = surface->map->info.b;
  1008     copy_color.a = surface->map->info.a;
  1009     surface->map->info.r = 0xFF;
  1010     surface->map->info.g = 0xFF;
  1011     surface->map->info.b = 0xFF;
  1012     surface->map->info.a = 0xFF;
  1013     surface->map->info.flags = 0;
  1014     SDL_InvalidateMap(surface->map);
  1015 
  1016     /* Copy over the image data */
  1017     bounds.x = 0;
  1018     bounds.y = 0;
  1019     bounds.w = surface->w;
  1020     bounds.h = surface->h;
  1021     ret = SDL_LowerBlit(surface, &bounds, convert, &bounds);
  1022 
  1023     /* Clean up the original surface, and update converted surface */
  1024     convert->map->info.r = copy_color.r;
  1025     convert->map->info.g = copy_color.g;
  1026     convert->map->info.b = copy_color.b;
  1027     convert->map->info.a = copy_color.a;
  1028     convert->map->info.flags =
  1029         (copy_flags &
  1030          ~(SDL_COPY_COLORKEY | SDL_COPY_BLEND
  1031            | SDL_COPY_RLE_DESIRED | SDL_COPY_RLE_COLORKEY |
  1032            SDL_COPY_RLE_ALPHAKEY));
  1033     surface->map->info.r = copy_color.r;
  1034     surface->map->info.g = copy_color.g;
  1035     surface->map->info.b = copy_color.b;
  1036     surface->map->info.a = copy_color.a;
  1037     surface->map->info.flags = copy_flags;
  1038     SDL_InvalidateMap(surface->map);
  1039 
  1040     /* SDL_LowerBlit failed, and so the conversion */
  1041     if (ret < 0) {
  1042         SDL_FreeSurface(convert);
  1043         return NULL;
  1044     }
  1045 
  1046     if (copy_flags & SDL_COPY_COLORKEY) {
  1047         SDL_bool set_colorkey_by_color = SDL_FALSE;
  1048         SDL_bool ignore_alpha          = SDL_TRUE;  /* Ignore, or not, alpha in colorkey comparison */
  1049 
  1050         if (surface->format->palette) {
  1051             if (format->palette &&
  1052                 surface->format->palette->ncolors <= format->palette->ncolors &&
  1053                 (SDL_memcmp(surface->format->palette->colors, format->palette->colors,
  1054                   surface->format->palette->ncolors * sizeof(SDL_Color)) == 0)) {
  1055                 /* The palette is identical, just set the same colorkey */
  1056                 SDL_SetColorKey(convert, 1, surface->map->info.colorkey);
  1057             } else if (format->Amask) {
  1058                 set_colorkey_by_color = SDL_TRUE;
  1059                 ignore_alpha = SDL_FALSE;
  1060             } else {
  1061                 set_colorkey_by_color = SDL_TRUE;
  1062             }
  1063         } else {
  1064             set_colorkey_by_color = SDL_TRUE;
  1065         }
  1066 
  1067         if (set_colorkey_by_color) {
  1068             SDL_Surface *tmp;
  1069             SDL_Surface *tmp2;
  1070             int converted_colorkey = 0;
  1071 
  1072             /* Create a dummy surface to get the colorkey converted */
  1073             tmp = SDL_CreateRGBSurface(0, 1, 1,
  1074                                    surface->format->BitsPerPixel, surface->format->Rmask,
  1075                                    surface->format->Gmask, surface->format->Bmask,
  1076                                    surface->format->Amask);
  1077 
  1078             /* Share the palette, if any */
  1079             if (surface->format->palette) {
  1080                 SDL_SetSurfacePalette(tmp, surface->format->palette);
  1081             }
  1082 
  1083             SDL_FillRect(tmp, NULL, surface->map->info.colorkey);
  1084 
  1085             tmp->map->info.flags &= ~SDL_COPY_COLORKEY;
  1086 
  1087             /* Convertion of the colorkey */
  1088             tmp2 = SDL_ConvertSurface(tmp, format, 0);
  1089 
  1090             /* Get the converted colorkey */
  1091             SDL_memcpy(&converted_colorkey, tmp2->pixels, tmp2->format->BytesPerPixel);
  1092 
  1093             SDL_FreeSurface(tmp);
  1094             SDL_FreeSurface(tmp2);
  1095 
  1096             /* Set the converted colorkey on the new surface */
  1097             SDL_SetColorKey(convert, 1, converted_colorkey);
  1098 
  1099             /* This is needed when converting for 3D texture upload */
  1100             SDL_ConvertColorkeyToAlpha(convert, ignore_alpha);
  1101         }
  1102     }
  1103     SDL_SetClipRect(convert, &surface->clip_rect);
  1104 
  1105     /* Enable alpha blending by default if the new surface has an
  1106      * alpha channel or alpha modulation */
  1107     if ((surface->format->Amask && format->Amask) ||
  1108         (copy_flags & SDL_COPY_MODULATE_ALPHA)) {
  1109         SDL_SetSurfaceBlendMode(convert, SDL_BLENDMODE_BLEND);
  1110     }
  1111     if ((copy_flags & SDL_COPY_RLE_DESIRED) || (flags & SDL_RLEACCEL)) {
  1112         SDL_SetSurfaceRLE(convert, SDL_RLEACCEL);
  1113     }
  1114 
  1115     /* We're ready to go! */
  1116     return (convert);
  1117 }
  1118 
  1119 SDL_Surface *
  1120 SDL_ConvertSurfaceFormat(SDL_Surface * surface, Uint32 pixel_format,
  1121                          Uint32 flags)
  1122 {
  1123     SDL_PixelFormat *fmt;
  1124     SDL_Surface *convert = NULL;
  1125 
  1126     fmt = SDL_AllocFormat(pixel_format);
  1127     if (fmt) {
  1128         convert = SDL_ConvertSurface(surface, fmt, flags);
  1129         SDL_FreeFormat(fmt);
  1130     }
  1131     return convert;
  1132 }
  1133 
  1134 /*
  1135  * Create a surface on the stack for quick blit operations
  1136  */
  1137 static SDL_INLINE SDL_bool
  1138 SDL_CreateSurfaceOnStack(int width, int height, Uint32 pixel_format,
  1139                          void * pixels, int pitch, SDL_Surface * surface,
  1140                          SDL_PixelFormat * format, SDL_BlitMap * blitmap)
  1141 {
  1142     if (SDL_ISPIXELFORMAT_INDEXED(pixel_format)) {
  1143         SDL_SetError("Indexed pixel formats not supported");
  1144         return SDL_FALSE;
  1145     }
  1146     if (SDL_InitFormat(format, pixel_format) < 0) {
  1147         return SDL_FALSE;
  1148     }
  1149 
  1150     SDL_zerop(surface);
  1151     surface->flags = SDL_PREALLOC;
  1152     surface->format = format;
  1153     surface->pixels = pixels;
  1154     surface->w = width;
  1155     surface->h = height;
  1156     surface->pitch = pitch;
  1157     /* We don't actually need to set up the clip rect for our purposes */
  1158     /* SDL_SetClipRect(surface, NULL); */
  1159 
  1160     /* Allocate an empty mapping */
  1161     SDL_zerop(blitmap);
  1162     blitmap->info.r = 0xFF;
  1163     blitmap->info.g = 0xFF;
  1164     blitmap->info.b = 0xFF;
  1165     blitmap->info.a = 0xFF;
  1166     surface->map = blitmap;
  1167 
  1168     /* The surface is ready to go */
  1169     surface->refcount = 1;
  1170     return SDL_TRUE;
  1171 }
  1172 
  1173 /*
  1174  * Copy a block of pixels of one format to another format
  1175  */
  1176 int SDL_ConvertPixels(int width, int height,
  1177                       Uint32 src_format, const void * src, int src_pitch,
  1178                       Uint32 dst_format, void * dst, int dst_pitch)
  1179 {
  1180     SDL_Surface src_surface, dst_surface;
  1181     SDL_PixelFormat src_fmt, dst_fmt;
  1182     SDL_BlitMap src_blitmap, dst_blitmap;
  1183     SDL_Rect rect;
  1184     void *nonconst_src = (void *) src;
  1185 
  1186     /* Check to make sure we are blitting somewhere, so we don't crash */
  1187     if (!dst) {
  1188         return SDL_InvalidParamError("dst");
  1189     }
  1190     if (!dst_pitch) {
  1191         return SDL_InvalidParamError("dst_pitch");
  1192     }
  1193 
  1194     if (SDL_ISPIXELFORMAT_FOURCC(src_format) && SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
  1195         return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
  1196     } else if (SDL_ISPIXELFORMAT_FOURCC(src_format)) {
  1197         return SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
  1198     } else if (SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
  1199         return SDL_ConvertPixels_RGB_to_YUV(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
  1200     }
  1201 
  1202     /* Fast path for same format copy */
  1203     if (src_format == dst_format) {
  1204         int i;
  1205         const int bpp = SDL_BYTESPERPIXEL(src_format);
  1206         width *= bpp;
  1207         for (i = height; i--;) {
  1208             SDL_memcpy(dst, src, width);
  1209             src = (const Uint8*)src + src_pitch;
  1210             dst = (Uint8*)dst + dst_pitch;
  1211         }
  1212         return 0;
  1213     }
  1214 
  1215     if (!SDL_CreateSurfaceOnStack(width, height, src_format, nonconst_src,
  1216                                   src_pitch,
  1217                                   &src_surface, &src_fmt, &src_blitmap)) {
  1218         return -1;
  1219     }
  1220     if (!SDL_CreateSurfaceOnStack(width, height, dst_format, dst, dst_pitch,
  1221                                   &dst_surface, &dst_fmt, &dst_blitmap)) {
  1222         return -1;
  1223     }
  1224 
  1225     /* Set up the rect and go! */
  1226     rect.x = 0;
  1227     rect.y = 0;
  1228     rect.w = width;
  1229     rect.h = height;
  1230     return SDL_LowerBlit(&src_surface, &rect, &dst_surface, &rect);
  1231 }
  1232 
  1233 /*
  1234  * Free a surface created by the above function.
  1235  */
  1236 void
  1237 SDL_FreeSurface(SDL_Surface * surface)
  1238 {
  1239     if (surface == NULL) {
  1240         return;
  1241     }
  1242     if (surface->flags & SDL_DONTFREE) {
  1243         return;
  1244     }
  1245     SDL_InvalidateMap(surface->map);
  1246 
  1247     if (--surface->refcount > 0) {
  1248         return;
  1249     }
  1250     while (surface->locked > 0) {
  1251         SDL_UnlockSurface(surface);
  1252     }
  1253     if (surface->flags & SDL_RLEACCEL) {
  1254         SDL_UnRLESurface(surface, 0);
  1255     }
  1256     if (surface->format) {
  1257         SDL_SetSurfacePalette(surface, NULL);
  1258         SDL_FreeFormat(surface->format);
  1259         surface->format = NULL;
  1260     }
  1261     if (!(surface->flags & SDL_PREALLOC)) {
  1262         SDL_free(surface->pixels);
  1263     }
  1264     if (surface->map) {
  1265         SDL_FreeBlitMap(surface->map);
  1266     }
  1267     SDL_free(surface);
  1268 }
  1269 
  1270 /* vi: set ts=4 sw=4 expandtab: */