src/render/software/SDL_render_sw.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 30 Oct 2018 07:00:03 -0700
changeset 12372 bced4041fcc0
parent 11958 d7582d7286aa
child 12381 dc9108cd4340
permissions -rw-r--r--
Fixed bug 4188 - Software renderer SDL_RenderCopyEx blits corrupt image under certain cases

Sylvain

Re-opening this issue.

It fixes the test-case, but it introduces a regression with another bug (bug #4313).

So here's a new patch that activate cropping of the source surface to solve the issue.
It also reverts the wrong changeset.
It prevents unneeded colorkey error message.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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     /* Only RLE encode textures without an alpha channel since the RLE coder
   243      * discards the color values of pixels with an alpha value of zero.
   244      */
   245     if (texture->access == SDL_TEXTUREACCESS_STATIC && !Amask) {
   246         SDL_SetSurfaceRLE(texture->driverdata, 1);
   247     }
   248 
   249     if (!texture->driverdata) {
   250         return -1;
   251     }
   252     return 0;
   253 }
   254 
   255 static int
   256 SW_SetTextureColorMod(SDL_Renderer * renderer, SDL_Texture * texture)
   257 {
   258     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   259     /* If the color mod is ever enabled (non-white), permanently disable RLE (which doesn't support
   260      * color mod) to avoid potentially frequent RLE encoding/decoding.
   261      */
   262     if ((texture->r & texture->g & texture->b) != 255) {
   263         SDL_SetSurfaceRLE(surface, 0);
   264     }
   265     return SDL_SetSurfaceColorMod(surface, texture->r, texture->g,
   266                                   texture->b);
   267 }
   268 
   269 static int
   270 SW_SetTextureAlphaMod(SDL_Renderer * renderer, SDL_Texture * texture)
   271 {
   272     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   273     /* If the texture ever has multiple alpha values (surface alpha plus alpha channel), permanently
   274      * disable RLE (which doesn't support this) to avoid potentially frequent RLE encoding/decoding.
   275      */
   276     if (texture->a != 255 && surface->format->Amask) {
   277         SDL_SetSurfaceRLE(surface, 0);
   278     }
   279     return SDL_SetSurfaceAlphaMod(surface, texture->a);
   280 }
   281 
   282 static int
   283 SW_SetTextureBlendMode(SDL_Renderer * renderer, SDL_Texture * texture)
   284 {
   285     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   286     /* If add or mod blending are ever enabled, permanently disable RLE (which doesn't support
   287      * them) to avoid potentially frequent RLE encoding/decoding.
   288      */
   289     if ((texture->blendMode == SDL_BLENDMODE_ADD || texture->blendMode == SDL_BLENDMODE_MOD)) {
   290         SDL_SetSurfaceRLE(surface, 0);
   291     }
   292     return SDL_SetSurfaceBlendMode(surface, texture->blendMode);
   293 }
   294 
   295 static int
   296 SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   297                  const SDL_Rect * rect, const void *pixels, int pitch)
   298 {
   299     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   300     Uint8 *src, *dst;
   301     int row;
   302     size_t length;
   303 
   304     if(SDL_MUSTLOCK(surface))
   305         SDL_LockSurface(surface);
   306     src = (Uint8 *) pixels;
   307     dst = (Uint8 *) surface->pixels +
   308                         rect->y * surface->pitch +
   309                         rect->x * surface->format->BytesPerPixel;
   310     length = rect->w * surface->format->BytesPerPixel;
   311     for (row = 0; row < rect->h; ++row) {
   312         SDL_memcpy(dst, src, length);
   313         src += pitch;
   314         dst += surface->pitch;
   315     }
   316     if(SDL_MUSTLOCK(surface))
   317         SDL_UnlockSurface(surface);
   318     return 0;
   319 }
   320 
   321 static int
   322 SW_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   323                const SDL_Rect * rect, void **pixels, int *pitch)
   324 {
   325     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   326 
   327     *pixels =
   328         (void *) ((Uint8 *) surface->pixels + rect->y * surface->pitch +
   329                   rect->x * surface->format->BytesPerPixel);
   330     *pitch = surface->pitch;
   331     return 0;
   332 }
   333 
   334 static void
   335 SW_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   336 {
   337 }
   338 
   339 static int
   340 SW_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
   341 {
   342     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   343 
   344     if (texture ) {
   345         data->surface = (SDL_Surface *) texture->driverdata;
   346     } else {
   347         data->surface = data->window;
   348     }
   349     return 0;
   350 }
   351 
   352 static int
   353 SW_UpdateViewport(SDL_Renderer * renderer)
   354 {
   355     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   356     SDL_Surface *surface = data->surface;
   357 
   358     if (!surface) {
   359         /* We'll update the viewport after we recreate the surface */
   360         return 0;
   361     }
   362 
   363     SDL_SetClipRect(data->surface, &renderer->viewport);
   364     return 0;
   365 }
   366 
   367 static int
   368 SW_UpdateClipRect(SDL_Renderer * renderer)
   369 {
   370     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   371     SDL_Surface *surface = data->surface;
   372     if (surface) {
   373         if (renderer->clipping_enabled) {
   374             SDL_Rect clip_rect;
   375             clip_rect = renderer->clip_rect;
   376             clip_rect.x += renderer->viewport.x;
   377             clip_rect.y += renderer->viewport.y;
   378             SDL_IntersectRect(&renderer->viewport, &clip_rect, &clip_rect);
   379             SDL_SetClipRect(surface, &clip_rect);
   380         } else {
   381             SDL_SetClipRect(surface, &renderer->viewport);
   382         }
   383     }
   384     return 0;
   385 }
   386 
   387 static int
   388 SW_RenderClear(SDL_Renderer * renderer)
   389 {
   390     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   391     Uint32 color;
   392     SDL_Rect clip_rect;
   393 
   394     if (!surface) {
   395         return -1;
   396     }
   397 
   398     color = SDL_MapRGBA(surface->format,
   399                         renderer->r, renderer->g, renderer->b, renderer->a);
   400 
   401     /* By definition the clear ignores the clip rect */
   402     clip_rect = surface->clip_rect;
   403     SDL_SetClipRect(surface, NULL);
   404     SDL_FillRect(surface, NULL, color);
   405     SDL_SetClipRect(surface, &clip_rect);
   406     return 0;
   407 }
   408 
   409 static int
   410 SW_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points,
   411                     int count)
   412 {
   413     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   414     SDL_Point *final_points;
   415     int i, status;
   416 
   417     if (!surface) {
   418         return -1;
   419     }
   420 
   421     final_points = SDL_stack_alloc(SDL_Point, count);
   422     if (!final_points) {
   423         return SDL_OutOfMemory();
   424     }
   425     if (renderer->viewport.x || renderer->viewport.y) {
   426         int x = renderer->viewport.x;
   427         int y = renderer->viewport.y;
   428 
   429         for (i = 0; i < count; ++i) {
   430             final_points[i].x = (int)(x + points[i].x);
   431             final_points[i].y = (int)(y + points[i].y);
   432         }
   433     } else {
   434         for (i = 0; i < count; ++i) {
   435             final_points[i].x = (int)points[i].x;
   436             final_points[i].y = (int)points[i].y;
   437         }
   438     }
   439 
   440     /* Draw the points! */
   441     if (renderer->blendMode == SDL_BLENDMODE_NONE) {
   442         Uint32 color = SDL_MapRGBA(surface->format,
   443                                    renderer->r, renderer->g, renderer->b,
   444                                    renderer->a);
   445 
   446         status = SDL_DrawPoints(surface, final_points, count, color);
   447     } else {
   448         status = SDL_BlendPoints(surface, final_points, count,
   449                                 renderer->blendMode,
   450                                 renderer->r, renderer->g, renderer->b,
   451                                 renderer->a);
   452     }
   453     SDL_stack_free(final_points);
   454 
   455     return status;
   456 }
   457 
   458 static int
   459 SW_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points,
   460                    int count)
   461 {
   462     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   463     SDL_Point *final_points;
   464     int i, status;
   465 
   466     if (!surface) {
   467         return -1;
   468     }
   469 
   470     final_points = SDL_stack_alloc(SDL_Point, count);
   471     if (!final_points) {
   472         return SDL_OutOfMemory();
   473     }
   474     if (renderer->viewport.x || renderer->viewport.y) {
   475         int x = renderer->viewport.x;
   476         int y = renderer->viewport.y;
   477 
   478         for (i = 0; i < count; ++i) {
   479             final_points[i].x = (int)(x + points[i].x);
   480             final_points[i].y = (int)(y + points[i].y);
   481         }
   482     } else {
   483         for (i = 0; i < count; ++i) {
   484             final_points[i].x = (int)points[i].x;
   485             final_points[i].y = (int)points[i].y;
   486         }
   487     }
   488 
   489     /* Draw the lines! */
   490     if (renderer->blendMode == SDL_BLENDMODE_NONE) {
   491         Uint32 color = SDL_MapRGBA(surface->format,
   492                                    renderer->r, renderer->g, renderer->b,
   493                                    renderer->a);
   494 
   495         status = SDL_DrawLines(surface, final_points, count, color);
   496     } else {
   497         status = SDL_BlendLines(surface, final_points, count,
   498                                 renderer->blendMode,
   499                                 renderer->r, renderer->g, renderer->b,
   500                                 renderer->a);
   501     }
   502     SDL_stack_free(final_points);
   503 
   504     return status;
   505 }
   506 
   507 static int
   508 SW_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
   509 {
   510     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   511     SDL_Rect *final_rects;
   512     int i, status;
   513 
   514     if (!surface) {
   515         return -1;
   516     }
   517 
   518     final_rects = SDL_stack_alloc(SDL_Rect, count);
   519     if (!final_rects) {
   520         return SDL_OutOfMemory();
   521     }
   522     if (renderer->viewport.x || renderer->viewport.y) {
   523         int x = renderer->viewport.x;
   524         int y = renderer->viewport.y;
   525 
   526         for (i = 0; i < count; ++i) {
   527             final_rects[i].x = (int)(x + rects[i].x);
   528             final_rects[i].y = (int)(y + rects[i].y);
   529             final_rects[i].w = SDL_max((int)rects[i].w, 1);
   530             final_rects[i].h = SDL_max((int)rects[i].h, 1);
   531         }
   532     } else {
   533         for (i = 0; i < count; ++i) {
   534             final_rects[i].x = (int)rects[i].x;
   535             final_rects[i].y = (int)rects[i].y;
   536             final_rects[i].w = SDL_max((int)rects[i].w, 1);
   537             final_rects[i].h = SDL_max((int)rects[i].h, 1);
   538         }
   539     }
   540 
   541     if (renderer->blendMode == SDL_BLENDMODE_NONE) {
   542         Uint32 color = SDL_MapRGBA(surface->format,
   543                                    renderer->r, renderer->g, renderer->b,
   544                                    renderer->a);
   545         status = SDL_FillRects(surface, final_rects, count, color);
   546     } else {
   547         status = SDL_BlendFillRects(surface, final_rects, count,
   548                                     renderer->blendMode,
   549                                     renderer->r, renderer->g, renderer->b,
   550                                     renderer->a);
   551     }
   552     SDL_stack_free(final_rects);
   553 
   554     return status;
   555 }
   556 
   557 static int
   558 SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
   559               const SDL_Rect * srcrect, const SDL_FRect * dstrect)
   560 {
   561     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   562     SDL_Surface *src = (SDL_Surface *) texture->driverdata;
   563     SDL_Rect final_rect;
   564 
   565     if (!surface) {
   566         return -1;
   567     }
   568 
   569     if (renderer->viewport.x || renderer->viewport.y) {
   570         final_rect.x = (int)(renderer->viewport.x + dstrect->x);
   571         final_rect.y = (int)(renderer->viewport.y + dstrect->y);
   572     } else {
   573         final_rect.x = (int)dstrect->x;
   574         final_rect.y = (int)dstrect->y;
   575     }
   576     final_rect.w = (int)dstrect->w;
   577     final_rect.h = (int)dstrect->h;
   578 
   579     if ( srcrect->w == final_rect.w && srcrect->h == final_rect.h ) {
   580         return SDL_BlitSurface(src, srcrect, surface, &final_rect);
   581     } else {
   582         /* If scaling is ever done, permanently disable RLE (which doesn't support scaling)
   583          * to avoid potentially frequent RLE encoding/decoding.
   584          */
   585         SDL_SetSurfaceRLE(surface, 0);
   586         return SDL_BlitScaled(src, srcrect, surface, &final_rect);
   587     }
   588 }
   589 
   590 static int
   591 SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
   592                 const SDL_Rect * srcrect, const SDL_FRect * dstrect,
   593                 const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip)
   594 {
   595     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   596     SDL_Surface *src = (SDL_Surface *) texture->driverdata;
   597     SDL_Rect final_rect, tmp_rect;
   598     SDL_Surface *src_clone, *src_rotated, *src_scaled;
   599     SDL_Surface *mask = NULL, *mask_rotated = NULL;
   600     int retval = 0, dstwidth, dstheight, abscenterx, abscentery;
   601     double cangle, sangle, px, py, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y;
   602     SDL_BlendMode blendmode;
   603     Uint8 alphaMod, rMod, gMod, bMod;
   604     int applyModulation = SDL_FALSE;
   605     int blitRequired = SDL_FALSE;
   606     int isOpaque = SDL_FALSE;
   607 
   608     if (!surface) {
   609         return -1;
   610     }
   611 
   612     if (renderer->viewport.x || renderer->viewport.y) {
   613         final_rect.x = (int)(renderer->viewport.x + dstrect->x);
   614         final_rect.y = (int)(renderer->viewport.y + dstrect->y);
   615     } else {
   616         final_rect.x = (int)dstrect->x;
   617         final_rect.y = (int)dstrect->y;
   618     }
   619     final_rect.w = (int)dstrect->w;
   620     final_rect.h = (int)dstrect->h;
   621 
   622     tmp_rect = final_rect;
   623     tmp_rect.x = 0;
   624     tmp_rect.y = 0;
   625 
   626     /* It is possible to encounter an RLE encoded surface here and locking it is
   627      * necessary because this code is going to access the pixel buffer directly.
   628      */
   629     if (SDL_MUSTLOCK(src)) {
   630         SDL_LockSurface(src);
   631     }
   632 
   633     /* Clone the source surface but use its pixel buffer directly.
   634      * The original source surface must be treated as read-only.
   635      */
   636     src_clone = SDL_CreateRGBSurfaceFrom(src->pixels, src->w, src->h, src->format->BitsPerPixel, src->pitch,
   637                                          src->format->Rmask, src->format->Gmask,
   638                                          src->format->Bmask, src->format->Amask);
   639     if (src_clone == NULL) {
   640         if (SDL_MUSTLOCK(src)) {
   641             SDL_UnlockSurface(src);
   642         }
   643         return -1;
   644     }
   645 
   646     SDL_GetSurfaceBlendMode(src, &blendmode);
   647     SDL_GetSurfaceAlphaMod(src, &alphaMod);
   648     SDL_GetSurfaceColorMod(src, &rMod, &gMod, &bMod);
   649 
   650     /* SDLgfx_rotateSurface only accepts 32-bit surfaces with a 8888 layout. Everything else has to be converted. */
   651     if (src->format->BitsPerPixel != 32 || SDL_PIXELLAYOUT(src->format->format) != SDL_PACKEDLAYOUT_8888 || !src->format->Amask) {
   652         blitRequired = SDL_TRUE;
   653     }
   654 
   655     /* If scaling and cropping is necessary, it has to be taken care of before the rotation. */
   656     if (!(srcrect->w == final_rect.w && srcrect->h == final_rect.h && srcrect->x == 0 && srcrect->y == 0)) {
   657         blitRequired = SDL_TRUE;
   658     }
   659 
   660     /* srcrect is not selecting the whole src surface, so cropping is needed */
   661     if (!(srcrect->w == src->w && srcrect->h == src->h && srcrect->x == 0 && srcrect->y == 0)) {
   662         blitRequired = SDL_TRUE;
   663     }
   664 
   665     /* The color and alpha modulation has to be applied before the rotation when using the NONE and MOD blend modes. */
   666     if ((blendmode == SDL_BLENDMODE_NONE || blendmode == SDL_BLENDMODE_MOD) && (alphaMod & rMod & gMod & bMod) != 255) {
   667         applyModulation = SDL_TRUE;
   668         SDL_SetSurfaceAlphaMod(src_clone, alphaMod);
   669         SDL_SetSurfaceColorMod(src_clone, rMod, gMod, bMod);
   670     }
   671 
   672     /* Opaque surfaces are much easier to handle with the NONE blend mode. */
   673     if (blendmode == SDL_BLENDMODE_NONE && !src->format->Amask && alphaMod == 255) {
   674         isOpaque = SDL_TRUE;
   675     }
   676 
   677     /* The NONE blend mode requires a mask for non-opaque surfaces. This mask will be used
   678      * to clear the pixels in the destination surface. The other steps are explained below.
   679      */
   680     if (blendmode == SDL_BLENDMODE_NONE && !isOpaque) {
   681         mask = SDL_CreateRGBSurface(0, final_rect.w, final_rect.h, 32,
   682                                     0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
   683         if (mask == NULL) {
   684             retval = -1;
   685         } else {
   686             SDL_SetSurfaceBlendMode(mask, SDL_BLENDMODE_MOD);
   687         }
   688     }
   689 
   690     /* Create a new surface should there be a format mismatch or if scaling, cropping,
   691      * or modulation is required. It's possible to use the source surface directly otherwise.
   692      */
   693     if (!retval && (blitRequired || applyModulation)) {
   694         SDL_Rect scale_rect = tmp_rect;
   695         src_scaled = SDL_CreateRGBSurface(0, final_rect.w, final_rect.h, 32,
   696                                           0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
   697         if (src_scaled == NULL) {
   698             retval = -1;
   699         } else {
   700             SDL_SetSurfaceBlendMode(src_clone, SDL_BLENDMODE_NONE);
   701             retval = SDL_BlitScaled(src_clone, srcrect, src_scaled, &scale_rect);
   702             SDL_FreeSurface(src_clone);
   703             src_clone = src_scaled;
   704             src_scaled = NULL;
   705         }
   706     }
   707 
   708     /* SDLgfx_rotateSurface is going to make decisions depending on the blend mode. */
   709     SDL_SetSurfaceBlendMode(src_clone, blendmode);
   710 
   711     if (!retval) {
   712         SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, &dstwidth, &dstheight, &cangle, &sangle);
   713         src_rotated = SDLgfx_rotateSurface(src_clone, angle, dstwidth/2, dstheight/2, (texture->scaleMode == SDL_ScaleModeNearest) ? 0 : 1, flip & SDL_FLIP_HORIZONTAL, flip & SDL_FLIP_VERTICAL, dstwidth, dstheight, cangle, sangle);
   714         if (src_rotated == NULL) {
   715             retval = -1;
   716         }
   717         if (!retval && mask != NULL) {
   718             /* The mask needed for the NONE blend mode gets rotated with the same parameters. */
   719             mask_rotated = SDLgfx_rotateSurface(mask, angle, dstwidth/2, dstheight/2, SDL_FALSE, 0, 0, dstwidth, dstheight, cangle, sangle);
   720             if (mask_rotated == NULL) {
   721                 retval = -1;
   722             }
   723         }
   724         if (!retval) {
   725             /* Find out where the new origin is by rotating the four final_rect points around the center and then taking the extremes */
   726             abscenterx = final_rect.x + (int)center->x;
   727             abscentery = final_rect.y + (int)center->y;
   728             /* Compensate the angle inversion to match the behaviour of the other backends */
   729             sangle = -sangle;
   730 
   731             /* Top Left */
   732             px = final_rect.x - abscenterx;
   733             py = final_rect.y - abscentery;
   734             p1x = px * cangle - py * sangle + abscenterx;
   735             p1y = px * sangle + py * cangle + abscentery;
   736 
   737             /* Top Right */
   738             px = final_rect.x + final_rect.w - abscenterx;
   739             py = final_rect.y - abscentery;
   740             p2x = px * cangle - py * sangle + abscenterx;
   741             p2y = px * sangle + py * cangle + abscentery;
   742 
   743             /* Bottom Left */
   744             px = final_rect.x - abscenterx;
   745             py = final_rect.y + final_rect.h - abscentery;
   746             p3x = px * cangle - py * sangle + abscenterx;
   747             p3y = px * sangle + py * cangle + abscentery;
   748 
   749             /* Bottom Right */
   750             px = final_rect.x + final_rect.w - abscenterx;
   751             py = final_rect.y + final_rect.h - abscentery;
   752             p4x = px * cangle - py * sangle + abscenterx;
   753             p4y = px * sangle + py * cangle + abscentery;
   754 
   755             tmp_rect.x = (int)MIN(MIN(p1x, p2x), MIN(p3x, p4x));
   756             tmp_rect.y = (int)MIN(MIN(p1y, p2y), MIN(p3y, p4y));
   757             tmp_rect.w = dstwidth;
   758             tmp_rect.h = dstheight;
   759 
   760             /* The NONE blend mode needs some special care with non-opaque surfaces.
   761              * Other blend modes or opaque surfaces can be blitted directly.
   762              */
   763             if (blendmode != SDL_BLENDMODE_NONE || isOpaque) {
   764                 if (applyModulation == SDL_FALSE) {
   765                     /* If the modulation wasn't already applied, make it happen now. */
   766                     SDL_SetSurfaceAlphaMod(src_rotated, alphaMod);
   767                     SDL_SetSurfaceColorMod(src_rotated, rMod, gMod, bMod);
   768                 }
   769                 retval = SDL_BlitSurface(src_rotated, NULL, surface, &tmp_rect);
   770             } else {
   771                 /* The NONE blend mode requires three steps to get the pixels onto the destination surface.
   772                  * First, the area where the rotated pixels will be blitted to get set to zero.
   773                  * This is accomplished by simply blitting a mask with the NONE blend mode.
   774                  * The colorkey set by the rotate function will discard the correct pixels.
   775                  */
   776                 SDL_Rect mask_rect = tmp_rect;
   777                 SDL_SetSurfaceBlendMode(mask_rotated, SDL_BLENDMODE_NONE);
   778                 retval = SDL_BlitSurface(mask_rotated, NULL, surface, &mask_rect);
   779                 if (!retval) {
   780                     /* The next step copies the alpha value. This is done with the BLEND blend mode and
   781                      * by modulating the source colors with 0. Since the destination is all zeros, this
   782                      * will effectively set the destination alpha to the source alpha.
   783                      */
   784                     SDL_SetSurfaceColorMod(src_rotated, 0, 0, 0);
   785                     mask_rect = tmp_rect;
   786                     retval = SDL_BlitSurface(src_rotated, NULL, surface, &mask_rect);
   787                     if (!retval) {
   788                         /* The last step gets the color values in place. The ADD blend mode simply adds them to
   789                          * the destination (where the color values are all zero). However, because the ADD blend
   790                          * mode modulates the colors with the alpha channel, a surface without an alpha mask needs
   791                          * to be created. This makes all source pixels opaque and the colors get copied correctly.
   792                          */
   793                         SDL_Surface *src_rotated_rgb;
   794                         src_rotated_rgb = SDL_CreateRGBSurfaceFrom(src_rotated->pixels, src_rotated->w, src_rotated->h,
   795                                                                    src_rotated->format->BitsPerPixel, src_rotated->pitch,
   796                                                                    src_rotated->format->Rmask, src_rotated->format->Gmask,
   797                                                                    src_rotated->format->Bmask, 0);
   798                         if (src_rotated_rgb == NULL) {
   799                             retval = -1;
   800                         } else {
   801                             SDL_SetSurfaceBlendMode(src_rotated_rgb, SDL_BLENDMODE_ADD);
   802                             retval = SDL_BlitSurface(src_rotated_rgb, NULL, surface, &tmp_rect);
   803                             SDL_FreeSurface(src_rotated_rgb);
   804                         }
   805                     }
   806                 }
   807                 SDL_FreeSurface(mask_rotated);
   808             }
   809             if (src_rotated != NULL) {
   810                 SDL_FreeSurface(src_rotated);
   811             }
   812         }
   813     }
   814 
   815     if (SDL_MUSTLOCK(src)) {
   816         SDL_UnlockSurface(src);
   817     }
   818     if (mask != NULL) {
   819         SDL_FreeSurface(mask);
   820     }
   821     if (src_clone != NULL) {
   822         SDL_FreeSurface(src_clone);
   823     }
   824     return retval;
   825 }
   826 
   827 static int
   828 SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   829                     Uint32 format, void * pixels, int pitch)
   830 {
   831     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   832     Uint32 src_format;
   833     void *src_pixels;
   834 
   835     if (!surface) {
   836         return -1;
   837     }
   838 
   839     /* NOTE: The rect is already adjusted according to the viewport by
   840      * SDL_RenderReadPixels.
   841      */
   842 
   843     if (rect->x < 0 || rect->x+rect->w > surface->w ||
   844         rect->y < 0 || rect->y+rect->h > surface->h) {
   845         return SDL_SetError("Tried to read outside of surface bounds");
   846     }
   847 
   848     src_format = surface->format->format;
   849     src_pixels = (void*)((Uint8 *) surface->pixels +
   850                     rect->y * surface->pitch +
   851                     rect->x * surface->format->BytesPerPixel);
   852 
   853     return SDL_ConvertPixels(rect->w, rect->h,
   854                              src_format, src_pixels, surface->pitch,
   855                              format, pixels, pitch);
   856 }
   857 
   858 static void
   859 SW_RenderPresent(SDL_Renderer * renderer)
   860 {
   861     SDL_Window *window = renderer->window;
   862 
   863     if (window) {
   864         SDL_UpdateWindowSurface(window);
   865     }
   866 }
   867 
   868 static void
   869 SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   870 {
   871     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   872 
   873     SDL_FreeSurface(surface);
   874 }
   875 
   876 static void
   877 SW_DestroyRenderer(SDL_Renderer * renderer)
   878 {
   879     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   880 
   881     SDL_free(data);
   882     SDL_free(renderer);
   883 }
   884 
   885 #endif /* !SDL_RENDER_DISABLED */
   886 
   887 /* vi: set ts=4 sw=4 expandtab: */