src/render/software/SDL_render_sw.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 13 Aug 2017 22:50:23 -0700
changeset 11281 42b62c737891
parent 11223 62db9336e07b
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Fixed bug 2646 - Problems with software renderer when SDL_SetRenderLogicalSize set

Fixed setting the software renderer clip rectangle when a viewport is set
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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 GetScaleQuality(void)
   592 {
   593     const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
   594 
   595     if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
   596         return 0;
   597     } else {
   598         return 1;
   599     }
   600 }
   601 
   602 static int
   603 SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
   604                 const SDL_Rect * srcrect, const SDL_FRect * dstrect,
   605                 const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip)
   606 {
   607     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   608     SDL_Surface *src = (SDL_Surface *) texture->driverdata;
   609     SDL_Rect final_rect, tmp_rect;
   610     SDL_Surface *src_clone, *src_rotated, *src_scaled;
   611     SDL_Surface *mask = NULL, *mask_rotated = NULL;
   612     int retval = 0, dstwidth, dstheight, abscenterx, abscentery;
   613     double cangle, sangle, px, py, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y;
   614     SDL_BlendMode blendmode;
   615     Uint8 alphaMod, rMod, gMod, bMod;
   616     int applyModulation = SDL_FALSE;
   617     int blitRequired = SDL_FALSE;
   618     int isOpaque = SDL_FALSE;
   619 
   620     if (!surface) {
   621         return -1;
   622     }
   623 
   624     if (renderer->viewport.x || renderer->viewport.y) {
   625         final_rect.x = (int)(renderer->viewport.x + dstrect->x);
   626         final_rect.y = (int)(renderer->viewport.y + dstrect->y);
   627     } else {
   628         final_rect.x = (int)dstrect->x;
   629         final_rect.y = (int)dstrect->y;
   630     }
   631     final_rect.w = (int)dstrect->w;
   632     final_rect.h = (int)dstrect->h;
   633 
   634     tmp_rect = final_rect;
   635     tmp_rect.x = 0;
   636     tmp_rect.y = 0;
   637 
   638     /* It is possible to encounter an RLE encoded surface here and locking it is
   639      * necessary because this code is going to access the pixel buffer directly.
   640      */
   641     if (SDL_MUSTLOCK(src)) {
   642         SDL_LockSurface(src);
   643     }
   644 
   645     /* Clone the source surface but use its pixel buffer directly.
   646      * The original source surface must be treated as read-only.
   647      */
   648     src_clone = SDL_CreateRGBSurfaceFrom(src->pixels, src->w, src->h, src->format->BitsPerPixel, src->pitch,
   649                                          src->format->Rmask, src->format->Gmask,
   650                                          src->format->Bmask, src->format->Amask);
   651     if (src_clone == NULL) {
   652         if (SDL_MUSTLOCK(src)) {
   653             SDL_UnlockSurface(src);
   654         }
   655         return -1;
   656     }
   657 
   658     SDL_GetSurfaceBlendMode(src, &blendmode);
   659     SDL_GetSurfaceAlphaMod(src, &alphaMod);
   660     SDL_GetSurfaceColorMod(src, &rMod, &gMod, &bMod);
   661 
   662     /* SDLgfx_rotateSurface only accepts 32-bit surfaces with a 8888 layout. Everything else has to be converted. */
   663     if (src->format->BitsPerPixel != 32 || SDL_PIXELLAYOUT(src->format->format) != SDL_PACKEDLAYOUT_8888 || !src->format->Amask) {
   664         blitRequired = SDL_TRUE;
   665     }
   666 
   667     /* If scaling and cropping is necessary, it has to be taken care of before the rotation. */
   668     if (!(srcrect->w == final_rect.w && srcrect->h == final_rect.h && srcrect->x == 0 && srcrect->y == 0)) {
   669         blitRequired = SDL_TRUE;
   670     }
   671 
   672     /* The color and alpha modulation has to be applied before the rotation when using the NONE and MOD blend modes. */
   673     if ((blendmode == SDL_BLENDMODE_NONE || blendmode == SDL_BLENDMODE_MOD) && (alphaMod & rMod & gMod & bMod) != 255) {
   674         applyModulation = SDL_TRUE;
   675         SDL_SetSurfaceAlphaMod(src_clone, alphaMod);
   676         SDL_SetSurfaceColorMod(src_clone, rMod, gMod, bMod);
   677     }
   678 
   679     /* Opaque surfaces are much easier to handle with the NONE blend mode. */
   680     if (blendmode == SDL_BLENDMODE_NONE && !src->format->Amask && alphaMod == 255) {
   681         isOpaque = SDL_TRUE;
   682     }
   683 
   684     /* The NONE blend mode requires a mask for non-opaque surfaces. This mask will be used
   685      * to clear the pixels in the destination surface. The other steps are explained below.
   686      */
   687     if (blendmode == SDL_BLENDMODE_NONE && !isOpaque) {
   688         mask = SDL_CreateRGBSurface(0, final_rect.w, final_rect.h, 32,
   689                                     0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
   690         if (mask == NULL) {
   691             retval = -1;
   692         } else {
   693             SDL_SetSurfaceBlendMode(mask, SDL_BLENDMODE_MOD);
   694         }
   695     }
   696 
   697     /* Create a new surface should there be a format mismatch or if scaling, cropping,
   698      * or modulation is required. It's possible to use the source surface directly otherwise.
   699      */
   700     if (!retval && (blitRequired || applyModulation)) {
   701         SDL_Rect scale_rect = tmp_rect;
   702         src_scaled = SDL_CreateRGBSurface(0, final_rect.w, final_rect.h, 32,
   703                                           0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
   704         if (src_scaled == NULL) {
   705             retval = -1;
   706         } else {
   707             SDL_SetSurfaceBlendMode(src_clone, SDL_BLENDMODE_NONE);
   708             retval = SDL_BlitScaled(src_clone, srcrect, src_scaled, &scale_rect);
   709             SDL_FreeSurface(src_clone);
   710             src_clone = src_scaled;
   711             src_scaled = NULL;
   712         }
   713     }
   714 
   715     /* SDLgfx_rotateSurface is going to make decisions depending on the blend mode. */
   716     SDL_SetSurfaceBlendMode(src_clone, blendmode);
   717 
   718     if (!retval) {
   719         SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, &dstwidth, &dstheight, &cangle, &sangle);
   720         src_rotated = SDLgfx_rotateSurface(src_clone, angle, dstwidth/2, dstheight/2, GetScaleQuality(), flip & SDL_FLIP_HORIZONTAL, flip & SDL_FLIP_VERTICAL, dstwidth, dstheight, cangle, sangle);
   721         if (src_rotated == NULL) {
   722             retval = -1;
   723         }
   724         if (!retval && mask != NULL) {
   725             /* The mask needed for the NONE blend mode gets rotated with the same parameters. */
   726             mask_rotated = SDLgfx_rotateSurface(mask, angle, dstwidth/2, dstheight/2, SDL_FALSE, 0, 0, dstwidth, dstheight, cangle, sangle);
   727             if (mask_rotated == NULL) {
   728                 retval = -1;
   729             }
   730         }
   731         if (!retval) {
   732             /* Find out where the new origin is by rotating the four final_rect points around the center and then taking the extremes */
   733             abscenterx = final_rect.x + (int)center->x;
   734             abscentery = final_rect.y + (int)center->y;
   735             /* Compensate the angle inversion to match the behaviour of the other backends */
   736             sangle = -sangle;
   737 
   738             /* Top Left */
   739             px = final_rect.x - abscenterx;
   740             py = final_rect.y - abscentery;
   741             p1x = px * cangle - py * sangle + abscenterx;
   742             p1y = px * sangle + py * cangle + abscentery;
   743 
   744             /* Top Right */
   745             px = final_rect.x + final_rect.w - abscenterx;
   746             py = final_rect.y - abscentery;
   747             p2x = px * cangle - py * sangle + abscenterx;
   748             p2y = px * sangle + py * cangle + abscentery;
   749 
   750             /* Bottom Left */
   751             px = final_rect.x - abscenterx;
   752             py = final_rect.y + final_rect.h - abscentery;
   753             p3x = px * cangle - py * sangle + abscenterx;
   754             p3y = px * sangle + py * cangle + abscentery;
   755 
   756             /* Bottom Right */
   757             px = final_rect.x + final_rect.w - abscenterx;
   758             py = final_rect.y + final_rect.h - abscentery;
   759             p4x = px * cangle - py * sangle + abscenterx;
   760             p4y = px * sangle + py * cangle + abscentery;
   761 
   762             tmp_rect.x = (int)MIN(MIN(p1x, p2x), MIN(p3x, p4x));
   763             tmp_rect.y = (int)MIN(MIN(p1y, p2y), MIN(p3y, p4y));
   764             tmp_rect.w = dstwidth;
   765             tmp_rect.h = dstheight;
   766 
   767             /* The NONE blend mode needs some special care with non-opaque surfaces.
   768              * Other blend modes or opaque surfaces can be blitted directly.
   769              */
   770             if (blendmode != SDL_BLENDMODE_NONE || isOpaque) {
   771                 if (applyModulation == SDL_FALSE) {
   772                     /* If the modulation wasn't already applied, make it happen now. */
   773                     SDL_SetSurfaceAlphaMod(src_rotated, alphaMod);
   774                     SDL_SetSurfaceColorMod(src_rotated, rMod, gMod, bMod);
   775                 }
   776                 retval = SDL_BlitSurface(src_rotated, NULL, surface, &tmp_rect);
   777             } else {
   778                 /* The NONE blend mode requires three steps to get the pixels onto the destination surface.
   779                  * First, the area where the rotated pixels will be blitted to get set to zero.
   780                  * This is accomplished by simply blitting a mask with the NONE blend mode.
   781                  * The colorkey set by the rotate function will discard the correct pixels.
   782                  */
   783                 SDL_Rect mask_rect = tmp_rect;
   784                 SDL_SetSurfaceBlendMode(mask_rotated, SDL_BLENDMODE_NONE);
   785                 retval = SDL_BlitSurface(mask_rotated, NULL, surface, &mask_rect);
   786                 if (!retval) {
   787                     /* The next step copies the alpha value. This is done with the BLEND blend mode and
   788                      * by modulating the source colors with 0. Since the destination is all zeros, this
   789                      * will effectively set the destination alpha to the source alpha.
   790                      */
   791                     SDL_SetSurfaceColorMod(src_rotated, 0, 0, 0);
   792                     mask_rect = tmp_rect;
   793                     retval = SDL_BlitSurface(src_rotated, NULL, surface, &mask_rect);
   794                     if (!retval) {
   795                         /* The last step gets the color values in place. The ADD blend mode simply adds them to
   796                          * the destination (where the color values are all zero). However, because the ADD blend
   797                          * mode modulates the colors with the alpha channel, a surface without an alpha mask needs
   798                          * to be created. This makes all source pixels opaque and the colors get copied correctly.
   799                          */
   800                         SDL_Surface *src_rotated_rgb;
   801                         src_rotated_rgb = SDL_CreateRGBSurfaceFrom(src_rotated->pixels, src_rotated->w, src_rotated->h,
   802                                                                    src_rotated->format->BitsPerPixel, src_rotated->pitch,
   803                                                                    src_rotated->format->Rmask, src_rotated->format->Gmask,
   804                                                                    src_rotated->format->Bmask, 0);
   805                         if (src_rotated_rgb == NULL) {
   806                             retval = -1;
   807                         } else {
   808                             SDL_SetSurfaceBlendMode(src_rotated_rgb, SDL_BLENDMODE_ADD);
   809                             retval = SDL_BlitSurface(src_rotated_rgb, NULL, surface, &tmp_rect);
   810                             SDL_FreeSurface(src_rotated_rgb);
   811                         }
   812                     }
   813                 }
   814                 SDL_FreeSurface(mask_rotated);
   815             }
   816             if (src_rotated != NULL) {
   817                 SDL_FreeSurface(src_rotated);
   818             }
   819         }
   820     }
   821 
   822     if (SDL_MUSTLOCK(src)) {
   823         SDL_UnlockSurface(src);
   824     }
   825     if (mask != NULL) {
   826         SDL_FreeSurface(mask);
   827     }
   828     if (src_clone != NULL) {
   829         SDL_FreeSurface(src_clone);
   830     }
   831     return retval;
   832 }
   833 
   834 static int
   835 SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   836                     Uint32 format, void * pixels, int pitch)
   837 {
   838     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   839     Uint32 src_format;
   840     void *src_pixels;
   841 
   842     if (!surface) {
   843         return -1;
   844     }
   845 
   846     /* NOTE: The rect is already adjusted according to the viewport by
   847      * SDL_RenderReadPixels.
   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: */