src/video/SDL_surface.c
author Sylvain Becker <sylvain.becker@gmail.com>
Mon, 04 Feb 2019 09:11:07 +0100
changeset 12591 66cd8731c3b1
parent 12590 b50bedb4bca0
permissions -rw-r--r--
Fixed bug 4484 - use SIMD aligned memory for SDL_Surface

Surfaces are allocated using SDL_SIMDAlloc()
They are marked with SDL_SIMD_ALIGNED flag to appropriatly free them with SDL_SIMDFree()
(Flag is cleared when pixels is free'd in RLE, in case user would hijack the pixels ptr)

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