src/render/software/SDL_render_sw.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 20 May 2019 00:41:18 -0400
changeset 12743 c653e9fa9ca9
parent 12665 4743da9c3eea
child 12836 32839175c0e4
permissions -rw-r--r--
vulkan: Swapped out a free() that should have been an SDL_free().

Fixes (for real this time!) the Visual Studio builds.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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 #include "SDL_assert.h"
    29 
    30 #include "SDL_draw.h"
    31 #include "SDL_blendfillrect.h"
    32 #include "SDL_blendline.h"
    33 #include "SDL_blendpoint.h"
    34 #include "SDL_drawline.h"
    35 #include "SDL_drawpoint.h"
    36 #include "SDL_rotate.h"
    37 
    38 /* SDL surface based renderer implementation */
    39 
    40 typedef struct
    41 {
    42     SDL_Surface *surface;
    43     SDL_Surface *window;
    44 } SW_RenderData;
    45 
    46 
    47 static SDL_Surface *
    48 SW_ActivateRenderer(SDL_Renderer * renderer)
    49 {
    50     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
    51 
    52     if (!data->surface) {
    53         data->surface = data->window;
    54     }
    55     if (!data->surface) {
    56         SDL_Surface *surface = SDL_GetWindowSurface(renderer->window);
    57         if (surface) {
    58             data->surface = data->window = surface;
    59         }
    60     }
    61     return data->surface;
    62 }
    63 
    64 static void
    65 SW_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
    66 {
    67     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
    68 
    69     if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
    70         data->surface = NULL;
    71         data->window = NULL;
    72     }
    73 }
    74 
    75 static int
    76 SW_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
    77 {
    78     SDL_Surface *surface = SW_ActivateRenderer(renderer);
    79 
    80     if (surface) {
    81         if (w) {
    82             *w = surface->w;
    83         }
    84         if (h) {
    85             *h = surface->h;
    86         }
    87         return 0;
    88     } else {
    89         SDL_SetError("Software renderer doesn't have an output surface");
    90         return -1;
    91     }
    92 }
    93 
    94 static int
    95 SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
    96 {
    97     int bpp;
    98     Uint32 Rmask, Gmask, Bmask, Amask;
    99 
   100     if (!SDL_PixelFormatEnumToMasks
   101         (texture->format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
   102         return SDL_SetError("Unknown texture format");
   103     }
   104 
   105     texture->driverdata =
   106         SDL_CreateRGBSurface(0, texture->w, texture->h, bpp, Rmask, Gmask,
   107                              Bmask, Amask);
   108     SDL_SetSurfaceColorMod(texture->driverdata, texture->r, texture->g,
   109                            texture->b);
   110     SDL_SetSurfaceAlphaMod(texture->driverdata, texture->a);
   111     SDL_SetSurfaceBlendMode(texture->driverdata, texture->blendMode);
   112 
   113     /* Only RLE encode textures without an alpha channel since the RLE coder
   114      * discards the color values of pixels with an alpha value of zero.
   115      */
   116     if (texture->access == SDL_TEXTUREACCESS_STATIC && !Amask) {
   117         SDL_SetSurfaceRLE(texture->driverdata, 1);
   118     }
   119 
   120     if (!texture->driverdata) {
   121         return -1;
   122     }
   123     return 0;
   124 }
   125 
   126 static int
   127 SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   128                  const SDL_Rect * rect, const void *pixels, int pitch)
   129 {
   130     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   131     Uint8 *src, *dst;
   132     int row;
   133     size_t length;
   134 
   135     if(SDL_MUSTLOCK(surface))
   136         SDL_LockSurface(surface);
   137     src = (Uint8 *) pixels;
   138     dst = (Uint8 *) surface->pixels +
   139                         rect->y * surface->pitch +
   140                         rect->x * surface->format->BytesPerPixel;
   141     length = rect->w * surface->format->BytesPerPixel;
   142     for (row = 0; row < rect->h; ++row) {
   143         SDL_memcpy(dst, src, length);
   144         src += pitch;
   145         dst += surface->pitch;
   146     }
   147     if(SDL_MUSTLOCK(surface))
   148         SDL_UnlockSurface(surface);
   149     return 0;
   150 }
   151 
   152 static int
   153 SW_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   154                const SDL_Rect * rect, void **pixels, int *pitch)
   155 {
   156     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   157 
   158     *pixels =
   159         (void *) ((Uint8 *) surface->pixels + rect->y * surface->pitch +
   160                   rect->x * surface->format->BytesPerPixel);
   161     *pitch = surface->pitch;
   162     return 0;
   163 }
   164 
   165 static void
   166 SW_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   167 {
   168 }
   169 
   170 static int
   171 SW_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
   172 {
   173     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   174 
   175     if (texture ) {
   176         data->surface = (SDL_Surface *) texture->driverdata;
   177     } else {
   178         data->surface = data->window;
   179     }
   180     return 0;
   181 }
   182 
   183 static int
   184 SW_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd)
   185 {
   186     return 0;  /* nothing to do in this backend. */
   187 }
   188 
   189 static int
   190 SW_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
   191 {
   192     SDL_Point *verts = (SDL_Point *) SDL_AllocateRenderVertices(renderer, count * sizeof (SDL_Point), 0, &cmd->data.draw.first);
   193     int i;
   194 
   195     if (!verts) {
   196         return -1;
   197     }
   198 
   199     cmd->data.draw.count = count;
   200 
   201     if (renderer->viewport.x || renderer->viewport.y) {
   202         const int x = renderer->viewport.x;
   203         const int y = renderer->viewport.y;
   204         for (i = 0; i < count; i++, verts++, points++) {
   205             verts->x = (int)(x + points->x);
   206             verts->y = (int)(y + points->y);
   207         }
   208     } else {
   209         for (i = 0; i < count; i++, verts++, points++) {
   210             verts->x = (int)points->x;
   211             verts->y = (int)points->y;
   212         }
   213     }
   214 
   215     return 0;
   216 }
   217 
   218 static int
   219 SW_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count)
   220 {
   221     SDL_Rect *verts = (SDL_Rect *) SDL_AllocateRenderVertices(renderer, count * sizeof (SDL_Rect), 0, &cmd->data.draw.first);
   222     int i;
   223 
   224     if (!verts) {
   225         return -1;
   226     }
   227 
   228     cmd->data.draw.count = count;
   229 
   230     if (renderer->viewport.x || renderer->viewport.y) {
   231         const int x = renderer->viewport.x;
   232         const int y = renderer->viewport.y;
   233 
   234         for (i = 0; i < count; i++, verts++, rects++) {
   235             verts->x = (int)(x + rects->x);
   236             verts->y = (int)(y + rects->y);
   237             verts->w = SDL_max((int)rects->w, 1);
   238             verts->h = SDL_max((int)rects->h, 1);
   239         }
   240     } else {
   241         for (i = 0; i < count; i++, verts++, rects++) {
   242             verts->x = (int)rects->x;
   243             verts->y = (int)rects->y;
   244             verts->w = SDL_max((int)rects->w, 1);
   245             verts->h = SDL_max((int)rects->h, 1);
   246         }
   247     }
   248 
   249     return 0;
   250 }
   251 
   252 static int
   253 SW_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
   254              const SDL_Rect * srcrect, const SDL_FRect * dstrect)
   255 {
   256     SDL_Rect *verts = (SDL_Rect *) SDL_AllocateRenderVertices(renderer, 2 * sizeof (SDL_Rect), 0, &cmd->data.draw.first);
   257 
   258     if (!verts) {
   259         return -1;
   260     }
   261 
   262     cmd->data.draw.count = 1;
   263 
   264     SDL_memcpy(verts, srcrect, sizeof (SDL_Rect));
   265     verts++;
   266 
   267     if (renderer->viewport.x || renderer->viewport.y) {
   268         verts->x = (int)(renderer->viewport.x + dstrect->x);
   269         verts->y = (int)(renderer->viewport.y + dstrect->y);
   270     } else {
   271         verts->x = (int)dstrect->x;
   272         verts->y = (int)dstrect->y;
   273     }
   274     verts->w = (int)dstrect->w;
   275     verts->h = (int)dstrect->h;
   276 
   277     return 0;
   278 }
   279 
   280 typedef struct CopyExData
   281 {
   282     SDL_Rect srcrect;
   283     SDL_Rect dstrect;
   284     double angle;
   285     SDL_FPoint center;
   286     SDL_RendererFlip flip;
   287 } CopyExData;
   288 
   289 static int
   290 SW_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
   291                const SDL_Rect * srcrect, const SDL_FRect * dstrect,
   292                const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
   293 {
   294     CopyExData *verts = (CopyExData *) SDL_AllocateRenderVertices(renderer, sizeof (CopyExData), 0, &cmd->data.draw.first);
   295 
   296     if (!verts) {
   297         return -1;
   298     }
   299 
   300     cmd->data.draw.count = 1;
   301 
   302     SDL_memcpy(&verts->srcrect, srcrect, sizeof (SDL_Rect));
   303 
   304     if (renderer->viewport.x || renderer->viewport.y) {
   305         verts->dstrect.x = (int)(renderer->viewport.x + dstrect->x);
   306         verts->dstrect.y = (int)(renderer->viewport.y + dstrect->y);
   307     } else {
   308         verts->dstrect.x = (int)dstrect->x;
   309         verts->dstrect.y = (int)dstrect->y;
   310     }
   311     verts->dstrect.w = (int)dstrect->w;
   312     verts->dstrect.h = (int)dstrect->h;
   313     verts->angle = angle;
   314     SDL_memcpy(&verts->center, center, sizeof (SDL_FPoint));
   315     verts->flip = flip;
   316 
   317     return 0;
   318 }
   319 
   320 static int
   321 SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Surface *surface, SDL_Texture * texture,
   322                 const SDL_Rect * srcrect, const SDL_Rect * final_rect,
   323                 const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip)
   324 {
   325     SDL_Surface *src = (SDL_Surface *) texture->driverdata;
   326     SDL_Rect tmp_rect;
   327     SDL_Surface *src_clone, *src_rotated, *src_scaled;
   328     SDL_Surface *mask = NULL, *mask_rotated = NULL;
   329     int retval = 0, dstwidth, dstheight, abscenterx, abscentery;
   330     double cangle, sangle, px, py, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y;
   331     SDL_BlendMode blendmode;
   332     Uint8 alphaMod, rMod, gMod, bMod;
   333     int applyModulation = SDL_FALSE;
   334     int blitRequired = SDL_FALSE;
   335     int isOpaque = SDL_FALSE;
   336 
   337     if (!surface) {
   338         return -1;
   339     }
   340 
   341     tmp_rect.x = 0;
   342     tmp_rect.y = 0;
   343     tmp_rect.w = final_rect->w;
   344     tmp_rect.h = final_rect->h;
   345 
   346     /* It is possible to encounter an RLE encoded surface here and locking it is
   347      * necessary because this code is going to access the pixel buffer directly.
   348      */
   349     if (SDL_MUSTLOCK(src)) {
   350         SDL_LockSurface(src);
   351     }
   352 
   353     /* Clone the source surface but use its pixel buffer directly.
   354      * The original source surface must be treated as read-only.
   355      */
   356     src_clone = SDL_CreateRGBSurfaceFrom(src->pixels, src->w, src->h, src->format->BitsPerPixel, src->pitch,
   357                                          src->format->Rmask, src->format->Gmask,
   358                                          src->format->Bmask, src->format->Amask);
   359     if (src_clone == NULL) {
   360         if (SDL_MUSTLOCK(src)) {
   361             SDL_UnlockSurface(src);
   362         }
   363         return -1;
   364     }
   365 
   366     SDL_GetSurfaceBlendMode(src, &blendmode);
   367     SDL_GetSurfaceAlphaMod(src, &alphaMod);
   368     SDL_GetSurfaceColorMod(src, &rMod, &gMod, &bMod);
   369 
   370     /* SDLgfx_rotateSurface only accepts 32-bit surfaces with a 8888 layout. Everything else has to be converted. */
   371     if (src->format->BitsPerPixel != 32 || SDL_PIXELLAYOUT(src->format->format) != SDL_PACKEDLAYOUT_8888 || !src->format->Amask) {
   372         blitRequired = SDL_TRUE;
   373     }
   374 
   375     /* If scaling and cropping is necessary, it has to be taken care of before the rotation. */
   376     if (!(srcrect->w == final_rect->w && srcrect->h == final_rect->h && srcrect->x == 0 && srcrect->y == 0)) {
   377         blitRequired = SDL_TRUE;
   378     }
   379 
   380     /* srcrect is not selecting the whole src surface, so cropping is needed */
   381     if (!(srcrect->w == src->w && srcrect->h == src->h && srcrect->x == 0 && srcrect->y == 0)) {
   382         blitRequired = SDL_TRUE;
   383     }
   384 
   385     /* The color and alpha modulation has to be applied before the rotation when using the NONE and MOD blend modes. */
   386     if ((blendmode == SDL_BLENDMODE_NONE || blendmode == SDL_BLENDMODE_MOD) && (alphaMod & rMod & gMod & bMod) != 255) {
   387         applyModulation = SDL_TRUE;
   388         SDL_SetSurfaceAlphaMod(src_clone, alphaMod);
   389         SDL_SetSurfaceColorMod(src_clone, rMod, gMod, bMod);
   390     }
   391 
   392     /* Opaque surfaces are much easier to handle with the NONE blend mode. */
   393     if (blendmode == SDL_BLENDMODE_NONE && !src->format->Amask && alphaMod == 255) {
   394         isOpaque = SDL_TRUE;
   395     }
   396 
   397     /* The NONE blend mode requires a mask for non-opaque surfaces. This mask will be used
   398      * to clear the pixels in the destination surface. The other steps are explained below.
   399      */
   400     if (blendmode == SDL_BLENDMODE_NONE && !isOpaque) {
   401         mask = SDL_CreateRGBSurface(0, final_rect->w, final_rect->h, 32,
   402                                     0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
   403         if (mask == NULL) {
   404             retval = -1;
   405         } else {
   406             SDL_SetSurfaceBlendMode(mask, SDL_BLENDMODE_MOD);
   407         }
   408     }
   409 
   410     /* Create a new surface should there be a format mismatch or if scaling, cropping,
   411      * or modulation is required. It's possible to use the source surface directly otherwise.
   412      */
   413     if (!retval && (blitRequired || applyModulation)) {
   414         SDL_Rect scale_rect = tmp_rect;
   415         src_scaled = SDL_CreateRGBSurface(0, final_rect->w, final_rect->h, 32,
   416                                           0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
   417         if (src_scaled == NULL) {
   418             retval = -1;
   419         } else {
   420             SDL_SetSurfaceBlendMode(src_clone, SDL_BLENDMODE_NONE);
   421             retval = SDL_BlitScaled(src_clone, srcrect, src_scaled, &scale_rect);
   422             SDL_FreeSurface(src_clone);
   423             src_clone = src_scaled;
   424             src_scaled = NULL;
   425         }
   426     }
   427 
   428     /* SDLgfx_rotateSurface is going to make decisions depending on the blend mode. */
   429     SDL_SetSurfaceBlendMode(src_clone, blendmode);
   430 
   431     if (!retval) {
   432         SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, &dstwidth, &dstheight, &cangle, &sangle);
   433         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);
   434         if (src_rotated == NULL) {
   435             retval = -1;
   436         }
   437         if (!retval && mask != NULL) {
   438             /* The mask needed for the NONE blend mode gets rotated with the same parameters. */
   439             mask_rotated = SDLgfx_rotateSurface(mask, angle, dstwidth/2, dstheight/2, SDL_FALSE, 0, 0, dstwidth, dstheight, cangle, sangle);
   440             if (mask_rotated == NULL) {
   441                 retval = -1;
   442             }
   443         }
   444         if (!retval) {
   445             /* Find out where the new origin is by rotating the four final_rect points around the center and then taking the extremes */
   446             abscenterx = final_rect->x + (int)center->x;
   447             abscentery = final_rect->y + (int)center->y;
   448             /* Compensate the angle inversion to match the behaviour of the other backends */
   449             sangle = -sangle;
   450 
   451             /* Top Left */
   452             px = final_rect->x - abscenterx;
   453             py = final_rect->y - abscentery;
   454             p1x = px * cangle - py * sangle + abscenterx;
   455             p1y = px * sangle + py * cangle + abscentery;
   456 
   457             /* Top Right */
   458             px = final_rect->x + final_rect->w - abscenterx;
   459             py = final_rect->y - abscentery;
   460             p2x = px * cangle - py * sangle + abscenterx;
   461             p2y = px * sangle + py * cangle + abscentery;
   462 
   463             /* Bottom Left */
   464             px = final_rect->x - abscenterx;
   465             py = final_rect->y + final_rect->h - abscentery;
   466             p3x = px * cangle - py * sangle + abscenterx;
   467             p3y = px * sangle + py * cangle + abscentery;
   468 
   469             /* Bottom Right */
   470             px = final_rect->x + final_rect->w - abscenterx;
   471             py = final_rect->y + final_rect->h - abscentery;
   472             p4x = px * cangle - py * sangle + abscenterx;
   473             p4y = px * sangle + py * cangle + abscentery;
   474 
   475             tmp_rect.x = (int)MIN(MIN(p1x, p2x), MIN(p3x, p4x));
   476             tmp_rect.y = (int)MIN(MIN(p1y, p2y), MIN(p3y, p4y));
   477             tmp_rect.w = dstwidth;
   478             tmp_rect.h = dstheight;
   479 
   480             /* The NONE blend mode needs some special care with non-opaque surfaces.
   481              * Other blend modes or opaque surfaces can be blitted directly.
   482              */
   483             if (blendmode != SDL_BLENDMODE_NONE || isOpaque) {
   484                 if (applyModulation == SDL_FALSE) {
   485                     /* If the modulation wasn't already applied, make it happen now. */
   486                     SDL_SetSurfaceAlphaMod(src_rotated, alphaMod);
   487                     SDL_SetSurfaceColorMod(src_rotated, rMod, gMod, bMod);
   488                 }
   489                 retval = SDL_BlitSurface(src_rotated, NULL, surface, &tmp_rect);
   490             } else {
   491                 /* The NONE blend mode requires three steps to get the pixels onto the destination surface.
   492                  * First, the area where the rotated pixels will be blitted to get set to zero.
   493                  * This is accomplished by simply blitting a mask with the NONE blend mode.
   494                  * The colorkey set by the rotate function will discard the correct pixels.
   495                  */
   496                 SDL_Rect mask_rect = tmp_rect;
   497                 SDL_SetSurfaceBlendMode(mask_rotated, SDL_BLENDMODE_NONE);
   498                 retval = SDL_BlitSurface(mask_rotated, NULL, surface, &mask_rect);
   499                 if (!retval) {
   500                     /* The next step copies the alpha value. This is done with the BLEND blend mode and
   501                      * by modulating the source colors with 0. Since the destination is all zeros, this
   502                      * will effectively set the destination alpha to the source alpha.
   503                      */
   504                     SDL_SetSurfaceColorMod(src_rotated, 0, 0, 0);
   505                     mask_rect = tmp_rect;
   506                     retval = SDL_BlitSurface(src_rotated, NULL, surface, &mask_rect);
   507                     if (!retval) {
   508                         /* The last step gets the color values in place. The ADD blend mode simply adds them to
   509                          * the destination (where the color values are all zero). However, because the ADD blend
   510                          * mode modulates the colors with the alpha channel, a surface without an alpha mask needs
   511                          * to be created. This makes all source pixels opaque and the colors get copied correctly.
   512                          */
   513                         SDL_Surface *src_rotated_rgb;
   514                         src_rotated_rgb = SDL_CreateRGBSurfaceFrom(src_rotated->pixels, src_rotated->w, src_rotated->h,
   515                                                                    src_rotated->format->BitsPerPixel, src_rotated->pitch,
   516                                                                    src_rotated->format->Rmask, src_rotated->format->Gmask,
   517                                                                    src_rotated->format->Bmask, 0);
   518                         if (src_rotated_rgb == NULL) {
   519                             retval = -1;
   520                         } else {
   521                             SDL_SetSurfaceBlendMode(src_rotated_rgb, SDL_BLENDMODE_ADD);
   522                             retval = SDL_BlitSurface(src_rotated_rgb, NULL, surface, &tmp_rect);
   523                             SDL_FreeSurface(src_rotated_rgb);
   524                         }
   525                     }
   526                 }
   527                 SDL_FreeSurface(mask_rotated);
   528             }
   529             if (src_rotated != NULL) {
   530                 SDL_FreeSurface(src_rotated);
   531             }
   532         }
   533     }
   534 
   535     if (SDL_MUSTLOCK(src)) {
   536         SDL_UnlockSurface(src);
   537     }
   538     if (mask != NULL) {
   539         SDL_FreeSurface(mask);
   540     }
   541     if (src_clone != NULL) {
   542         SDL_FreeSurface(src_clone);
   543     }
   544     return retval;
   545 }
   546 
   547 static void
   548 PrepTextureForCopy(const SDL_RenderCommand *cmd)
   549 {
   550     const Uint8 r = cmd->data.draw.r;
   551     const Uint8 g = cmd->data.draw.g;
   552     const Uint8 b = cmd->data.draw.b;
   553     const Uint8 a = cmd->data.draw.a;
   554     const SDL_BlendMode blend = cmd->data.draw.blend;
   555     SDL_Texture *texture = cmd->data.draw.texture;
   556     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   557     const SDL_bool colormod = ((r & g & b) != 0xFF);
   558     const SDL_bool alphamod = (a != 0xFF);
   559     const SDL_bool blending = ((blend == SDL_BLENDMODE_ADD) || (blend == SDL_BLENDMODE_MOD));
   560 
   561     if (colormod || alphamod || blending) {
   562         SDL_SetSurfaceRLE(surface, 0);
   563     }
   564 
   565     /* !!! FIXME: we can probably avoid some of these calls. */
   566     SDL_SetSurfaceColorMod(surface, r, g, b);
   567     SDL_SetSurfaceAlphaMod(surface, a);
   568     SDL_SetSurfaceBlendMode(surface, blend);
   569 }
   570 
   571 static int
   572 SW_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
   573 {
   574     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   575     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   576     const SDL_Rect *viewport = NULL;
   577     const SDL_Rect *cliprect = NULL;
   578 
   579     if (!surface) {
   580         return -1;
   581     }
   582 
   583     while (cmd) {
   584         switch (cmd->command) {
   585             case SDL_RENDERCMD_SETDRAWCOLOR: {
   586                 break;  /* Not used in this backend. */
   587             }
   588 
   589             case SDL_RENDERCMD_SETVIEWPORT: {
   590                 viewport = &cmd->data.viewport.rect;
   591                 SDL_SetClipRect(data->surface, viewport);
   592                 break;
   593             }
   594 
   595             case SDL_RENDERCMD_SETCLIPRECT: {
   596                 SDL_assert(viewport != NULL);
   597                 cliprect = cmd->data.cliprect.enabled ? &cmd->data.cliprect.rect : NULL;
   598                 if (cliprect) {
   599                     SDL_Rect clip_rect;
   600                     clip_rect.x = cliprect->x + viewport->x;
   601                     clip_rect.y = cliprect->y + viewport->y;
   602                     clip_rect.w = cliprect->w;
   603                     clip_rect.h = cliprect->h;
   604                     SDL_IntersectRect(viewport, &clip_rect, &clip_rect);
   605                     SDL_SetClipRect(surface, &clip_rect);
   606                 } else {
   607                     SDL_SetClipRect(surface, viewport);
   608                 }
   609                 break;
   610             }
   611 
   612             case SDL_RENDERCMD_CLEAR: {
   613                 const Uint8 r = cmd->data.color.r;
   614                 const Uint8 g = cmd->data.color.g;
   615                 const Uint8 b = cmd->data.color.b;
   616                 const Uint8 a = cmd->data.color.a;
   617                 const SDL_Rect clip_rect = surface->clip_rect;
   618                 /* By definition the clear ignores the clip rect */
   619                 SDL_SetClipRect(surface, NULL);
   620                 SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, r, g, b, a));
   621                 SDL_SetClipRect(surface, &clip_rect);
   622                 break;
   623             }
   624 
   625             case SDL_RENDERCMD_DRAW_POINTS: {
   626                 const Uint8 r = cmd->data.draw.r;
   627                 const Uint8 g = cmd->data.draw.g;
   628                 const Uint8 b = cmd->data.draw.b;
   629                 const Uint8 a = cmd->data.draw.a;
   630                 const int count = (int) cmd->data.draw.count;
   631                 const SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first);
   632                 const SDL_BlendMode blend = cmd->data.draw.blend;
   633                 if (blend == SDL_BLENDMODE_NONE) {
   634                     SDL_DrawPoints(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
   635                 } else {
   636                     SDL_BlendPoints(surface, verts, count, blend, r, g, b, a);
   637                 }
   638                 break;
   639             }
   640 
   641             case SDL_RENDERCMD_DRAW_LINES: {
   642                 const Uint8 r = cmd->data.draw.r;
   643                 const Uint8 g = cmd->data.draw.g;
   644                 const Uint8 b = cmd->data.draw.b;
   645                 const Uint8 a = cmd->data.draw.a;
   646                 const int count = (int) cmd->data.draw.count;
   647                 const SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first);
   648                 const SDL_BlendMode blend = cmd->data.draw.blend;
   649                 if (blend == SDL_BLENDMODE_NONE) {
   650                     SDL_DrawLines(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
   651                 } else {
   652                     SDL_BlendLines(surface, verts, count, blend, r, g, b, a);
   653                 }
   654                 break;
   655             }
   656 
   657             case SDL_RENDERCMD_FILL_RECTS: {
   658                 const Uint8 r = cmd->data.draw.r;
   659                 const Uint8 g = cmd->data.draw.g;
   660                 const Uint8 b = cmd->data.draw.b;
   661                 const Uint8 a = cmd->data.draw.a;
   662                 const int count = (int) cmd->data.draw.count;
   663                 const SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first);
   664                 const SDL_BlendMode blend = cmd->data.draw.blend;
   665                 if (blend == SDL_BLENDMODE_NONE) {
   666                     SDL_FillRects(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
   667                 } else {
   668                     SDL_BlendFillRects(surface, verts, count, blend, r, g, b, a);
   669                 }
   670                 break;
   671             }
   672 
   673             case SDL_RENDERCMD_COPY: {
   674                 SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first);
   675                 const SDL_Rect *srcrect = verts;
   676                 SDL_Rect *dstrect = verts + 1;
   677                 SDL_Texture *texture = cmd->data.draw.texture;
   678                 SDL_Surface *src = (SDL_Surface *) texture->driverdata;
   679 
   680                 PrepTextureForCopy(cmd);
   681 
   682                 if ( srcrect->w == dstrect->w && srcrect->h == dstrect->h ) {
   683                     SDL_BlitSurface(src, srcrect, surface, dstrect);
   684                 } else {
   685                     /* If scaling is ever done, permanently disable RLE (which doesn't support scaling)
   686                      * to avoid potentially frequent RLE encoding/decoding.
   687                      */
   688                     SDL_SetSurfaceRLE(surface, 0);
   689                     SDL_BlitScaled(src, srcrect, surface, dstrect);
   690                 }
   691                 break;
   692             }
   693 
   694             case SDL_RENDERCMD_COPY_EX: {
   695                 const CopyExData *copydata = (CopyExData *) (((Uint8 *) vertices) + cmd->data.draw.first);
   696                 PrepTextureForCopy(cmd);
   697                 SW_RenderCopyEx(renderer, surface, cmd->data.draw.texture, &copydata->srcrect,
   698                                 &copydata->dstrect, copydata->angle, &copydata->center, copydata->flip);
   699                 break;
   700             }
   701 
   702             case SDL_RENDERCMD_NO_OP:
   703                 break;
   704         }
   705 
   706         cmd = cmd->next;
   707     }
   708 
   709     return 0;
   710 }
   711 
   712 static int
   713 SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   714                     Uint32 format, void * pixels, int pitch)
   715 {
   716     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   717     Uint32 src_format;
   718     void *src_pixels;
   719 
   720     if (!surface) {
   721         return -1;
   722     }
   723 
   724     /* NOTE: The rect is already adjusted according to the viewport by
   725      * SDL_RenderReadPixels.
   726      */
   727 
   728     if (rect->x < 0 || rect->x+rect->w > surface->w ||
   729         rect->y < 0 || rect->y+rect->h > surface->h) {
   730         return SDL_SetError("Tried to read outside of surface bounds");
   731     }
   732 
   733     src_format = surface->format->format;
   734     src_pixels = (void*)((Uint8 *) surface->pixels +
   735                     rect->y * surface->pitch +
   736                     rect->x * surface->format->BytesPerPixel);
   737 
   738     return SDL_ConvertPixels(rect->w, rect->h,
   739                              src_format, src_pixels, surface->pitch,
   740                              format, pixels, pitch);
   741 }
   742 
   743 static void
   744 SW_RenderPresent(SDL_Renderer * renderer)
   745 {
   746     SDL_Window *window = renderer->window;
   747 
   748     if (window) {
   749         SDL_UpdateWindowSurface(window);
   750     }
   751 }
   752 
   753 static void
   754 SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   755 {
   756     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   757 
   758     SDL_FreeSurface(surface);
   759 }
   760 
   761 static void
   762 SW_DestroyRenderer(SDL_Renderer * renderer)
   763 {
   764     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   765 
   766     SDL_free(data);
   767     SDL_free(renderer);
   768 }
   769 
   770 SDL_Renderer *
   771 SW_CreateRendererForSurface(SDL_Surface * surface)
   772 {
   773     SDL_Renderer *renderer;
   774     SW_RenderData *data;
   775 
   776     if (!surface) {
   777         SDL_SetError("Can't create renderer for NULL surface");
   778         return NULL;
   779     }
   780 
   781     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   782     if (!renderer) {
   783         SDL_OutOfMemory();
   784         return NULL;
   785     }
   786 
   787     data = (SW_RenderData *) SDL_calloc(1, sizeof(*data));
   788     if (!data) {
   789         SW_DestroyRenderer(renderer);
   790         SDL_OutOfMemory();
   791         return NULL;
   792     }
   793     data->surface = surface;
   794     data->window = surface;
   795 
   796     renderer->WindowEvent = SW_WindowEvent;
   797     renderer->GetOutputSize = SW_GetOutputSize;
   798     renderer->CreateTexture = SW_CreateTexture;
   799     renderer->UpdateTexture = SW_UpdateTexture;
   800     renderer->LockTexture = SW_LockTexture;
   801     renderer->UnlockTexture = SW_UnlockTexture;
   802     renderer->SetRenderTarget = SW_SetRenderTarget;
   803     renderer->QueueSetViewport = SW_QueueSetViewport;
   804     renderer->QueueSetDrawColor = SW_QueueSetViewport;  /* SetViewport and SetDrawColor are (currently) no-ops. */
   805     renderer->QueueDrawPoints = SW_QueueDrawPoints;
   806     renderer->QueueDrawLines = SW_QueueDrawPoints;  /* lines and points queue vertices the same way. */
   807     renderer->QueueFillRects = SW_QueueFillRects;
   808     renderer->QueueCopy = SW_QueueCopy;
   809     renderer->QueueCopyEx = SW_QueueCopyEx;
   810     renderer->RunCommandQueue = SW_RunCommandQueue;
   811     renderer->RenderReadPixels = SW_RenderReadPixels;
   812     renderer->RenderPresent = SW_RenderPresent;
   813     renderer->DestroyTexture = SW_DestroyTexture;
   814     renderer->DestroyRenderer = SW_DestroyRenderer;
   815     renderer->info = SW_RenderDriver.info;
   816     renderer->driverdata = data;
   817 
   818     SW_ActivateRenderer(renderer);
   819 
   820     return renderer;
   821 }
   822 
   823 static SDL_Renderer *
   824 SW_CreateRenderer(SDL_Window * window, Uint32 flags)
   825 {
   826     SDL_Surface *surface;
   827 
   828     surface = SDL_GetWindowSurface(window);
   829     if (!surface) {
   830         return NULL;
   831     }
   832     return SW_CreateRendererForSurface(surface);
   833 }
   834 
   835 SDL_RenderDriver SW_RenderDriver = {
   836     SW_CreateRenderer,
   837     {
   838      "software",
   839      SDL_RENDERER_SOFTWARE | SDL_RENDERER_TARGETTEXTURE,
   840      8,
   841      {
   842       SDL_PIXELFORMAT_ARGB8888,
   843       SDL_PIXELFORMAT_ABGR8888,
   844       SDL_PIXELFORMAT_RGBA8888,
   845       SDL_PIXELFORMAT_BGRA8888,
   846       SDL_PIXELFORMAT_RGB888,
   847       SDL_PIXELFORMAT_BGR888,
   848       SDL_PIXELFORMAT_RGB565,
   849       SDL_PIXELFORMAT_RGB555
   850      },
   851      0,
   852      0}
   853 };
   854 
   855 #endif /* !SDL_RENDER_DISABLED */
   856 
   857 /* vi: set ts=4 sw=4 expandtab: */