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