src/render/software/SDL_render_sw.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 07 May 2018 19:52:25 -0700
changeset 11958 d7582d7286aa
parent 11811 5d94cb6b24d3
child 12288 005e1d1bcf05
child 12372 bced4041fcc0
permissions -rw-r--r--
Fixed bug 4134 - Render targets lose scale quality after minimizing a fullscreen window

Olli-Samuli Lehmus

If one creates a window with the SDL_WINDOW_FULLSCREEN_DESKTOP flag, and creates a render target with SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"), and afterwards sets SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"), after minimizing the window, the scale quality hint is lost on the render target. Textures however do keep their interpolation modes.
     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     /* The color and alpha modulation has to be applied before the rotation when using the NONE and MOD blend modes. */
   661     if ((blendmode == SDL_BLENDMODE_NONE || blendmode == SDL_BLENDMODE_MOD) && (alphaMod & rMod & gMod & bMod) != 255) {
   662         applyModulation = SDL_TRUE;
   663         SDL_SetSurfaceAlphaMod(src_clone, alphaMod);
   664         SDL_SetSurfaceColorMod(src_clone, rMod, gMod, bMod);
   665     }
   666 
   667     /* Opaque surfaces are much easier to handle with the NONE blend mode. */
   668     if (blendmode == SDL_BLENDMODE_NONE && !src->format->Amask && alphaMod == 255) {
   669         isOpaque = SDL_TRUE;
   670     }
   671 
   672     /* The NONE blend mode requires a mask for non-opaque surfaces. This mask will be used
   673      * to clear the pixels in the destination surface. The other steps are explained below.
   674      */
   675     if (blendmode == SDL_BLENDMODE_NONE && !isOpaque) {
   676         mask = SDL_CreateRGBSurface(0, final_rect.w, final_rect.h, 32,
   677                                     0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
   678         if (mask == NULL) {
   679             retval = -1;
   680         } else {
   681             SDL_SetSurfaceBlendMode(mask, SDL_BLENDMODE_MOD);
   682         }
   683     }
   684 
   685     /* Create a new surface should there be a format mismatch or if scaling, cropping,
   686      * or modulation is required. It's possible to use the source surface directly otherwise.
   687      */
   688     if (!retval && (blitRequired || applyModulation)) {
   689         SDL_Rect scale_rect = tmp_rect;
   690         src_scaled = SDL_CreateRGBSurface(0, final_rect.w, final_rect.h, 32,
   691                                           0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
   692         if (src_scaled == NULL) {
   693             retval = -1;
   694         } else {
   695             SDL_SetSurfaceBlendMode(src_clone, SDL_BLENDMODE_NONE);
   696             retval = SDL_BlitScaled(src_clone, srcrect, src_scaled, &scale_rect);
   697             SDL_FreeSurface(src_clone);
   698             src_clone = src_scaled;
   699             src_scaled = NULL;
   700         }
   701     }
   702 
   703     /* SDLgfx_rotateSurface is going to make decisions depending on the blend mode. */
   704     SDL_SetSurfaceBlendMode(src_clone, blendmode);
   705 
   706     if (!retval) {
   707         SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, &dstwidth, &dstheight, &cangle, &sangle);
   708         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);
   709         if (src_rotated == NULL) {
   710             retval = -1;
   711         }
   712         if (!retval && mask != NULL) {
   713             /* The mask needed for the NONE blend mode gets rotated with the same parameters. */
   714             mask_rotated = SDLgfx_rotateSurface(mask, angle, dstwidth/2, dstheight/2, SDL_FALSE, 0, 0, dstwidth, dstheight, cangle, sangle);
   715             if (mask_rotated == NULL) {
   716                 retval = -1;
   717             }
   718         }
   719         if (!retval) {
   720             /* Find out where the new origin is by rotating the four final_rect points around the center and then taking the extremes */
   721             abscenterx = final_rect.x + (int)center->x;
   722             abscentery = final_rect.y + (int)center->y;
   723             /* Compensate the angle inversion to match the behaviour of the other backends */
   724             sangle = -sangle;
   725 
   726             /* Top Left */
   727             px = final_rect.x - abscenterx;
   728             py = final_rect.y - abscentery;
   729             p1x = px * cangle - py * sangle + abscenterx;
   730             p1y = px * sangle + py * cangle + abscentery;
   731 
   732             /* Top Right */
   733             px = final_rect.x + final_rect.w - abscenterx;
   734             py = final_rect.y - abscentery;
   735             p2x = px * cangle - py * sangle + abscenterx;
   736             p2y = px * sangle + py * cangle + abscentery;
   737 
   738             /* Bottom Left */
   739             px = final_rect.x - abscenterx;
   740             py = final_rect.y + final_rect.h - abscentery;
   741             p3x = px * cangle - py * sangle + abscenterx;
   742             p3y = px * sangle + py * cangle + abscentery;
   743 
   744             /* Bottom Right */
   745             px = final_rect.x + final_rect.w - abscenterx;
   746             py = final_rect.y + final_rect.h - abscentery;
   747             p4x = px * cangle - py * sangle + abscenterx;
   748             p4y = px * sangle + py * cangle + abscentery;
   749 
   750             tmp_rect.x = (int)MIN(MIN(p1x, p2x), MIN(p3x, p4x));
   751             tmp_rect.y = (int)MIN(MIN(p1y, p2y), MIN(p3y, p4y));
   752             tmp_rect.w = dstwidth;
   753             tmp_rect.h = dstheight;
   754 
   755             /* The NONE blend mode needs some special care with non-opaque surfaces.
   756              * Other blend modes or opaque surfaces can be blitted directly.
   757              */
   758             if (blendmode != SDL_BLENDMODE_NONE || isOpaque) {
   759                 if (applyModulation == SDL_FALSE) {
   760                     /* If the modulation wasn't already applied, make it happen now. */
   761                     SDL_SetSurfaceAlphaMod(src_rotated, alphaMod);
   762                     SDL_SetSurfaceColorMod(src_rotated, rMod, gMod, bMod);
   763                 }
   764                 retval = SDL_BlitSurface(src_rotated, NULL, surface, &tmp_rect);
   765             } else {
   766                 /* The NONE blend mode requires three steps to get the pixels onto the destination surface.
   767                  * First, the area where the rotated pixels will be blitted to get set to zero.
   768                  * This is accomplished by simply blitting a mask with the NONE blend mode.
   769                  * The colorkey set by the rotate function will discard the correct pixels.
   770                  */
   771                 SDL_Rect mask_rect = tmp_rect;
   772                 SDL_SetSurfaceBlendMode(mask_rotated, SDL_BLENDMODE_NONE);
   773                 retval = SDL_BlitSurface(mask_rotated, NULL, surface, &mask_rect);
   774                 if (!retval) {
   775                     /* The next step copies the alpha value. This is done with the BLEND blend mode and
   776                      * by modulating the source colors with 0. Since the destination is all zeros, this
   777                      * will effectively set the destination alpha to the source alpha.
   778                      */
   779                     SDL_SetSurfaceColorMod(src_rotated, 0, 0, 0);
   780                     mask_rect = tmp_rect;
   781                     retval = SDL_BlitSurface(src_rotated, NULL, surface, &mask_rect);
   782                     if (!retval) {
   783                         /* The last step gets the color values in place. The ADD blend mode simply adds them to
   784                          * the destination (where the color values are all zero). However, because the ADD blend
   785                          * mode modulates the colors with the alpha channel, a surface without an alpha mask needs
   786                          * to be created. This makes all source pixels opaque and the colors get copied correctly.
   787                          */
   788                         SDL_Surface *src_rotated_rgb;
   789                         src_rotated_rgb = SDL_CreateRGBSurfaceFrom(src_rotated->pixels, src_rotated->w, src_rotated->h,
   790                                                                    src_rotated->format->BitsPerPixel, src_rotated->pitch,
   791                                                                    src_rotated->format->Rmask, src_rotated->format->Gmask,
   792                                                                    src_rotated->format->Bmask, 0);
   793                         if (src_rotated_rgb == NULL) {
   794                             retval = -1;
   795                         } else {
   796                             SDL_SetSurfaceBlendMode(src_rotated_rgb, SDL_BLENDMODE_ADD);
   797                             retval = SDL_BlitSurface(src_rotated_rgb, NULL, surface, &tmp_rect);
   798                             SDL_FreeSurface(src_rotated_rgb);
   799                         }
   800                     }
   801                 }
   802                 SDL_FreeSurface(mask_rotated);
   803             }
   804             if (src_rotated != NULL) {
   805                 SDL_FreeSurface(src_rotated);
   806             }
   807         }
   808     }
   809 
   810     if (SDL_MUSTLOCK(src)) {
   811         SDL_UnlockSurface(src);
   812     }
   813     if (mask != NULL) {
   814         SDL_FreeSurface(mask);
   815     }
   816     if (src_clone != NULL) {
   817         SDL_FreeSurface(src_clone);
   818     }
   819     return retval;
   820 }
   821 
   822 static int
   823 SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   824                     Uint32 format, void * pixels, int pitch)
   825 {
   826     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   827     Uint32 src_format;
   828     void *src_pixels;
   829 
   830     if (!surface) {
   831         return -1;
   832     }
   833 
   834     /* NOTE: The rect is already adjusted according to the viewport by
   835      * SDL_RenderReadPixels.
   836      */
   837 
   838     if (rect->x < 0 || rect->x+rect->w > surface->w ||
   839         rect->y < 0 || rect->y+rect->h > surface->h) {
   840         return SDL_SetError("Tried to read outside of surface bounds");
   841     }
   842 
   843     src_format = surface->format->format;
   844     src_pixels = (void*)((Uint8 *) surface->pixels +
   845                     rect->y * surface->pitch +
   846                     rect->x * surface->format->BytesPerPixel);
   847 
   848     return SDL_ConvertPixels(rect->w, rect->h,
   849                              src_format, src_pixels, surface->pitch,
   850                              format, pixels, pitch);
   851 }
   852 
   853 static void
   854 SW_RenderPresent(SDL_Renderer * renderer)
   855 {
   856     SDL_Window *window = renderer->window;
   857 
   858     if (window) {
   859         SDL_UpdateWindowSurface(window);
   860     }
   861 }
   862 
   863 static void
   864 SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   865 {
   866     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   867 
   868     SDL_FreeSurface(surface);
   869 }
   870 
   871 static void
   872 SW_DestroyRenderer(SDL_Renderer * renderer)
   873 {
   874     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   875 
   876     SDL_free(data);
   877     SDL_free(renderer);
   878 }
   879 
   880 #endif /* !SDL_RENDER_DISABLED */
   881 
   882 /* vi: set ts=4 sw=4 expandtab: */