src/render/SDL_render.c
author Sylvain Becker <sylvain.becker@gmail.com>
Tue, 17 Mar 2020 15:47:30 +0100
changeset 13644 b1ebbd8cafef
parent 13643 b68bc68d5cce
child 13696 ea20a7434b98
permissions -rw-r--r--
Fix warnining implicit declaration of SDL_DetectPalette (Thanks meyraud705)
slouken@5154
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@13422
     3
  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
slouken@5154
     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@5154
     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@5154
    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@5154
    20
*/
icculus@8093
    21
#include "../SDL_internal.h"
slouken@5154
    22
slouken@5154
    23
/* The SDL 2D rendering system */
slouken@5154
    24
slouken@7760
    25
#include "SDL_assert.h"
slouken@5192
    26
#include "SDL_hints.h"
slouken@5221
    27
#include "SDL_log.h"
slouken@5154
    28
#include "SDL_render.h"
slouken@5154
    29
#include "SDL_sysrender.h"
slouken@5195
    30
#include "software/SDL_render_sw_c.h"
sylvain@13644
    31
#include "../video/SDL_pixels_c.h"
slouken@5154
    32
sylvain@12910
    33
#if defined(__ANDROID__)
sylvain@12910
    34
#  include "../core/android/SDL_android.h"
sylvain@12910
    35
#endif
slouken@5154
    36
slouken@5528
    37
#define SDL_WINDOWRENDERDATA    "_SDL_WindowRenderData"
slouken@5528
    38
slouken@5154
    39
#define CHECK_RENDERER_MAGIC(renderer, retval) \
slouken@11294
    40
    SDL_assert(renderer && renderer->magic == &renderer_magic); \
slouken@5154
    41
    if (!renderer || renderer->magic != &renderer_magic) { \
slouken@5154
    42
        SDL_SetError("Invalid renderer"); \
slouken@5154
    43
        return retval; \
slouken@5154
    44
    }
slouken@5154
    45
slouken@5154
    46
#define CHECK_TEXTURE_MAGIC(texture, retval) \
slouken@11294
    47
    SDL_assert(texture && texture->magic == &texture_magic); \
slouken@5154
    48
    if (!texture || texture->magic != &texture_magic) { \
slouken@5154
    49
        SDL_SetError("Invalid texture"); \
slouken@5154
    50
        return retval; \
slouken@5154
    51
    }
slouken@5154
    52
slouken@11282
    53
/* Predefined blend modes */
slouken@11282
    54
#define SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation, \
slouken@11282
    55
                              srcAlphaFactor, dstAlphaFactor, alphaOperation) \
slouken@11282
    56
    (SDL_BlendMode)(((Uint32)colorOperation << 0) | \
slouken@11282
    57
                    ((Uint32)srcColorFactor << 4) | \
slouken@11282
    58
                    ((Uint32)dstColorFactor << 8) | \
slouken@11282
    59
                    ((Uint32)alphaOperation << 16) | \
slouken@11282
    60
                    ((Uint32)srcAlphaFactor << 20) | \
slouken@11282
    61
                    ((Uint32)dstAlphaFactor << 24))
slouken@11282
    62
slouken@11282
    63
#define SDL_BLENDMODE_NONE_FULL \
slouken@11282
    64
    SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD, \
slouken@11282
    65
                          SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD)
slouken@11282
    66
slouken@11282
    67
#define SDL_BLENDMODE_BLEND_FULL \
slouken@11282
    68
    SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \
slouken@11282
    69
                          SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD)
slouken@11282
    70
slouken@11282
    71
#define SDL_BLENDMODE_ADD_FULL \
slouken@11282
    72
    SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, \
slouken@11282
    73
                          SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD)
slouken@11282
    74
slouken@11282
    75
#define SDL_BLENDMODE_MOD_FULL \
slouken@11282
    76
    SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_COLOR, SDL_BLENDOPERATION_ADD, \
slouken@11282
    77
                          SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD)
slouken@5154
    78
slouken@13418
    79
#define SDL_BLENDMODE_MUL_FULL \
slouken@13418
    80
    SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_DST_COLOR, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \
slouken@13418
    81
                          SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD)
slouken@13418
    82
slouken@7109
    83
#if !SDL_RENDER_DISABLED
slouken@5154
    84
static const SDL_RenderDriver *render_drivers[] = {
slouken@8599
    85
#if SDL_VIDEO_RENDER_D3D
slouken@8599
    86
    &D3D_RenderDriver,
slouken@8599
    87
#endif
slouken@8591
    88
#if SDL_VIDEO_RENDER_D3D11
slouken@8591
    89
    &D3D11_RenderDriver,
slouken@8591
    90
#endif
icculus@12482
    91
#if SDL_VIDEO_RENDER_METAL
icculus@12482
    92
    &METAL_RenderDriver,
icculus@12482
    93
#endif
slouken@5154
    94
#if SDL_VIDEO_RENDER_OGL
slouken@5154
    95
    &GL_RenderDriver,
slouken@5154
    96
#endif
slouken@5209
    97
#if SDL_VIDEO_RENDER_OGL_ES2
slouken@5209
    98
    &GLES2_RenderDriver,
slouken@5209
    99
#endif
slouken@5154
   100
#if SDL_VIDEO_RENDER_OGL_ES
slouken@5201
   101
    &GLES_RenderDriver,
slouken@5201
   102
#endif
slouken@5199
   103
#if SDL_VIDEO_RENDER_DIRECTFB
slouken@5199
   104
    &DirectFB_RenderDriver,
slouken@5199
   105
#endif
kimonline@7009
   106
#if SDL_VIDEO_RENDER_PSP
kimonline@7009
   107
    &PSP_RenderDriver,
kimonline@7009
   108
#endif
sylvain@13433
   109
#if SDL_VIDEO_RENDER_SW
slouken@5154
   110
    &SW_RenderDriver
sylvain@13433
   111
#endif
slouken@7109
   112
};
slouken@5226
   113
#endif /* !SDL_RENDER_DISABLED */
slouken@7109
   114
slouken@5154
   115
static char renderer_magic;
slouken@5154
   116
static char texture_magic;
slouken@5154
   117
icculus@12216
   118
static SDL_INLINE void
icculus@12216
   119
DebugLogRenderCommands(const SDL_RenderCommand *cmd)
icculus@12216
   120
{
icculus@12216
   121
#if 0
icculus@12216
   122
    unsigned int i = 1;
icculus@12216
   123
    SDL_Log("Render commands to flush:");
icculus@12216
   124
    while (cmd) {
icculus@12216
   125
        switch (cmd->command) {
icculus@12216
   126
            case SDL_RENDERCMD_NO_OP:
icculus@12216
   127
                SDL_Log(" %u. no-op", i++);
icculus@12216
   128
                break;
icculus@12216
   129
icculus@12216
   130
            case SDL_RENDERCMD_SETVIEWPORT:
icculus@12216
   131
                SDL_Log(" %u. set viewport (first=%u, rect={(%d, %d), %dx%d})", i++,
icculus@12216
   132
                        (unsigned int) cmd->data.viewport.first,
icculus@12216
   133
                        cmd->data.viewport.rect.x, cmd->data.viewport.rect.y,
icculus@12216
   134
                        cmd->data.viewport.rect.w, cmd->data.viewport.rect.h);
icculus@12216
   135
                break;
icculus@12216
   136
icculus@12216
   137
            case SDL_RENDERCMD_SETCLIPRECT:
icculus@12216
   138
                SDL_Log(" %u. set cliprect (enabled=%s, rect={(%d, %d), %dx%d})", i++,
icculus@12216
   139
                        cmd->data.cliprect.enabled ? "true" : "false",
icculus@12216
   140
                        cmd->data.cliprect.rect.x, cmd->data.cliprect.rect.y,
icculus@12216
   141
                        cmd->data.cliprect.rect.w, cmd->data.cliprect.rect.h);
icculus@12216
   142
                break;
icculus@12216
   143
icculus@12216
   144
            case SDL_RENDERCMD_SETDRAWCOLOR:
icculus@12216
   145
                SDL_Log(" %u. set draw color (first=%u, r=%d, g=%d, b=%d, a=%d)", i++,
icculus@12216
   146
                        (unsigned int) cmd->data.color.first,
icculus@12216
   147
                        (int) cmd->data.color.r, (int) cmd->data.color.g,
icculus@12216
   148
                        (int) cmd->data.color.b, (int) cmd->data.color.a);
icculus@12216
   149
                break;
icculus@12216
   150
icculus@12216
   151
            case SDL_RENDERCMD_CLEAR:
icculus@12216
   152
                SDL_Log(" %u. clear (first=%u, r=%d, g=%d, b=%d, a=%d)", i++,
icculus@12216
   153
                        (unsigned int) cmd->data.color.first,
icculus@12216
   154
                        (int) cmd->data.color.r, (int) cmd->data.color.g,
icculus@12216
   155
                        (int) cmd->data.color.b, (int) cmd->data.color.a);
icculus@12216
   156
                break;
icculus@12216
   157
icculus@12216
   158
            case SDL_RENDERCMD_DRAW_POINTS:
icculus@12216
   159
                SDL_Log(" %u. draw points (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++,
icculus@12216
   160
                        (unsigned int) cmd->data.draw.first,
icculus@12216
   161
                        (unsigned int) cmd->data.draw.count,
icculus@12216
   162
                        (int) cmd->data.draw.r, (int) cmd->data.draw.g,
icculus@12216
   163
                        (int) cmd->data.draw.b, (int) cmd->data.draw.a,
icculus@12216
   164
                        (int) cmd->data.draw.blend);
icculus@12216
   165
                break;
icculus@12216
   166
icculus@12216
   167
            case SDL_RENDERCMD_DRAW_LINES:
icculus@12216
   168
                SDL_Log(" %u. draw lines (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++,
icculus@12216
   169
                        (unsigned int) cmd->data.draw.first,
icculus@12216
   170
                        (unsigned int) cmd->data.draw.count,
icculus@12216
   171
                        (int) cmd->data.draw.r, (int) cmd->data.draw.g,
icculus@12216
   172
                        (int) cmd->data.draw.b, (int) cmd->data.draw.a,
icculus@12216
   173
                        (int) cmd->data.draw.blend);
icculus@12216
   174
                break;
icculus@12216
   175
icculus@12216
   176
            case SDL_RENDERCMD_FILL_RECTS:
icculus@12216
   177
                SDL_Log(" %u. fill rects (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++,
icculus@12216
   178
                        (unsigned int) cmd->data.draw.first,
icculus@12216
   179
                        (unsigned int) cmd->data.draw.count,
icculus@12216
   180
                        (int) cmd->data.draw.r, (int) cmd->data.draw.g,
icculus@12216
   181
                        (int) cmd->data.draw.b, (int) cmd->data.draw.a,
icculus@12216
   182
                        (int) cmd->data.draw.blend);
icculus@12216
   183
                break;
icculus@12216
   184
icculus@12216
   185
            case SDL_RENDERCMD_COPY:
icculus@12216
   186
                SDL_Log(" %u. copy (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, tex=%p)", i++,
icculus@12216
   187
                        (unsigned int) cmd->data.draw.first,
icculus@12216
   188
                        (unsigned int) cmd->data.draw.count,
icculus@12216
   189
                        (int) cmd->data.draw.r, (int) cmd->data.draw.g,
icculus@12216
   190
                        (int) cmd->data.draw.b, (int) cmd->data.draw.a,
icculus@12216
   191
                        (int) cmd->data.draw.blend, cmd->data.draw.texture);
icculus@12216
   192
                break;
icculus@12216
   193
icculus@12216
   194
icculus@12216
   195
            case SDL_RENDERCMD_COPY_EX:
icculus@12216
   196
                SDL_Log(" %u. copyex (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, tex=%p)", i++,
icculus@12216
   197
                        (unsigned int) cmd->data.draw.first,
icculus@12216
   198
                        (unsigned int) cmd->data.draw.count,
icculus@12216
   199
                        (int) cmd->data.draw.r, (int) cmd->data.draw.g,
icculus@12216
   200
                        (int) cmd->data.draw.b, (int) cmd->data.draw.a,
icculus@12216
   201
                        (int) cmd->data.draw.blend, cmd->data.draw.texture);
icculus@12216
   202
                break;
icculus@12216
   203
        }
icculus@12216
   204
        cmd = cmd->next;
icculus@12216
   205
    }
icculus@12216
   206
#endif
icculus@12216
   207
}
icculus@12211
   208
icculus@12211
   209
static int
icculus@12211
   210
FlushRenderCommands(SDL_Renderer *renderer)
icculus@12211
   211
{
icculus@12211
   212
    int retval;
icculus@12214
   213
icculus@12211
   214
    SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
icculus@12211
   215
icculus@12211
   216
    if (renderer->render_commands == NULL) {  /* nothing to do! */
icculus@12211
   217
        SDL_assert(renderer->vertex_data_used == 0);
icculus@12211
   218
        return 0;
icculus@12211
   219
    }
icculus@12211
   220
icculus@12216
   221
    DebugLogRenderCommands(renderer->render_commands);
icculus@12216
   222
icculus@12211
   223
    retval = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used);
icculus@12211
   224
icculus@12211
   225
    /* Move the whole render command queue to the unused pool so we can reuse them next time. */
icculus@12211
   226
    if (renderer->render_commands_tail != NULL) {
icculus@12211
   227
        renderer->render_commands_tail->next = renderer->render_commands_pool;
icculus@12211
   228
        renderer->render_commands_pool = renderer->render_commands;
icculus@12211
   229
        renderer->render_commands_tail = NULL;
icculus@12211
   230
        renderer->render_commands = NULL;
icculus@12211
   231
    }
icculus@12211
   232
    renderer->vertex_data_used = 0;
icculus@12211
   233
    renderer->render_command_generation++;
icculus@12214
   234
    renderer->color_queued = SDL_FALSE;
icculus@12214
   235
    renderer->viewport_queued = SDL_FALSE;
icculus@12214
   236
    renderer->cliprect_queued = SDL_FALSE;
icculus@12211
   237
    return retval;
icculus@12211
   238
}
icculus@12211
   239
icculus@12211
   240
static int
icculus@12211
   241
FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture)
icculus@12211
   242
{
icculus@12211
   243
    SDL_Renderer *renderer = texture->renderer;
icculus@12211
   244
    if (texture->last_command_generation == renderer->render_command_generation) {
icculus@12211
   245
        /* the current command queue depends on this texture, flush the queue now before it changes */
icculus@12211
   246
        return FlushRenderCommands(renderer);
icculus@12211
   247
    }
icculus@12211
   248
    return 0;
icculus@12211
   249
}
icculus@12211
   250
icculus@12211
   251
static SDL_INLINE int
icculus@12211
   252
FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer)
icculus@12211
   253
{
icculus@12211
   254
    return renderer->batching ? 0 : FlushRenderCommands(renderer);
icculus@12211
   255
}
icculus@12211
   256
icculus@12290
   257
int
icculus@12290
   258
SDL_RenderFlush(SDL_Renderer * renderer)
icculus@12290
   259
{
icculus@12290
   260
    return FlushRenderCommands(renderer);
icculus@12290
   261
}
icculus@12290
   262
icculus@12211
   263
void *
icculus@12214
   264
SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, const size_t alignment, size_t *offset)
icculus@12211
   265
{
icculus@12214
   266
    const size_t needed = renderer->vertex_data_used + numbytes + alignment;
slime73@13019
   267
    size_t current_offset = renderer->vertex_data_used;
slime73@13019
   268
slime73@13019
   269
    size_t aligner = (alignment && ((current_offset & (alignment - 1)) != 0)) ? (alignment - (current_offset & (alignment - 1))) : 0;
slime73@13019
   270
    size_t aligned = current_offset + aligner;
slime73@13019
   271
slime73@13019
   272
    if (renderer->vertex_data_allocation < needed) {
slime73@13019
   273
        const size_t current_allocation = renderer->vertex_data ? renderer->vertex_data_allocation : 1024;
slime73@13019
   274
        size_t newsize = current_allocation * 2;
slime73@13019
   275
        void *ptr;
slime73@13019
   276
        while (newsize < needed) {
slime73@13019
   277
            newsize *= 2;
icculus@12214
   278
        }
slime73@13019
   279
        ptr = SDL_realloc(renderer->vertex_data, newsize);
icculus@12211
   280
        if (ptr == NULL) {
icculus@12211
   281
            SDL_OutOfMemory();
icculus@12211
   282
            return NULL;
icculus@12211
   283
        }
icculus@12211
   284
        renderer->vertex_data = ptr;
icculus@12211
   285
        renderer->vertex_data_allocation = newsize;
icculus@12211
   286
    }
icculus@12211
   287
icculus@12211
   288
    if (offset) {
icculus@12214
   289
        *offset = aligned;
icculus@12211
   290
    }
icculus@12211
   291
icculus@12214
   292
    renderer->vertex_data_used += aligner + numbytes;
icculus@12214
   293
slime73@13019
   294
    return ((Uint8 *) renderer->vertex_data) + aligned;
icculus@12211
   295
}
icculus@12211
   296
icculus@12211
   297
static SDL_RenderCommand *
icculus@12211
   298
AllocateRenderCommand(SDL_Renderer *renderer)
icculus@12211
   299
{
icculus@12211
   300
    SDL_RenderCommand *retval = NULL;
icculus@12211
   301
icculus@12211
   302
    /* !!! FIXME: are there threading limitations in SDL's render API? If not, we need to mutex this. */
icculus@12211
   303
    retval = renderer->render_commands_pool;
icculus@12211
   304
    if (retval != NULL) {
icculus@12211
   305
        renderer->render_commands_pool = retval->next;
icculus@12211
   306
        retval->next = NULL;
icculus@12211
   307
    } else {
icculus@12211
   308
        retval = SDL_calloc(1, sizeof (*retval));
icculus@12211
   309
        if (!retval) {
icculus@12211
   310
            SDL_OutOfMemory();
icculus@12211
   311
            return NULL;
icculus@12211
   312
        }
icculus@12211
   313
    }
icculus@12211
   314
icculus@12211
   315
    SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
icculus@12211
   316
    if (renderer->render_commands_tail != NULL) {
icculus@12211
   317
        renderer->render_commands_tail->next = retval;
icculus@12211
   318
    } else {
icculus@12211
   319
        renderer->render_commands = retval;
icculus@12211
   320
    }
icculus@12211
   321
    renderer->render_commands_tail = retval;
icculus@12211
   322
icculus@12211
   323
    return retval;
icculus@12211
   324
}
icculus@12211
   325
icculus@12211
   326
static int
icculus@12214
   327
QueueCmdSetViewport(SDL_Renderer *renderer)
icculus@12211
   328
{
icculus@12214
   329
    int retval = 0;
icculus@12214
   330
    if (!renderer->viewport_queued || (SDL_memcmp(&renderer->viewport, &renderer->last_queued_viewport, sizeof (SDL_Rect)) != 0)) {
icculus@12214
   331
        SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
icculus@12214
   332
        retval = -1;
icculus@12214
   333
        if (cmd != NULL) {
icculus@12214
   334
            cmd->command = SDL_RENDERCMD_SETVIEWPORT;
icculus@12214
   335
            cmd->data.viewport.first = 0;  /* render backend will fill this in. */
icculus@12214
   336
            SDL_memcpy(&cmd->data.viewport.rect, &renderer->viewport, sizeof (renderer->viewport));
icculus@12214
   337
            retval = renderer->QueueSetViewport(renderer, cmd);
icculus@12214
   338
            if (retval < 0) {
icculus@12214
   339
                cmd->command = SDL_RENDERCMD_NO_OP;
icculus@12214
   340
            } else {
icculus@12214
   341
                SDL_memcpy(&renderer->last_queued_viewport, &renderer->viewport, sizeof (SDL_Rect));
icculus@12214
   342
                renderer->viewport_queued = SDL_TRUE;
icculus@12214
   343
            }
icculus@12214
   344
        }
icculus@12211
   345
    }
icculus@12236
   346
    return retval;
icculus@12211
   347
}
icculus@12211
   348
icculus@12211
   349
static int
icculus@12214
   350
QueueCmdSetClipRect(SDL_Renderer *renderer)
icculus@12211
   351
{
icculus@12214
   352
    int retval = 0;
icculus@12214
   353
    if ((!renderer->cliprect_queued) ||
icculus@12214
   354
         (renderer->clipping_enabled != renderer->last_queued_cliprect_enabled) ||
icculus@12214
   355
         (SDL_memcmp(&renderer->clip_rect, &renderer->last_queued_cliprect, sizeof (SDL_Rect)) != 0)) {
icculus@12214
   356
        SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
icculus@12214
   357
        if (cmd == NULL) {
icculus@12214
   358
            retval = -1;
icculus@12214
   359
        } else {
icculus@12214
   360
            cmd->command = SDL_RENDERCMD_SETCLIPRECT;
icculus@12214
   361
            cmd->data.cliprect.enabled = renderer->clipping_enabled;
icculus@12214
   362
            SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (cmd->data.cliprect.rect));
icculus@12214
   363
            SDL_memcpy(&renderer->last_queued_cliprect, &renderer->clip_rect, sizeof (SDL_Rect));
icculus@12214
   364
            renderer->last_queued_cliprect_enabled = renderer->clipping_enabled;
icculus@12214
   365
            renderer->cliprect_queued = SDL_TRUE;
icculus@12214
   366
        }
icculus@12211
   367
    }
icculus@12236
   368
    return retval;
icculus@12214
   369
}
icculus@12214
   370
icculus@12214
   371
static int
icculus@12214
   372
QueueCmdSetDrawColor(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uint8 b, const Uint8 a)
icculus@12214
   373
{
icculus@12214
   374
    const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
icculus@12214
   375
    int retval = 0;
icculus@12214
   376
    
icculus@12214
   377
    if (!renderer->color_queued || (color != renderer->last_queued_color)) {
icculus@12214
   378
        SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
icculus@12214
   379
        retval = -1;
icculus@12214
   380
icculus@12214
   381
        if (cmd != NULL) {
icculus@12214
   382
            cmd->command = SDL_RENDERCMD_SETDRAWCOLOR;
icculus@12214
   383
            cmd->data.color.first = 0;  /* render backend will fill this in. */
icculus@12214
   384
            cmd->data.color.r = r;
icculus@12214
   385
            cmd->data.color.g = g;
icculus@12214
   386
            cmd->data.color.b = b;
icculus@12214
   387
            cmd->data.color.a = a;
icculus@12214
   388
            retval = renderer->QueueSetDrawColor(renderer, cmd);
icculus@12214
   389
            if (retval < 0) {
icculus@12214
   390
                cmd->command = SDL_RENDERCMD_NO_OP;
icculus@12214
   391
            } else {
icculus@12214
   392
                renderer->last_queued_color = color;
icculus@12214
   393
                renderer->color_queued = SDL_TRUE;
icculus@12214
   394
            }
icculus@12214
   395
        }
icculus@12214
   396
    }
icculus@12236
   397
    return retval;
icculus@12211
   398
}
icculus@12211
   399
icculus@12211
   400
static int
icculus@12211
   401
QueueCmdClear(SDL_Renderer *renderer)
icculus@12211
   402
{
icculus@12211
   403
    SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
icculus@12211
   404
    if (cmd == NULL) {
icculus@12211
   405
        return -1;
icculus@12211
   406
    }
icculus@12211
   407
icculus@12211
   408
    cmd->command = SDL_RENDERCMD_CLEAR;
icculus@12214
   409
    cmd->data.color.first = 0;
icculus@12211
   410
    cmd->data.color.r = renderer->r;
icculus@12211
   411
    cmd->data.color.g = renderer->g;
icculus@12211
   412
    cmd->data.color.b = renderer->b;
icculus@12211
   413
    cmd->data.color.a = renderer->a;
icculus@12236
   414
    return 0;
icculus@12211
   415
}
icculus@12211
   416
icculus@12214
   417
static int
icculus@12214
   418
PrepQueueCmdDraw(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uint8 b, const Uint8 a)
icculus@12214
   419
{
slime73@13023
   420
    int retval = QueueCmdSetDrawColor(renderer, r, g, b, a);
slime73@13023
   421
slime73@13023
   422
    /* Set the viewport and clip rect directly before draws, so the backends
slime73@13023
   423
     * don't have to worry about that state not being valid at draw time. */
slime73@13022
   424
    if (retval == 0 && !renderer->viewport_queued) {
icculus@12214
   425
        retval = QueueCmdSetViewport(renderer);
icculus@12214
   426
    }
slime73@13022
   427
    if (retval == 0 && !renderer->cliprect_queued) {
icculus@12214
   428
        retval = QueueCmdSetClipRect(renderer);
icculus@12214
   429
    }
icculus@12214
   430
    return retval;
icculus@12214
   431
}
icculus@12214
   432
icculus@12211
   433
static SDL_RenderCommand *
icculus@12211
   434
PrepQueueCmdDrawSolid(SDL_Renderer *renderer, const SDL_RenderCommandType cmdtype)
icculus@12211
   435
{
icculus@12262
   436
    /* !!! FIXME: drop this draw if viewport w or h is zero. */
icculus@12214
   437
    SDL_RenderCommand *cmd = NULL;
icculus@12214
   438
    if (PrepQueueCmdDraw(renderer, renderer->r, renderer->g, renderer->b, renderer->a) == 0) {
icculus@12214
   439
        cmd = AllocateRenderCommand(renderer);
icculus@12214
   440
        if (cmd != NULL) {
icculus@12214
   441
            cmd->command = cmdtype;
icculus@12214
   442
            cmd->data.draw.first = 0;  /* render backend will fill this in. */
icculus@12214
   443
            cmd->data.draw.count = 0;  /* render backend will fill this in. */
icculus@12214
   444
            cmd->data.draw.r = renderer->r;
icculus@12214
   445
            cmd->data.draw.g = renderer->g;
icculus@12214
   446
            cmd->data.draw.b = renderer->b;
icculus@12214
   447
            cmd->data.draw.a = renderer->a;
icculus@12214
   448
            cmd->data.draw.blend = renderer->blendMode;
icculus@12214
   449
            cmd->data.draw.texture = NULL;  /* no texture. */
icculus@12214
   450
        }
icculus@12211
   451
    }
icculus@12211
   452
    return cmd;
icculus@12211
   453
}
icculus@12211
   454
icculus@12211
   455
static int
icculus@12211
   456
QueueCmdDrawPoints(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
icculus@12211
   457
{
icculus@12211
   458
    SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_POINTS);
icculus@12211
   459
    int retval = -1;
icculus@12211
   460
    if (cmd != NULL) {
icculus@12211
   461
        retval = renderer->QueueDrawPoints(renderer, cmd, points, count);
icculus@12211
   462
        if (retval < 0) {
icculus@12211
   463
            cmd->command = SDL_RENDERCMD_NO_OP;
icculus@12211
   464
        }
icculus@12211
   465
    }
icculus@12236
   466
    return retval;
icculus@12211
   467
}
icculus@12211
   468
icculus@12211
   469
static int
icculus@12211
   470
QueueCmdDrawLines(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
icculus@12211
   471
{
icculus@12211
   472
    SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_LINES);
icculus@12211
   473
    int retval = -1;
icculus@12211
   474
    if (cmd != NULL) {
icculus@12211
   475
        retval = renderer->QueueDrawLines(renderer, cmd, points, count);
icculus@12211
   476
        if (retval < 0) {
icculus@12211
   477
            cmd->command = SDL_RENDERCMD_NO_OP;
icculus@12211
   478
        }
icculus@12211
   479
    }
icculus@12236
   480
    return retval;
icculus@12211
   481
}
icculus@12211
   482
icculus@12211
   483
static int
icculus@12211
   484
QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect * rects, const int count)
icculus@12211
   485
{
icculus@12211
   486
    SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_FILL_RECTS);
icculus@12211
   487
    int retval = -1;
icculus@12211
   488
    if (cmd != NULL) {
icculus@12211
   489
        retval = renderer->QueueFillRects(renderer, cmd, rects, count);
icculus@12211
   490
        if (retval < 0) {
icculus@12211
   491
            cmd->command = SDL_RENDERCMD_NO_OP;
icculus@12211
   492
        }
icculus@12211
   493
    }
icculus@12236
   494
    return retval;
icculus@12211
   495
}
icculus@12211
   496
icculus@12211
   497
static SDL_RenderCommand *
icculus@12211
   498
PrepQueueCmdDrawTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_RenderCommandType cmdtype)
icculus@12211
   499
{
icculus@12262
   500
    /* !!! FIXME: drop this draw if viewport w or h is zero. */
icculus@12214
   501
    SDL_RenderCommand *cmd = NULL;
icculus@12214
   502
    if (PrepQueueCmdDraw(renderer, texture->r, texture->g, texture->b, texture->a) == 0) {
icculus@12214
   503
        cmd = AllocateRenderCommand(renderer);
icculus@12214
   504
        if (cmd != NULL) {
icculus@12214
   505
            cmd->command = cmdtype;
icculus@12214
   506
            cmd->data.draw.first = 0;  /* render backend will fill this in. */
icculus@12214
   507
            cmd->data.draw.count = 0;  /* render backend will fill this in. */
icculus@12214
   508
            cmd->data.draw.r = texture->r;
icculus@12214
   509
            cmd->data.draw.g = texture->g;
icculus@12214
   510
            cmd->data.draw.b = texture->b;
icculus@12214
   511
            cmd->data.draw.a = texture->a;
icculus@12214
   512
            cmd->data.draw.blend = texture->blendMode;
icculus@12214
   513
            cmd->data.draw.texture = texture;
icculus@12214
   514
        }
icculus@12211
   515
    }
icculus@12211
   516
    return cmd;
icculus@12211
   517
}
icculus@12211
   518
icculus@12211
   519
static int
icculus@12211
   520
QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_FRect * dstrect)
icculus@12211
   521
{
icculus@12211
   522
    SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY);
icculus@12211
   523
    int retval = -1;
icculus@12211
   524
    if (cmd != NULL) {
icculus@12211
   525
        retval = renderer->QueueCopy(renderer, cmd, texture, srcrect, dstrect);
icculus@12211
   526
        if (retval < 0) {
icculus@12211
   527
            cmd->command = SDL_RENDERCMD_NO_OP;
icculus@12211
   528
        }
icculus@12211
   529
    }
icculus@12236
   530
    return retval;
icculus@12211
   531
}
icculus@12211
   532
icculus@12211
   533
static int
icculus@12211
   534
QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture * texture,
icculus@12211
   535
               const SDL_Rect * srcquad, const SDL_FRect * dstrect,
icculus@12211
   536
               const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
icculus@12211
   537
{
icculus@12211
   538
    SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY_EX);
icculus@12264
   539
    int retval = -1;
icculus@12211
   540
    SDL_assert(renderer->QueueCopyEx != NULL);  /* should have caught at higher level. */
icculus@12211
   541
    if (cmd != NULL) {
icculus@12211
   542
        retval = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip);
icculus@12211
   543
        if (retval < 0) {
icculus@12211
   544
            cmd->command = SDL_RENDERCMD_NO_OP;
icculus@12211
   545
        }
icculus@12211
   546
    }
icculus@12236
   547
    return retval;
icculus@12211
   548
}
icculus@12211
   549
icculus@12211
   550
slouken@6530
   551
static int UpdateLogicalSize(SDL_Renderer *renderer);
slouken@6530
   552
slouken@5154
   553
int
slouken@5154
   554
SDL_GetNumRenderDrivers(void)
slouken@5154
   555
{
slouken@7109
   556
#if !SDL_RENDER_DISABLED
slouken@5154
   557
    return SDL_arraysize(render_drivers);
slouken@7109
   558
#else
slouken@7109
   559
    return 0;
slouken@7109
   560
#endif
slouken@5154
   561
}
slouken@5154
   562
slouken@5154
   563
int
slouken@5154
   564
SDL_GetRenderDriverInfo(int index, SDL_RendererInfo * info)
slouken@5154
   565
{
slouken@7109
   566
#if !SDL_RENDER_DISABLED
slouken@5154
   567
    if (index < 0 || index >= SDL_GetNumRenderDrivers()) {
icculus@7037
   568
        return SDL_SetError("index must be in the range of 0 - %d",
icculus@7037
   569
                            SDL_GetNumRenderDrivers() - 1);
slouken@5154
   570
    }
slouken@5154
   571
    *info = render_drivers[index]->info;
slouken@5154
   572
    return 0;
slouken@7109
   573
#else
slouken@7109
   574
    return SDL_SetError("SDL not built with rendering support");
slouken@7109
   575
#endif
slouken@5154
   576
}
slouken@5154
   577
slouken@12021
   578
static void GetWindowViewportValues(SDL_Renderer *renderer, int *logical_w, int *logical_h, SDL_Rect *viewport, SDL_FPoint *scale)
slouken@12021
   579
{
slouken@12021
   580
    SDL_LockMutex(renderer->target_mutex);
slouken@12021
   581
    *logical_w = renderer->target ? renderer->logical_w_backup : renderer->logical_w;
slouken@12021
   582
    *logical_h = renderer->target ? renderer->logical_h_backup : renderer->logical_h;
slouken@12021
   583
    *viewport = renderer->target ? renderer->viewport_backup : renderer->viewport;
slouken@12021
   584
    *scale = renderer->target ? renderer->scale_backup : renderer->scale;
slouken@12021
   585
    SDL_UnlockMutex(renderer->target_mutex);
slouken@12021
   586
}
slouken@12021
   587
slouken@11272
   588
static int SDLCALL
slouken@5154
   589
SDL_RendererEventWatch(void *userdata, SDL_Event *event)
slouken@5154
   590
{
slouken@5154
   591
    SDL_Renderer *renderer = (SDL_Renderer *)userdata;
slouken@5154
   592
slouken@5297
   593
    if (event->type == SDL_WINDOWEVENT) {
slouken@5154
   594
        SDL_Window *window = SDL_GetWindowFromID(event->window.windowID);
slouken@5154
   595
        if (window == renderer->window) {
slouken@5297
   596
            if (renderer->WindowEvent) {
slouken@5297
   597
                renderer->WindowEvent(renderer, &event->window);
slouken@5297
   598
            }
slouken@5297
   599
slouken@7240
   600
            if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
slouken@9084
   601
                /* Make sure we're operating on the default render target */
slouken@9084
   602
                SDL_Texture *saved_target = SDL_GetRenderTarget(renderer);
slouken@9084
   603
                if (saved_target) {
slouken@9084
   604
                    SDL_SetRenderTarget(renderer, NULL);
slouken@9084
   605
                }
slouken@9084
   606
slouken@6530
   607
                if (renderer->logical_w) {
slouken@6530
   608
                    UpdateLogicalSize(renderer);
slouken@7240
   609
                } else {
slouken@7240
   610
                    /* Window was resized, reset viewport */
slouken@6528
   611
                    int w, h;
slouken@6528
   612
urkle@7746
   613
                    if (renderer->GetOutputSize) {
urkle@7746
   614
                        renderer->GetOutputSize(renderer, &w, &h);
urkle@7746
   615
                    } else {
urkle@7746
   616
                        SDL_GetWindowSize(renderer->window, &w, &h);
urkle@7746
   617
                    }
urkle@7746
   618
slouken@6260
   619
                    if (renderer->target) {
slouken@6260
   620
                        renderer->viewport_backup.x = 0;
slouken@6260
   621
                        renderer->viewport_backup.y = 0;
slouken@6260
   622
                        renderer->viewport_backup.w = w;
slouken@6260
   623
                        renderer->viewport_backup.h = h;
slouken@6260
   624
                    } else {
slouken@6528
   625
                        renderer->viewport.x = 0;
slouken@6528
   626
                        renderer->viewport.y = 0;
slouken@6528
   627
                        renderer->viewport.w = w;
slouken@6528
   628
                        renderer->viewport.h = h;
icculus@12214
   629
                        QueueCmdSetViewport(renderer);
icculus@12236
   630
                        FlushRenderCommandsIfNotBatching(renderer);
slouken@6260
   631
                    }
slouken@6260
   632
                }
slouken@9084
   633
slouken@9084
   634
                if (saved_target) {
slouken@9084
   635
                    SDL_SetRenderTarget(renderer, saved_target);
slouken@9084
   636
                }
slouken@6260
   637
            } else if (event->window.event == SDL_WINDOWEVENT_HIDDEN) {
slouken@6260
   638
                renderer->hidden = SDL_TRUE;
slouken@6260
   639
            } else if (event->window.event == SDL_WINDOWEVENT_SHOWN) {
slouken@6260
   640
                if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)) {
slouken@6260
   641
                    renderer->hidden = SDL_FALSE;
slouken@6260
   642
                }
slouken@6060
   643
            } else if (event->window.event == SDL_WINDOWEVENT_MINIMIZED) {
slouken@6260
   644
                renderer->hidden = SDL_TRUE;
dludwig@9968
   645
            } else if (event->window.event == SDL_WINDOWEVENT_RESTORED || 
dludwig@9968
   646
                       event->window.event == SDL_WINDOWEVENT_MAXIMIZED) {
slouken@6260
   647
                if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN)) {
slouken@6260
   648
                    renderer->hidden = SDL_FALSE;
slouken@6260
   649
                }
slouken@5297
   650
            }
slouken@5154
   651
        }
slouken@6530
   652
    } else if (event->type == SDL_MOUSEMOTION) {
slouken@9673
   653
        SDL_Window *window = SDL_GetWindowFromID(event->motion.windowID);
slouken@12021
   654
        if (window == renderer->window) {
slouken@12021
   655
            int logical_w, logical_h;
slouken@12021
   656
            SDL_Rect viewport;
slouken@12021
   657
            SDL_FPoint scale;
slouken@12021
   658
            GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
slouken@12021
   659
            if (logical_w) {
slouken@12021
   660
                event->motion.x -= (int)(viewport.x * renderer->dpi_scale.x);
slouken@12021
   661
                event->motion.y -= (int)(viewport.y * renderer->dpi_scale.y);
slouken@12021
   662
                event->motion.x = (int)(event->motion.x / (scale.x * renderer->dpi_scale.x));
slouken@12021
   663
                event->motion.y = (int)(event->motion.y / (scale.y * renderer->dpi_scale.y));
slouken@12021
   664
                if (event->motion.xrel > 0) {
slouken@12021
   665
                    event->motion.xrel = SDL_max(1, (int)(event->motion.xrel / (scale.x * renderer->dpi_scale.x)));
slouken@12021
   666
                } else if (event->motion.xrel < 0) {
slouken@12021
   667
                    event->motion.xrel = SDL_min(-1, (int)(event->motion.xrel / (scale.x * renderer->dpi_scale.x)));
slouken@12021
   668
                }
slouken@12021
   669
                if (event->motion.yrel > 0) {
slouken@12021
   670
                    event->motion.yrel = SDL_max(1, (int)(event->motion.yrel / (scale.y * renderer->dpi_scale.y)));
slouken@12021
   671
                } else if (event->motion.yrel < 0) {
slouken@12021
   672
                    event->motion.yrel = SDL_min(-1, (int)(event->motion.yrel / (scale.y * renderer->dpi_scale.y)));
slouken@12021
   673
                }
slouken@7496
   674
            }
slouken@6530
   675
        }
slouken@6530
   676
    } else if (event->type == SDL_MOUSEBUTTONDOWN ||
slouken@6530
   677
               event->type == SDL_MOUSEBUTTONUP) {
slouken@9673
   678
        SDL_Window *window = SDL_GetWindowFromID(event->button.windowID);
slouken@12021
   679
        if (window == renderer->window) {
slouken@12021
   680
            int logical_w, logical_h;
slouken@12021
   681
            SDL_Rect viewport;
slouken@12021
   682
            SDL_FPoint scale;
slouken@12021
   683
            GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
slouken@12021
   684
            if (logical_w) {
slouken@12021
   685
                event->button.x -= (int)(viewport.x * renderer->dpi_scale.x);
slouken@12021
   686
                event->button.y -= (int)(viewport.y * renderer->dpi_scale.y);
slouken@12021
   687
                event->button.x = (int)(event->button.x / (scale.x * renderer->dpi_scale.x));
slouken@12021
   688
                event->button.y = (int)(event->button.y / (scale.y * renderer->dpi_scale.y));
slouken@12021
   689
            }
slouken@6530
   690
        }
slouken@11603
   691
    } else if (event->type == SDL_FINGERDOWN ||
slouken@11603
   692
               event->type == SDL_FINGERUP ||
slouken@11603
   693
               event->type == SDL_FINGERMOTION) {
slouken@12021
   694
        int logical_w, logical_h;
slouken@12021
   695
        SDL_Rect viewport;
slouken@12021
   696
        SDL_FPoint scale;
slouken@12021
   697
        GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
slouken@12021
   698
        if (logical_w) {
sylvain@13117
   699
            int w, h;
sylvain@13117
   700
sylvain@13117
   701
            if (renderer->GetOutputSize) {
sylvain@13117
   702
                renderer->GetOutputSize(renderer, &w, &h);
sylvain@13117
   703
            } else {
sylvain@13117
   704
                SDL_GetWindowSize(renderer->window, &w, &h);
sylvain@13117
   705
            }
slouken@11603
   706
slouken@11603
   707
            event->tfinger.x *= (w - 1);
slouken@11603
   708
            event->tfinger.y *= (h - 1);
slouken@11603
   709
slouken@12021
   710
            event->tfinger.x -= (viewport.x * renderer->dpi_scale.x);
slouken@12021
   711
            event->tfinger.y -= (viewport.y * renderer->dpi_scale.y);
slouken@12021
   712
            event->tfinger.x = (event->tfinger.x / (scale.x * renderer->dpi_scale.x));
slouken@12021
   713
            event->tfinger.y = (event->tfinger.y / (scale.y * renderer->dpi_scale.y));
slouken@11603
   714
slouken@12021
   715
            if (logical_w > 1) {
slouken@12021
   716
                event->tfinger.x = event->tfinger.x / (logical_w - 1);
slouken@11603
   717
            } else {
slouken@11603
   718
                event->tfinger.x = 0.5f;
slouken@11603
   719
            }
slouken@12021
   720
            if (logical_h > 1) {
slouken@12021
   721
                event->tfinger.y = event->tfinger.y / (logical_h - 1);
slouken@11603
   722
            } else {
slouken@11603
   723
                event->tfinger.y = 0.5f;
slouken@11603
   724
            }
slouken@11603
   725
        }
slouken@5154
   726
    }
slouken@11603
   727
slouken@5154
   728
    return 0;
slouken@5154
   729
}
slouken@5154
   730
slouken@6258
   731
int
slouken@6258
   732
SDL_CreateWindowAndRenderer(int width, int height, Uint32 window_flags,
slouken@6258
   733
                            SDL_Window **window, SDL_Renderer **renderer)
slouken@6258
   734
{
slouken@6258
   735
    *window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED,
slouken@6258
   736
                                     SDL_WINDOWPOS_UNDEFINED,
slouken@6258
   737
                                     width, height, window_flags);
slouken@6258
   738
    if (!*window) {
slouken@6258
   739
        *renderer = NULL;
slouken@6258
   740
        return -1;
slouken@6258
   741
    }
slouken@6258
   742
slouken@6258
   743
    *renderer = SDL_CreateRenderer(*window, -1, 0);
slouken@6258
   744
    if (!*renderer) {
slouken@6258
   745
        return -1;
slouken@6258
   746
    }
slouken@6258
   747
slouken@6258
   748
    return 0;
slouken@6258
   749
}
slouken@6258
   750
icculus@12211
   751
static SDL_INLINE
icculus@12211
   752
void VerifyDrawQueueFunctions(const SDL_Renderer *renderer)
icculus@12211
   753
{
icculus@12211
   754
    /* all of these functions are required to be implemented, even as no-ops, so we don't
icculus@12211
   755
        have to check that they aren't NULL over and over. */
icculus@12214
   756
    SDL_assert(renderer->QueueSetViewport != NULL);
icculus@12214
   757
    SDL_assert(renderer->QueueSetDrawColor != NULL);
icculus@12211
   758
    SDL_assert(renderer->QueueDrawPoints != NULL);
icculus@12211
   759
    SDL_assert(renderer->QueueDrawLines != NULL);
icculus@12211
   760
    SDL_assert(renderer->QueueFillRects != NULL);
icculus@12211
   761
    SDL_assert(renderer->QueueCopy != NULL);
icculus@12211
   762
    SDL_assert(renderer->RunCommandQueue != NULL);
icculus@12211
   763
}
icculus@12211
   764
slouken@5154
   765
SDL_Renderer *
slouken@5154
   766
SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags)
slouken@5154
   767
{
slouken@7109
   768
#if !SDL_RENDER_DISABLED
slouken@5154
   769
    SDL_Renderer *renderer = NULL;
slouken@5154
   770
    int n = SDL_GetNumRenderDrivers();
icculus@12211
   771
    SDL_bool batching = SDL_TRUE;
slouken@5192
   772
    const char *hint;
slouken@5192
   773
sylvain@12910
   774
#if defined(__ANDROID__)
sylvain@12910
   775
    Android_ActivityMutex_Lock_Running();
sylvain@12910
   776
#endif
sylvain@12910
   777
slouken@5528
   778
    if (!window) {
slouken@5528
   779
        SDL_SetError("Invalid window");
sylvain@12909
   780
        goto error;
slouken@5528
   781
    }
slouken@5528
   782
slouken@5528
   783
    if (SDL_GetRenderer(window)) {
slouken@5528
   784
        SDL_SetError("Renderer already associated with window");
sylvain@12909
   785
        goto error;
slouken@5528
   786
    }
slouken@5528
   787
slouken@10499
   788
    if (SDL_GetHint(SDL_HINT_RENDER_VSYNC)) {
slouken@10499
   789
        if (SDL_GetHintBoolean(SDL_HINT_RENDER_VSYNC, SDL_TRUE)) {
slouken@10499
   790
            flags |= SDL_RENDERER_PRESENTVSYNC;
slouken@10499
   791
        } else {
slouken@5192
   792
            flags &= ~SDL_RENDERER_PRESENTVSYNC;
slouken@5192
   793
        }
slouken@5192
   794
    }
slouken@5154
   795
slouken@5154
   796
    if (index < 0) {
slouken@5192
   797
        hint = SDL_GetHint(SDL_HINT_RENDER_DRIVER);
slouken@5192
   798
        if (hint) {
slouken@5154
   799
            for (index = 0; index < n; ++index) {
slouken@5154
   800
                const SDL_RenderDriver *driver = render_drivers[index];
slouken@5154
   801
slouken@5192
   802
                if (SDL_strcasecmp(hint, driver->info.name) == 0) {
slouken@5154
   803
                    /* Create a new renderer instance */
slouken@5154
   804
                    renderer = driver->CreateRenderer(window, flags);
icculus@12211
   805
                    if (renderer) {
icculus@12211
   806
                        batching = SDL_FALSE;
icculus@12211
   807
                    }
slouken@5154
   808
                    break;
slouken@5154
   809
                }
slouken@5154
   810
            }
slouken@5192
   811
        }
slouken@5192
   812
slouken@5192
   813
        if (!renderer) {
slouken@5154
   814
            for (index = 0; index < n; ++index) {
slouken@5154
   815
                const SDL_RenderDriver *driver = render_drivers[index];
slouken@5154
   816
slouken@5154
   817
                if ((driver->info.flags & flags) == flags) {
slouken@5154
   818
                    /* Create a new renderer instance */
slouken@5154
   819
                    renderer = driver->CreateRenderer(window, flags);
slouken@5154
   820
                    if (renderer) {
slouken@5154
   821
                        /* Yay, we got one! */
slouken@5154
   822
                        break;
slouken@5154
   823
                    }
slouken@5154
   824
                }
slouken@5154
   825
            }
slouken@5154
   826
        }
slouken@5154
   827
        if (index == n) {
slouken@5154
   828
            SDL_SetError("Couldn't find matching render driver");
sylvain@12909
   829
            goto error;
slouken@5154
   830
        }
slouken@5154
   831
    } else {
slouken@5154
   832
        if (index >= SDL_GetNumRenderDrivers()) {
slouken@5154
   833
            SDL_SetError("index must be -1 or in the range of 0 - %d",
slouken@5154
   834
                         SDL_GetNumRenderDrivers() - 1);
sylvain@12909
   835
            goto error;
slouken@5154
   836
        }
slouken@5154
   837
        /* Create a new renderer instance */
slouken@5154
   838
        renderer = render_drivers[index]->CreateRenderer(window, flags);
icculus@12211
   839
        batching = SDL_FALSE;
slouken@5154
   840
    }
slouken@5154
   841
sylvain@12909
   842
    if (!renderer) {
sylvain@12909
   843
        goto error;
sylvain@12909
   844
    }
sylvain@12909
   845
sylvain@12909
   846
    VerifyDrawQueueFunctions(renderer);
sylvain@12909
   847
sylvain@12909
   848
    /* let app/user override batching decisions. */
sylvain@12909
   849
    if (renderer->always_batch) {
sylvain@12909
   850
        batching = SDL_TRUE;
sylvain@12909
   851
    } else if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) {
sylvain@12909
   852
        batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE);
sylvain@12909
   853
    }
sylvain@12909
   854
sylvain@12909
   855
    renderer->batching = batching;
sylvain@12909
   856
    renderer->magic = &renderer_magic;
sylvain@12909
   857
    renderer->window = window;
sylvain@12909
   858
    renderer->target_mutex = SDL_CreateMutex();
sylvain@12909
   859
    renderer->scale.x = 1.0f;
sylvain@12909
   860
    renderer->scale.y = 1.0f;
sylvain@12909
   861
    renderer->dpi_scale.x = 1.0f;
sylvain@12909
   862
    renderer->dpi_scale.y = 1.0f;
sylvain@12909
   863
sylvain@12909
   864
    /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
sylvain@12909
   865
    renderer->render_command_generation = 1;
sylvain@12909
   866
sylvain@12909
   867
    if (window && renderer->GetOutputSize) {
sylvain@12909
   868
        int window_w, window_h;
sylvain@12909
   869
        int output_w, output_h;
sylvain@12909
   870
        if (renderer->GetOutputSize(renderer, &output_w, &output_h) == 0) {
sylvain@12909
   871
            SDL_GetWindowSize(renderer->window, &window_w, &window_h);
sylvain@12909
   872
            renderer->dpi_scale.x = (float)window_w / output_w;
sylvain@12909
   873
            renderer->dpi_scale.y = (float)window_h / output_h;
icculus@12211
   874
        }
sylvain@12909
   875
    }
sylvain@12909
   876
sylvain@12909
   877
    if (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN|SDL_WINDOW_MINIMIZED)) {
sylvain@12909
   878
        renderer->hidden = SDL_TRUE;
sylvain@12909
   879
    } else {
sylvain@12909
   880
        renderer->hidden = SDL_FALSE;
slouken@5154
   881
    }
sylvain@12909
   882
sylvain@12909
   883
    SDL_SetWindowData(window, SDL_WINDOWRENDERDATA, renderer);
sylvain@12909
   884
sylvain@12909
   885
    SDL_RenderSetViewport(renderer, NULL);
sylvain@12909
   886
sylvain@12909
   887
    SDL_AddEventWatch(SDL_RendererEventWatch, renderer);
sylvain@12909
   888
sylvain@12909
   889
    SDL_LogInfo(SDL_LOG_CATEGORY_RENDER,
sylvain@12909
   890
                "Created renderer: %s", renderer->info.name);
sylvain@12909
   891
sylvain@12910
   892
#if defined(__ANDROID__)
sylvain@12910
   893
    Android_ActivityMutex_Unlock();
sylvain@12910
   894
#endif
slouken@5154
   895
    return renderer;
sylvain@12909
   896
sylvain@12909
   897
error:
sylvain@12910
   898
sylvain@12910
   899
#if defined(__ANDROID__)
sylvain@12910
   900
    Android_ActivityMutex_Unlock();
sylvain@12910
   901
#endif
sylvain@12909
   902
    return NULL;
sylvain@12909
   903
slouken@7109
   904
#else
slouken@7109
   905
    SDL_SetError("SDL not built with rendering support");
slouken@7109
   906
    return NULL;
slouken@7109
   907
#endif
slouken@5154
   908
}
slouken@5154
   909
slouken@5166
   910
SDL_Renderer *
slouken@5166
   911
SDL_CreateSoftwareRenderer(SDL_Surface * surface)
slouken@5166
   912
{
sylvain@13433
   913
#if !SDL_RENDER_DISABLED && SDL_VIDEO_RENDER_SW
slouken@5297
   914
    SDL_Renderer *renderer;
slouken@5297
   915
slouken@5297
   916
    renderer = SW_CreateRendererForSurface(surface);
slouken@5297
   917
slouken@5297
   918
    if (renderer) {
icculus@12211
   919
        VerifyDrawQueueFunctions(renderer);
slouken@5297
   920
        renderer->magic = &renderer_magic;
slouken@12021
   921
        renderer->target_mutex = SDL_CreateMutex();
slouken@6532
   922
        renderer->scale.x = 1.0f;
slouken@6532
   923
        renderer->scale.y = 1.0f;
slouken@5297
   924
icculus@12211
   925
        /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
icculus@12211
   926
        renderer->render_command_generation = 1;
icculus@12211
   927
slouken@5297
   928
        SDL_RenderSetViewport(renderer, NULL);
slouken@5297
   929
    }
slouken@5297
   930
    return renderer;
slouken@5226
   931
#else
slouken@5226
   932
    SDL_SetError("SDL not built with rendering support");
slouken@5226
   933
    return NULL;
slouken@5226
   934
#endif /* !SDL_RENDER_DISABLED */
slouken@5166
   935
}
slouken@5166
   936
slouken@5528
   937
SDL_Renderer *
slouken@5528
   938
SDL_GetRenderer(SDL_Window * window)
slouken@5528
   939
{
slouken@5528
   940
    return (SDL_Renderer *)SDL_GetWindowData(window, SDL_WINDOWRENDERDATA);
slouken@5528
   941
}
slouken@5528
   942
slouken@5154
   943
int
slouken@5154
   944
SDL_GetRendererInfo(SDL_Renderer * renderer, SDL_RendererInfo * info)
slouken@5154
   945
{
slouken@5154
   946
    CHECK_RENDERER_MAGIC(renderer, -1);
slouken@5154
   947
slouken@5154
   948
    *info = renderer->info;
slouken@5154
   949
    return 0;
slouken@5154
   950
}
slouken@5154
   951
slouken@7239
   952
int
slouken@7239
   953
SDL_GetRendererOutputSize(SDL_Renderer * renderer, int *w, int *h)
slouken@7239
   954
{
slouken@7239
   955
    CHECK_RENDERER_MAGIC(renderer, -1);
slouken@7239
   956
slouken@7239
   957
    if (renderer->target) {
slouken@7239
   958
        return SDL_QueryTexture(renderer->target, NULL, NULL, w, h);
urkle@7746
   959
    } else if (renderer->GetOutputSize) {
urkle@7746
   960
        return renderer->GetOutputSize(renderer, w, h);
slouken@7239
   961
    } else if (renderer->window) {
slouken@7239
   962
        SDL_GetWindowSize(renderer->window, w, h);
slouken@7239
   963
        return 0;
slouken@7239
   964
    } else {
icculus@8654
   965
        SDL_assert(0 && "This should never happen");
philipp@7898
   966
        return SDL_SetError("Renderer doesn't support querying output size");
slouken@7239
   967
    }
slouken@7239
   968
}
slouken@7239
   969
slouken@5156
   970
static SDL_bool
slouken@11282
   971
IsSupportedBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
slouken@11282
   972
{
slouken@11282
   973
    switch (blendMode)
slouken@11282
   974
    {
slouken@11282
   975
    /* These are required to be supported by all renderers */
slouken@11282
   976
    case SDL_BLENDMODE_NONE:
slouken@11282
   977
    case SDL_BLENDMODE_BLEND:
slouken@11282
   978
    case SDL_BLENDMODE_ADD:
slouken@11282
   979
    case SDL_BLENDMODE_MOD:
slouken@13418
   980
    case SDL_BLENDMODE_MUL:
slouken@11282
   981
        return SDL_TRUE;
slouken@11282
   982
slouken@11282
   983
    default:
slouken@11282
   984
        return renderer->SupportsBlendMode && renderer->SupportsBlendMode(renderer, blendMode);
slouken@11282
   985
    }
slouken@11282
   986
}
slouken@11282
   987
slouken@11282
   988
static SDL_bool
slouken@5156
   989
IsSupportedFormat(SDL_Renderer * renderer, Uint32 format)
slouken@5156
   990
{
slouken@5156
   991
    Uint32 i;
slouken@5156
   992
slouken@5156
   993
    for (i = 0; i < renderer->info.num_texture_formats; ++i) {
slouken@5156
   994
        if (renderer->info.texture_formats[i] == format) {
slouken@5156
   995
            return SDL_TRUE;
slouken@5156
   996
        }
slouken@5156
   997
    }
slouken@5156
   998
    return SDL_FALSE;
slouken@5156
   999
}
slouken@5156
  1000
slouken@5156
  1001
static Uint32
slouken@5156
  1002
GetClosestSupportedFormat(SDL_Renderer * renderer, Uint32 format)
slouken@5156
  1003
{
slouken@5156
  1004
    Uint32 i;
slouken@5156
  1005
slouken@5268
  1006
    if (SDL_ISPIXELFORMAT_FOURCC(format)) {
slouken@5268
  1007
        /* Look for an exact match */
slouken@5268
  1008
        for (i = 0; i < renderer->info.num_texture_formats; ++i) {
slouken@5268
  1009
            if (renderer->info.texture_formats[i] == format) {
slouken@5268
  1010
                return renderer->info.texture_formats[i];
slouken@5268
  1011
            }
slouken@5268
  1012
        }
slouken@5268
  1013
    } else {
slouken@5268
  1014
        SDL_bool hasAlpha = SDL_ISPIXELFORMAT_ALPHA(format);
slouken@5268
  1015
slouken@5268
  1016
        /* We just want to match the first format that has the same channels */
slouken@5268
  1017
        for (i = 0; i < renderer->info.num_texture_formats; ++i) {
slouken@5300
  1018
            if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) &&
slouken@5300
  1019
                SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == hasAlpha) {
slouken@5268
  1020
                return renderer->info.texture_formats[i];
slouken@5268
  1021
            }
slouken@5156
  1022
        }
slouken@5156
  1023
    }
slouken@5156
  1024
    return renderer->info.texture_formats[0];
slouken@5156
  1025
}
slouken@5156
  1026
slouken@12242
  1027
slouken@12242
  1028
static SDL_ScaleMode SDL_GetScaleMode(void)
slouken@11958
  1029
{
slouken@11958
  1030
    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
slouken@11958
  1031
slouken@11958
  1032
    if (!hint || SDL_strcasecmp(hint, "nearest") == 0) {
slouken@11958
  1033
        return SDL_ScaleModeNearest;
slouken@11958
  1034
    } else if (SDL_strcasecmp(hint, "linear") == 0) {
slouken@11958
  1035
        return SDL_ScaleModeLinear;
slouken@11958
  1036
    } else if (SDL_strcasecmp(hint, "best") == 0) {
slouken@11958
  1037
        return SDL_ScaleModeBest;
slouken@11958
  1038
    } else {
slouken@11958
  1039
        return (SDL_ScaleMode)SDL_atoi(hint);
slouken@11958
  1040
    }
slouken@11958
  1041
}
slouken@11958
  1042
slouken@5154
  1043
SDL_Texture *
slouken@5154
  1044
SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h)
slouken@5154
  1045
{
slouken@5154
  1046
    SDL_Texture *texture;
slouken@5154
  1047
slouken@5154
  1048
    CHECK_RENDERER_MAGIC(renderer, NULL);
slouken@5154
  1049
slouken@5413
  1050
    if (!format) {
slouken@5413
  1051
        format = renderer->info.texture_formats[0];
slouken@5413
  1052
    }
icculus@8650
  1053
    if (SDL_BYTESPERPIXEL(format) == 0) {
icculus@8650
  1054
        SDL_SetError("Invalid texture format");
icculus@8650
  1055
        return NULL;
icculus@8650
  1056
    }
slouken@5156
  1057
    if (SDL_ISPIXELFORMAT_INDEXED(format)) {
slouken@5156
  1058
        SDL_SetError("Palettized textures are not supported");
slouken@5156
  1059
        return NULL;
slouken@5156
  1060
    }
slouken@5154
  1061
    if (w <= 0 || h <= 0) {
slouken@5154
  1062
        SDL_SetError("Texture dimensions can't be 0");
slouken@5156
  1063
        return NULL;
slouken@5154
  1064
    }
slouken@7770
  1065
    if ((renderer->info.max_texture_width && w > renderer->info.max_texture_width) ||
slouken@7770
  1066
        (renderer->info.max_texture_height && h > renderer->info.max_texture_height)) {
slouken@7770
  1067
        SDL_SetError("Texture dimensions are limited to %dx%d", renderer->info.max_texture_width, renderer->info.max_texture_height);
slouken@7770
  1068
        return NULL;
slouken@7770
  1069
    }
slouken@5154
  1070
    texture = (SDL_Texture *) SDL_calloc(1, sizeof(*texture));
slouken@5154
  1071
    if (!texture) {
slouken@5154
  1072
        SDL_OutOfMemory();
slouken@5156
  1073
        return NULL;
slouken@5154
  1074
    }
slouken@5154
  1075
    texture->magic = &texture_magic;
slouken@5154
  1076
    texture->format = format;
slouken@5154
  1077
    texture->access = access;
slouken@5154
  1078
    texture->w = w;
slouken@5154
  1079
    texture->h = h;
slouken@5154
  1080
    texture->r = 255;
slouken@5154
  1081
    texture->g = 255;
slouken@5154
  1082
    texture->b = 255;
slouken@5154
  1083
    texture->a = 255;
slouken@11958
  1084
    texture->scaleMode = SDL_GetScaleMode();
slouken@5154
  1085
    texture->renderer = renderer;
slouken@5154
  1086
    texture->next = renderer->textures;
slouken@5154
  1087
    if (renderer->textures) {
slouken@5154
  1088
        renderer->textures->prev = texture;
slouken@5154
  1089
    }
slouken@5154
  1090
    renderer->textures = texture;
slouken@5154
  1091
slouken@5156
  1092
    if (IsSupportedFormat(renderer, format)) {
slouken@5156
  1093
        if (renderer->CreateTexture(renderer, texture) < 0) {
slouken@5156
  1094
            SDL_DestroyTexture(texture);
philipp@9849
  1095
            return NULL;
slouken@5156
  1096
        }
slouken@5156
  1097
    } else {
slouken@5156
  1098
        texture->native = SDL_CreateTexture(renderer,
slouken@5156
  1099
                                GetClosestSupportedFormat(renderer, format),
slouken@5156
  1100
                                access, w, h);
slouken@5156
  1101
        if (!texture->native) {
slouken@5156
  1102
            SDL_DestroyTexture(texture);
slouken@5156
  1103
            return NULL;
slouken@5156
  1104
        }
slouken@5156
  1105
slouken@6497
  1106
        /* Swap textures to have texture before texture->native in the list */
slouken@6497
  1107
        texture->native->next = texture->next;
slouken@6533
  1108
        if (texture->native->next) {
slouken@6533
  1109
            texture->native->next->prev = texture->native;
slouken@6533
  1110
        }
slouken@6497
  1111
        texture->prev = texture->native->prev;
slouken@6533
  1112
        if (texture->prev) {
slouken@6533
  1113
            texture->prev->next = texture;
slouken@6533
  1114
        }
slouken@6497
  1115
        texture->native->prev = texture;
slouken@6497
  1116
        texture->next = texture->native;
slouken@6497
  1117
        renderer->textures = texture;
slouken@6497
  1118
slouken@5156
  1119
        if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
sylvain@13433
  1120
#if SDL_HAVE_YUV
slouken@5156
  1121
            texture->yuv = SDL_SW_CreateYUVTexture(format, w, h);
sylvain@13433
  1122
#else
sylvain@13433
  1123
            SDL_SetError("SDL not built with YUV support");
sylvain@13433
  1124
#endif
slouken@5156
  1125
            if (!texture->yuv) {
slouken@5156
  1126
                SDL_DestroyTexture(texture);
slouken@5156
  1127
                return NULL;
slouken@5156
  1128
            }
slouken@5156
  1129
        } else if (access == SDL_TEXTUREACCESS_STREAMING) {
slouken@5156
  1130
            /* The pitch is 4 byte aligned */
slouken@5156
  1131
            texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3);
slouken@5342
  1132
            texture->pixels = SDL_calloc(1, texture->pitch * h);
slouken@5156
  1133
            if (!texture->pixels) {
slouken@5156
  1134
                SDL_DestroyTexture(texture);
slouken@5156
  1135
                return NULL;
slouken@5156
  1136
            }
slouken@5156
  1137
        }
slouken@5154
  1138
    }
slouken@5154
  1139
    return texture;
slouken@5154
  1140
}
slouken@5154
  1141
slouken@5154
  1142
SDL_Texture *
slouken@5158
  1143
SDL_CreateTextureFromSurface(SDL_Renderer * renderer, SDL_Surface * surface)
slouken@5154
  1144
{
slouken@5158
  1145
    const SDL_PixelFormat *fmt;
slouken@5158
  1146
    SDL_bool needAlpha;
sylvain@12561
  1147
    SDL_bool direct_update;
slouken@12665
  1148
    int i;
slouken@12739
  1149
    Uint32 format = SDL_PIXELFORMAT_UNKNOWN;
slouken@5158
  1150
    SDL_Texture *texture;
slouken@5154
  1151
slouken@5154
  1152
    CHECK_RENDERER_MAGIC(renderer, NULL);
slouken@5154
  1153
slouken@5154
  1154
    if (!surface) {
slouken@5154
  1155
        SDL_SetError("SDL_CreateTextureFromSurface() passed NULL surface");
slouken@5154
  1156
        return NULL;
slouken@5154
  1157
    }
slouken@5158
  1158
slouken@5158
  1159
    /* See what the best texture format is */
slouken@5154
  1160
    fmt = surface->format;
slouken@12223
  1161
    if (fmt->Amask || SDL_HasColorKey(surface)) {
slouken@5158
  1162
        needAlpha = SDL_TRUE;
slouken@5154
  1163
    } else {
slouken@5158
  1164
        needAlpha = SDL_FALSE;
slouken@5158
  1165
    }
sylvain@12474
  1166
sylvain@12474
  1167
    /* If Palette contains alpha values, promotes to alpha format */
sylvain@12474
  1168
    if (fmt->palette) {
sylvain@13643
  1169
        SDL_bool is_opaque, has_alpha_channel;
sylvain@13643
  1170
        SDL_DetectPalette(fmt->palette, &is_opaque, &has_alpha_channel);
sylvain@13643
  1171
        if (!is_opaque) {
sylvain@13643
  1172
            needAlpha = SDL_TRUE;
sylvain@12474
  1173
        }
sylvain@12474
  1174
    }
sylvain@12474
  1175
slouken@12739
  1176
    /* Try to have the best pixel format for the texture */
slouken@12739
  1177
    /* No alpha, but a colorkey => promote to alpha */
slouken@12739
  1178
    if (!fmt->Amask && SDL_HasColorKey(surface)) {
slouken@12739
  1179
        if (fmt->format == SDL_PIXELFORMAT_RGB888) {
slouken@12808
  1180
            for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
slouken@12739
  1181
                if (renderer->info.texture_formats[i] == SDL_PIXELFORMAT_ARGB8888) {
slouken@12739
  1182
                    format = SDL_PIXELFORMAT_ARGB8888;
slouken@12739
  1183
                    break;
slouken@12739
  1184
                }
slouken@12739
  1185
            }
slouken@12739
  1186
        } else if (fmt->format == SDL_PIXELFORMAT_BGR888) {
slouken@12808
  1187
            for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
slouken@12739
  1188
                if (renderer->info.texture_formats[i] == SDL_PIXELFORMAT_ABGR8888) {
slouken@12739
  1189
                    format = SDL_PIXELFORMAT_ABGR8888;
slouken@12739
  1190
                    break;
slouken@12739
  1191
                }
slouken@12739
  1192
            }
slouken@12739
  1193
        }
slouken@12739
  1194
    } else {
slouken@12739
  1195
        /* Exact match would be fine */
slouken@12808
  1196
        for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
slouken@12739
  1197
            if (renderer->info.texture_formats[i] == fmt->format) {
slouken@12739
  1198
                format = fmt->format;
slouken@12739
  1199
                break;
slouken@12739
  1200
            }
slouken@12739
  1201
        }
slouken@12739
  1202
    }
slouken@12739
  1203
slouken@12739
  1204
    /* Fallback, choose a valid pixel format */
slouken@12739
  1205
    if (format == SDL_PIXELFORMAT_UNKNOWN) {
slouken@12739
  1206
        format = renderer->info.texture_formats[0];
slouken@12808
  1207
        for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
slouken@12739
  1208
            if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) &&
slouken@12739
  1209
                    SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == needAlpha) {
slouken@12739
  1210
                format = renderer->info.texture_formats[i];
slouken@12739
  1211
                break;
slouken@12739
  1212
            }
slouken@5154
  1213
        }
slouken@5154
  1214
    }
slouken@5154
  1215
slouken@5158
  1216
    texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STATIC,
slouken@5158
  1217
                                surface->w, surface->h);
slouken@5154
  1218
    if (!texture) {
slouken@5158
  1219
        return NULL;
slouken@5154
  1220
    }
slouken@5158
  1221
slouken@5288
  1222
    if (format == surface->format->format) {
sylvain@12561
  1223
        if (surface->format->Amask && SDL_HasColorKey(surface)) {
sylvain@12561
  1224
            /* Surface and Renderer formats are identicals. 
sylvain@12561
  1225
             * Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */
sylvain@12561
  1226
            direct_update = SDL_FALSE;
sylvain@12561
  1227
        } else {
sylvain@12561
  1228
            /* Update Texture directly */
sylvain@12561
  1229
            direct_update = SDL_TRUE;
sylvain@12561
  1230
        }
sylvain@12561
  1231
    } else {
sylvain@12561
  1232
        /* Surface and Renderer formats are differents, it needs an intermediate conversion. */
sylvain@12561
  1233
        direct_update = SDL_FALSE;
sylvain@12561
  1234
    }
sylvain@12561
  1235
sylvain@12561
  1236
    if (direct_update) {
slouken@5154
  1237
        if (SDL_MUSTLOCK(surface)) {
slouken@5154
  1238
            SDL_LockSurface(surface);
slouken@5158
  1239
            SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
slouken@5154
  1240
            SDL_UnlockSurface(surface);
slouken@5154
  1241
        } else {
slouken@5158
  1242
            SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
slouken@5154
  1243
        }
slouken@5154
  1244
    } else {
slouken@5297
  1245
        SDL_PixelFormat *dst_fmt;
slouken@5158
  1246
        SDL_Surface *temp = NULL;
slouken@5154
  1247
slouken@5154
  1248
        /* Set up a destination surface for the texture update */
slouken@5297
  1249
        dst_fmt = SDL_AllocFormat(format);
slouken@9078
  1250
        if (!dst_fmt) {
slouken@9078
  1251
           SDL_DestroyTexture(texture);
slouken@9078
  1252
           return NULL;
slouken@9078
  1253
        }
slouken@5297
  1254
        temp = SDL_ConvertSurface(surface, dst_fmt, 0);
slouken@5297
  1255
        SDL_FreeFormat(dst_fmt);
slouken@5158
  1256
        if (temp) {
slouken@5158
  1257
            SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
slouken@5158
  1258
            SDL_FreeSurface(temp);
slouken@5158
  1259
        } else {
slouken@5154
  1260
            SDL_DestroyTexture(texture);
slouken@5158
  1261
            return NULL;
slouken@5154
  1262
        }
slouken@5154
  1263
    }
slouken@5154
  1264
slouken@5154
  1265
    {
slouken@5154
  1266
        Uint8 r, g, b, a;
slouken@5154
  1267
        SDL_BlendMode blendMode;
slouken@5154
  1268
slouken@5154
  1269
        SDL_GetSurfaceColorMod(surface, &r, &g, &b);
slouken@5154
  1270
        SDL_SetTextureColorMod(texture, r, g, b);
slouken@5154
  1271
slouken@5154
  1272
        SDL_GetSurfaceAlphaMod(surface, &a);
slouken@5154
  1273
        SDL_SetTextureAlphaMod(texture, a);
slouken@5154
  1274
slouken@12223
  1275
        if (SDL_HasColorKey(surface)) {
slouken@5154
  1276
            /* We converted to a texture with alpha format */
slouken@5154
  1277
            SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
slouken@5154
  1278
        } else {
slouken@5154
  1279
            SDL_GetSurfaceBlendMode(surface, &blendMode);
slouken@5154
  1280
            SDL_SetTextureBlendMode(texture, blendMode);
slouken@5154
  1281
        }
slouken@5154
  1282
    }
slouken@5154
  1283
    return texture;
slouken@5154
  1284
}
slouken@5154
  1285
slouken@5154
  1286
int
slouken@5154
  1287
SDL_QueryTexture(SDL_Texture * texture, Uint32 * format, int *access,
slouken@5154
  1288
                 int *w, int *h)
slouken@5154
  1289
{
slouken@5154
  1290
    CHECK_TEXTURE_MAGIC(texture, -1);
slouken@5154
  1291
slouken@5154
  1292
    if (format) {
slouken@5154
  1293
        *format = texture->format;
slouken@5154
  1294
    }
slouken@5154
  1295
    if (access) {
slouken@5154
  1296
        *access = texture->access;
slouken@5154
  1297
    }
slouken@5154
  1298
    if (w) {
slouken@5154
  1299
        *w = texture->w;
slouken@5154
  1300
    }
slouken@5154
  1301
    if (h) {
slouken@5154
  1302
        *h = texture->h;
slouken@5154
  1303
    }
slouken@5154
  1304
    return 0;
slouken@5154
  1305
}
slouken@5154
  1306
slouken@5154
  1307
int
slouken@5154
  1308
SDL_SetTextureColorMod(SDL_Texture * texture, Uint8 r, Uint8 g, Uint8 b)
slouken@5154
  1309
{
slouken@5154
  1310
    CHECK_TEXTURE_MAGIC(texture, -1);
slouken@5154
  1311
slouken@5154
  1312
    if (r < 255 || g < 255 || b < 255) {
slouken@5154
  1313
        texture->modMode |= SDL_TEXTUREMODULATE_COLOR;
slouken@5154
  1314
    } else {
slouken@5154
  1315
        texture->modMode &= ~SDL_TEXTUREMODULATE_COLOR;
slouken@5154
  1316
    }
slouken@5154
  1317
    texture->r = r;
slouken@5154
  1318
    texture->g = g;
slouken@5154
  1319
    texture->b = b;
slouken@5156
  1320
    if (texture->native) {
slouken@5156
  1321
        return SDL_SetTextureColorMod(texture->native, r, g, b);
slouken@5154
  1322
    }
icculus@12211
  1323
    return 0;
slouken@5154
  1324
}
slouken@5154
  1325
slouken@5154
  1326
int
slouken@5154
  1327
SDL_GetTextureColorMod(SDL_Texture * texture, Uint8 * r, Uint8 * g,
slouken@5154
  1328
                       Uint8 * b)
slouken@5154
  1329
{
slouken@5154
  1330
    CHECK_TEXTURE_MAGIC(texture, -1);
slouken@5154
  1331
slouken@5154
  1332
    if (r) {
slouken@5154
  1333
        *r = texture->r;
slouken@5154
  1334
    }
slouken@5154
  1335
    if (g) {
slouken@5154
  1336
        *g = texture->g;
slouken@5154
  1337
    }
slouken@5154
  1338
    if (b) {
slouken@5154
  1339
        *b = texture->b;
slouken@5154
  1340
    }
slouken@5154
  1341
    return 0;
slouken@5154
  1342
}
slouken@5154
  1343
slouken@5154
  1344
int
slouken@5154
  1345
SDL_SetTextureAlphaMod(SDL_Texture * texture, Uint8 alpha)
slouken@5154
  1346
{
slouken@5154
  1347
    CHECK_TEXTURE_MAGIC(texture, -1);
slouken@5154
  1348
slouken@5154
  1349
    if (alpha < 255) {
slouken@5154
  1350
        texture->modMode |= SDL_TEXTUREMODULATE_ALPHA;
slouken@5154
  1351
    } else {
slouken@5154
  1352
        texture->modMode &= ~SDL_TEXTUREMODULATE_ALPHA;
slouken@5154
  1353
    }
slouken@5154
  1354
    texture->a = alpha;
slouken@5156
  1355
    if (texture->native) {
slouken@5156
  1356
        return SDL_SetTextureAlphaMod(texture->native, alpha);
slouken@5154
  1357
    }
icculus@12211
  1358
    return 0;
slouken@5154
  1359
}
slouken@5154
  1360
slouken@5154
  1361
int
slouken@5154
  1362
SDL_GetTextureAlphaMod(SDL_Texture * texture, Uint8 * alpha)
slouken@5154
  1363
{
slouken@5154
  1364
    CHECK_TEXTURE_MAGIC(texture, -1);
slouken@5154
  1365
slouken@5154
  1366
    if (alpha) {
slouken@5154
  1367
        *alpha = texture->a;
slouken@5154
  1368
    }
slouken@5154
  1369
    return 0;
slouken@5154
  1370
}
slouken@5154
  1371
slouken@5154
  1372
int
slouken@5154
  1373
SDL_SetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode blendMode)
slouken@5154
  1374
{
slouken@5154
  1375
    SDL_Renderer *renderer;
slouken@5154
  1376
slouken@5154
  1377
    CHECK_TEXTURE_MAGIC(texture, -1);
slouken@5154
  1378
slouken@5154
  1379
    renderer = texture->renderer;
slouken@11282
  1380
    if (!IsSupportedBlendMode(renderer, blendMode)) {
slouken@11282
  1381
        return SDL_Unsupported();
slouken@11282
  1382
    }
slouken@5154
  1383
    texture->blendMode = blendMode;
slouken@5156
  1384
    if (texture->native) {
slouken@5180
  1385
        return SDL_SetTextureBlendMode(texture->native, blendMode);
slouken@5154
  1386
    }
icculus@12211
  1387
    return 0;
slouken@5154
  1388
}
slouken@5154
  1389
slouken@5154
  1390
int
slouken@5154
  1391
SDL_GetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode *blendMode)
slouken@5154
  1392
{
slouken@5154
  1393
    CHECK_TEXTURE_MAGIC(texture, -1);
slouken@5154
  1394
slouken@5154
  1395
    if (blendMode) {
slouken@5154
  1396
        *blendMode = texture->blendMode;
slouken@5154
  1397
    }
slouken@5154
  1398
    return 0;
slouken@5154
  1399
}
slouken@5154
  1400
slouken@13376
  1401
int
slouken@13376
  1402
SDL_SetTextureScaleMode(SDL_Texture * texture, SDL_ScaleMode scaleMode)
slouken@13376
  1403
{
slouken@13376
  1404
    SDL_Renderer *renderer;
slouken@13376
  1405
slouken@13376
  1406
    CHECK_TEXTURE_MAGIC(texture, -1);
slouken@13376
  1407
slouken@13376
  1408
    renderer = texture->renderer;
slouken@13376
  1409
    renderer->SetTextureScaleMode(renderer, texture, scaleMode);
slouken@13376
  1410
    texture->scaleMode = scaleMode;
slouken@13376
  1411
    if (texture->native) {
slouken@13376
  1412
        return SDL_SetTextureScaleMode(texture->native, scaleMode);
slouken@13376
  1413
    }
slouken@13376
  1414
    return 0;
slouken@13376
  1415
}
slouken@13376
  1416
slouken@13376
  1417
int
slouken@13376
  1418
SDL_GetTextureScaleMode(SDL_Texture * texture, SDL_ScaleMode *scaleMode)
slouken@13376
  1419
{
slouken@13376
  1420
    CHECK_TEXTURE_MAGIC(texture, -1);
slouken@13376
  1421
slouken@13376
  1422
    if (scaleMode) {
slouken@13376
  1423
        *scaleMode = texture->scaleMode;
slouken@13376
  1424
    }
slouken@13376
  1425
    return 0;
slouken@13376
  1426
}
slouken@13376
  1427
slouken@13440
  1428
#if SDL_HAVE_YUV
slouken@5156
  1429
static int
slouken@5156
  1430
SDL_UpdateTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
slouken@5156
  1431
                     const void *pixels, int pitch)
slouken@5156
  1432
{
slouken@5156
  1433
    SDL_Texture *native = texture->native;
slouken@5156
  1434
    SDL_Rect full_rect;
slouken@5156
  1435
slouken@5156
  1436
    if (SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch) < 0) {
slouken@5156
  1437
        return -1;
slouken@5156
  1438
    }
slouken@5156
  1439
slouken@5156
  1440
    full_rect.x = 0;
slouken@5156
  1441
    full_rect.y = 0;
slouken@5156
  1442
    full_rect.w = texture->w;
slouken@5156
  1443
    full_rect.h = texture->h;
slouken@5156
  1444
    rect = &full_rect;
slouken@5156
  1445
slouken@5156
  1446
    if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
slouken@5156
  1447
        /* We can lock the texture and copy to it */
icculus@10650
  1448
        void *native_pixels = NULL;
icculus@10650
  1449
        int native_pitch = 0;
slouken@5156
  1450
slouken@5156
  1451
        if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
slouken@5156
  1452
            return -1;
slouken@5156
  1453
        }
slouken@5156
  1454
        SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
slouken@5156
  1455
                            rect->w, rect->h, native_pixels, native_pitch);
slouken@5156
  1456
        SDL_UnlockTexture(native);
slouken@5156
  1457
    } else {
slouken@5156
  1458
        /* Use a temporary buffer for updating */
icculus@10650
  1459
        const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
icculus@10650
  1460
        const size_t alloclen = rect->h * temp_pitch;
icculus@10650
  1461
        if (alloclen > 0) {
icculus@10650
  1462
            void *temp_pixels = SDL_malloc(alloclen);
icculus@10650
  1463
            if (!temp_pixels) {
icculus@10650
  1464
                return SDL_OutOfMemory();
icculus@10650
  1465
            }
icculus@10650
  1466
            SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
icculus@10650
  1467
                                rect->w, rect->h, temp_pixels, temp_pitch);
icculus@10650
  1468
            SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
icculus@10650
  1469
            SDL_free(temp_pixels);
slouken@5156
  1470
        }
slouken@5156
  1471
    }
slouken@5156
  1472
    return 0;
slouken@5156
  1473
}
slouken@13440
  1474
#endif /* SDL_HAVE_YUV */
slouken@5156
  1475
slouken@5156
  1476
static int
slouken@5156
  1477
SDL_UpdateTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
slouken@5156
  1478
                        const void *pixels, int pitch)
slouken@5156
  1479
{
slouken@5156
  1480
    SDL_Texture *native = texture->native;
slouken@5156
  1481
icculus@10650
  1482
    if (!rect->w || !rect->h) {
icculus@10650
  1483
        return 0;  /* nothing to do. */
icculus@10650
  1484
    }
icculus@10650
  1485
slouken@5156
  1486
    if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
slouken@5156
  1487
        /* We can lock the texture and copy to it */
icculus@10650
  1488
        void *native_pixels = NULL;
icculus@10650
  1489
        int native_pitch = 0;
slouken@5156
  1490
slouken@5156
  1491
        if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
slouken@5156
  1492
            return -1;
slouken@5156
  1493
        }
slouken@5156
  1494
        SDL_ConvertPixels(rect->w, rect->h,
slouken@5156
  1495
                          texture->format, pixels, pitch,
slouken@5156
  1496
                          native->format, native_pixels, native_pitch);
slouken@5156
  1497
        SDL_UnlockTexture(native);
slouken@5156
  1498
    } else {
slouken@5156
  1499
        /* Use a temporary buffer for updating */
icculus@10650
  1500
        const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
icculus@10650
  1501
        const size_t alloclen = rect->h * temp_pitch;
icculus@10650
  1502
        if (alloclen > 0) {
icculus@10650
  1503
            void *temp_pixels = SDL_malloc(alloclen);
icculus@10650
  1504
            if (!temp_pixels) {
icculus@10650
  1505
                return SDL_OutOfMemory();
icculus@10650
  1506
            }
icculus@10650
  1507
            SDL_ConvertPixels(rect->w, rect->h,
icculus@10650
  1508
                              texture->format, pixels, pitch,
icculus@10650
  1509
                              native->format, temp_pixels, temp_pitch);
icculus@10650
  1510
            SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
icculus@10650
  1511
            SDL_free(temp_pixels);
slouken@5156
  1512
        }
slouken@5156
  1513
    }
slouken@5156
  1514
    return 0;
slouken@5156
  1515
}
slouken@5156
  1516
slouken@5154
  1517
int
slouken@5154
  1518
SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect,
slouken@5154
  1519
                  const void *pixels, int pitch)
slouken@5154
  1520
{
slouken@5154
  1521
    SDL_Rect full_rect;
slouken@5154
  1522
slouken@5154
  1523
    CHECK_TEXTURE_MAGIC(texture, -1);
slouken@5154
  1524
slouken@7409
  1525
    if (!pixels) {
slouken@7409
  1526
        return SDL_InvalidParamError("pixels");
slouken@7409
  1527
    }
slouken@7409
  1528
    if (!pitch) {
slouken@7409
  1529
        return SDL_InvalidParamError("pitch");
slouken@7409
  1530
    }
slouken@7409
  1531
slouken@5154
  1532
    if (!rect) {
slouken@5154
  1533
        full_rect.x = 0;
slouken@5154
  1534
        full_rect.y = 0;
slouken@5154
  1535
        full_rect.w = texture->w;
slouken@5154
  1536
        full_rect.h = texture->h;
slouken@5154
  1537
        rect = &full_rect;
slouken@5154
  1538
    }
slouken@5156
  1539
icculus@9636
  1540
    if ((rect->w == 0) || (rect->h == 0)) {
icculus@9636
  1541
        return 0;  /* nothing to do. */
sylvain@13433
  1542
#if SDL_HAVE_YUV
icculus@9636
  1543
    } else if (texture->yuv) {
slouken@5156
  1544
        return SDL_UpdateTextureYUV(texture, rect, pixels, pitch);
sylvain@13433
  1545
#endif
slouken@5156
  1546
    } else if (texture->native) {
slouken@5156
  1547
        return SDL_UpdateTextureNative(texture, rect, pixels, pitch);
slouken@5156
  1548
    } else {
icculus@12211
  1549
        SDL_Renderer *renderer = texture->renderer;
icculus@12211
  1550
        if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
icculus@12211
  1551
            return -1;
icculus@12211
  1552
        }
slouken@5156
  1553
        return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
slouken@5156
  1554
    }
slouken@5156
  1555
}
slouken@5156
  1556
slouken@13440
  1557
#if SDL_HAVE_YUV
slouken@5156
  1558
static int
slouken@7759
  1559
SDL_UpdateTextureYUVPlanar(SDL_Texture * texture, const SDL_Rect * rect,
slouken@7759
  1560
                           const Uint8 *Yplane, int Ypitch,
slouken@7759
  1561
                           const Uint8 *Uplane, int Upitch,
slouken@7759
  1562
                           const Uint8 *Vplane, int Vpitch)
slouken@7759
  1563
{
slouken@7759
  1564
    SDL_Texture *native = texture->native;
slouken@7759
  1565
    SDL_Rect full_rect;
slouken@7759
  1566
slouken@7759
  1567
    if (SDL_SW_UpdateYUVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch) < 0) {
slouken@7759
  1568
        return -1;
slouken@7759
  1569
    }
slouken@7759
  1570
slouken@7759
  1571
    full_rect.x = 0;
slouken@7759
  1572
    full_rect.y = 0;
slouken@7759
  1573
    full_rect.w = texture->w;
slouken@7759
  1574
    full_rect.h = texture->h;
slouken@7759
  1575
    rect = &full_rect;
slouken@7759
  1576
icculus@10650
  1577
    if (!rect->w || !rect->h) {
icculus@10650
  1578
        return 0;  /* nothing to do. */
icculus@10650
  1579
    }
icculus@10650
  1580
slouken@7759
  1581
    if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
slouken@7759
  1582
        /* We can lock the texture and copy to it */
icculus@10650
  1583
        void *native_pixels = NULL;
icculus@10650
  1584
        int native_pitch = 0;
slouken@7759
  1585
slouken@7759
  1586
        if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
slouken@7759
  1587
            return -1;
slouken@7759
  1588
        }
slouken@7759
  1589
        SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
slouken@7759
  1590
                            rect->w, rect->h, native_pixels, native_pitch);
slouken@7759
  1591
        SDL_UnlockTexture(native);
slouken@7759
  1592
    } else {
slouken@7759
  1593
        /* Use a temporary buffer for updating */
icculus@10650
  1594
        const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
icculus@10650
  1595
        const size_t alloclen = rect->h * temp_pitch;
icculus@10650
  1596
        if (alloclen > 0) {
icculus@10650
  1597
            void *temp_pixels = SDL_malloc(alloclen);
icculus@10650
  1598
            if (!temp_pixels) {
icculus@10650
  1599
                return SDL_OutOfMemory();
icculus@10650
  1600
            }
icculus@10650
  1601
            SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
icculus@10650
  1602
                                rect->w, rect->h, temp_pixels, temp_pitch);
icculus@10650
  1603
            SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
icculus@10650
  1604
            SDL_free(temp_pixels);
slouken@7759
  1605
        }
slouken@7759
  1606
    }
slouken@7759
  1607
    return 0;
slouken@7759
  1608
}
slouken@13440
  1609
#endif /* SDL_HAVE_YUV */
slouken@7759
  1610
slouken@7759
  1611
int SDL_UpdateYUVTexture(SDL_Texture * texture, const SDL_Rect * rect,
slouken@7759
  1612
                         const Uint8 *Yplane, int Ypitch,
slouken@7759
  1613
                         const Uint8 *Uplane, int Upitch,
slouken@7759
  1614
                         const Uint8 *Vplane, int Vpitch)
slouken@7759
  1615
{
sylvain@13433
  1616
#if SDL_HAVE_YUV
slouken@7759
  1617
    SDL_Renderer *renderer;
slouken@7759
  1618
    SDL_Rect full_rect;
slouken@7759
  1619
slouken@7759
  1620
    CHECK_TEXTURE_MAGIC(texture, -1);
slouken@7759
  1621
slouken@7759
  1622
    if (!Yplane) {
slouken@7759
  1623
        return SDL_InvalidParamError("Yplane");
slouken@7759
  1624
    }
slouken@7759
  1625
    if (!Ypitch) {
slouken@7759
  1626
        return SDL_InvalidParamError("Ypitch");
slouken@7759
  1627
    }
slouken@7759
  1628
    if (!Uplane) {
slouken@7759
  1629
        return SDL_InvalidParamError("Uplane");
slouken@7759
  1630
    }
slouken@7759
  1631
    if (!Upitch) {
slouken@7759
  1632
        return SDL_InvalidParamError("Upitch");
slouken@7759
  1633
    }
slouken@7759
  1634
    if (!Vplane) {
slouken@7759
  1635
        return SDL_InvalidParamError("Vplane");
slouken@7759
  1636
    }
slouken@7759
  1637
    if (!Vpitch) {
slouken@7759
  1638
        return SDL_InvalidParamError("Vpitch");
slouken@7759
  1639
    }
slouken@7759
  1640
slouken@7759
  1641
    if (texture->format != SDL_PIXELFORMAT_YV12 &&
slouken@7759
  1642
        texture->format != SDL_PIXELFORMAT_IYUV) {
slouken@7759
  1643
        return SDL_SetError("Texture format must by YV12 or IYUV");
slouken@7759
  1644
    }
slouken@7759
  1645
slouken@7759
  1646
    if (!rect) {
slouken@7759
  1647
        full_rect.x = 0;
slouken@7759
  1648
        full_rect.y = 0;
slouken@7759
  1649
        full_rect.w = texture->w;
slouken@7759
  1650
        full_rect.h = texture->h;
slouken@7759
  1651
        rect = &full_rect;
slouken@7759
  1652
    }
slouken@7759
  1653
icculus@10650
  1654
    if (!rect->w || !rect->h) {
icculus@10650
  1655
        return 0;  /* nothing to do. */
icculus@10650
  1656
    }
icculus@10650
  1657
slouken@7759
  1658
    if (texture->yuv) {
slouken@7759
  1659
        return SDL_UpdateTextureYUVPlanar(texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
slouken@7759
  1660
    } else {
slouken@7759
  1661
        SDL_assert(!texture->native);
slouken@7759
  1662
        renderer = texture->renderer;
slouken@7759
  1663
        SDL_assert(renderer->UpdateTextureYUV);
slouken@9084
  1664
        if (renderer->UpdateTextureYUV) {
icculus@12211
  1665
            if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
icculus@12211
  1666
                return -1;
icculus@12211
  1667
            }
slouken@9084
  1668
            return renderer->UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
slouken@9084
  1669
        } else {
slouken@9084
  1670
            return SDL_Unsupported();
slouken@9084
  1671
        }
slouken@9084
  1672
    }
sylvain@13433
  1673
#else
sylvain@13433
  1674
    return -1;
sylvain@13433
  1675
#endif
slouken@7759
  1676
}
slouken@7759
  1677
slouken@13440
  1678
#if SDL_HAVE_YUV
slouken@7759
  1679
static int
slouken@5156
  1680
SDL_LockTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
slouken@5156
  1681
                   void **pixels, int *pitch)
slouken@5156
  1682
{
slouken@5156
  1683
    return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch);
slouken@5156
  1684
}
slouken@13440
  1685
#endif /* SDL_HAVE_YUV */
slouken@5156
  1686
slouken@5156
  1687
static int
slouken@5156
  1688
SDL_LockTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
slouken@5156
  1689
                      void **pixels, int *pitch)
slouken@5156
  1690
{
slouken@5156
  1691
    texture->locked_rect = *rect;
slouken@5156
  1692
    *pixels = (void *) ((Uint8 *) texture->pixels +
slouken@5156
  1693
                        rect->y * texture->pitch +
slouken@5156
  1694
                        rect->x * SDL_BYTESPERPIXEL(texture->format));
slouken@5156
  1695
    *pitch = texture->pitch;
slouken@5156
  1696
    return 0;
slouken@5154
  1697
}
slouken@5154
  1698
slouken@5154
  1699
int
slouken@5156
  1700
SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect,
slouken@5154
  1701
                void **pixels, int *pitch)
slouken@5154
  1702
{
slouken@5154
  1703
    SDL_Rect full_rect;
slouken@5154
  1704
slouken@5154
  1705
    CHECK_TEXTURE_MAGIC(texture, -1);
slouken@5154
  1706
slouken@5154
  1707
    if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
icculus@7037
  1708
        return SDL_SetError("SDL_LockTexture(): texture must be streaming");
slouken@5154
  1709
    }
slouken@5156
  1710
slouken@5154
  1711
    if (!rect) {
slouken@5154
  1712
        full_rect.x = 0;
slouken@5154
  1713
        full_rect.y = 0;
slouken@5154
  1714
        full_rect.w = texture->w;
slouken@5154
  1715
        full_rect.h = texture->h;
slouken@5154
  1716
        rect = &full_rect;
slouken@5154
  1717
    }
slouken@5156
  1718
sylvain@13433
  1719
#if SDL_HAVE_YUV
slouken@5156
  1720
    if (texture->yuv) {
icculus@12211
  1721
        if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
icculus@12211
  1722
            return -1;
icculus@12211
  1723
        }
slouken@5156
  1724
        return SDL_LockTextureYUV(texture, rect, pixels, pitch);
sylvain@13433
  1725
    } else
sylvain@13433
  1726
#endif
sylvain@13433
  1727
    if (texture->native) {
icculus@12211
  1728
        /* Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. */
slouken@5156
  1729
        return SDL_LockTextureNative(texture, rect, pixels, pitch);
slouken@5156
  1730
    } else {
icculus@12211
  1731
        SDL_Renderer *renderer = texture->renderer;
icculus@12211
  1732
        if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
icculus@12211
  1733
            return -1;
icculus@12211
  1734
        }
slouken@5156
  1735
        return renderer->LockTexture(renderer, texture, rect, pixels, pitch);
slouken@5156
  1736
    }
slouken@5156
  1737
}
slouken@5156
  1738
sylvain@13099
  1739
int
sylvain@13099
  1740
SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect,
sylvain@13099
  1741
                         SDL_Surface **surface)
sylvain@13099
  1742
{
sylvain@13101
  1743
    SDL_Rect real_rect;
sylvain@13099
  1744
    void *pixels = NULL;
sylvain@13099
  1745
    int pitch, ret;
sylvain@13099
  1746
sylvain@13099
  1747
    if (texture == NULL || surface == NULL) {
sylvain@13099
  1748
        return -1;
sylvain@13099
  1749
    }
sylvain@13099
  1750
sylvain@13101
  1751
    real_rect.x = 0;
sylvain@13101
  1752
    real_rect.y = 0;
sylvain@13101
  1753
    real_rect.w = texture->w;
sylvain@13101
  1754
    real_rect.h = texture->h;
sylvain@13101
  1755
sylvain@13101
  1756
    if (rect) {
sylvain@13101
  1757
        SDL_IntersectRect(rect, &real_rect, &real_rect);
sylvain@13099
  1758
    }
sylvain@13099
  1759
sylvain@13101
  1760
    ret = SDL_LockTexture(texture, &real_rect, &pixels, &pitch);
sylvain@13099
  1761
    if (ret < 0) {
sylvain@13099
  1762
        return ret;
sylvain@13099
  1763
    }
sylvain@13099
  1764
sylvain@13101
  1765
    texture->locked_surface = SDL_CreateRGBSurfaceWithFormatFrom(pixels, real_rect.w, real_rect.h, 0, pitch, texture->format);
sylvain@13099
  1766
    if (texture->locked_surface == NULL) {
sylvain@13099
  1767
        SDL_UnlockTexture(texture);
sylvain@13099
  1768
        return -1;
sylvain@13099
  1769
    }
sylvain@13099
  1770
sylvain@13099
  1771
    *surface = texture->locked_surface;
sylvain@13099
  1772
    return 0;
sylvain@13099
  1773
}
sylvain@13099
  1774
slouken@13440
  1775
#if SDL_HAVE_YUV
slouken@5156
  1776
static void
slouken@5156
  1777
SDL_UnlockTextureYUV(SDL_Texture * texture)
slouken@5156
  1778
{
slouken@5156
  1779
    SDL_Texture *native = texture->native;
icculus@8649
  1780
    void *native_pixels = NULL;
icculus@8649
  1781
    int native_pitch = 0;
slouken@5156
  1782
    SDL_Rect rect;
slouken@5156
  1783
slouken@5156
  1784
    rect.x = 0;
slouken@5156
  1785
    rect.y = 0;
slouken@5156
  1786
    rect.w = texture->w;
slouken@5156
  1787
    rect.h = texture->h;
slouken@5156
  1788
slouken@5156
  1789
    if (SDL_LockTexture(native, &rect, &native_pixels, &native_pitch) < 0) {
slouken@5156
  1790
        return;
slouken@5156
  1791
    }
slouken@5156
  1792
    SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format,
slouken@5156
  1793
                        rect.w, rect.h, native_pixels, native_pitch);
slouken@5156
  1794
    SDL_UnlockTexture(native);
slouken@5156
  1795
}
slouken@13440
  1796
#endif /* SDL_HAVE_YUV */
slouken@5156
  1797
slouken@6044
  1798
static void
slouken@5156
  1799
SDL_UnlockTextureNative(SDL_Texture * texture)
slouken@5156
  1800
{
slouken@5156
  1801
    SDL_Texture *native = texture->native;
icculus@8649
  1802
    void *native_pixels = NULL;
icculus@8649
  1803
    int native_pitch = 0;
slouken@5156
  1804
    const SDL_Rect *rect = &texture->locked_rect;
slouken@5156
  1805
    const void* pixels = (void *) ((Uint8 *) texture->pixels +
slouken@5156
  1806
                        rect->y * texture->pitch +
slouken@5156
  1807
                        rect->x * SDL_BYTESPERPIXEL(texture->format));
slouken@5156
  1808
    int pitch = texture->pitch;
slouken@5156
  1809
slouken@5156
  1810
    if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
slouken@5156
  1811
        return;
slouken@5156
  1812
    }
slouken@5156
  1813
    SDL_ConvertPixels(rect->w, rect->h,
slouken@5156
  1814
                      texture->format, pixels, pitch,
slouken@5156
  1815
                      native->format, native_pixels, native_pitch);
slouken@5156
  1816
    SDL_UnlockTexture(native);
slouken@5154
  1817
}
slouken@5154
  1818
slouken@5154
  1819
void
slouken@5154
  1820
SDL_UnlockTexture(SDL_Texture * texture)
slouken@5154
  1821
{
slouken@5154
  1822
    CHECK_TEXTURE_MAGIC(texture, );
slouken@5154
  1823
slouken@5154
  1824
    if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
slouken@5154
  1825
        return;
slouken@5154
  1826
    }
sylvain@13433
  1827
#if SDL_HAVE_YUV
slouken@5156
  1828
    if (texture->yuv) {
slouken@5156
  1829
        SDL_UnlockTextureYUV(texture);
sylvain@13433
  1830
    } else
sylvain@13433
  1831
#endif
sylvain@13433
  1832
    if (texture->native) {
slouken@5156
  1833
        SDL_UnlockTextureNative(texture);
slouken@5156
  1834
    } else {
icculus@12211
  1835
        SDL_Renderer *renderer = texture->renderer;
slouken@5156
  1836
        renderer->UnlockTexture(renderer, texture);
slouken@5154
  1837
    }
sylvain@13099
  1838
sylvain@13099
  1839
    SDL_FreeSurface(texture->locked_surface);
sylvain@13099
  1840
    texture->locked_surface = NULL;
slouken@5154
  1841
}
slouken@5154
  1842
slouken@6246
  1843
SDL_bool
slouken@6246
  1844
SDL_RenderTargetSupported(SDL_Renderer *renderer)
slouken@6246
  1845
{
slouken@6247
  1846
    if (!renderer || !renderer->SetRenderTarget) {
slouken@6246
  1847
        return SDL_FALSE;
slouken@6246
  1848
    }
slouken@6246
  1849
    return (renderer->info.flags & SDL_RENDERER_TARGETTEXTURE) != 0;
slouken@6246
  1850
}
slouken@6246
  1851
slouken@6246
  1852
int
slouken@6247
  1853
SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
slouken@6246
  1854
{
slouken@6246
  1855
    if (!SDL_RenderTargetSupported(renderer)) {
icculus@7037
  1856
        return SDL_Unsupported();
slouken@6246
  1857
    }
slouken@6246
  1858
    if (texture == renderer->target) {
slouken@6246
  1859
        /* Nothing to do! */
slouken@6246
  1860
        return 0;
slouken@6246
  1861
    }
slouken@6246
  1862
icculus@12211
  1863
    FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
icculus@12211
  1864
slouken@6246
  1865
    /* texture == NULL is valid and means reset the target to the window */
slouken@6246
  1866
    if (texture) {
slouken@6246
  1867
        CHECK_TEXTURE_MAGIC(texture, -1);
slouken@6246
  1868
        if (renderer != texture->renderer) {
icculus@7037
  1869
            return SDL_SetError("Texture was not created with this renderer");
slouken@6246
  1870
        }
gabomdq@6337
  1871
        if (texture->access != SDL_TEXTUREACCESS_TARGET) {
icculus@7037
  1872
            return SDL_SetError("Texture not created with SDL_TEXTUREACCESS_TARGET");
slouken@6246
  1873
        }
slouken@6246
  1874
        if (texture->native) {
slouken@6246
  1875
            /* Always render to the native texture */
slouken@6246
  1876
            texture = texture->native;
slouken@6246
  1877
        }
slouken@6246
  1878
    }
slouken@6246
  1879
slouken@12021
  1880
    SDL_LockMutex(renderer->target_mutex);
slouken@12021
  1881
slouken@6246
  1882
    if (texture && !renderer->target) {
slouken@6246
  1883
        /* Make a backup of the viewport */
slouken@6246
  1884
        renderer->viewport_backup = renderer->viewport;
slouken@7141
  1885
        renderer->clip_rect_backup = renderer->clip_rect;
jorgenpt@8728
  1886
        renderer->clipping_enabled_backup = renderer->clipping_enabled;
slouken@6528
  1887
        renderer->scale_backup = renderer->scale;
slouken@6581
  1888
        renderer->logical_w_backup = renderer->logical_w;
slouken@6581
  1889
        renderer->logical_h_backup = renderer->logical_h;
slouken@6246
  1890
    }
slouken@6246
  1891
    renderer->target = texture;
slouken@6246
  1892
slouken@6247
  1893
    if (renderer->SetRenderTarget(renderer, texture) < 0) {
slouken@12021
  1894
        SDL_UnlockMutex(renderer->target_mutex);
slouken@6246
  1895
        return -1;
slouken@6246
  1896
    }
slouken@6246
  1897
slouken@6246
  1898
    if (texture) {
slouken@6528
  1899
        renderer->viewport.x = 0;
slouken@6528
  1900
        renderer->viewport.y = 0;
slouken@6528
  1901
        renderer->viewport.w = texture->w;
slouken@6528
  1902
        renderer->viewport.h = texture->h;
slouken@10428
  1903
        SDL_zero(renderer->clip_rect);
slouken@10428
  1904
        renderer->clipping_enabled = SDL_FALSE;
slouken@6528
  1905
        renderer->scale.x = 1.0f;
slouken@6528
  1906
        renderer->scale.y = 1.0f;
slouken@7339
  1907
        renderer->logical_w = texture->w;
slouken@7339
  1908
        renderer->logical_h = texture->h;
slouken@6246
  1909
    } else {
slouken@6528
  1910
        renderer->viewport = renderer->viewport_backup;
slouken@7141
  1911
        renderer->clip_rect = renderer->clip_rect_backup;
jorgenpt@8728
  1912
        renderer->clipping_enabled = renderer->clipping_enabled_backup;
slouken@6528
  1913
        renderer->scale = renderer->scale_backup;
slouken@6581
  1914
        renderer->logical_w = renderer->logical_w_backup;
slouken@6581
  1915
        renderer->logical_h = renderer->logical_h_backup;
slouken@6246
  1916
    }
slouken@12021
  1917
slouken@12021
  1918
    SDL_UnlockMutex(renderer->target_mutex);
slouken@12021
  1919
icculus@12214
  1920
    if (QueueCmdSetViewport(renderer) < 0) {
slouken@6246
  1921
        return -1;
slouken@6246
  1922
    }
icculus@12214
  1923
    if (QueueCmdSetClipRect(renderer) < 0) {
slouken@7141
  1924
        return -1;
slouken@7141
  1925
    }
slouken@6246
  1926
slouken@6246
  1927
    /* All set! */
icculus@12236
  1928
    return FlushRenderCommandsIfNotBatching(renderer);
slouken@6246
  1929
}
slouken@6246
  1930
slouken@6578
  1931
SDL_Texture *
slouken@6578
  1932
SDL_GetRenderTarget(SDL_Renderer *renderer)
slouken@6578
  1933
{
slouken@6578
  1934
    return renderer->target;
slouken@6578
  1935
}
slouken@6578
  1936
slouken@6530
  1937
static int
slouken@6530
  1938
UpdateLogicalSize(SDL_Renderer *renderer)
slouken@6530
  1939
{
icculus@8654
  1940
    int w = 1, h = 1;
slouken@6530
  1941
    float want_aspect;
slouken@6530
  1942
    float real_aspect;
slouken@6530
  1943
    float scale;
slouken@6530
  1944
    SDL_Rect viewport;
ewing@11072
  1945
    /* 0 is for letterbox, 1 is for overscan */
ewing@11072
  1946
    int scale_policy = 0;
slouken@11771
  1947
    const char *hint;
slouken@6530
  1948
slouken@10532
  1949
    if (!renderer->logical_w || !renderer->logical_h) {
slouken@10532
  1950
        return 0;
slouken@10532
  1951
    }
slouken@7239
  1952
    if (SDL_GetRendererOutputSize(renderer, &w, &h) < 0) {
slouken@7239
  1953
        return -1;
slouken@6530
  1954
    }
slouken@6530
  1955
slouken@11771
  1956
    hint = SDL_GetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE);
slouken@11771
  1957
    if (hint && (*hint == '1' || SDL_strcasecmp(hint, "overscan") == 0))  {
sezeroz@11960
  1958
#if SDL_VIDEO_RENDER_D3D
slouken@11771
  1959
        SDL_bool overscan_supported = SDL_TRUE;
slouken@11771
  1960
        /* Unfortunately, Direct3D 9 doesn't support negative viewport numbers
slouken@11771
  1961
           which the overscan implementation relies on.
ewing@11076
  1962
        */
slouken@11771
  1963
        if (SDL_strcasecmp(SDL_GetCurrentVideoDriver(), "direct3d") == 0) {
slouken@11771
  1964
            overscan_supported = SDL_FALSE;
slouken@11771
  1965
        }
slouken@11771
  1966
        if (overscan_supported) {
ewing@11076
  1967
            scale_policy = 1;
ewing@11076
  1968
        }
sezeroz@11960
  1969
#else
sezeroz@11960
  1970
        scale_policy = 1;
sezeroz@11960
  1971
#endif
ewing@11072
  1972
    }
slouken@11771
  1973
slouken@6530
  1974
    want_aspect = (float)renderer->logical_w / renderer->logical_h;
slouken@6530
  1975
    real_aspect = (float)w / h;
slouken@6530
  1976
slouken@6530
  1977
    /* Clear the scale because we're setting viewport in output coordinates */
slouken@6530
  1978
    SDL_RenderSetScale(renderer, 1.0f, 1.0f);
slouken@6530
  1979
flibitijibibo@10039
  1980
    if (renderer->integer_scale) {
flibitijibibo@10039
  1981
        if (want_aspect > real_aspect) {
philipp@10054
  1982
            scale = (float)(w / renderer->logical_w);
flibitijibibo@10039
  1983
        } else {
philipp@10054
  1984
            scale = (float)(h / renderer->logical_h);
flibitijibibo@10039
  1985
        }
flibitijibibo@10039
  1986
        viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
flibitijibibo@10039
  1987
        viewport.x = (w - viewport.w) / 2;
flibitijibibo@10039
  1988
        viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
flibitijibibo@10039
  1989
        viewport.y = (h - viewport.h) / 2;
flibitijibibo@10039
  1990
flibitijibibo@10039
  1991
        SDL_RenderSetViewport(renderer, &viewport);
flibitijibibo@10039
  1992
    } else if (SDL_fabs(want_aspect-real_aspect) < 0.0001) {
slouken@6530
  1993
        /* The aspect ratios are the same, just scale appropriately */
slouken@6530
  1994
        scale = (float)w / renderer->logical_w;
slouken@6530
  1995
        SDL_RenderSetViewport(renderer, NULL);
slouken@6530
  1996
    } else if (want_aspect > real_aspect) {
ewing@11072
  1997
        if (scale_policy == 1) {
ewing@11072
  1998
            /* We want a wider aspect ratio than is available - 
ewing@11072
  1999
             zoom so logical height matches the real height 
ewing@11072
  2000
             and the width will grow off the screen 
ewing@11072
  2001
             */
ewing@11072
  2002
            scale = (float)h / renderer->logical_h;
ewing@11072
  2003
            viewport.y = 0;
ewing@11072
  2004
            viewport.h = h;
ewing@11072
  2005
            viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
ewing@11072
  2006
            viewport.x = (w - viewport.w) / 2;
ewing@11072
  2007
            SDL_RenderSetViewport(renderer, &viewport);
ewing@11072
  2008
        } else {
ewing@11072
  2009
            /* We want a wider aspect ratio than is available - letterbox it */
ewing@11072
  2010
            scale = (float)w / renderer->logical_w;
ewing@11072
  2011
            viewport.x = 0;
ewing@11072
  2012
            viewport.w = w;
ewing@11072
  2013
            viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
ewing@11072
  2014
            viewport.y = (h - viewport.h) / 2;
ewing@11072
  2015
            SDL_RenderSetViewport(renderer, &viewport);
ewing@11072
  2016
        }
slouken@6530
  2017
    } else {
ewing@11072
  2018
        if (scale_policy == 1) {
ewing@11072
  2019
            /* We want a narrower aspect ratio than is available -
ewing@11072
  2020
             zoom so logical width matches the real width
ewing@11072
  2021
             and the height will grow off the screen
ewing@11072
  2022
             */
ewing@11072
  2023
            scale = (float)w / renderer->logical_w;
ewing@11072
  2024
            viewport.x = 0;
ewing@11072
  2025
            viewport.w = w;
ewing@11072
  2026
            viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
ewing@11072
  2027
            viewport.y = (h - viewport.h) / 2;
ewing@11072
  2028
            SDL_RenderSetViewport(renderer, &viewport);
ewing@11072
  2029
        } else {
ewing@11072
  2030
            /* We want a narrower aspect ratio than is available - use side-bars */
ewing@11072
  2031
             scale = (float)h / renderer->logical_h;
ewing@11072
  2032
             viewport.y = 0;
ewing@11072
  2033
             viewport.h = h;
ewing@11072
  2034
             viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
ewing@11072
  2035
             viewport.x = (w - viewport.w) / 2;
ewing@11072
  2036
             SDL_RenderSetViewport(renderer, &viewport);
ewing@11072
  2037
        }
slouken@6530
  2038
    }
slouken@6530
  2039
slouken@6530
  2040
    /* Set the new scale */
slouken@6530
  2041
    SDL_RenderSetScale(renderer, scale, scale);
slouken@6531
  2042
slouken@6531
  2043
    return 0;
slouken@6530
  2044
}
slouken@6530
  2045
slouken@6530
  2046
int
slouken@6530
  2047
SDL_RenderSetLogicalSize(SDL_Renderer * renderer, int w, int h)
slouken@6530
  2048
{
slouken@6530
  2049
    CHECK_RENDERER_MAGIC(renderer, -1);
slouken@6530
  2050
slouken@6530
  2051
    if (!w || !h) {
slouken@6530
  2052
        /* Clear any previous logical resolution */
slouken@6530
  2053
        renderer->logical_w = 0;
slouken@6530
  2054
        renderer->logical_h = 0;
slouken@6530
  2055
        SDL_RenderSetViewport(renderer, NULL);
slouken@6530
  2056
        SDL_RenderSetScale(renderer, 1.0f, 1.0f);
slouken@6530
  2057
        return 0;
slouken@6530
  2058
    }
slouken@6530
  2059
slouken@6530
  2060
    renderer->logical_w = w;
slouken@6530
  2061
    renderer->logical_h = h;
slouken@6530
  2062
slouken@6530
  2063
    return UpdateLogicalSize(renderer);
slouken@6530
  2064
}
slouken@6530
  2065
slouken@6530
  2066
void
slouken@6530
  2067
SDL_RenderGetLogicalSize(SDL_Renderer * renderer, int *w, int *h)
slouken@6530
  2068
{
slouken@6530
  2069
    CHECK_RENDERER_MAGIC(renderer, );
slouken@6530
  2070
slouken@6530
  2071
    if (w) {
slouken@6530
  2072
        *w = renderer->logical_w;
slouken@6530
  2073
    }
slouken@6530
  2074
    if (h) {
slouken@6530
  2075
        *h = renderer->logical_h;
slouken@6530
  2076
    }
slouken@6530
  2077
}
slouken@6530
  2078
slouken@5297
  2079
int
flibitijibibo@10039
  2080
SDL_RenderSetIntegerScale(SDL_Renderer * renderer, SDL_bool enable)
flibitijibibo@10039
  2081
{
flibitijibibo@10039
  2082
    CHECK_RENDERER_MAGIC(renderer, -1);
flibitijibibo@10039
  2083
flibitijibibo@10039
  2084
    renderer->integer_scale = enable;
flibitijibibo@10039
  2085
flibitijibibo@10039
  2086
    return UpdateLogicalSize(renderer);
flibitijibibo@10039
  2087
}
flibitijibibo@10039
  2088
flibitijibibo@10039
  2089
SDL_bool
flibitijibibo@10039
  2090
SDLCALL SDL_RenderGetIntegerScale(SDL_Renderer * renderer)
flibitijibibo@10039
  2091
{
flibitijibibo@10039
  2092
    CHECK_RENDERER_MAGIC(renderer, SDL_FALSE);
flibitijibibo@10039
  2093
flibitijibibo@10039
  2094
    return renderer->integer_scale;
flibitijibibo@10039
  2095
}
flibitijibibo@10039
  2096
flibitijibibo@10039
  2097
int
slouken@5297
  2098
SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect)
slouken@5297
  2099
{
icculus@12236
  2100
    int retval;
slouken@5297
  2101
    CHECK_RENDERER_MAGIC(renderer, -1);
slouken@5297
  2102
slouken@5297
  2103
    if (rect) {
slouken@6528
  2104
        renderer->viewport.x = (int)SDL_floor(rect->x * renderer->scale.x);
slouken@6528
  2105
        renderer->viewport.y = (int)SDL_floor(rect->y * renderer->scale.y);
slouken@6528
  2106
        renderer->viewport.w = (int)SDL_ceil(rect->w * renderer->scale.x);
slouken@6528
  2107
        renderer->viewport.h = (int)SDL_ceil(rect->h * renderer->scale.y);
slouken@5297
  2108
    } else {
slouken@5297
  2109
        renderer->viewport.x = 0;
slouken@5297
  2110
        renderer->viewport.y = 0;
slouken@7239
  2111
        if (SDL_GetRendererOutputSize(renderer, &renderer->viewport.w, &renderer->viewport.h) < 0) {
slouken@7239
  2112
            return -1;
slouken@5297
  2113
        }
slouken@5297
  2114
    }
icculus@12236
  2115
    retval = QueueCmdSetViewport(renderer);
icculus@12236
  2116
    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
slouken@5297
  2117
}
slouken@5297
  2118
slouken@5224
  2119
void
slouken@5297
  2120
SDL_RenderGetViewport(SDL_Renderer * renderer, SDL_Rect * rect)
slouken@5224
  2121
{
slouken@5224
  2122
    CHECK_RENDERER_MAGIC(renderer, );
slouken@5224
  2123
slouken@6528
  2124
    if (rect) {
slouken@6528
  2125
        rect->x = (int)(renderer->viewport.x / renderer->scale.x);
slouken@6528
  2126
        rect->y = (int)(renderer->viewport.y / renderer->scale.y);
slouken@6528
  2127
        rect->w = (int)(renderer->viewport.w / renderer->scale.x);
slouken@6528
  2128
        rect->h = (int)(renderer->viewport.h / renderer->scale.y);
slouken@6528
  2129
    }
slouken@6528
  2130
}
slouken@6528
  2131
slouken@6528
  2132
int
slouken@7141
  2133
SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect)
slouken@7141
  2134
{
icculus@12236
  2135
    int retval;
philipp@7143
  2136
    CHECK_RENDERER_MAGIC(renderer, -1)
slouken@7141
  2137
slouken@7141
  2138
    if (rect) {
jorgenpt@8728
  2139
        renderer->clipping_enabled = SDL_TRUE;
slouken@7141
  2140
        renderer->clip_rect.x = (int)SDL_floor(rect->x * renderer->scale.x);
slouken@7141
  2141
        renderer->clip_rect.y = (int)SDL_floor(rect->y * renderer->scale.y);
slouken@7141
  2142
        renderer->clip_rect.w = (int)SDL_ceil(rect->w * renderer->scale.x);
slouken@7141
  2143
        renderer->clip_rect.h = (int)SDL_ceil(rect->h * renderer->scale.y);
slouken@7141
  2144
    } else {
jorgenpt@8728
  2145
        renderer->clipping_enabled = SDL_FALSE;
slouken@7141
  2146
        SDL_zero(renderer->clip_rect);
slouken@7141
  2147
    }
icculus@12236
  2148
icculus@12236
  2149
    retval = QueueCmdSetClipRect(renderer);
icculus@12236
  2150
    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
slouken@7141
  2151
}
slouken@7141
  2152
slouken@7141
  2153
void
slouken@7141
  2154
SDL_RenderGetClipRect(SDL_Renderer * renderer, SDL_Rect * rect)
slouken@7141
  2155
{
slouken@7141
  2156
    CHECK_RENDERER_MAGIC(renderer, )
slouken@7141
  2157
slouken@7141
  2158
    if (rect) {
slouken@7141
  2159
        rect->x = (int)(renderer->clip_rect.x / renderer->scale.x);
slouken@7141
  2160
        rect->y = (int)(renderer->clip_rect.y / renderer->scale.y);
slouken@7141
  2161
        rect->w = (int)(renderer->clip_rect.w / renderer->scale.x);
slouken@7141
  2162
        rect->h = (int)(renderer->clip_rect.h / renderer->scale.y);
slouken@7141
  2163
    }
slouken@7141
  2164
}
slouken@7141
  2165
jorgenpt@8728
  2166
SDL_bool
jorgenpt@8728
  2167
SDL_RenderIsClipEnabled(SDL_Renderer * renderer)
jorgenpt@8728
  2168
{
jorgenpt@8728
  2169
    CHECK_RENDERER_MAGIC(renderer, SDL_FALSE)
jorgenpt@8728
  2170
    return renderer->clipping_enabled;
jorgenpt@8728
  2171
}
jorgenpt@8728
  2172
slouken@7141
  2173
int
slouken@6528
  2174
SDL_RenderSetScale(SDL_Renderer * renderer, float scaleX, float scaleY)
slouken@6528
  2175
{
slouken@6528
  2176
    CHECK_RENDERER_MAGIC(renderer, -1);
slouken@6528
  2177
slouken@6528
  2178
    renderer->scale.x = scaleX;
slouken@6528
  2179
    renderer->scale.y = scaleY;
slouken@6528
  2180
    return 0;
slouken@6528
  2181
}
slouken@6528
  2182
slouken@6528
  2183
void
slouken@6528
  2184
SDL_RenderGetScale(SDL_Renderer * renderer, float *scaleX, float *scaleY)
slouken@6528
  2185
{
slouken@6528
  2186
    CHECK_RENDERER_MAGIC(renderer, );
slouken@6528
  2187
slouken@6528
  2188
    if (scaleX) {
slouken@6528
  2189
        *scaleX = renderer->scale.x;
slouken@6528
  2190
    }
slouken@6528
  2191
    if (scaleY) {
slouken@6528
  2192
        *scaleY = renderer->scale.y;
slouken@6528
  2193
    }
slouken@5224
  2194
}
slouken@5224
  2195
slouken@5154
  2196
int
slouken@5154
  2197
SDL_SetRenderDrawColor(SDL_Renderer * renderer,
slouken@5154
  2198
                       Uint8 r, Uint8 g, Uint8 b, Uint8 a)
slouken@5154
  2199
{
slouken@5154
  2200
    CHECK_RENDERER_MAGIC(renderer, -1);
slouken@5154
  2201
slouken@5154
  2202
    renderer->r = r;
slouken@5154
  2203
    renderer->g = g;
slouken@5154
  2204
    renderer->b = b;
slouken@5154
  2205
    renderer->a = a;
slouken@5154
  2206
    return 0;
slouken@5154
  2207
}
slouken@5154
  2208
slouken@5154
  2209
int
slouken@5154
  2210
SDL_GetRenderDrawColor(SDL_Renderer * renderer,
slouken@5154
  2211
                       Uint8 * r, Uint8 * g, Uint8 * b, Uint8 * a)
slouken@5154
  2212
{
slouken@5154
  2213
    CHECK_RENDERER_MAGIC(renderer, -1);
slouken@5154
  2214
slouken@5154
  2215
    if (r) {
slouken@5154
  2216
        *r = renderer->r;
slouken@5154
  2217
    }
slouken@5154
  2218
    if (g) {
slouken@5154
  2219
        *g = renderer->g;
slouken@5154
  2220
    }
slouken@5154
  2221
    if (b) {
slouken@5154
  2222
        *b = renderer->b;
slouken@5154
  2223
    }
slouken@5154
  2224
    if (a) {
slouken@5154
  2225
        *a = renderer->a;
slouken@5154
  2226
    }
slouken@5154
  2227
    return 0;
slouken@5154
  2228
}
slouken@5154
  2229
slouken@5154
  2230
int
slouken@5154
  2231
SDL_SetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
slouken@5154
  2232
{
slouken@5154
  2233
    CHECK_RENDERER_MAGIC(renderer, -1);
slouken@5154
  2234
slouken@11282
  2235
    if (!IsSupportedBlendMode(renderer, blendMode)) {
slouken@11282
  2236
        return SDL_Unsupported();
slouken@11282
  2237
    }
slouken@5154
  2238
    renderer->blendMode = blendMode;
slouken@5154
  2239
    return 0;
slouken@5154
  2240
}
slouken@5154
  2241
slouken@5154
  2242
int
slouken@5154
  2243
SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode *blendMode)
slouken@5154
  2244
{
slouken@5154
  2245
    CHECK_RENDERER_MAGIC(renderer, -1);
slouken@5154
  2246
slouken@5154
  2247
    *blendMode = renderer->blendMode;
slouken@5154
  2248
    return 0;
slouken@5154
  2249
}
slouken@5154
  2250
slouken@5154
  2251
int
slouken@5154
  2252
SDL_RenderClear(SDL_Renderer * renderer)
slouken@5154
  2253
{
icculus@12236
  2254
    int retval;
slouken@5154
  2255
    CHECK_RENDERER_MAGIC(renderer, -1);
icculus@12236
  2256
    retval = QueueCmdClear(renderer);
icculus@12236
  2257
    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
slouken@5154
  2258
}
slouken@5154
  2259
icculus@12352
  2260
icculus@12352
  2261
/* !!! FIXME: delete all the duplicate code for the integer versions in 2.1,
icculus@12352
  2262
   !!! FIXME:  making the floating point versions the only available APIs. */
icculus@12352
  2263
slouken@5154
  2264
int
slouken@5154
  2265
SDL_RenderDrawPoint(SDL_Renderer * renderer, int x, int y)
slouken@5154
  2266
{
sezeroz@12383
  2267
    SDL_FPoint fpoint;
sezeroz@12383
  2268
    fpoint.x = (float) x;
sezeroz@12383
  2269
    fpoint.y = (float) y;
icculus@12352
  2270
    return SDL_RenderDrawPointsF(renderer, &fpoint, 1);
icculus@12352
  2271
}
icculus@12352
  2272
icculus@12352
  2273
int
icculus@12352
  2274
SDL_RenderDrawPointF(SDL_Renderer * renderer, float x, float y)
icculus@12352
  2275
{
sezeroz@12383
  2276
    SDL_FPoint fpoint;
sezeroz@12383
  2277
    fpoint.x = x;
sezeroz@12383
  2278
    fpoint.y = y;
icculus@12352
  2279
    return SDL_RenderDrawPointsF(renderer, &fpoint, 1);
slouken@5154
  2280
}
slouken@5154
  2281
slouken@6528
  2282
static int
slouken@6528
  2283
RenderDrawPointsWithRects(SDL_Renderer * renderer,
icculus@12352
  2284
                          const SDL_Point * points, const int count)
slouken@6528
  2285
{
icculus@12236
  2286
    int retval = -1;
icculus@12349
  2287
    SDL_bool isstack;
icculus@12352
  2288
    SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack);
icculus@12352
  2289
    int i;
icculus@12352
  2290
slouken@6528
  2291
    if (!frects) {
icculus@7037
  2292
        return SDL_OutOfMemory();
slouken@6528
  2293
    }
icculus@12352
  2294
slouken@6528
  2295
    for (i = 0; i < count; ++i) {
slouken@6528
  2296
        frects[i].x = points[i].x * renderer->scale.x;
slouken@6528
  2297
        frects[i].y = points[i].y * renderer->scale.y;
slouken@6528
  2298
        frects[i].w = renderer->scale.x;
slouken@6528
  2299
        frects[i].h = renderer->scale.y;
slouken@6528
  2300
    }
slouken@6528
  2301
icculus@12236
  2302
    retval = QueueCmdFillRects(renderer, frects, count);
slouken@6528
  2303
icculus@12349
  2304
    SDL_small_free(frects, isstack);
slouken@6528
  2305
icculus@12236
  2306
    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
slouken@6528
  2307
}
slouken@6528
  2308
slouken@5154
  2309
int
slouken@5154
  2310
SDL_RenderDrawPoints(SDL_Renderer * renderer,
slouken@5154
  2311
                     const SDL_Point * points, int count)
slouken@5154
  2312
{
slouken@6528
  2313
    SDL_FPoint *fpoints;
slouken@6528
  2314
    int i;
icculus@12236
  2315
    int retval;
icculus@12349
  2316
    SDL_bool isstack;
slouken@6528
  2317
slouken@5154
  2318
    CHECK_RENDERER_MAGIC(renderer, -1);
slouken@5154
  2319
slouken@5154
  2320
    if (!points) {
icculus@7037
  2321
        return SDL_SetError("SDL_RenderDrawPoints(): Passed NULL points");
slouken@5154
  2322
    }
slouken@5154
  2323
    if (count < 1) {
slouken@5154
  2324
        return 0;
slouken@5154
  2325
    }
slouken@10440
  2326
slouken@6260
  2327
    /* Don't draw while we're hidden */
slouken@6260
  2328
    if (renderer->hidden) {
slouken@6060
  2329
        return 0;
slouken@6060
  2330
    }
slouken@6528
  2331
slouken@6528
  2332
    if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
slouken@6528
  2333
        return RenderDrawPointsWithRects(renderer, points, count);
slouken@6528
  2334
    }
slouken@6528
  2335
icculus@12349
  2336
    fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
slouken@6528
  2337
    if (!fpoints) {
icculus@7037
  2338
        return SDL_OutOfMemory();
slouken@6528
  2339
    }
slouken@6528
  2340
    for (i = 0; i < count; ++i) {
slouken@6528
  2341
        fpoints[i].x = points[i].x * renderer->scale.x;
slouken@6528
  2342
        fpoints[i].y = points[i].y * renderer->scale.y;
slouken@6528
  2343
    }
slouken@6528
  2344
icculus@12236
  2345
    retval = QueueCmdDrawPoints(renderer, fpoints, count);
slouken@6528
  2346
icculus@12349
  2347
    SDL_small_free(fpoints, isstack);
slouken@6528
  2348
icculus@12236
  2349
    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
slouken@5154
  2350
}
slouken@5154
  2351
icculus@12352
  2352
static int
icculus@12352
  2353
RenderDrawPointsWithRectsF(SDL_Renderer * renderer,
icculus@12352
  2354
                           const SDL_FPoint * fpoints, const int count)
icculus@12352
  2355
{
icculus@12352
  2356
    int retval = -1;
icculus@12352
  2357
    SDL_bool isstack;
icculus@12352
  2358
    SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack);
icculus@12352
  2359
    int i;
icculus@12352
  2360
icculus@12352
  2361
    if (!frects) {
icculus@12352
  2362
        return SDL_OutOfMemory();
icculus@12352
  2363
    }
icculus@12352
  2364
icculus@12352
  2365
    for (i = 0; i < count; ++i) {
icculus@12352
  2366
        frects[i].x = fpoints[i].x * renderer->scale.x;
icculus@12352
  2367
        frects[i].y = fpoints[i].y * renderer->scale.y;
icculus@12352
  2368
        frects[i].w = renderer->scale.x;
icculus@12352
  2369
        frects[i].h = renderer->scale.y;
icculus@12352
  2370
    }
icculus@12352
  2371
icculus@12352
  2372
    retval = QueueCmdFillRects(renderer, frects, count);
icculus@12352
  2373
icculus@12352
  2374
    SDL_small_free(frects, isstack);
icculus@12352
  2375
icculus@12352
  2376
    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
icculus@12352
  2377
}
icculus@12352
  2378
icculus@12352
  2379
int
icculus@12352
  2380
SDL_RenderDrawPointsF(SDL_Renderer * renderer,
icculus@12352
  2381
                      const SDL_FPoint * points, int count)
icculus@12352
  2382
{
icculus@12352
  2383
    SDL_FPoint *fpoints;
icculus@12352
  2384
    int i;
icculus@12352
  2385
    int retval;
icculus@12352
  2386
    SDL_bool isstack;
icculus@12352
  2387
icculus@12352
  2388
    CHECK_RENDERER_MAGIC(renderer, -1);
icculus@12352
  2389
icculus@12352
  2390
    if (!points) {
icculus@12352
  2391
        return SDL_SetError("SDL_RenderDrawFPoints(): Passed NULL points");
icculus@12352
  2392
    }
icculus@12352
  2393
    if (count < 1) {
icculus@12352
  2394
        return 0;
icculus@12352
  2395
    }
icculus@12352
  2396
icculus@12352
  2397
    /* Don't draw while we're hidden */
icculus@12352
  2398
    if (renderer->hidden) {
icculus@12352
  2399
        return 0;
icculus@12352
  2400
    }
icculus@12352
  2401
icculus@12352
  2402
    if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
icculus@12352
  2403
        return RenderDrawPointsWithRectsF(renderer, points, count);
icculus@12352
  2404
    }
icculus@12352
  2405
icculus@12352
  2406
    fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
icculus@12352
  2407
    if (!fpoints) {
icculus@12352
  2408
        return SDL_OutOfMemory();
icculus@12352
  2409
    }
icculus@12352
  2410
    for (i = 0; i < count; ++i) {
icculus@12352
  2411
        fpoints[i].x = points[i].x * renderer->scale.x;
icculus@12352
  2412
        fpoints[i].y = points[i].y * renderer->scale.y;
icculus@12352