src/video/SDL_surface.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 04 Sep 2017 11:46:14 -0700
changeset 11448 52dcef74bdc5
parent 11289 ca3c2c98f2e1
child 11496 3d1698bc2747
permissions -rw-r--r--
Fixed bug 3790 - Memory leak with surfaces blitting on each other

bastien.bouclet

When creating two surfaces and blitting them onto the other, SDL's internal reference counting fails, and one of the surfaces is not freed when calling SDL_FreeSurface.

Example code :

SDL_Surface *s1 = SDL_CreateRGBSurfaceWithFormat(0, 640, 480, 32, SDL_PIXELFORMAT_ARGB8888);
SDL_Surface *s2 = SDL_CreateRGBSurfaceWithFormat(0, 640, 480, 32, SDL_PIXELFORMAT_ARGB8888);

SDL_BlitSurface(s1, NULL, s2, NULL);
SDL_BlitSurface(s2, NULL, s1, NULL);

SDL_FreeSurface(s2);
SDL_FreeSurface(s1);

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