src/render/software/SDL_render_sw.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 07 Oct 2016 18:00:30 -0700
changeset 10489 1e1ce9f6d215
parent 9998 f67cf37e9cd4
child 10612 6b2307dbec54
permissions -rw-r--r--
Fixed bug 3029 - software renderer cuts off edges when rotate-blitting with a multiple of 90 degrees

Adam M.

When doing a rotated texture copy with the software renderer, where the angle is a multiple of 90 degrees, one or two edges of the image get cut off. This is because of the following line in sw_rotate.c:
if ((unsigned)dx < (unsigned)sw && (unsigned)dy < (unsigned)sh) {
which is effectively saying:
if (dx >= 0 && dx < src->w-1 && dy >= 0 && dy < src->h-1) {

As a result, it doesn't process pixels in the right column or bottom row of the source image (except when they're accessed as part of the bilinear filtering for nearby pixels). This causes it to look like the edges are cut off, and it's especially obvious with an exact multiple of 90 degrees.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 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 #if !SDL_RENDER_DISABLED
    24 
    25 #include "../SDL_sysrender.h"
    26 #include "SDL_render_sw_c.h"
    27 #include "SDL_hints.h"
    28 
    29 #include "SDL_draw.h"
    30 #include "SDL_blendfillrect.h"
    31 #include "SDL_blendline.h"
    32 #include "SDL_blendpoint.h"
    33 #include "SDL_drawline.h"
    34 #include "SDL_drawpoint.h"
    35 #include "SDL_rotate.h"
    36 
    37 /* SDL surface based renderer implementation */
    38 
    39 static SDL_Renderer *SW_CreateRenderer(SDL_Window * window, Uint32 flags);
    40 static void SW_WindowEvent(SDL_Renderer * renderer,
    41                            const SDL_WindowEvent *event);
    42 static int SW_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
    43 static int SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    44 static int SW_SetTextureColorMod(SDL_Renderer * renderer,
    45                                  SDL_Texture * texture);
    46 static int SW_SetTextureAlphaMod(SDL_Renderer * renderer,
    47                                  SDL_Texture * texture);
    48 static int SW_SetTextureBlendMode(SDL_Renderer * renderer,
    49                                   SDL_Texture * texture);
    50 static int SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    51                             const SDL_Rect * rect, const void *pixels,
    52                             int pitch);
    53 static int SW_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    54                           const SDL_Rect * rect, void **pixels, int *pitch);
    55 static void SW_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    56 static int SW_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
    57 static int SW_UpdateViewport(SDL_Renderer * renderer);
    58 static int SW_UpdateClipRect(SDL_Renderer * renderer);
    59 static int SW_RenderClear(SDL_Renderer * renderer);
    60 static int SW_RenderDrawPoints(SDL_Renderer * renderer,
    61                                const SDL_FPoint * points, int count);
    62 static int SW_RenderDrawLines(SDL_Renderer * renderer,
    63                               const SDL_FPoint * points, int count);
    64 static int SW_RenderFillRects(SDL_Renderer * renderer,
    65                               const SDL_FRect * rects, int count);
    66 static int SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
    67                          const SDL_Rect * srcrect, const SDL_FRect * dstrect);
    68 static int SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
    69                           const SDL_Rect * srcrect, const SDL_FRect * dstrect,
    70                           const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip);
    71 static int SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
    72                                Uint32 format, void * pixels, int pitch);
    73 static void SW_RenderPresent(SDL_Renderer * renderer);
    74 static void SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    75 static void SW_DestroyRenderer(SDL_Renderer * renderer);
    76 
    77 
    78 SDL_RenderDriver SW_RenderDriver = {
    79     SW_CreateRenderer,
    80     {
    81      "software",
    82      SDL_RENDERER_SOFTWARE | SDL_RENDERER_TARGETTEXTURE,
    83      8,
    84      {
    85       SDL_PIXELFORMAT_ARGB8888,
    86       SDL_PIXELFORMAT_ABGR8888,
    87       SDL_PIXELFORMAT_RGBA8888,
    88       SDL_PIXELFORMAT_BGRA8888,
    89       SDL_PIXELFORMAT_RGB888,
    90       SDL_PIXELFORMAT_BGR888,
    91       SDL_PIXELFORMAT_RGB565,
    92       SDL_PIXELFORMAT_RGB555
    93      },
    94      0,
    95      0}
    96 };
    97 
    98 typedef struct
    99 {
   100     SDL_Surface *surface;
   101     SDL_Surface *window;
   102 } SW_RenderData;
   103 
   104 
   105 static SDL_Surface *
   106 SW_ActivateRenderer(SDL_Renderer * renderer)
   107 {
   108     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   109 
   110     if (!data->surface) {
   111         data->surface = data->window;
   112     }
   113     if (!data->surface) {
   114         SDL_Surface *surface = SDL_GetWindowSurface(renderer->window);
   115         if (surface) {
   116             data->surface = data->window = surface;
   117 
   118             SW_UpdateViewport(renderer);
   119             SW_UpdateClipRect(renderer);
   120         }
   121     }
   122     return data->surface;
   123 }
   124 
   125 SDL_Renderer *
   126 SW_CreateRendererForSurface(SDL_Surface * surface)
   127 {
   128     SDL_Renderer *renderer;
   129     SW_RenderData *data;
   130 
   131     if (!surface) {
   132         SDL_SetError("Can't create renderer for NULL surface");
   133         return NULL;
   134     }
   135 
   136     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   137     if (!renderer) {
   138         SDL_OutOfMemory();
   139         return NULL;
   140     }
   141 
   142     data = (SW_RenderData *) SDL_calloc(1, sizeof(*data));
   143     if (!data) {
   144         SW_DestroyRenderer(renderer);
   145         SDL_OutOfMemory();
   146         return NULL;
   147     }
   148     data->surface = surface;
   149     data->window = surface;
   150 
   151     renderer->WindowEvent = SW_WindowEvent;
   152     renderer->GetOutputSize = SW_GetOutputSize;
   153     renderer->CreateTexture = SW_CreateTexture;
   154     renderer->SetTextureColorMod = SW_SetTextureColorMod;
   155     renderer->SetTextureAlphaMod = SW_SetTextureAlphaMod;
   156     renderer->SetTextureBlendMode = SW_SetTextureBlendMode;
   157     renderer->UpdateTexture = SW_UpdateTexture;
   158     renderer->LockTexture = SW_LockTexture;
   159     renderer->UnlockTexture = SW_UnlockTexture;
   160     renderer->SetRenderTarget = SW_SetRenderTarget;
   161     renderer->UpdateViewport = SW_UpdateViewport;
   162     renderer->UpdateClipRect = SW_UpdateClipRect;
   163     renderer->RenderClear = SW_RenderClear;
   164     renderer->RenderDrawPoints = SW_RenderDrawPoints;
   165     renderer->RenderDrawLines = SW_RenderDrawLines;
   166     renderer->RenderFillRects = SW_RenderFillRects;
   167     renderer->RenderCopy = SW_RenderCopy;
   168     renderer->RenderCopyEx = SW_RenderCopyEx;
   169     renderer->RenderReadPixels = SW_RenderReadPixels;
   170     renderer->RenderPresent = SW_RenderPresent;
   171     renderer->DestroyTexture = SW_DestroyTexture;
   172     renderer->DestroyRenderer = SW_DestroyRenderer;
   173     renderer->info = SW_RenderDriver.info;
   174     renderer->driverdata = data;
   175 
   176     SW_ActivateRenderer(renderer);
   177 
   178     return renderer;
   179 }
   180 
   181 SDL_Renderer *
   182 SW_CreateRenderer(SDL_Window * window, Uint32 flags)
   183 {
   184     SDL_Surface *surface;
   185 
   186     surface = SDL_GetWindowSurface(window);
   187     if (!surface) {
   188         return NULL;
   189     }
   190     return SW_CreateRendererForSurface(surface);
   191 }
   192 
   193 static void
   194 SW_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
   195 {
   196     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   197 
   198     if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
   199         data->surface = NULL;
   200         data->window = NULL;
   201     }
   202 }
   203 
   204 static int
   205 SW_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
   206 {
   207     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   208 
   209     if (surface) {
   210         if (w) {
   211             *w = surface->w;
   212         }
   213         if (h) {
   214             *h = surface->h;
   215         }
   216         return 0;
   217     } else {
   218         SDL_SetError("Software renderer doesn't have an output surface");
   219         return -1;
   220     }
   221 }
   222 
   223 static int
   224 SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   225 {
   226     int bpp;
   227     Uint32 Rmask, Gmask, Bmask, Amask;
   228 
   229     if (!SDL_PixelFormatEnumToMasks
   230         (texture->format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
   231         return SDL_SetError("Unknown texture format");
   232     }
   233 
   234     texture->driverdata =
   235         SDL_CreateRGBSurface(0, texture->w, texture->h, bpp, Rmask, Gmask,
   236                              Bmask, Amask);
   237     SDL_SetSurfaceColorMod(texture->driverdata, texture->r, texture->g,
   238                            texture->b);
   239     SDL_SetSurfaceAlphaMod(texture->driverdata, texture->a);
   240     SDL_SetSurfaceBlendMode(texture->driverdata, texture->blendMode);
   241 
   242     if (texture->access == SDL_TEXTUREACCESS_STATIC) {
   243         SDL_SetSurfaceRLE(texture->driverdata, 1);
   244     }
   245 
   246     if (!texture->driverdata) {
   247         return -1;
   248     }
   249     return 0;
   250 }
   251 
   252 static int
   253 SW_SetTextureColorMod(SDL_Renderer * renderer, SDL_Texture * texture)
   254 {
   255     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   256     /* If the color mod is ever enabled (non-white), permanently disable RLE (which doesn't support
   257      * color mod) to avoid potentially frequent RLE encoding/decoding.
   258      */
   259     if ((texture->r & texture->g & texture->b) != 255) {
   260         SDL_SetSurfaceRLE(surface, 0);
   261     }
   262     return SDL_SetSurfaceColorMod(surface, texture->r, texture->g,
   263                                   texture->b);
   264 }
   265 
   266 static int
   267 SW_SetTextureAlphaMod(SDL_Renderer * renderer, SDL_Texture * texture)
   268 {
   269     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   270     /* If the texture ever has multiple alpha values (surface alpha plus alpha channel), permanently
   271      * disable RLE (which doesn't support this) to avoid potentially frequent RLE encoding/decoding.
   272      */
   273     if (texture->a != 255 && surface->format->Amask) {
   274         SDL_SetSurfaceRLE(surface, 0);
   275     }
   276     return SDL_SetSurfaceAlphaMod(surface, texture->a);
   277 }
   278 
   279 static int
   280 SW_SetTextureBlendMode(SDL_Renderer * renderer, SDL_Texture * texture)
   281 {
   282     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   283     /* If add or mod blending are ever enabled, permanently disable RLE (which doesn't support
   284      * them) to avoid potentially frequent RLE encoding/decoding.
   285      */
   286     if ((texture->blendMode == SDL_BLENDMODE_ADD || texture->blendMode == SDL_BLENDMODE_MOD)) {
   287         SDL_SetSurfaceRLE(surface, 0);
   288     }
   289     return SDL_SetSurfaceBlendMode(surface, texture->blendMode);
   290 }
   291 
   292 static int
   293 SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   294                  const SDL_Rect * rect, const void *pixels, int pitch)
   295 {
   296     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   297     Uint8 *src, *dst;
   298     int row;
   299     size_t length;
   300 
   301     if(SDL_MUSTLOCK(surface))
   302         SDL_LockSurface(surface);
   303     src = (Uint8 *) pixels;
   304     dst = (Uint8 *) surface->pixels +
   305                         rect->y * surface->pitch +
   306                         rect->x * surface->format->BytesPerPixel;
   307     length = rect->w * surface->format->BytesPerPixel;
   308     for (row = 0; row < rect->h; ++row) {
   309         SDL_memcpy(dst, src, length);
   310         src += pitch;
   311         dst += surface->pitch;
   312     }
   313     if(SDL_MUSTLOCK(surface))
   314         SDL_UnlockSurface(surface);
   315     return 0;
   316 }
   317 
   318 static int
   319 SW_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   320                const SDL_Rect * rect, void **pixels, int *pitch)
   321 {
   322     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   323 
   324     *pixels =
   325         (void *) ((Uint8 *) surface->pixels + rect->y * surface->pitch +
   326                   rect->x * surface->format->BytesPerPixel);
   327     *pitch = surface->pitch;
   328     return 0;
   329 }
   330 
   331 static void
   332 SW_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   333 {
   334 }
   335 
   336 static int
   337 SW_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
   338 {
   339     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   340 
   341     if (texture ) {
   342         data->surface = (SDL_Surface *) texture->driverdata;
   343     } else {
   344         data->surface = data->window;
   345     }
   346     return 0;
   347 }
   348 
   349 static int
   350 SW_UpdateViewport(SDL_Renderer * renderer)
   351 {
   352     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   353     SDL_Surface *surface = data->surface;
   354 
   355     if (!surface) {
   356         /* We'll update the viewport after we recreate the surface */
   357         return 0;
   358     }
   359 
   360     SDL_SetClipRect(data->surface, &renderer->viewport);
   361     return 0;
   362 }
   363 
   364 static int
   365 SW_UpdateClipRect(SDL_Renderer * renderer)
   366 {
   367     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   368     SDL_Surface *surface = data->surface;
   369     if (surface) {
   370         if (renderer->clipping_enabled) {
   371             SDL_SetClipRect(surface, &renderer->clip_rect);
   372         } else {
   373             SDL_SetClipRect(surface, NULL);
   374         }
   375     }
   376     return 0;
   377 }
   378 
   379 static int
   380 SW_RenderClear(SDL_Renderer * renderer)
   381 {
   382     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   383     Uint32 color;
   384     SDL_Rect clip_rect;
   385 
   386     if (!surface) {
   387         return -1;
   388     }
   389 
   390     color = SDL_MapRGBA(surface->format,
   391                         renderer->r, renderer->g, renderer->b, renderer->a);
   392 
   393     /* By definition the clear ignores the clip rect */
   394     clip_rect = surface->clip_rect;
   395     SDL_SetClipRect(surface, NULL);
   396     SDL_FillRect(surface, NULL, color);
   397     SDL_SetClipRect(surface, &clip_rect);
   398     return 0;
   399 }
   400 
   401 static int
   402 SW_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points,
   403                     int count)
   404 {
   405     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   406     SDL_Point *final_points;
   407     int i, status;
   408 
   409     if (!surface) {
   410         return -1;
   411     }
   412 
   413     final_points = SDL_stack_alloc(SDL_Point, count);
   414     if (!final_points) {
   415         return SDL_OutOfMemory();
   416     }
   417     if (renderer->viewport.x || renderer->viewport.y) {
   418         int x = renderer->viewport.x;
   419         int y = renderer->viewport.y;
   420 
   421         for (i = 0; i < count; ++i) {
   422             final_points[i].x = (int)(x + points[i].x);
   423             final_points[i].y = (int)(y + points[i].y);
   424         }
   425     } else {
   426         for (i = 0; i < count; ++i) {
   427             final_points[i].x = (int)points[i].x;
   428             final_points[i].y = (int)points[i].y;
   429         }
   430     }
   431 
   432     /* Draw the points! */
   433     if (renderer->blendMode == SDL_BLENDMODE_NONE) {
   434         Uint32 color = SDL_MapRGBA(surface->format,
   435                                    renderer->r, renderer->g, renderer->b,
   436                                    renderer->a);
   437 
   438         status = SDL_DrawPoints(surface, final_points, count, color);
   439     } else {
   440         status = SDL_BlendPoints(surface, final_points, count,
   441                                 renderer->blendMode,
   442                                 renderer->r, renderer->g, renderer->b,
   443                                 renderer->a);
   444     }
   445     SDL_stack_free(final_points);
   446 
   447     return status;
   448 }
   449 
   450 static int
   451 SW_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points,
   452                    int count)
   453 {
   454     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   455     SDL_Point *final_points;
   456     int i, status;
   457 
   458     if (!surface) {
   459         return -1;
   460     }
   461 
   462     final_points = SDL_stack_alloc(SDL_Point, count);
   463     if (!final_points) {
   464         return SDL_OutOfMemory();
   465     }
   466     if (renderer->viewport.x || renderer->viewport.y) {
   467         int x = renderer->viewport.x;
   468         int y = renderer->viewport.y;
   469 
   470         for (i = 0; i < count; ++i) {
   471             final_points[i].x = (int)(x + points[i].x);
   472             final_points[i].y = (int)(y + points[i].y);
   473         }
   474     } else {
   475         for (i = 0; i < count; ++i) {
   476             final_points[i].x = (int)points[i].x;
   477             final_points[i].y = (int)points[i].y;
   478         }
   479     }
   480 
   481     /* Draw the lines! */
   482     if (renderer->blendMode == SDL_BLENDMODE_NONE) {
   483         Uint32 color = SDL_MapRGBA(surface->format,
   484                                    renderer->r, renderer->g, renderer->b,
   485                                    renderer->a);
   486 
   487         status = SDL_DrawLines(surface, final_points, count, color);
   488     } else {
   489         status = SDL_BlendLines(surface, final_points, count,
   490                                 renderer->blendMode,
   491                                 renderer->r, renderer->g, renderer->b,
   492                                 renderer->a);
   493     }
   494     SDL_stack_free(final_points);
   495 
   496     return status;
   497 }
   498 
   499 static int
   500 SW_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
   501 {
   502     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   503     SDL_Rect *final_rects;
   504     int i, status;
   505 
   506     if (!surface) {
   507         return -1;
   508     }
   509 
   510     final_rects = SDL_stack_alloc(SDL_Rect, count);
   511     if (!final_rects) {
   512         return SDL_OutOfMemory();
   513     }
   514     if (renderer->viewport.x || renderer->viewport.y) {
   515         int x = renderer->viewport.x;
   516         int y = renderer->viewport.y;
   517 
   518         for (i = 0; i < count; ++i) {
   519             final_rects[i].x = (int)(x + rects[i].x);
   520             final_rects[i].y = (int)(y + rects[i].y);
   521             final_rects[i].w = SDL_max((int)rects[i].w, 1);
   522             final_rects[i].h = SDL_max((int)rects[i].h, 1);
   523         }
   524     } else {
   525         for (i = 0; i < count; ++i) {
   526             final_rects[i].x = (int)rects[i].x;
   527             final_rects[i].y = (int)rects[i].y;
   528             final_rects[i].w = SDL_max((int)rects[i].w, 1);
   529             final_rects[i].h = SDL_max((int)rects[i].h, 1);
   530         }
   531     }
   532 
   533     if (renderer->blendMode == SDL_BLENDMODE_NONE) {
   534         Uint32 color = SDL_MapRGBA(surface->format,
   535                                    renderer->r, renderer->g, renderer->b,
   536                                    renderer->a);
   537         status = SDL_FillRects(surface, final_rects, count, color);
   538     } else {
   539         status = SDL_BlendFillRects(surface, final_rects, count,
   540                                     renderer->blendMode,
   541                                     renderer->r, renderer->g, renderer->b,
   542                                     renderer->a);
   543     }
   544     SDL_stack_free(final_rects);
   545 
   546     return status;
   547 }
   548 
   549 static int
   550 SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
   551               const SDL_Rect * srcrect, const SDL_FRect * dstrect)
   552 {
   553     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   554     SDL_Surface *src = (SDL_Surface *) texture->driverdata;
   555     SDL_Rect final_rect;
   556 
   557     if (!surface) {
   558         return -1;
   559     }
   560 
   561     if (renderer->viewport.x || renderer->viewport.y) {
   562         final_rect.x = (int)(renderer->viewport.x + dstrect->x);
   563         final_rect.y = (int)(renderer->viewport.y + dstrect->y);
   564     } else {
   565         final_rect.x = (int)dstrect->x;
   566         final_rect.y = (int)dstrect->y;
   567     }
   568     final_rect.w = (int)dstrect->w;
   569     final_rect.h = (int)dstrect->h;
   570 
   571     if ( srcrect->w == final_rect.w && srcrect->h == final_rect.h ) {
   572         return SDL_BlitSurface(src, srcrect, surface, &final_rect);
   573     } else {
   574         /* If scaling is ever done, permanently disable RLE (which doesn't support scaling)
   575          * to avoid potentially frequent RLE encoding/decoding.
   576          */
   577         SDL_SetSurfaceRLE(surface, 0);
   578         return SDL_BlitScaled(src, srcrect, surface, &final_rect);
   579     }
   580 }
   581 
   582 static int
   583 GetScaleQuality(void)
   584 {
   585     const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
   586 
   587     if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
   588         return 0;
   589     } else {
   590         return 1;
   591     }
   592 }
   593 
   594 static int
   595 SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
   596                 const SDL_Rect * srcrect, const SDL_FRect * dstrect,
   597                 const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip)
   598 {
   599     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   600     SDL_Surface *src = (SDL_Surface *) texture->driverdata;
   601     SDL_Rect final_rect, tmp_rect;
   602     SDL_Surface *surface_rotated, *surface_scaled;
   603     int retval, dstwidth, dstheight, abscenterx, abscentery;
   604     double cangle, sangle, px, py, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y;
   605 
   606     if (!surface) {
   607         return -1;
   608     }
   609 
   610     if (renderer->viewport.x || renderer->viewport.y) {
   611         final_rect.x = (int)(renderer->viewport.x + dstrect->x);
   612         final_rect.y = (int)(renderer->viewport.y + dstrect->y);
   613     } else {
   614         final_rect.x = (int)dstrect->x;
   615         final_rect.y = (int)dstrect->y;
   616     }
   617     final_rect.w = (int)dstrect->w;
   618     final_rect.h = (int)dstrect->h;
   619 
   620     /* SDLgfx_rotateSurface doesn't accept a source rectangle, so crop and scale if we need to */
   621     tmp_rect = final_rect;
   622     tmp_rect.x = 0;
   623     tmp_rect.y = 0;
   624     if (srcrect->w == final_rect.w && srcrect->h == final_rect.h && srcrect->x == 0 && srcrect->y == 0) {
   625         surface_scaled = src; /* but if we don't need to, just use the original */
   626         retval = 0;
   627     } else {
   628         SDL_Surface *blit_src = src;
   629         Uint32 colorkey;
   630         SDL_BlendMode blendMode;
   631         Uint8 alphaMod, r, g, b;
   632         SDL_bool cloneSource = SDL_FALSE;
   633 
   634         surface_scaled = SDL_CreateRGBSurface(SDL_SWSURFACE, final_rect.w, final_rect.h, src->format->BitsPerPixel,
   635                                               src->format->Rmask, src->format->Gmask,
   636                                               src->format->Bmask, src->format->Amask );
   637         if (!surface_scaled) {
   638             return -1;
   639         }
   640 
   641         /* copy the color key, alpha mod, blend mode, and color mod so the scaled surface behaves like the source */
   642         if (SDL_GetColorKey(src, &colorkey) == 0) {
   643             SDL_SetColorKey(surface_scaled, SDL_TRUE, colorkey);
   644             cloneSource = SDL_TRUE;
   645         }
   646         SDL_GetSurfaceAlphaMod(src, &alphaMod); /* these will be copied to surface_scaled below if necessary */
   647         SDL_GetSurfaceBlendMode(src, &blendMode);
   648         SDL_GetSurfaceColorMod(src, &r, &g, &b);
   649 
   650         /* now we need to blit the src into surface_scaled. since we want to copy the colors from the source to
   651          * surface_scaled rather than blend them, etc. we'll need to disable the blend mode, alpha mod, etc.
   652          * but we don't want to modify src (in case it's being used on other threads), so we'll need to clone it
   653          * before changing the blend options
   654          */
   655         cloneSource |= blendMode != SDL_BLENDMODE_NONE || (alphaMod & r & g & b) != 255;
   656         if (cloneSource) {
   657             blit_src = SDL_ConvertSurface(src, src->format, src->flags); /* clone src */
   658             if (!blit_src) {
   659                 SDL_FreeSurface(surface_scaled);
   660                 return -1;
   661             }
   662             SDL_SetSurfaceAlphaMod(blit_src, 255); /* disable all blending options in blit_src */
   663             SDL_SetSurfaceBlendMode(blit_src, SDL_BLENDMODE_NONE);
   664             SDL_SetColorKey(blit_src, 0, 0);
   665             SDL_SetSurfaceColorMod(blit_src, 255, 255, 255);
   666             SDL_SetSurfaceRLE(blit_src, 0); /* don't RLE encode a surface we'll only use once */
   667 
   668             SDL_SetSurfaceAlphaMod(surface_scaled, alphaMod); /* copy blending options to surface_scaled */
   669             SDL_SetSurfaceBlendMode(surface_scaled, blendMode);
   670             SDL_SetSurfaceColorMod(surface_scaled, r, g, b);
   671         }
   672 
   673         retval = SDL_BlitScaled(blit_src, srcrect, surface_scaled, &tmp_rect);
   674         if (blit_src != src) {
   675             SDL_FreeSurface(blit_src);
   676         }
   677     }
   678 
   679     if (!retval) {
   680         SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, &dstwidth, &dstheight, &cangle, &sangle);
   681         surface_rotated = SDLgfx_rotateSurface(surface_scaled, angle, dstwidth/2, dstheight/2, GetScaleQuality(), flip & SDL_FLIP_HORIZONTAL, flip & SDL_FLIP_VERTICAL, dstwidth, dstheight, cangle, sangle);
   682         if(surface_rotated) {
   683             /* Find out where the new origin is by rotating the four final_rect points around the center and then taking the extremes */
   684             abscenterx = final_rect.x + (int)center->x;
   685             abscentery = final_rect.y + (int)center->y;
   686             /* Compensate the angle inversion to match the behaviour of the other backends */
   687             sangle = -sangle;
   688 
   689             /* Top Left */
   690             px = final_rect.x - abscenterx;
   691             py = final_rect.y - abscentery;
   692             p1x = px * cangle - py * sangle + abscenterx;
   693             p1y = px * sangle + py * cangle + abscentery;
   694 
   695             /* Top Right */
   696             px = final_rect.x + final_rect.w - abscenterx;
   697             py = final_rect.y - abscentery;
   698             p2x = px * cangle - py * sangle + abscenterx;
   699             p2y = px * sangle + py * cangle + abscentery;
   700 
   701             /* Bottom Left */
   702             px = final_rect.x - abscenterx;
   703             py = final_rect.y + final_rect.h - abscentery;
   704             p3x = px * cangle - py * sangle + abscenterx;
   705             p3y = px * sangle + py * cangle + abscentery;
   706 
   707             /* Bottom Right */
   708             px = final_rect.x + final_rect.w - abscenterx;
   709             py = final_rect.y + final_rect.h - abscentery;
   710             p4x = px * cangle - py * sangle + abscenterx;
   711             p4y = px * sangle + py * cangle + abscentery;
   712 
   713             tmp_rect.x = (int)MIN(MIN(p1x, p2x), MIN(p3x, p4x));
   714             tmp_rect.y = (int)MIN(MIN(p1y, p2y), MIN(p3y, p4y));
   715             tmp_rect.w = dstwidth;
   716             tmp_rect.h = dstheight;
   717 
   718             retval = SDL_BlitSurface(surface_rotated, NULL, surface, &tmp_rect);
   719             SDL_FreeSurface(surface_rotated);
   720         }
   721     }
   722 
   723     if (surface_scaled != src) {
   724         SDL_FreeSurface(surface_scaled);
   725     }
   726     return retval;
   727 }
   728 
   729 static int
   730 SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   731                     Uint32 format, void * pixels, int pitch)
   732 {
   733     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   734     Uint32 src_format;
   735     void *src_pixels;
   736     SDL_Rect final_rect;
   737 
   738     if (!surface) {
   739         return -1;
   740     }
   741 
   742     if (renderer->viewport.x || renderer->viewport.y) {
   743         final_rect.x = renderer->viewport.x + rect->x;
   744         final_rect.y = renderer->viewport.y + rect->y;
   745         final_rect.w = rect->w;
   746         final_rect.h = rect->h;
   747         rect = &final_rect;
   748     }
   749 
   750     if (rect->x < 0 || rect->x+rect->w > surface->w ||
   751         rect->y < 0 || rect->y+rect->h > surface->h) {
   752         return SDL_SetError("Tried to read outside of surface bounds");
   753     }
   754 
   755     src_format = surface->format->format;
   756     src_pixels = (void*)((Uint8 *) surface->pixels +
   757                     rect->y * surface->pitch +
   758                     rect->x * surface->format->BytesPerPixel);
   759 
   760     return SDL_ConvertPixels(rect->w, rect->h,
   761                              src_format, src_pixels, surface->pitch,
   762                              format, pixels, pitch);
   763 }
   764 
   765 static void
   766 SW_RenderPresent(SDL_Renderer * renderer)
   767 {
   768     SDL_Window *window = renderer->window;
   769 
   770     if (window) {
   771         SDL_UpdateWindowSurface(window);
   772     }
   773 }
   774 
   775 static void
   776 SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   777 {
   778     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   779 
   780     SDL_FreeSurface(surface);
   781 }
   782 
   783 static void
   784 SW_DestroyRenderer(SDL_Renderer * renderer)
   785 {
   786     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   787 
   788     SDL_free(data);
   789     SDL_free(renderer);
   790 }
   791 
   792 #endif /* !SDL_RENDER_DISABLED */
   793 
   794 /* vi: set ts=4 sw=4 expandtab: */