src/render/software/SDL_render_sw.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 31 Oct 2018 15:03:41 -0400
changeset 12381 dc9108cd4340
parent 12372 bced4041fcc0
parent 12288 005e1d1bcf05
child 12383 f6430feceeda
permissions -rw-r--r--
Merge SDL-ryan-batching-renderer branch to default.
     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     /* 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 
   558     if (texture->modMode & SDL_TEXTUREMODULATE_COLOR) {
   559         SDL_SetSurfaceRLE(surface, 0);
   560         SDL_SetSurfaceColorMod(surface, r, g, b);
   561     }
   562 
   563     if ((texture->modMode & SDL_TEXTUREMODULATE_ALPHA) && surface->format->Amask) {
   564         SDL_SetSurfaceRLE(surface, 0);
   565         SDL_SetSurfaceAlphaMod(surface, a);
   566     }
   567 
   568     if ((blend == SDL_BLENDMODE_ADD) || (blend == SDL_BLENDMODE_MOD)) {
   569         SDL_SetSurfaceRLE(surface, 0);
   570     }
   571     SDL_SetSurfaceBlendMode(surface, blend);
   572 }
   573 
   574 static int
   575 SW_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
   576 {
   577     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   578     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   579     const SDL_Rect *viewport = NULL;
   580     const SDL_Rect *cliprect = NULL;
   581 
   582     if (!surface) {
   583         return -1;
   584     }
   585 
   586     while (cmd) {
   587         switch (cmd->command) {
   588             case SDL_RENDERCMD_SETDRAWCOLOR: {
   589                 break;  /* Not used in this backend. */
   590             }
   591 
   592             case SDL_RENDERCMD_SETVIEWPORT: {
   593                 viewport = &cmd->data.viewport.rect;
   594                 SDL_SetClipRect(data->surface, viewport);
   595                 break;
   596             }
   597 
   598             case SDL_RENDERCMD_SETCLIPRECT: {
   599                 SDL_assert(viewport != NULL);
   600                 cliprect = cmd->data.cliprect.enabled ? &cmd->data.cliprect.rect : NULL;
   601                 if (cliprect) {
   602                     SDL_Rect clip_rect = { cliprect->x + viewport->x, cliprect->y + viewport->y, cliprect->w, cliprect->h };
   603                     SDL_IntersectRect(viewport, &clip_rect, &clip_rect);
   604                     SDL_SetClipRect(surface, &clip_rect);
   605                 } else {
   606                     SDL_SetClipRect(surface, viewport);
   607                 }
   608                 break;
   609             }
   610 
   611             case SDL_RENDERCMD_CLEAR: {
   612                 const Uint8 r = cmd->data.color.r;
   613                 const Uint8 g = cmd->data.color.g;
   614                 const Uint8 b = cmd->data.color.b;
   615                 const Uint8 a = cmd->data.color.a;
   616                 const SDL_Rect clip_rect = surface->clip_rect;
   617                 /* By definition the clear ignores the clip rect */
   618                 SDL_SetClipRect(surface, NULL);
   619                 SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, r, g, b, a));
   620                 SDL_SetClipRect(surface, &clip_rect);
   621                 break;
   622             }
   623 
   624             case SDL_RENDERCMD_DRAW_POINTS: {
   625                 const Uint8 r = cmd->data.draw.r;
   626                 const Uint8 g = cmd->data.draw.g;
   627                 const Uint8 b = cmd->data.draw.b;
   628                 const Uint8 a = cmd->data.draw.a;
   629                 const size_t count = cmd->data.draw.count;
   630                 const SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first);
   631                 const SDL_BlendMode blend = cmd->data.draw.blend;
   632                 if (blend == SDL_BLENDMODE_NONE) {
   633                     SDL_DrawPoints(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
   634                 } else {
   635                     SDL_BlendPoints(surface, verts, count, blend, r, g, b, a);
   636                 }
   637                 break;
   638             }
   639 
   640             case SDL_RENDERCMD_DRAW_LINES: {
   641                 const Uint8 r = cmd->data.draw.r;
   642                 const Uint8 g = cmd->data.draw.g;
   643                 const Uint8 b = cmd->data.draw.b;
   644                 const Uint8 a = cmd->data.draw.a;
   645                 const size_t count = cmd->data.draw.count;
   646                 const SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first);
   647                 const SDL_BlendMode blend = cmd->data.draw.blend;
   648                 if (blend == SDL_BLENDMODE_NONE) {
   649                     SDL_DrawLines(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
   650                 } else {
   651                     SDL_BlendLines(surface, verts, count, blend, r, g, b, a);
   652                 }
   653                 break;
   654             }
   655 
   656             case SDL_RENDERCMD_FILL_RECTS: {
   657                 const Uint8 r = cmd->data.draw.r;
   658                 const Uint8 g = cmd->data.draw.g;
   659                 const Uint8 b = cmd->data.draw.b;
   660                 const Uint8 a = cmd->data.draw.a;
   661                 const size_t count = cmd->data.draw.count;
   662                 const SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first);
   663                 const SDL_BlendMode blend = cmd->data.draw.blend;
   664                 if (blend == SDL_BLENDMODE_NONE) {
   665                     SDL_FillRects(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
   666                 } else {
   667                     SDL_BlendFillRects(surface, verts, count, blend, r, g, b, a);
   668                 }
   669                 break;
   670             }
   671 
   672             case SDL_RENDERCMD_COPY: {
   673                 SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first);
   674                 const SDL_Rect *srcrect = verts;
   675                 SDL_Rect *dstrect = verts + 1;
   676                 SDL_Texture *texture = cmd->data.draw.texture;
   677                 SDL_Surface *src = (SDL_Surface *) texture->driverdata;
   678 
   679                 PrepTextureForCopy(cmd);
   680 
   681                 if ( srcrect->w == dstrect->w && srcrect->h == dstrect->h ) {
   682                     SDL_BlitSurface(src, srcrect, surface, dstrect);
   683                 } else {
   684                     /* If scaling is ever done, permanently disable RLE (which doesn't support scaling)
   685                      * to avoid potentially frequent RLE encoding/decoding.
   686                      */
   687                     SDL_SetSurfaceRLE(surface, 0);
   688                     SDL_BlitScaled(src, srcrect, surface, dstrect);
   689                 }
   690                 break;
   691             }
   692 
   693             case SDL_RENDERCMD_COPY_EX: {
   694                 const CopyExData *copydata = (CopyExData *) (((Uint8 *) vertices) + cmd->data.draw.first);
   695                 PrepTextureForCopy(cmd);
   696                 SW_RenderCopyEx(renderer, surface, cmd->data.draw.texture, &copydata->srcrect,
   697                                 &copydata->dstrect, copydata->angle, &copydata->center, copydata->flip);
   698                 break;
   699             }
   700 
   701             case SDL_RENDERCMD_NO_OP:
   702                 break;
   703         }
   704 
   705         cmd = cmd->next;
   706     }
   707 
   708     return 0;
   709 }
   710 
   711 static int
   712 SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   713                     Uint32 format, void * pixels, int pitch)
   714 {
   715     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   716     Uint32 src_format;
   717     void *src_pixels;
   718 
   719     if (!surface) {
   720         return -1;
   721     }
   722 
   723     /* NOTE: The rect is already adjusted according to the viewport by
   724      * SDL_RenderReadPixels.
   725      */
   726 
   727     if (rect->x < 0 || rect->x+rect->w > surface->w ||
   728         rect->y < 0 || rect->y+rect->h > surface->h) {
   729         return SDL_SetError("Tried to read outside of surface bounds");
   730     }
   731 
   732     src_format = surface->format->format;
   733     src_pixels = (void*)((Uint8 *) surface->pixels +
   734                     rect->y * surface->pitch +
   735                     rect->x * surface->format->BytesPerPixel);
   736 
   737     return SDL_ConvertPixels(rect->w, rect->h,
   738                              src_format, src_pixels, surface->pitch,
   739                              format, pixels, pitch);
   740 }
   741 
   742 static void
   743 SW_RenderPresent(SDL_Renderer * renderer)
   744 {
   745     SDL_Window *window = renderer->window;
   746 
   747     if (window) {
   748         SDL_UpdateWindowSurface(window);
   749     }
   750 }
   751 
   752 static void
   753 SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   754 {
   755     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   756 
   757     SDL_FreeSurface(surface);
   758 }
   759 
   760 static void
   761 SW_DestroyRenderer(SDL_Renderer * renderer)
   762 {
   763     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   764 
   765     SDL_free(data);
   766     SDL_free(renderer);
   767 }
   768 
   769 SDL_Renderer *
   770 SW_CreateRendererForSurface(SDL_Surface * surface)
   771 {
   772     SDL_Renderer *renderer;
   773     SW_RenderData *data;
   774 
   775     if (!surface) {
   776         SDL_SetError("Can't create renderer for NULL surface");
   777         return NULL;
   778     }
   779 
   780     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   781     if (!renderer) {
   782         SDL_OutOfMemory();
   783         return NULL;
   784     }
   785 
   786     data = (SW_RenderData *) SDL_calloc(1, sizeof(*data));
   787     if (!data) {
   788         SW_DestroyRenderer(renderer);
   789         SDL_OutOfMemory();
   790         return NULL;
   791     }
   792     data->surface = surface;
   793     data->window = surface;
   794 
   795     renderer->WindowEvent = SW_WindowEvent;
   796     renderer->GetOutputSize = SW_GetOutputSize;
   797     renderer->CreateTexture = SW_CreateTexture;
   798     renderer->UpdateTexture = SW_UpdateTexture;
   799     renderer->LockTexture = SW_LockTexture;
   800     renderer->UnlockTexture = SW_UnlockTexture;
   801     renderer->SetRenderTarget = SW_SetRenderTarget;
   802     renderer->QueueSetViewport = SW_QueueSetViewport;
   803     renderer->QueueSetDrawColor = SW_QueueSetViewport;  /* SetViewport and SetDrawColor are (currently) no-ops. */
   804     renderer->QueueDrawPoints = SW_QueueDrawPoints;
   805     renderer->QueueDrawLines = SW_QueueDrawPoints;  /* lines and points queue vertices the same way. */
   806     renderer->QueueFillRects = SW_QueueFillRects;
   807     renderer->QueueCopy = SW_QueueCopy;
   808     renderer->QueueCopyEx = SW_QueueCopyEx;
   809     renderer->RunCommandQueue = SW_RunCommandQueue;
   810     renderer->RenderReadPixels = SW_RenderReadPixels;
   811     renderer->RenderPresent = SW_RenderPresent;
   812     renderer->DestroyTexture = SW_DestroyTexture;
   813     renderer->DestroyRenderer = SW_DestroyRenderer;
   814     renderer->info = SW_RenderDriver.info;
   815     renderer->driverdata = data;
   816 
   817     SW_ActivateRenderer(renderer);
   818 
   819     return renderer;
   820 }
   821 
   822 SDL_Renderer *
   823 SW_CreateRenderer(SDL_Window * window, Uint32 flags)
   824 {
   825     SDL_Surface *surface;
   826 
   827     surface = SDL_GetWindowSurface(window);
   828     if (!surface) {
   829         return NULL;
   830     }
   831     return SW_CreateRendererForSurface(surface);
   832 }
   833 
   834 SDL_RenderDriver SW_RenderDriver = {
   835     SW_CreateRenderer,
   836     {
   837      "software",
   838      SDL_RENDERER_SOFTWARE | SDL_RENDERER_TARGETTEXTURE,
   839      8,
   840      {
   841       SDL_PIXELFORMAT_ARGB8888,
   842       SDL_PIXELFORMAT_ABGR8888,
   843       SDL_PIXELFORMAT_RGBA8888,
   844       SDL_PIXELFORMAT_BGRA8888,
   845       SDL_PIXELFORMAT_RGB888,
   846       SDL_PIXELFORMAT_BGR888,
   847       SDL_PIXELFORMAT_RGB565,
   848       SDL_PIXELFORMAT_RGB555
   849      },
   850      0,
   851      0}
   852 };
   853 
   854 #endif /* !SDL_RENDER_DISABLED */
   855 
   856 /* vi: set ts=4 sw=4 expandtab: */