src/render/software/SDL_render_sw.c
author Ozkan Sezer <sezeroz@gmail.com>
Thu, 01 Nov 2018 20:04:24 +0300
changeset 12383 f6430feceeda
parent 12381 dc9108cd4340
child 12411 148df74b141c
permissions -rw-r--r--
fix build using Watcom :

./src/render/SDL_render.c(2168): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2168): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2175): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2175): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2322): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2322): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2322): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2322): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2329): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2329): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2329): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2329): Error! E1054: Expression must be constant

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