src/render/software/SDL_render_sw.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 15 Nov 2016 01:12:27 -0800
changeset 10612 6b2307dbec54
parent 10489 1e1ce9f6d215
child 10737 3406a0f8b041
permissions -rw-r--r--
Fixed bug 3359 - Software renderer does incorrect blending with SDL_RenderCopyEx

Simon Hug

The software renderer produces incorrect results when blending textures at an angle with certain blend modes. It seems that there were some edge cases that weren't considered when the SW_RenderCopyEx function was last changed. Or another bug possibly covered up the problem. (More on that in another bug report.)

Most of the issues come from the fact that the rotating function sets a black colorkey. This is problematic because black is most likely appearing in the surface and the final blit will ignore these pixels. Unless a colorkey is already set (the software renderer currently never sets one), it's very hard to find a free color. Of course it could scan over the whole image until one is found, but that seems inefficient.

The following blend modes have issues when drawn at an angle.

NONE: The black pixels get ignored, making them essentially transparent. This breaks the 'dstRGBA = srcRGBA' definition of the NONE blend mode.

MOD: Again, the black pixels get ignored. This also breaks the 'dstRGB = dstRGB * srcRGB' definition of the MOD blend mode, where black pixels would make the destination black as well. A white colorkey will work though, with some preparations.

BLEND: There are some issues when blending a texture with a translucent RGBA target texture. I - uh - forgot what the problem here exactly is.

This patch fixes the issues mentioned above. It mainly changes the code so it tries to do things without the colorkey and removes the automatic format conversion part from the SDLgfx_rotateSurface function. Getting the format right is something the caller has to do now and the required code has been added to the SW_RenderCopyEx function.

There's a small change to the SW_CreateTexture function. RLE encoding a surface with an alpha mask can be a lossy process. Depending on how the user uses the RGBA channels, this may be undesired. The change that surfaces with an alpha mask don't get encoded makes the software renderer consistent with the other renderers.

The SW_RenderCopyEx function now does these steps: Lock the source surface if necessary. Create a clone of the source by using the pixel buffer directly. Check the format and set a flag if a conversion is necessary. Check if scaling or cropping is necessary and set the flag for that as well. Check if color and alpha modulation has to be done before the rotate. Check if the source is an opaque surface. If not, it creates a mask surface that is necessary for the NONE blend mode. If any of the flags were set, a new surface is created and the source will be converted, scaled, cropped, and modulated. The rest of the function stays somewhat the same. The mask also needs to be rotated of course and then there is the NONE blend mode...

It's surprisingly hard to get the pixel from a rotated surface to the destination buffer without affecting the pixel outside the rotated area. I found a way to do this with three blits which is pretty hard on the performance. Perhaps someone has an idea how to do this faster?

As mentioned above, the SDLgfx_rotateSurface now only takes 8-bit paletted or 32-bit with alpha mask surfaces. It additionally sets the new surfaces up for the MOD blend mode.

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