src/render/SDL_render.c
author Sylvain Becker <sylvain.becker@gmail.com>
Fri, 28 Jun 2019 16:38:42 +0200
changeset 12910 dd9169424181
parent 12909 8e27bc5f03d8
child 13019 ecad0a0684ad
permissions -rw-r--r--
Android: concurrency issues, make sure Activity is in running State when calling
functions like SDL_CreateWindow, SDL_CreateRenderer, Android_GLES_CreateContext

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