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