sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_render_sw.c (31761B)


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