src/render/software/SDL_render_sw.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 03 Oct 2018 23:37:29 -0400
branchSDL-ryan-batching-renderer
changeset 12288 005e1d1bcf05
parent 11958 d7582d7286aa
child 12381 dc9108cd4340
permissions -rw-r--r--
render: moved software renderer to new interface.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if !SDL_RENDER_DISABLED
    24 
    25 #include "../SDL_sysrender.h"
    26 #include "SDL_render_sw_c.h"
    27 #include "SDL_hints.h"
    28 #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     size_t 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     size_t 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     /* The color and alpha modulation has to be applied before the rotation when using the NONE and MOD blend modes. */
   381     if ((blendmode == SDL_BLENDMODE_NONE || blendmode == SDL_BLENDMODE_MOD) && (alphaMod & rMod & gMod & bMod) != 255) {
   382         applyModulation = SDL_TRUE;
   383         SDL_SetSurfaceAlphaMod(src_clone, alphaMod);
   384         SDL_SetSurfaceColorMod(src_clone, rMod, gMod, bMod);
   385     }
   386 
   387     /* Opaque surfaces are much easier to handle with the NONE blend mode. */
   388     if (blendmode == SDL_BLENDMODE_NONE && !src->format->Amask && alphaMod == 255) {
   389         isOpaque = SDL_TRUE;
   390     }
   391 
   392     /* The NONE blend mode requires a mask for non-opaque surfaces. This mask will be used
   393      * to clear the pixels in the destination surface. The other steps are explained below.
   394      */
   395     if (blendmode == SDL_BLENDMODE_NONE && !isOpaque) {
   396         mask = SDL_CreateRGBSurface(0, final_rect->w, final_rect->h, 32,
   397                                     0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
   398         if (mask == NULL) {
   399             retval = -1;
   400         } else {
   401             SDL_SetSurfaceBlendMode(mask, SDL_BLENDMODE_MOD);
   402         }
   403     }
   404 
   405     /* Create a new surface should there be a format mismatch or if scaling, cropping,
   406      * or modulation is required. It's possible to use the source surface directly otherwise.
   407      */
   408     if (!retval && (blitRequired || applyModulation)) {
   409         SDL_Rect scale_rect = tmp_rect;
   410         src_scaled = SDL_CreateRGBSurface(0, final_rect->w, final_rect->h, 32,
   411                                           0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
   412         if (src_scaled == NULL) {
   413             retval = -1;
   414         } else {
   415             SDL_SetSurfaceBlendMode(src_clone, SDL_BLENDMODE_NONE);
   416             retval = SDL_BlitScaled(src_clone, srcrect, src_scaled, &scale_rect);
   417             SDL_FreeSurface(src_clone);
   418             src_clone = src_scaled;
   419             src_scaled = NULL;
   420         }
   421     }
   422 
   423     /* SDLgfx_rotateSurface is going to make decisions depending on the blend mode. */
   424     SDL_SetSurfaceBlendMode(src_clone, blendmode);
   425 
   426     if (!retval) {
   427         SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, &dstwidth, &dstheight, &cangle, &sangle);
   428         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);
   429         if (src_rotated == NULL) {
   430             retval = -1;
   431         }
   432         if (!retval && mask != NULL) {
   433             /* The mask needed for the NONE blend mode gets rotated with the same parameters. */
   434             mask_rotated = SDLgfx_rotateSurface(mask, angle, dstwidth/2, dstheight/2, SDL_FALSE, 0, 0, dstwidth, dstheight, cangle, sangle);
   435             if (mask_rotated == NULL) {
   436                 retval = -1;
   437             }
   438         }
   439         if (!retval) {
   440             /* Find out where the new origin is by rotating the four final_rect points around the center and then taking the extremes */
   441             abscenterx = final_rect->x + (int)center->x;
   442             abscentery = final_rect->y + (int)center->y;
   443             /* Compensate the angle inversion to match the behaviour of the other backends */
   444             sangle = -sangle;
   445 
   446             /* Top Left */
   447             px = final_rect->x - abscenterx;
   448             py = final_rect->y - abscentery;
   449             p1x = px * cangle - py * sangle + abscenterx;
   450             p1y = px * sangle + py * cangle + abscentery;
   451 
   452             /* Top Right */
   453             px = final_rect->x + final_rect->w - abscenterx;
   454             py = final_rect->y - abscentery;
   455             p2x = px * cangle - py * sangle + abscenterx;
   456             p2y = px * sangle + py * cangle + abscentery;
   457 
   458             /* Bottom Left */
   459             px = final_rect->x - abscenterx;
   460             py = final_rect->y + final_rect->h - abscentery;
   461             p3x = px * cangle - py * sangle + abscenterx;
   462             p3y = px * sangle + py * cangle + abscentery;
   463 
   464             /* Bottom Right */
   465             px = final_rect->x + final_rect->w - abscenterx;
   466             py = final_rect->y + final_rect->h - abscentery;
   467             p4x = px * cangle - py * sangle + abscenterx;
   468             p4y = px * sangle + py * cangle + abscentery;
   469 
   470             tmp_rect.x = (int)MIN(MIN(p1x, p2x), MIN(p3x, p4x));
   471             tmp_rect.y = (int)MIN(MIN(p1y, p2y), MIN(p3y, p4y));
   472             tmp_rect.w = dstwidth;
   473             tmp_rect.h = dstheight;
   474 
   475             /* The NONE blend mode needs some special care with non-opaque surfaces.
   476              * Other blend modes or opaque surfaces can be blitted directly.
   477              */
   478             if (blendmode != SDL_BLENDMODE_NONE || isOpaque) {
   479                 if (applyModulation == SDL_FALSE) {
   480                     /* If the modulation wasn't already applied, make it happen now. */
   481                     SDL_SetSurfaceAlphaMod(src_rotated, alphaMod);
   482                     SDL_SetSurfaceColorMod(src_rotated, rMod, gMod, bMod);
   483                 }
   484                 retval = SDL_BlitSurface(src_rotated, NULL, surface, &tmp_rect);
   485             } else {
   486                 /* The NONE blend mode requires three steps to get the pixels onto the destination surface.
   487                  * First, the area where the rotated pixels will be blitted to get set to zero.
   488                  * This is accomplished by simply blitting a mask with the NONE blend mode.
   489                  * The colorkey set by the rotate function will discard the correct pixels.
   490                  */
   491                 SDL_Rect mask_rect = tmp_rect;
   492                 SDL_SetSurfaceBlendMode(mask_rotated, SDL_BLENDMODE_NONE);
   493                 retval = SDL_BlitSurface(mask_rotated, NULL, surface, &mask_rect);
   494                 if (!retval) {
   495                     /* The next step copies the alpha value. This is done with the BLEND blend mode and
   496                      * by modulating the source colors with 0. Since the destination is all zeros, this
   497                      * will effectively set the destination alpha to the source alpha.
   498                      */
   499                     SDL_SetSurfaceColorMod(src_rotated, 0, 0, 0);
   500                     mask_rect = tmp_rect;
   501                     retval = SDL_BlitSurface(src_rotated, NULL, surface, &mask_rect);
   502                     if (!retval) {
   503                         /* The last step gets the color values in place. The ADD blend mode simply adds them to
   504                          * the destination (where the color values are all zero). However, because the ADD blend
   505                          * mode modulates the colors with the alpha channel, a surface without an alpha mask needs
   506                          * to be created. This makes all source pixels opaque and the colors get copied correctly.
   507                          */
   508                         SDL_Surface *src_rotated_rgb;
   509                         src_rotated_rgb = SDL_CreateRGBSurfaceFrom(src_rotated->pixels, src_rotated->w, src_rotated->h,
   510                                                                    src_rotated->format->BitsPerPixel, src_rotated->pitch,
   511                                                                    src_rotated->format->Rmask, src_rotated->format->Gmask,
   512                                                                    src_rotated->format->Bmask, 0);
   513                         if (src_rotated_rgb == NULL) {
   514                             retval = -1;
   515                         } else {
   516                             SDL_SetSurfaceBlendMode(src_rotated_rgb, SDL_BLENDMODE_ADD);
   517                             retval = SDL_BlitSurface(src_rotated_rgb, NULL, surface, &tmp_rect);
   518                             SDL_FreeSurface(src_rotated_rgb);
   519                         }
   520                     }
   521                 }
   522                 SDL_FreeSurface(mask_rotated);
   523             }
   524             if (src_rotated != NULL) {
   525                 SDL_FreeSurface(src_rotated);
   526             }
   527         }
   528     }
   529 
   530     if (SDL_MUSTLOCK(src)) {
   531         SDL_UnlockSurface(src);
   532     }
   533     if (mask != NULL) {
   534         SDL_FreeSurface(mask);
   535     }
   536     if (src_clone != NULL) {
   537         SDL_FreeSurface(src_clone);
   538     }
   539     return retval;
   540 }
   541 
   542 static void
   543 PrepTextureForCopy(const SDL_RenderCommand *cmd)
   544 {
   545     const Uint8 r = cmd->data.draw.r;
   546     const Uint8 g = cmd->data.draw.g;
   547     const Uint8 b = cmd->data.draw.b;
   548     const Uint8 a = cmd->data.draw.a;
   549     const SDL_BlendMode blend = cmd->data.draw.blend;
   550     SDL_Texture *texture = cmd->data.draw.texture;
   551     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   552 
   553     if (texture->modMode & SDL_TEXTUREMODULATE_COLOR) {
   554         SDL_SetSurfaceRLE(surface, 0);
   555         SDL_SetSurfaceColorMod(surface, r, g, b);
   556     }
   557 
   558     if ((texture->modMode & SDL_TEXTUREMODULATE_ALPHA) && surface->format->Amask) {
   559         SDL_SetSurfaceRLE(surface, 0);
   560         SDL_SetSurfaceAlphaMod(surface, a);
   561     }
   562 
   563     if ((blend == SDL_BLENDMODE_ADD) || (blend == SDL_BLENDMODE_MOD)) {
   564         SDL_SetSurfaceRLE(surface, 0);
   565     }
   566     SDL_SetSurfaceBlendMode(surface, blend);
   567 }
   568 
   569 static int
   570 SW_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
   571 {
   572     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   573     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   574     const SDL_Rect *viewport = NULL;
   575     const SDL_Rect *cliprect = NULL;
   576 
   577     if (!surface) {
   578         return -1;
   579     }
   580 
   581     while (cmd) {
   582         switch (cmd->command) {
   583             case SDL_RENDERCMD_SETDRAWCOLOR: {
   584                 break;  /* Not used in this backend. */
   585             }
   586 
   587             case SDL_RENDERCMD_SETVIEWPORT: {
   588                 viewport = &cmd->data.viewport.rect;
   589                 SDL_SetClipRect(data->surface, viewport);
   590                 break;
   591             }
   592 
   593             case SDL_RENDERCMD_SETCLIPRECT: {
   594                 SDL_assert(viewport != NULL);
   595                 cliprect = cmd->data.cliprect.enabled ? &cmd->data.cliprect.rect : NULL;
   596                 if (cliprect) {
   597                     SDL_Rect clip_rect = { cliprect->x + viewport->x, cliprect->y + viewport->y, cliprect->w, cliprect->h };
   598                     SDL_IntersectRect(viewport, &clip_rect, &clip_rect);
   599                     SDL_SetClipRect(surface, &clip_rect);
   600                 } else {
   601                     SDL_SetClipRect(surface, viewport);
   602                 }
   603                 break;
   604             }
   605 
   606             case SDL_RENDERCMD_CLEAR: {
   607                 const Uint8 r = cmd->data.color.r;
   608                 const Uint8 g = cmd->data.color.g;
   609                 const Uint8 b = cmd->data.color.b;
   610                 const Uint8 a = cmd->data.color.a;
   611                 const SDL_Rect clip_rect = surface->clip_rect;
   612                 /* By definition the clear ignores the clip rect */
   613                 SDL_SetClipRect(surface, NULL);
   614                 SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, r, g, b, a));
   615                 SDL_SetClipRect(surface, &clip_rect);
   616                 break;
   617             }
   618 
   619             case SDL_RENDERCMD_DRAW_POINTS: {
   620                 const Uint8 r = cmd->data.draw.r;
   621                 const Uint8 g = cmd->data.draw.g;
   622                 const Uint8 b = cmd->data.draw.b;
   623                 const Uint8 a = cmd->data.draw.a;
   624                 const size_t count = cmd->data.draw.count;
   625                 const SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first);
   626                 const SDL_BlendMode blend = cmd->data.draw.blend;
   627                 if (blend == SDL_BLENDMODE_NONE) {
   628                     SDL_DrawPoints(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
   629                 } else {
   630                     SDL_BlendPoints(surface, verts, count, blend, r, g, b, a);
   631                 }
   632                 break;
   633             }
   634 
   635             case SDL_RENDERCMD_DRAW_LINES: {
   636                 const Uint8 r = cmd->data.draw.r;
   637                 const Uint8 g = cmd->data.draw.g;
   638                 const Uint8 b = cmd->data.draw.b;
   639                 const Uint8 a = cmd->data.draw.a;
   640                 const size_t count = cmd->data.draw.count;
   641                 const SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first);
   642                 const SDL_BlendMode blend = cmd->data.draw.blend;
   643                 if (blend == SDL_BLENDMODE_NONE) {
   644                     SDL_DrawLines(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
   645                 } else {
   646                     SDL_BlendLines(surface, verts, count, blend, r, g, b, a);
   647                 }
   648                 break;
   649             }
   650 
   651             case SDL_RENDERCMD_FILL_RECTS: {
   652                 const Uint8 r = cmd->data.draw.r;
   653                 const Uint8 g = cmd->data.draw.g;
   654                 const Uint8 b = cmd->data.draw.b;
   655                 const Uint8 a = cmd->data.draw.a;
   656                 const size_t count = cmd->data.draw.count;
   657                 const SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first);
   658                 const SDL_BlendMode blend = cmd->data.draw.blend;
   659                 if (blend == SDL_BLENDMODE_NONE) {
   660                     SDL_FillRects(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
   661                 } else {
   662                     SDL_BlendFillRects(surface, verts, count, blend, r, g, b, a);
   663                 }
   664                 break;
   665             }
   666 
   667             case SDL_RENDERCMD_COPY: {
   668                 SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first);
   669                 const SDL_Rect *srcrect = verts;
   670                 SDL_Rect *dstrect = verts + 1;
   671                 SDL_Texture *texture = cmd->data.draw.texture;
   672                 SDL_Surface *src = (SDL_Surface *) texture->driverdata;
   673 
   674                 PrepTextureForCopy(cmd);
   675 
   676                 if ( srcrect->w == dstrect->w && srcrect->h == dstrect->h ) {
   677                     SDL_BlitSurface(src, srcrect, surface, dstrect);
   678                 } else {
   679                     /* If scaling is ever done, permanently disable RLE (which doesn't support scaling)
   680                      * to avoid potentially frequent RLE encoding/decoding.
   681                      */
   682                     SDL_SetSurfaceRLE(surface, 0);
   683                     SDL_BlitScaled(src, srcrect, surface, dstrect);
   684                 }
   685                 break;
   686             }
   687 
   688             case SDL_RENDERCMD_COPY_EX: {
   689                 const CopyExData *copydata = (CopyExData *) (((Uint8 *) vertices) + cmd->data.draw.first);
   690                 PrepTextureForCopy(cmd);
   691                 SW_RenderCopyEx(renderer, surface, cmd->data.draw.texture, &copydata->srcrect,
   692                                 &copydata->dstrect, copydata->angle, &copydata->center, copydata->flip);
   693                 break;
   694             }
   695 
   696             case SDL_RENDERCMD_NO_OP:
   697                 break;
   698         }
   699 
   700         cmd = cmd->next;
   701     }
   702 
   703     return 0;
   704 }
   705 
   706 static int
   707 SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   708                     Uint32 format, void * pixels, int pitch)
   709 {
   710     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   711     Uint32 src_format;
   712     void *src_pixels;
   713 
   714     if (!surface) {
   715         return -1;
   716     }
   717 
   718     /* NOTE: The rect is already adjusted according to the viewport by
   719      * SDL_RenderReadPixels.
   720      */
   721 
   722     if (rect->x < 0 || rect->x+rect->w > surface->w ||
   723         rect->y < 0 || rect->y+rect->h > surface->h) {
   724         return SDL_SetError("Tried to read outside of surface bounds");
   725     }
   726 
   727     src_format = surface->format->format;
   728     src_pixels = (void*)((Uint8 *) surface->pixels +
   729                     rect->y * surface->pitch +
   730                     rect->x * surface->format->BytesPerPixel);
   731 
   732     return SDL_ConvertPixels(rect->w, rect->h,
   733                              src_format, src_pixels, surface->pitch,
   734                              format, pixels, pitch);
   735 }
   736 
   737 static void
   738 SW_RenderPresent(SDL_Renderer * renderer)
   739 {
   740     SDL_Window *window = renderer->window;
   741 
   742     if (window) {
   743         SDL_UpdateWindowSurface(window);
   744     }
   745 }
   746 
   747 static void
   748 SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   749 {
   750     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   751 
   752     SDL_FreeSurface(surface);
   753 }
   754 
   755 static void
   756 SW_DestroyRenderer(SDL_Renderer * renderer)
   757 {
   758     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   759 
   760     SDL_free(data);
   761     SDL_free(renderer);
   762 }
   763 
   764 SDL_Renderer *
   765 SW_CreateRendererForSurface(SDL_Surface * surface)
   766 {
   767     SDL_Renderer *renderer;
   768     SW_RenderData *data;
   769 
   770     if (!surface) {
   771         SDL_SetError("Can't create renderer for NULL surface");
   772         return NULL;
   773     }
   774 
   775     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   776     if (!renderer) {
   777         SDL_OutOfMemory();
   778         return NULL;
   779     }
   780 
   781     data = (SW_RenderData *) SDL_calloc(1, sizeof(*data));
   782     if (!data) {
   783         SW_DestroyRenderer(renderer);
   784         SDL_OutOfMemory();
   785         return NULL;
   786     }
   787     data->surface = surface;
   788     data->window = surface;
   789 
   790     renderer->WindowEvent = SW_WindowEvent;
   791     renderer->GetOutputSize = SW_GetOutputSize;
   792     renderer->CreateTexture = SW_CreateTexture;
   793     renderer->UpdateTexture = SW_UpdateTexture;
   794     renderer->LockTexture = SW_LockTexture;
   795     renderer->UnlockTexture = SW_UnlockTexture;
   796     renderer->SetRenderTarget = SW_SetRenderTarget;
   797     renderer->QueueSetViewport = SW_QueueSetViewport;
   798     renderer->QueueSetDrawColor = SW_QueueSetViewport;  /* SetViewport and SetDrawColor are (currently) no-ops. */
   799     renderer->QueueDrawPoints = SW_QueueDrawPoints;
   800     renderer->QueueDrawLines = SW_QueueDrawPoints;  /* lines and points queue vertices the same way. */
   801     renderer->QueueFillRects = SW_QueueFillRects;
   802     renderer->QueueCopy = SW_QueueCopy;
   803     renderer->QueueCopyEx = SW_QueueCopyEx;
   804     renderer->RunCommandQueue = SW_RunCommandQueue;
   805     renderer->RenderReadPixels = SW_RenderReadPixels;
   806     renderer->RenderPresent = SW_RenderPresent;
   807     renderer->DestroyTexture = SW_DestroyTexture;
   808     renderer->DestroyRenderer = SW_DestroyRenderer;
   809     renderer->info = SW_RenderDriver.info;
   810     renderer->driverdata = data;
   811 
   812     SW_ActivateRenderer(renderer);
   813 
   814     return renderer;
   815 }
   816 
   817 SDL_Renderer *
   818 SW_CreateRenderer(SDL_Window * window, Uint32 flags)
   819 {
   820     SDL_Surface *surface;
   821 
   822     surface = SDL_GetWindowSurface(window);
   823     if (!surface) {
   824         return NULL;
   825     }
   826     return SW_CreateRendererForSurface(surface);
   827 }
   828 
   829 SDL_RenderDriver SW_RenderDriver = {
   830     SW_CreateRenderer,
   831     {
   832      "software",
   833      SDL_RENDERER_SOFTWARE | SDL_RENDERER_TARGETTEXTURE,
   834      8,
   835      {
   836       SDL_PIXELFORMAT_ARGB8888,
   837       SDL_PIXELFORMAT_ABGR8888,
   838       SDL_PIXELFORMAT_RGBA8888,
   839       SDL_PIXELFORMAT_BGRA8888,
   840       SDL_PIXELFORMAT_RGB888,
   841       SDL_PIXELFORMAT_BGR888,
   842       SDL_PIXELFORMAT_RGB565,
   843       SDL_PIXELFORMAT_RGB555
   844      },
   845      0,
   846      0}
   847 };
   848 
   849 #endif /* !SDL_RENDER_DISABLED */
   850 
   851 /* vi: set ts=4 sw=4 expandtab: */