src/render/software/SDL_render_sw.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 15 Nov 2016 01:12:27 -0800
changeset 10612 6b2307dbec54
parent 10489 1e1ce9f6d215
child 10737 3406a0f8b041
permissions -rw-r--r--
Fixed bug 3359 - Software renderer does incorrect blending with SDL_RenderCopyEx

Simon Hug

The software renderer produces incorrect results when blending textures at an angle with certain blend modes. It seems that there were some edge cases that weren't considered when the SW_RenderCopyEx function was last changed. Or another bug possibly covered up the problem. (More on that in another bug report.)

Most of the issues come from the fact that the rotating function sets a black colorkey. This is problematic because black is most likely appearing in the surface and the final blit will ignore these pixels. Unless a colorkey is already set (the software renderer currently never sets one), it's very hard to find a free color. Of course it could scan over the whole image until one is found, but that seems inefficient.

The following blend modes have issues when drawn at an angle.

NONE: The black pixels get ignored, making them essentially transparent. This breaks the 'dstRGBA = srcRGBA' definition of the NONE blend mode.

MOD: Again, the black pixels get ignored. This also breaks the 'dstRGB = dstRGB * srcRGB' definition of the MOD blend mode, where black pixels would make the destination black as well. A white colorkey will work though, with some preparations.

BLEND: There are some issues when blending a texture with a translucent RGBA target texture. I - uh - forgot what the problem here exactly is.

This patch fixes the issues mentioned above. It mainly changes the code so it tries to do things without the colorkey and removes the automatic format conversion part from the SDLgfx_rotateSurface function. Getting the format right is something the caller has to do now and the required code has been added to the SW_RenderCopyEx function.

There's a small change to the SW_CreateTexture function. RLE encoding a surface with an alpha mask can be a lossy process. Depending on how the user uses the RGBA channels, this may be undesired. The change that surfaces with an alpha mask don't get encoded makes the software renderer consistent with the other renderers.

The SW_RenderCopyEx function now does these steps: Lock the source surface if necessary. Create a clone of the source by using the pixel buffer directly. Check the format and set a flag if a conversion is necessary. Check if scaling or cropping is necessary and set the flag for that as well. Check if color and alpha modulation has to be done before the rotate. Check if the source is an opaque surface. If not, it creates a mask surface that is necessary for the NONE blend mode. If any of the flags were set, a new surface is created and the source will be converted, scaled, cropped, and modulated. The rest of the function stays somewhat the same. The mask also needs to be rotated of course and then there is the NONE blend mode...

It's surprisingly hard to get the pixel from a rotated surface to the destination buffer without affecting the pixel outside the rotated area. I found a way to do this with three blits which is pretty hard on the performance. Perhaps someone has an idea how to do this faster?

As mentioned above, the SDLgfx_rotateSurface now only takes 8-bit paletted or 32-bit with alpha mask surfaces. It additionally sets the new surfaces up for the MOD blend mode.

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