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