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