sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_render.c (104748B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
      4 
      5   This software is provided 'as-is', without any express or implied
      6   warranty.  In no event will the authors be held liable for any damages
      7   arising from the use of this software.
      8 
      9   Permission is granted to anyone to use this software for any purpose,
     10   including commercial applications, and to alter it and redistribute it
     11   freely, subject to the following restrictions:
     12 
     13   1. The origin of this software must not be misrepresented; you must not
     14      claim that you wrote the original software. If you use this software
     15      in a product, an acknowledgment in the product documentation would be
     16      appreciated but is not required.
     17   2. Altered source versions must be plainly marked as such, and must not be
     18      misrepresented as being the original software.
     19   3. This notice may not be removed or altered from any source distribution.
     20 */
     21 #include "../SDL_internal.h"
     22 
     23 /* The SDL 2D rendering system */
     24 
     25 #include "SDL_hints.h"
     26 #include "SDL_render.h"
     27 #include "SDL_sysrender.h"
     28 #include "software/SDL_render_sw_c.h"
     29 #include "../video/SDL_pixels_c.h"
     30 
     31 #if defined(__ANDROID__)
     32 #  include "../core/android/SDL_android.h"
     33 #endif
     34 
     35 #define SDL_WINDOWRENDERDATA    "_SDL_WindowRenderData"
     36 
     37 #define CHECK_RENDERER_MAGIC(renderer, retval) \
     38     SDL_assert(renderer && renderer->magic == &renderer_magic); \
     39     if (!renderer || renderer->magic != &renderer_magic) { \
     40         SDL_SetError("Invalid renderer"); \
     41         return retval; \
     42     }
     43 
     44 #define CHECK_TEXTURE_MAGIC(texture, retval) \
     45     SDL_assert(texture && texture->magic == &texture_magic); \
     46     if (!texture || texture->magic != &texture_magic) { \
     47         SDL_SetError("Invalid texture"); \
     48         return retval; \
     49     }
     50 
     51 /* Predefined blend modes */
     52 #define SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation, \
     53                               srcAlphaFactor, dstAlphaFactor, alphaOperation) \
     54     (SDL_BlendMode)(((Uint32)colorOperation << 0) | \
     55                     ((Uint32)srcColorFactor << 4) | \
     56                     ((Uint32)dstColorFactor << 8) | \
     57                     ((Uint32)alphaOperation << 16) | \
     58                     ((Uint32)srcAlphaFactor << 20) | \
     59                     ((Uint32)dstAlphaFactor << 24))
     60 
     61 #define SDL_BLENDMODE_NONE_FULL \
     62     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD, \
     63                           SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD)
     64 
     65 #define SDL_BLENDMODE_BLEND_FULL \
     66     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \
     67                           SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD)
     68 
     69 #define SDL_BLENDMODE_ADD_FULL \
     70     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, \
     71                           SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD)
     72 
     73 #define SDL_BLENDMODE_MOD_FULL \
     74     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_COLOR, SDL_BLENDOPERATION_ADD, \
     75                           SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD)
     76 
     77 #define SDL_BLENDMODE_MUL_FULL \
     78     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_DST_COLOR, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \
     79                           SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD)
     80 
     81 #if !SDL_RENDER_DISABLED
     82 static const SDL_RenderDriver *render_drivers[] = {
     83 #if SDL_VIDEO_RENDER_D3D
     84     &D3D_RenderDriver,
     85 #endif
     86 #if SDL_VIDEO_RENDER_D3D11
     87     &D3D11_RenderDriver,
     88 #endif
     89 #if SDL_VIDEO_RENDER_METAL
     90     &METAL_RenderDriver,
     91 #endif
     92 #if SDL_VIDEO_RENDER_OGL
     93     &GL_RenderDriver,
     94 #endif
     95 #if SDL_VIDEO_RENDER_OGL_ES2
     96     &GLES2_RenderDriver,
     97 #endif
     98 #if SDL_VIDEO_RENDER_OGL_ES
     99     &GLES_RenderDriver,
    100 #endif
    101 #if SDL_VIDEO_RENDER_DIRECTFB
    102     &DirectFB_RenderDriver,
    103 #endif
    104 #if SDL_VIDEO_RENDER_PSP
    105     &PSP_RenderDriver,
    106 #endif
    107 #if SDL_VIDEO_RENDER_SW
    108     &SW_RenderDriver
    109 #endif
    110 };
    111 #endif /* !SDL_RENDER_DISABLED */
    112 
    113 static char renderer_magic;
    114 static char texture_magic;
    115 
    116 static SDL_INLINE void
    117 DebugLogRenderCommands(const SDL_RenderCommand *cmd)
    118 {
    119 #if 0
    120     unsigned int i = 1;
    121     SDL_Log("Render commands to flush:");
    122     while (cmd) {
    123         switch (cmd->command) {
    124             case SDL_RENDERCMD_NO_OP:
    125                 SDL_Log(" %u. no-op", i++);
    126                 break;
    127 
    128             case SDL_RENDERCMD_SETVIEWPORT:
    129                 SDL_Log(" %u. set viewport (first=%u, rect={(%d, %d), %dx%d})", i++,
    130                         (unsigned int) cmd->data.viewport.first,
    131                         cmd->data.viewport.rect.x, cmd->data.viewport.rect.y,
    132                         cmd->data.viewport.rect.w, cmd->data.viewport.rect.h);
    133                 break;
    134 
    135             case SDL_RENDERCMD_SETCLIPRECT:
    136                 SDL_Log(" %u. set cliprect (enabled=%s, rect={(%d, %d), %dx%d})", i++,
    137                         cmd->data.cliprect.enabled ? "true" : "false",
    138                         cmd->data.cliprect.rect.x, cmd->data.cliprect.rect.y,
    139                         cmd->data.cliprect.rect.w, cmd->data.cliprect.rect.h);
    140                 break;
    141 
    142             case SDL_RENDERCMD_SETDRAWCOLOR:
    143                 SDL_Log(" %u. set draw color (first=%u, r=%d, g=%d, b=%d, a=%d)", i++,
    144                         (unsigned int) cmd->data.color.first,
    145                         (int) cmd->data.color.r, (int) cmd->data.color.g,
    146                         (int) cmd->data.color.b, (int) cmd->data.color.a);
    147                 break;
    148 
    149             case SDL_RENDERCMD_CLEAR:
    150                 SDL_Log(" %u. clear (first=%u, r=%d, g=%d, b=%d, a=%d)", i++,
    151                         (unsigned int) cmd->data.color.first,
    152                         (int) cmd->data.color.r, (int) cmd->data.color.g,
    153                         (int) cmd->data.color.b, (int) cmd->data.color.a);
    154                 break;
    155 
    156             case SDL_RENDERCMD_DRAW_POINTS:
    157                 SDL_Log(" %u. draw points (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++,
    158                         (unsigned int) cmd->data.draw.first,
    159                         (unsigned int) cmd->data.draw.count,
    160                         (int) cmd->data.draw.r, (int) cmd->data.draw.g,
    161                         (int) cmd->data.draw.b, (int) cmd->data.draw.a,
    162                         (int) cmd->data.draw.blend);
    163                 break;
    164 
    165             case SDL_RENDERCMD_DRAW_LINES:
    166                 SDL_Log(" %u. draw lines (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++,
    167                         (unsigned int) cmd->data.draw.first,
    168                         (unsigned int) cmd->data.draw.count,
    169                         (int) cmd->data.draw.r, (int) cmd->data.draw.g,
    170                         (int) cmd->data.draw.b, (int) cmd->data.draw.a,
    171                         (int) cmd->data.draw.blend);
    172                 break;
    173 
    174             case SDL_RENDERCMD_FILL_RECTS:
    175                 SDL_Log(" %u. fill rects (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++,
    176                         (unsigned int) cmd->data.draw.first,
    177                         (unsigned int) cmd->data.draw.count,
    178                         (int) cmd->data.draw.r, (int) cmd->data.draw.g,
    179                         (int) cmd->data.draw.b, (int) cmd->data.draw.a,
    180                         (int) cmd->data.draw.blend);
    181                 break;
    182 
    183             case SDL_RENDERCMD_COPY:
    184                 SDL_Log(" %u. copy (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, tex=%p)", i++,
    185                         (unsigned int) cmd->data.draw.first,
    186                         (unsigned int) cmd->data.draw.count,
    187                         (int) cmd->data.draw.r, (int) cmd->data.draw.g,
    188                         (int) cmd->data.draw.b, (int) cmd->data.draw.a,
    189                         (int) cmd->data.draw.blend, cmd->data.draw.texture);
    190                 break;
    191 
    192 
    193             case SDL_RENDERCMD_COPY_EX:
    194                 SDL_Log(" %u. copyex (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, tex=%p)", i++,
    195                         (unsigned int) cmd->data.draw.first,
    196                         (unsigned int) cmd->data.draw.count,
    197                         (int) cmd->data.draw.r, (int) cmd->data.draw.g,
    198                         (int) cmd->data.draw.b, (int) cmd->data.draw.a,
    199                         (int) cmd->data.draw.blend, cmd->data.draw.texture);
    200                 break;
    201         }
    202         cmd = cmd->next;
    203     }
    204 #endif
    205 }
    206 
    207 static int
    208 FlushRenderCommands(SDL_Renderer *renderer)
    209 {
    210     int retval;
    211 
    212     SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
    213 
    214     if (renderer->render_commands == NULL) {  /* nothing to do! */
    215         SDL_assert(renderer->vertex_data_used == 0);
    216         return 0;
    217     }
    218 
    219     DebugLogRenderCommands(renderer->render_commands);
    220 
    221     retval = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used);
    222 
    223     /* Move the whole render command queue to the unused pool so we can reuse them next time. */
    224     if (renderer->render_commands_tail != NULL) {
    225         renderer->render_commands_tail->next = renderer->render_commands_pool;
    226         renderer->render_commands_pool = renderer->render_commands;
    227         renderer->render_commands_tail = NULL;
    228         renderer->render_commands = NULL;
    229     }
    230     renderer->vertex_data_used = 0;
    231     renderer->render_command_generation++;
    232     renderer->color_queued = SDL_FALSE;
    233     renderer->viewport_queued = SDL_FALSE;
    234     renderer->cliprect_queued = SDL_FALSE;
    235     return retval;
    236 }
    237 
    238 static int
    239 FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture)
    240 {
    241     SDL_Renderer *renderer = texture->renderer;
    242     if (texture->last_command_generation == renderer->render_command_generation) {
    243         /* the current command queue depends on this texture, flush the queue now before it changes */
    244         return FlushRenderCommands(renderer);
    245     }
    246     return 0;
    247 }
    248 
    249 static SDL_INLINE int
    250 FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer)
    251 {
    252     return renderer->batching ? 0 : FlushRenderCommands(renderer);
    253 }
    254 
    255 int
    256 SDL_RenderFlush(SDL_Renderer * renderer)
    257 {
    258     return FlushRenderCommands(renderer);
    259 }
    260 
    261 void *
    262 SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, const size_t alignment, size_t *offset)
    263 {
    264     const size_t needed = renderer->vertex_data_used + numbytes + alignment;
    265     size_t current_offset = renderer->vertex_data_used;
    266 
    267     size_t aligner = (alignment && ((current_offset & (alignment - 1)) != 0)) ? (alignment - (current_offset & (alignment - 1))) : 0;
    268     size_t aligned = current_offset + aligner;
    269 
    270     if (renderer->vertex_data_allocation < needed) {
    271         const size_t current_allocation = renderer->vertex_data ? renderer->vertex_data_allocation : 1024;
    272         size_t newsize = current_allocation * 2;
    273         void *ptr;
    274         while (newsize < needed) {
    275             newsize *= 2;
    276         }
    277         ptr = SDL_realloc(renderer->vertex_data, newsize);
    278         if (ptr == NULL) {
    279             SDL_OutOfMemory();
    280             return NULL;
    281         }
    282         renderer->vertex_data = ptr;
    283         renderer->vertex_data_allocation = newsize;
    284     }
    285 
    286     if (offset) {
    287         *offset = aligned;
    288     }
    289 
    290     renderer->vertex_data_used += aligner + numbytes;
    291 
    292     return ((Uint8 *) renderer->vertex_data) + aligned;
    293 }
    294 
    295 static SDL_RenderCommand *
    296 AllocateRenderCommand(SDL_Renderer *renderer)
    297 {
    298     SDL_RenderCommand *retval = NULL;
    299 
    300     /* !!! FIXME: are there threading limitations in SDL's render API? If not, we need to mutex this. */
    301     retval = renderer->render_commands_pool;
    302     if (retval != NULL) {
    303         renderer->render_commands_pool = retval->next;
    304         retval->next = NULL;
    305     } else {
    306         retval = SDL_calloc(1, sizeof (*retval));
    307         if (!retval) {
    308             SDL_OutOfMemory();
    309             return NULL;
    310         }
    311     }
    312 
    313     SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
    314     if (renderer->render_commands_tail != NULL) {
    315         renderer->render_commands_tail->next = retval;
    316     } else {
    317         renderer->render_commands = retval;
    318     }
    319     renderer->render_commands_tail = retval;
    320 
    321     return retval;
    322 }
    323 
    324 static int
    325 QueueCmdSetViewport(SDL_Renderer *renderer)
    326 {
    327     int retval = 0;
    328     if (!renderer->viewport_queued || (SDL_memcmp(&renderer->viewport, &renderer->last_queued_viewport, sizeof (SDL_Rect)) != 0)) {
    329         SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
    330         retval = -1;
    331         if (cmd != NULL) {
    332             cmd->command = SDL_RENDERCMD_SETVIEWPORT;
    333             cmd->data.viewport.first = 0;  /* render backend will fill this in. */
    334             SDL_memcpy(&cmd->data.viewport.rect, &renderer->viewport, sizeof (renderer->viewport));
    335             retval = renderer->QueueSetViewport(renderer, cmd);
    336             if (retval < 0) {
    337                 cmd->command = SDL_RENDERCMD_NO_OP;
    338             } else {
    339                 SDL_memcpy(&renderer->last_queued_viewport, &renderer->viewport, sizeof (SDL_Rect));
    340                 renderer->viewport_queued = SDL_TRUE;
    341             }
    342         }
    343     }
    344     return retval;
    345 }
    346 
    347 static int
    348 QueueCmdSetClipRect(SDL_Renderer *renderer)
    349 {
    350     int retval = 0;
    351     if ((!renderer->cliprect_queued) ||
    352          (renderer->clipping_enabled != renderer->last_queued_cliprect_enabled) ||
    353          (SDL_memcmp(&renderer->clip_rect, &renderer->last_queued_cliprect, sizeof (SDL_Rect)) != 0)) {
    354         SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
    355         if (cmd == NULL) {
    356             retval = -1;
    357         } else {
    358             cmd->command = SDL_RENDERCMD_SETCLIPRECT;
    359             cmd->data.cliprect.enabled = renderer->clipping_enabled;
    360             SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (cmd->data.cliprect.rect));
    361             SDL_memcpy(&renderer->last_queued_cliprect, &renderer->clip_rect, sizeof (SDL_Rect));
    362             renderer->last_queued_cliprect_enabled = renderer->clipping_enabled;
    363             renderer->cliprect_queued = SDL_TRUE;
    364         }
    365     }
    366     return retval;
    367 }
    368 
    369 static int
    370 QueueCmdSetDrawColor(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uint8 b, const Uint8 a)
    371 {
    372     const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
    373     int retval = 0;
    374     
    375     if (!renderer->color_queued || (color != renderer->last_queued_color)) {
    376         SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
    377         retval = -1;
    378 
    379         if (cmd != NULL) {
    380             cmd->command = SDL_RENDERCMD_SETDRAWCOLOR;
    381             cmd->data.color.first = 0;  /* render backend will fill this in. */
    382             cmd->data.color.r = r;
    383             cmd->data.color.g = g;
    384             cmd->data.color.b = b;
    385             cmd->data.color.a = a;
    386             retval = renderer->QueueSetDrawColor(renderer, cmd);
    387             if (retval < 0) {
    388                 cmd->command = SDL_RENDERCMD_NO_OP;
    389             } else {
    390                 renderer->last_queued_color = color;
    391                 renderer->color_queued = SDL_TRUE;
    392             }
    393         }
    394     }
    395     return retval;
    396 }
    397 
    398 static int
    399 QueueCmdClear(SDL_Renderer *renderer)
    400 {
    401     SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
    402     if (cmd == NULL) {
    403         return -1;
    404     }
    405 
    406     cmd->command = SDL_RENDERCMD_CLEAR;
    407     cmd->data.color.first = 0;
    408     cmd->data.color.r = renderer->r;
    409     cmd->data.color.g = renderer->g;
    410     cmd->data.color.b = renderer->b;
    411     cmd->data.color.a = renderer->a;
    412     return 0;
    413 }
    414 
    415 static int
    416 PrepQueueCmdDraw(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uint8 b, const Uint8 a)
    417 {
    418     int retval = QueueCmdSetDrawColor(renderer, r, g, b, a);
    419 
    420     /* Set the viewport and clip rect directly before draws, so the backends
    421      * don't have to worry about that state not being valid at draw time. */
    422     if (retval == 0 && !renderer->viewport_queued) {
    423         retval = QueueCmdSetViewport(renderer);
    424     }
    425     if (retval == 0 && !renderer->cliprect_queued) {
    426         retval = QueueCmdSetClipRect(renderer);
    427     }
    428     return retval;
    429 }
    430 
    431 static SDL_RenderCommand *
    432 PrepQueueCmdDrawSolid(SDL_Renderer *renderer, const SDL_RenderCommandType cmdtype)
    433 {
    434     /* !!! FIXME: drop this draw if viewport w or h is zero. */
    435     SDL_RenderCommand *cmd = NULL;
    436     if (PrepQueueCmdDraw(renderer, renderer->r, renderer->g, renderer->b, renderer->a) == 0) {
    437         cmd = AllocateRenderCommand(renderer);
    438         if (cmd != NULL) {
    439             cmd->command = cmdtype;
    440             cmd->data.draw.first = 0;  /* render backend will fill this in. */
    441             cmd->data.draw.count = 0;  /* render backend will fill this in. */
    442             cmd->data.draw.r = renderer->r;
    443             cmd->data.draw.g = renderer->g;
    444             cmd->data.draw.b = renderer->b;
    445             cmd->data.draw.a = renderer->a;
    446             cmd->data.draw.blend = renderer->blendMode;
    447             cmd->data.draw.texture = NULL;  /* no texture. */
    448         }
    449     }
    450     return cmd;
    451 }
    452 
    453 static int
    454 QueueCmdDrawPoints(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
    455 {
    456     SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_POINTS);
    457     int retval = -1;
    458     if (cmd != NULL) {
    459         retval = renderer->QueueDrawPoints(renderer, cmd, points, count);
    460         if (retval < 0) {
    461             cmd->command = SDL_RENDERCMD_NO_OP;
    462         }
    463     }
    464     return retval;
    465 }
    466 
    467 static int
    468 QueueCmdDrawLines(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
    469 {
    470     SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_LINES);
    471     int retval = -1;
    472     if (cmd != NULL) {
    473         retval = renderer->QueueDrawLines(renderer, cmd, points, count);
    474         if (retval < 0) {
    475             cmd->command = SDL_RENDERCMD_NO_OP;
    476         }
    477     }
    478     return retval;
    479 }
    480 
    481 static int
    482 QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect * rects, const int count)
    483 {
    484     SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_FILL_RECTS);
    485     int retval = -1;
    486     if (cmd != NULL) {
    487         retval = renderer->QueueFillRects(renderer, cmd, rects, count);
    488         if (retval < 0) {
    489             cmd->command = SDL_RENDERCMD_NO_OP;
    490         }
    491     }
    492     return retval;
    493 }
    494 
    495 static SDL_RenderCommand *
    496 PrepQueueCmdDrawTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_RenderCommandType cmdtype)
    497 {
    498     /* !!! FIXME: drop this draw if viewport w or h is zero. */
    499     SDL_RenderCommand *cmd = NULL;
    500     if (PrepQueueCmdDraw(renderer, texture->r, texture->g, texture->b, texture->a) == 0) {
    501         cmd = AllocateRenderCommand(renderer);
    502         if (cmd != NULL) {
    503             cmd->command = cmdtype;
    504             cmd->data.draw.first = 0;  /* render backend will fill this in. */
    505             cmd->data.draw.count = 0;  /* render backend will fill this in. */
    506             cmd->data.draw.r = texture->r;
    507             cmd->data.draw.g = texture->g;
    508             cmd->data.draw.b = texture->b;
    509             cmd->data.draw.a = texture->a;
    510             cmd->data.draw.blend = texture->blendMode;
    511             cmd->data.draw.texture = texture;
    512         }
    513     }
    514     return cmd;
    515 }
    516 
    517 static int
    518 QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_FRect * dstrect)
    519 {
    520     SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY);
    521     int retval = -1;
    522     if (cmd != NULL) {
    523         retval = renderer->QueueCopy(renderer, cmd, texture, srcrect, dstrect);
    524         if (retval < 0) {
    525             cmd->command = SDL_RENDERCMD_NO_OP;
    526         }
    527     }
    528     return retval;
    529 }
    530 
    531 static int
    532 QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture * texture,
    533                const SDL_Rect * srcquad, const SDL_FRect * dstrect,
    534                const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
    535 {
    536     SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY_EX);
    537     int retval = -1;
    538     SDL_assert(renderer->QueueCopyEx != NULL);  /* should have caught at higher level. */
    539     if (cmd != NULL) {
    540         retval = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip);
    541         if (retval < 0) {
    542             cmd->command = SDL_RENDERCMD_NO_OP;
    543         }
    544     }
    545     return retval;
    546 }
    547 
    548 
    549 static int UpdateLogicalSize(SDL_Renderer *renderer);
    550 
    551 int
    552 SDL_GetNumRenderDrivers(void)
    553 {
    554 #if !SDL_RENDER_DISABLED
    555     return SDL_arraysize(render_drivers);
    556 #else
    557     return 0;
    558 #endif
    559 }
    560 
    561 int
    562 SDL_GetRenderDriverInfo(int index, SDL_RendererInfo * info)
    563 {
    564 #if !SDL_RENDER_DISABLED
    565     if (index < 0 || index >= SDL_GetNumRenderDrivers()) {
    566         return SDL_SetError("index must be in the range of 0 - %d",
    567                             SDL_GetNumRenderDrivers() - 1);
    568     }
    569     *info = render_drivers[index]->info;
    570     return 0;
    571 #else
    572     return SDL_SetError("SDL not built with rendering support");
    573 #endif
    574 }
    575 
    576 static void GetWindowViewportValues(SDL_Renderer *renderer, int *logical_w, int *logical_h, SDL_Rect *viewport, SDL_FPoint *scale)
    577 {
    578     SDL_LockMutex(renderer->target_mutex);
    579     *logical_w = renderer->target ? renderer->logical_w_backup : renderer->logical_w;
    580     *logical_h = renderer->target ? renderer->logical_h_backup : renderer->logical_h;
    581     *viewport = renderer->target ? renderer->viewport_backup : renderer->viewport;
    582     *scale = renderer->target ? renderer->scale_backup : renderer->scale;
    583     SDL_UnlockMutex(renderer->target_mutex);
    584 }
    585 
    586 static int SDLCALL
    587 SDL_RendererEventWatch(void *userdata, SDL_Event *event)
    588 {
    589     SDL_Renderer *renderer = (SDL_Renderer *)userdata;
    590 
    591     if (event->type == SDL_WINDOWEVENT) {
    592         SDL_Window *window = SDL_GetWindowFromID(event->window.windowID);
    593         if (window == renderer->window) {
    594             if (renderer->WindowEvent) {
    595                 renderer->WindowEvent(renderer, &event->window);
    596             }
    597 
    598             if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
    599                 /* Make sure we're operating on the default render target */
    600                 SDL_Texture *saved_target = SDL_GetRenderTarget(renderer);
    601                 if (saved_target) {
    602                     SDL_SetRenderTarget(renderer, NULL);
    603                 }
    604 
    605                 if (renderer->logical_w) {
    606                     UpdateLogicalSize(renderer);
    607                 } else {
    608                     /* Window was resized, reset viewport */
    609                     int w, h;
    610 
    611                     if (renderer->GetOutputSize) {
    612                         renderer->GetOutputSize(renderer, &w, &h);
    613                     } else {
    614                         SDL_GetWindowSize(renderer->window, &w, &h);
    615                     }
    616 
    617                     if (renderer->target) {
    618                         renderer->viewport_backup.x = 0;
    619                         renderer->viewport_backup.y = 0;
    620                         renderer->viewport_backup.w = w;
    621                         renderer->viewport_backup.h = h;
    622                     } else {
    623                         renderer->viewport.x = 0;
    624                         renderer->viewport.y = 0;
    625                         renderer->viewport.w = w;
    626                         renderer->viewport.h = h;
    627                         QueueCmdSetViewport(renderer);
    628                         FlushRenderCommandsIfNotBatching(renderer);
    629                     }
    630                 }
    631 
    632                 if (saved_target) {
    633                     SDL_SetRenderTarget(renderer, saved_target);
    634                 }
    635             } else if (event->window.event == SDL_WINDOWEVENT_HIDDEN) {
    636                 renderer->hidden = SDL_TRUE;
    637             } else if (event->window.event == SDL_WINDOWEVENT_SHOWN) {
    638                 if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)) {
    639                     renderer->hidden = SDL_FALSE;
    640                 }
    641             } else if (event->window.event == SDL_WINDOWEVENT_MINIMIZED) {
    642                 renderer->hidden = SDL_TRUE;
    643             } else if (event->window.event == SDL_WINDOWEVENT_RESTORED || 
    644                        event->window.event == SDL_WINDOWEVENT_MAXIMIZED) {
    645                 if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN)) {
    646                     renderer->hidden = SDL_FALSE;
    647                 }
    648             }
    649         }
    650     } else if (event->type == SDL_MOUSEMOTION) {
    651         SDL_Window *window = SDL_GetWindowFromID(event->motion.windowID);
    652         if (window == renderer->window) {
    653             int logical_w, logical_h;
    654             SDL_Rect viewport;
    655             SDL_FPoint scale;
    656             GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
    657             if (logical_w) {
    658                 event->motion.x -= (int)(viewport.x * renderer->dpi_scale.x);
    659                 event->motion.y -= (int)(viewport.y * renderer->dpi_scale.y);
    660                 event->motion.x = (int)(event->motion.x / (scale.x * renderer->dpi_scale.x));
    661                 event->motion.y = (int)(event->motion.y / (scale.y * renderer->dpi_scale.y));
    662                 if (event->motion.xrel != 0 && renderer->relative_scaling) {
    663                     float rel = renderer->xrel + event->motion.xrel / (scale.x * renderer->dpi_scale.x);
    664                     float trunc = SDL_truncf(rel);
    665                     renderer->xrel = rel - trunc;
    666                     event->motion.xrel = (Sint32) trunc;
    667                 }
    668                 if (event->motion.yrel != 0 && renderer->relative_scaling) {
    669                     float rel = renderer->yrel + event->motion.yrel / (scale.y * renderer->dpi_scale.y);
    670                     float trunc = SDL_truncf(rel);
    671                     renderer->yrel = rel - trunc;
    672                     event->motion.yrel = (Sint32) trunc;
    673                 }
    674             }
    675         }
    676     } else if (event->type == SDL_MOUSEBUTTONDOWN ||
    677                event->type == SDL_MOUSEBUTTONUP) {
    678         SDL_Window *window = SDL_GetWindowFromID(event->button.windowID);
    679         if (window == renderer->window) {
    680             int logical_w, logical_h;
    681             SDL_Rect viewport;
    682             SDL_FPoint scale;
    683             GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
    684             if (logical_w) {
    685                 event->button.x -= (int)(viewport.x * renderer->dpi_scale.x);
    686                 event->button.y -= (int)(viewport.y * renderer->dpi_scale.y);
    687                 event->button.x = (int)(event->button.x / (scale.x * renderer->dpi_scale.x));
    688                 event->button.y = (int)(event->button.y / (scale.y * renderer->dpi_scale.y));
    689             }
    690         }                               
    691     } else if (event->type == SDL_FINGERDOWN ||
    692                event->type == SDL_FINGERUP ||
    693                event->type == SDL_FINGERMOTION) {
    694         int logical_w, logical_h;
    695         float physical_w, physical_h;
    696         SDL_Rect viewport;
    697         SDL_FPoint scale;
    698         GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
    699 
    700         /* !!! FIXME: we probably should drop events that are outside of the
    701            !!! FIXME: viewport, but we can't do that from an event watcher,
    702            !!! FIXME: and we would have to track if a touch happened outside
    703            !!! FIXME: the viewport and then slid into it to insert extra
    704            !!! FIXME: events, which is a mess, so for now we just clamp these
    705            !!! FIXME: events to the edge. */
    706 
    707         if (renderer->GetOutputSize) {
    708             int w, h;
    709             renderer->GetOutputSize(renderer, &w, &h);
    710             physical_w = (float) w;
    711             physical_h = (float) h;
    712         } else {
    713             int w, h;
    714             SDL_GetWindowSize(renderer->window, &w, &h);
    715             physical_w = ((float) w) * renderer->dpi_scale.x;
    716             physical_h = ((float) h) * renderer->dpi_scale.y;
    717         }
    718 
    719         if (physical_w == 0.0f) {  /* nowhere for the touch to go, avoid division by zero and put it dead center. */
    720             event->tfinger.x = 0.5f;
    721         } else {
    722             const float normalized_viewport_x = ((float) viewport.x) / physical_w;
    723             const float normalized_viewport_w = ((float) viewport.w) / physical_w;
    724             if (event->tfinger.x <= normalized_viewport_x) {
    725                 event->tfinger.x = 0.0f;  /* to the left of the viewport, clamp to the edge. */
    726             } else if (event->tfinger.x >= (normalized_viewport_x + normalized_viewport_w)) {
    727                 event->tfinger.x = 1.0f;  /* to the right of the viewport, clamp to the edge. */
    728             } else {
    729                 event->tfinger.x = (event->tfinger.x - normalized_viewport_x) / normalized_viewport_w;
    730             }
    731         }
    732 
    733         if (physical_h == 0.0f) {  /* nowhere for the touch to go, avoid division by zero and put it dead center. */
    734             event->tfinger.y = 0.5f;
    735         } else {
    736             const float normalized_viewport_y = ((float) viewport.y) / physical_h;
    737             const float normalized_viewport_h = ((float) viewport.h) / physical_h;
    738             if (event->tfinger.y <= normalized_viewport_y) {
    739                 event->tfinger.y = 0.0f;  /* to the left of the viewport, clamp to the edge. */
    740             } else if (event->tfinger.y >= (normalized_viewport_y + normalized_viewport_h)) {
    741                 event->tfinger.y = 1.0f;  /* to the right of the viewport, clamp to the edge. */
    742             } else {
    743                 event->tfinger.y = (event->tfinger.y - normalized_viewport_y) / normalized_viewport_h;
    744             }
    745         }
    746     }
    747 
    748     return 0;
    749 }
    750 
    751 int
    752 SDL_CreateWindowAndRenderer(int width, int height, Uint32 window_flags,
    753                             SDL_Window **window, SDL_Renderer **renderer)
    754 {
    755     *window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED,
    756                                      SDL_WINDOWPOS_UNDEFINED,
    757                                      width, height, window_flags);
    758     if (!*window) {
    759         *renderer = NULL;
    760         return -1;
    761     }
    762 
    763     *renderer = SDL_CreateRenderer(*window, -1, 0);
    764     if (!*renderer) {
    765         return -1;
    766     }
    767 
    768     return 0;
    769 }
    770 
    771 static SDL_INLINE
    772 void VerifyDrawQueueFunctions(const SDL_Renderer *renderer)
    773 {
    774     /* all of these functions are required to be implemented, even as no-ops, so we don't
    775         have to check that they aren't NULL over and over. */
    776     SDL_assert(renderer->QueueSetViewport != NULL);
    777     SDL_assert(renderer->QueueSetDrawColor != NULL);
    778     SDL_assert(renderer->QueueDrawPoints != NULL);
    779     SDL_assert(renderer->QueueDrawLines != NULL);
    780     SDL_assert(renderer->QueueFillRects != NULL);
    781     SDL_assert(renderer->QueueCopy != NULL);
    782     SDL_assert(renderer->RunCommandQueue != NULL);
    783 }
    784 
    785 SDL_Renderer *
    786 SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags)
    787 {
    788 #if !SDL_RENDER_DISABLED
    789     SDL_Renderer *renderer = NULL;
    790     int n = SDL_GetNumRenderDrivers();
    791     SDL_bool batching = SDL_TRUE;
    792     const char *hint;
    793 
    794 #if defined(__ANDROID__)
    795     Android_ActivityMutex_Lock_Running();
    796 #endif
    797 
    798     if (!window) {
    799         SDL_SetError("Invalid window");
    800         goto error;
    801     }
    802 
    803     if (SDL_GetRenderer(window)) {
    804         SDL_SetError("Renderer already associated with window");
    805         goto error;
    806     }
    807 
    808     if (SDL_GetHint(SDL_HINT_RENDER_VSYNC)) {
    809         if (SDL_GetHintBoolean(SDL_HINT_RENDER_VSYNC, SDL_TRUE)) {
    810             flags |= SDL_RENDERER_PRESENTVSYNC;
    811         } else {
    812             flags &= ~SDL_RENDERER_PRESENTVSYNC;
    813         }
    814     }
    815 
    816     if (index < 0) {
    817         hint = SDL_GetHint(SDL_HINT_RENDER_DRIVER);
    818         if (hint) {
    819             for (index = 0; index < n; ++index) {
    820                 const SDL_RenderDriver *driver = render_drivers[index];
    821 
    822                 if (SDL_strcasecmp(hint, driver->info.name) == 0) {
    823                     /* Create a new renderer instance */
    824                     renderer = driver->CreateRenderer(window, flags);
    825                     if (renderer) {
    826                         batching = SDL_FALSE;
    827                     }
    828                     break;
    829                 }
    830             }
    831         }
    832 
    833         if (!renderer) {
    834             for (index = 0; index < n; ++index) {
    835                 const SDL_RenderDriver *driver = render_drivers[index];
    836 
    837                 if ((driver->info.flags & flags) == flags) {
    838                     /* Create a new renderer instance */
    839                     renderer = driver->CreateRenderer(window, flags);
    840                     if (renderer) {
    841                         /* Yay, we got one! */
    842                         break;
    843                     }
    844                 }
    845             }
    846         }
    847         if (index == n) {
    848             SDL_SetError("Couldn't find matching render driver");
    849             goto error;
    850         }
    851     } else {
    852         if (index >= SDL_GetNumRenderDrivers()) {
    853             SDL_SetError("index must be -1 or in the range of 0 - %d",
    854                          SDL_GetNumRenderDrivers() - 1);
    855             goto error;
    856         }
    857         /* Create a new renderer instance */
    858         renderer = render_drivers[index]->CreateRenderer(window, flags);
    859         batching = SDL_FALSE;
    860     }
    861 
    862     if (!renderer) {
    863         goto error;
    864     }
    865 
    866     VerifyDrawQueueFunctions(renderer);
    867 
    868     /* let app/user override batching decisions. */
    869     if (renderer->always_batch) {
    870         batching = SDL_TRUE;
    871     } else if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) {
    872         batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE);
    873     }
    874 
    875     renderer->batching = batching;
    876     renderer->magic = &renderer_magic;
    877     renderer->window = window;
    878     renderer->target_mutex = SDL_CreateMutex();
    879     renderer->scale.x = 1.0f;
    880     renderer->scale.y = 1.0f;
    881     renderer->dpi_scale.x = 1.0f;
    882     renderer->dpi_scale.y = 1.0f;
    883 
    884     /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
    885     renderer->render_command_generation = 1;
    886 
    887     if (window && renderer->GetOutputSize) {
    888         int window_w, window_h;
    889         int output_w, output_h;
    890         if (renderer->GetOutputSize(renderer, &output_w, &output_h) == 0) {
    891             SDL_GetWindowSize(renderer->window, &window_w, &window_h);
    892             renderer->dpi_scale.x = (float)window_w / output_w;
    893             renderer->dpi_scale.y = (float)window_h / output_h;
    894         }
    895     }
    896 
    897     renderer->relative_scaling = SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_SCALING, SDL_TRUE);
    898 
    899     if (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN|SDL_WINDOW_MINIMIZED)) {
    900         renderer->hidden = SDL_TRUE;
    901     } else {
    902         renderer->hidden = SDL_FALSE;
    903     }
    904 
    905     SDL_SetWindowData(window, SDL_WINDOWRENDERDATA, renderer);
    906 
    907     SDL_RenderSetViewport(renderer, NULL);
    908 
    909     SDL_AddEventWatch(SDL_RendererEventWatch, renderer);
    910 
    911     SDL_LogInfo(SDL_LOG_CATEGORY_RENDER,
    912                 "Created renderer: %s", renderer->info.name);
    913 
    914 #if defined(__ANDROID__)
    915     Android_ActivityMutex_Unlock();
    916 #endif
    917     return renderer;
    918 
    919 error:
    920 
    921 #if defined(__ANDROID__)
    922     Android_ActivityMutex_Unlock();
    923 #endif
    924     return NULL;
    925 
    926 #else
    927     SDL_SetError("SDL not built with rendering support");
    928     return NULL;
    929 #endif
    930 }
    931 
    932 SDL_Renderer *
    933 SDL_CreateSoftwareRenderer(SDL_Surface * surface)
    934 {
    935 #if !SDL_RENDER_DISABLED && SDL_VIDEO_RENDER_SW
    936     SDL_Renderer *renderer;
    937 
    938     renderer = SW_CreateRendererForSurface(surface);
    939 
    940     if (renderer) {
    941         VerifyDrawQueueFunctions(renderer);
    942         renderer->magic = &renderer_magic;
    943         renderer->target_mutex = SDL_CreateMutex();
    944         renderer->scale.x = 1.0f;
    945         renderer->scale.y = 1.0f;
    946 
    947         /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
    948         renderer->render_command_generation = 1;
    949 
    950         SDL_RenderSetViewport(renderer, NULL);
    951     }
    952     return renderer;
    953 #else
    954     SDL_SetError("SDL not built with rendering support");
    955     return NULL;
    956 #endif /* !SDL_RENDER_DISABLED */
    957 }
    958 
    959 SDL_Renderer *
    960 SDL_GetRenderer(SDL_Window * window)
    961 {
    962     return (SDL_Renderer *)SDL_GetWindowData(window, SDL_WINDOWRENDERDATA);
    963 }
    964 
    965 int
    966 SDL_GetRendererInfo(SDL_Renderer * renderer, SDL_RendererInfo * info)
    967 {
    968     CHECK_RENDERER_MAGIC(renderer, -1);
    969 
    970     *info = renderer->info;
    971     return 0;
    972 }
    973 
    974 int
    975 SDL_GetRendererOutputSize(SDL_Renderer * renderer, int *w, int *h)
    976 {
    977     CHECK_RENDERER_MAGIC(renderer, -1);
    978 
    979     if (renderer->target) {
    980         return SDL_QueryTexture(renderer->target, NULL, NULL, w, h);
    981     } else if (renderer->GetOutputSize) {
    982         return renderer->GetOutputSize(renderer, w, h);
    983     } else if (renderer->window) {
    984         SDL_GetWindowSize(renderer->window, w, h);
    985         return 0;
    986     } else {
    987         SDL_assert(0 && "This should never happen");
    988         return SDL_SetError("Renderer doesn't support querying output size");
    989     }
    990 }
    991 
    992 static SDL_bool
    993 IsSupportedBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
    994 {
    995     switch (blendMode)
    996     {
    997     /* These are required to be supported by all renderers */
    998     case SDL_BLENDMODE_NONE:
    999     case SDL_BLENDMODE_BLEND:
   1000     case SDL_BLENDMODE_ADD:
   1001     case SDL_BLENDMODE_MOD:
   1002     case SDL_BLENDMODE_MUL:
   1003         return SDL_TRUE;
   1004 
   1005     default:
   1006         return renderer->SupportsBlendMode && renderer->SupportsBlendMode(renderer, blendMode);
   1007     }
   1008 }
   1009 
   1010 static SDL_bool
   1011 IsSupportedFormat(SDL_Renderer * renderer, Uint32 format)
   1012 {
   1013     Uint32 i;
   1014 
   1015     for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   1016         if (renderer->info.texture_formats[i] == format) {
   1017             return SDL_TRUE;
   1018         }
   1019     }
   1020     return SDL_FALSE;
   1021 }
   1022 
   1023 static Uint32
   1024 GetClosestSupportedFormat(SDL_Renderer * renderer, Uint32 format)
   1025 {
   1026     Uint32 i;
   1027 
   1028     if (SDL_ISPIXELFORMAT_FOURCC(format)) {
   1029         /* Look for an exact match */
   1030         for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   1031             if (renderer->info.texture_formats[i] == format) {
   1032                 return renderer->info.texture_formats[i];
   1033             }
   1034         }
   1035     } else {
   1036         SDL_bool hasAlpha = SDL_ISPIXELFORMAT_ALPHA(format);
   1037 
   1038         /* We just want to match the first format that has the same channels */
   1039         for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   1040             if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) &&
   1041                 SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == hasAlpha) {
   1042                 return renderer->info.texture_formats[i];
   1043             }
   1044         }
   1045     }
   1046     return renderer->info.texture_formats[0];
   1047 }
   1048 
   1049 
   1050 static SDL_ScaleMode SDL_GetScaleMode(void)
   1051 {
   1052     const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
   1053 
   1054     if (!hint || SDL_strcasecmp(hint, "nearest") == 0) {
   1055         return SDL_ScaleModeNearest;
   1056     } else if (SDL_strcasecmp(hint, "linear") == 0) {
   1057         return SDL_ScaleModeLinear;
   1058     } else if (SDL_strcasecmp(hint, "best") == 0) {
   1059         return SDL_ScaleModeBest;
   1060     } else {
   1061         return (SDL_ScaleMode)SDL_atoi(hint);
   1062     }
   1063 }
   1064 
   1065 SDL_Texture *
   1066 SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h)
   1067 {
   1068     SDL_Texture *texture;
   1069 
   1070     CHECK_RENDERER_MAGIC(renderer, NULL);
   1071 
   1072     if (!format) {
   1073         format = renderer->info.texture_formats[0];
   1074     }
   1075     if (SDL_BYTESPERPIXEL(format) == 0) {
   1076         SDL_SetError("Invalid texture format");
   1077         return NULL;
   1078     }
   1079     if (SDL_ISPIXELFORMAT_INDEXED(format)) {
   1080         SDL_SetError("Palettized textures are not supported");
   1081         return NULL;
   1082     }
   1083     if (w <= 0 || h <= 0) {
   1084         SDL_SetError("Texture dimensions can't be 0");
   1085         return NULL;
   1086     }
   1087     if ((renderer->info.max_texture_width && w > renderer->info.max_texture_width) ||
   1088         (renderer->info.max_texture_height && h > renderer->info.max_texture_height)) {
   1089         SDL_SetError("Texture dimensions are limited to %dx%d", renderer->info.max_texture_width, renderer->info.max_texture_height);
   1090         return NULL;
   1091     }
   1092     texture = (SDL_Texture *) SDL_calloc(1, sizeof(*texture));
   1093     if (!texture) {
   1094         SDL_OutOfMemory();
   1095         return NULL;
   1096     }
   1097     texture->magic = &texture_magic;
   1098     texture->format = format;
   1099     texture->access = access;
   1100     texture->w = w;
   1101     texture->h = h;
   1102     texture->r = 255;
   1103     texture->g = 255;
   1104     texture->b = 255;
   1105     texture->a = 255;
   1106     texture->scaleMode = SDL_GetScaleMode();
   1107     texture->renderer = renderer;
   1108     texture->next = renderer->textures;
   1109     if (renderer->textures) {
   1110         renderer->textures->prev = texture;
   1111     }
   1112     renderer->textures = texture;
   1113 
   1114     if (IsSupportedFormat(renderer, format)) {
   1115         if (renderer->CreateTexture(renderer, texture) < 0) {
   1116             SDL_DestroyTexture(texture);
   1117             return NULL;
   1118         }
   1119     } else {
   1120         texture->native = SDL_CreateTexture(renderer,
   1121                                 GetClosestSupportedFormat(renderer, format),
   1122                                 access, w, h);
   1123         if (!texture->native) {
   1124             SDL_DestroyTexture(texture);
   1125             return NULL;
   1126         }
   1127 
   1128         /* Swap textures to have texture before texture->native in the list */
   1129         texture->native->next = texture->next;
   1130         if (texture->native->next) {
   1131             texture->native->next->prev = texture->native;
   1132         }
   1133         texture->prev = texture->native->prev;
   1134         if (texture->prev) {
   1135             texture->prev->next = texture;
   1136         }
   1137         texture->native->prev = texture;
   1138         texture->next = texture->native;
   1139         renderer->textures = texture;
   1140 
   1141         if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
   1142 #if SDL_HAVE_YUV
   1143             texture->yuv = SDL_SW_CreateYUVTexture(format, w, h);
   1144 #else
   1145             SDL_SetError("SDL not built with YUV support");
   1146 #endif
   1147             if (!texture->yuv) {
   1148                 SDL_DestroyTexture(texture);
   1149                 return NULL;
   1150             }
   1151         } else if (access == SDL_TEXTUREACCESS_STREAMING) {
   1152             /* The pitch is 4 byte aligned */
   1153             texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3);
   1154             texture->pixels = SDL_calloc(1, texture->pitch * h);
   1155             if (!texture->pixels) {
   1156                 SDL_DestroyTexture(texture);
   1157                 return NULL;
   1158             }
   1159         }
   1160     }
   1161     return texture;
   1162 }
   1163 
   1164 SDL_Texture *
   1165 SDL_CreateTextureFromSurface(SDL_Renderer * renderer, SDL_Surface * surface)
   1166 {
   1167     const SDL_PixelFormat *fmt;
   1168     SDL_bool needAlpha;
   1169     SDL_bool direct_update;
   1170     int i;
   1171     Uint32 format = SDL_PIXELFORMAT_UNKNOWN;
   1172     SDL_Texture *texture;
   1173 
   1174     CHECK_RENDERER_MAGIC(renderer, NULL);
   1175 
   1176     if (!surface) {
   1177         SDL_SetError("SDL_CreateTextureFromSurface() passed NULL surface");
   1178         return NULL;
   1179     }
   1180 
   1181     /* See what the best texture format is */
   1182     fmt = surface->format;
   1183     if (fmt->Amask || SDL_HasColorKey(surface)) {
   1184         needAlpha = SDL_TRUE;
   1185     } else {
   1186         needAlpha = SDL_FALSE;
   1187     }
   1188 
   1189     /* If Palette contains alpha values, promotes to alpha format */
   1190     if (fmt->palette) {
   1191         SDL_bool is_opaque, has_alpha_channel;
   1192         SDL_DetectPalette(fmt->palette, &is_opaque, &has_alpha_channel);
   1193         if (!is_opaque) {
   1194             needAlpha = SDL_TRUE;
   1195         }
   1196     }
   1197 
   1198     /* Try to have the best pixel format for the texture */
   1199     /* No alpha, but a colorkey => promote to alpha */
   1200     if (!fmt->Amask && SDL_HasColorKey(surface)) {
   1201         if (fmt->format == SDL_PIXELFORMAT_RGB888) {
   1202             for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
   1203                 if (renderer->info.texture_formats[i] == SDL_PIXELFORMAT_ARGB8888) {
   1204                     format = SDL_PIXELFORMAT_ARGB8888;
   1205                     break;
   1206                 }
   1207             }
   1208         } else if (fmt->format == SDL_PIXELFORMAT_BGR888) {
   1209             for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
   1210                 if (renderer->info.texture_formats[i] == SDL_PIXELFORMAT_ABGR8888) {
   1211                     format = SDL_PIXELFORMAT_ABGR8888;
   1212                     break;
   1213                 }
   1214             }
   1215         }
   1216     } else {
   1217         /* Exact match would be fine */
   1218         for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
   1219             if (renderer->info.texture_formats[i] == fmt->format) {
   1220                 format = fmt->format;
   1221                 break;
   1222             }
   1223         }
   1224     }
   1225 
   1226     /* Fallback, choose a valid pixel format */
   1227     if (format == SDL_PIXELFORMAT_UNKNOWN) {
   1228         format = renderer->info.texture_formats[0];
   1229         for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
   1230             if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) &&
   1231                     SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == needAlpha) {
   1232                 format = renderer->info.texture_formats[i];
   1233                 break;
   1234             }
   1235         }
   1236     }
   1237 
   1238     texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STATIC,
   1239                                 surface->w, surface->h);
   1240     if (!texture) {
   1241         return NULL;
   1242     }
   1243 
   1244     if (format == surface->format->format) {
   1245         if (surface->format->Amask && SDL_HasColorKey(surface)) {
   1246             /* Surface and Renderer formats are identicals. 
   1247              * Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */
   1248             direct_update = SDL_FALSE;
   1249         } else {
   1250             /* Update Texture directly */
   1251             direct_update = SDL_TRUE;
   1252         }
   1253     } else {
   1254         /* Surface and Renderer formats are differents, it needs an intermediate conversion. */
   1255         direct_update = SDL_FALSE;
   1256     }
   1257 
   1258     if (direct_update) {
   1259         if (SDL_MUSTLOCK(surface)) {
   1260             SDL_LockSurface(surface);
   1261             SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
   1262             SDL_UnlockSurface(surface);
   1263         } else {
   1264             SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
   1265         }
   1266     } else {
   1267         SDL_PixelFormat *dst_fmt;
   1268         SDL_Surface *temp = NULL;
   1269 
   1270         /* Set up a destination surface for the texture update */
   1271         dst_fmt = SDL_AllocFormat(format);
   1272         if (!dst_fmt) {
   1273            SDL_DestroyTexture(texture);
   1274            return NULL;
   1275         }
   1276         temp = SDL_ConvertSurface(surface, dst_fmt, 0);
   1277         SDL_FreeFormat(dst_fmt);
   1278         if (temp) {
   1279             SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
   1280             SDL_FreeSurface(temp);
   1281         } else {
   1282             SDL_DestroyTexture(texture);
   1283             return NULL;
   1284         }
   1285     }
   1286 
   1287     {
   1288         Uint8 r, g, b, a;
   1289         SDL_BlendMode blendMode;
   1290 
   1291         SDL_GetSurfaceColorMod(surface, &r, &g, &b);
   1292         SDL_SetTextureColorMod(texture, r, g, b);
   1293 
   1294         SDL_GetSurfaceAlphaMod(surface, &a);
   1295         SDL_SetTextureAlphaMod(texture, a);
   1296 
   1297         if (SDL_HasColorKey(surface)) {
   1298             /* We converted to a texture with alpha format */
   1299             SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
   1300         } else {
   1301             SDL_GetSurfaceBlendMode(surface, &blendMode);
   1302             SDL_SetTextureBlendMode(texture, blendMode);
   1303         }
   1304     }
   1305     return texture;
   1306 }
   1307 
   1308 int
   1309 SDL_QueryTexture(SDL_Texture * texture, Uint32 * format, int *access,
   1310                  int *w, int *h)
   1311 {
   1312     CHECK_TEXTURE_MAGIC(texture, -1);
   1313 
   1314     if (format) {
   1315         *format = texture->format;
   1316     }
   1317     if (access) {
   1318         *access = texture->access;
   1319     }
   1320     if (w) {
   1321         *w = texture->w;
   1322     }
   1323     if (h) {
   1324         *h = texture->h;
   1325     }
   1326     return 0;
   1327 }
   1328 
   1329 int
   1330 SDL_SetTextureColorMod(SDL_Texture * texture, Uint8 r, Uint8 g, Uint8 b)
   1331 {
   1332     CHECK_TEXTURE_MAGIC(texture, -1);
   1333 
   1334     if (r < 255 || g < 255 || b < 255) {
   1335         texture->modMode |= SDL_TEXTUREMODULATE_COLOR;
   1336     } else {
   1337         texture->modMode &= ~SDL_TEXTUREMODULATE_COLOR;
   1338     }
   1339     texture->r = r;
   1340     texture->g = g;
   1341     texture->b = b;
   1342     if (texture->native) {
   1343         return SDL_SetTextureColorMod(texture->native, r, g, b);
   1344     }
   1345     return 0;
   1346 }
   1347 
   1348 int
   1349 SDL_GetTextureColorMod(SDL_Texture * texture, Uint8 * r, Uint8 * g,
   1350                        Uint8 * b)
   1351 {
   1352     CHECK_TEXTURE_MAGIC(texture, -1);
   1353 
   1354     if (r) {
   1355         *r = texture->r;
   1356     }
   1357     if (g) {
   1358         *g = texture->g;
   1359     }
   1360     if (b) {
   1361         *b = texture->b;
   1362     }
   1363     return 0;
   1364 }
   1365 
   1366 int
   1367 SDL_SetTextureAlphaMod(SDL_Texture * texture, Uint8 alpha)
   1368 {
   1369     CHECK_TEXTURE_MAGIC(texture, -1);
   1370 
   1371     if (alpha < 255) {
   1372         texture->modMode |= SDL_TEXTUREMODULATE_ALPHA;
   1373     } else {
   1374         texture->modMode &= ~SDL_TEXTUREMODULATE_ALPHA;
   1375     }
   1376     texture->a = alpha;
   1377     if (texture->native) {
   1378         return SDL_SetTextureAlphaMod(texture->native, alpha);
   1379     }
   1380     return 0;
   1381 }
   1382 
   1383 int
   1384 SDL_GetTextureAlphaMod(SDL_Texture * texture, Uint8 * alpha)
   1385 {
   1386     CHECK_TEXTURE_MAGIC(texture, -1);
   1387 
   1388     if (alpha) {
   1389         *alpha = texture->a;
   1390     }
   1391     return 0;
   1392 }
   1393 
   1394 int
   1395 SDL_SetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode blendMode)
   1396 {
   1397     SDL_Renderer *renderer;
   1398 
   1399     CHECK_TEXTURE_MAGIC(texture, -1);
   1400 
   1401     renderer = texture->renderer;
   1402     if (!IsSupportedBlendMode(renderer, blendMode)) {
   1403         return SDL_Unsupported();
   1404     }
   1405     texture->blendMode = blendMode;
   1406     if (texture->native) {
   1407         return SDL_SetTextureBlendMode(texture->native, blendMode);
   1408     }
   1409     return 0;
   1410 }
   1411 
   1412 int
   1413 SDL_GetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode *blendMode)
   1414 {
   1415     CHECK_TEXTURE_MAGIC(texture, -1);
   1416 
   1417     if (blendMode) {
   1418         *blendMode = texture->blendMode;
   1419     }
   1420     return 0;
   1421 }
   1422 
   1423 int
   1424 SDL_SetTextureScaleMode(SDL_Texture * texture, SDL_ScaleMode scaleMode)
   1425 {
   1426     SDL_Renderer *renderer;
   1427 
   1428     CHECK_TEXTURE_MAGIC(texture, -1);
   1429 
   1430     renderer = texture->renderer;
   1431     renderer->SetTextureScaleMode(renderer, texture, scaleMode);
   1432     texture->scaleMode = scaleMode;
   1433     if (texture->native) {
   1434         return SDL_SetTextureScaleMode(texture->native, scaleMode);
   1435     }
   1436     return 0;
   1437 }
   1438 
   1439 int
   1440 SDL_GetTextureScaleMode(SDL_Texture * texture, SDL_ScaleMode *scaleMode)
   1441 {
   1442     CHECK_TEXTURE_MAGIC(texture, -1);
   1443 
   1444     if (scaleMode) {
   1445         *scaleMode = texture->scaleMode;
   1446     }
   1447     return 0;
   1448 }
   1449 
   1450 #if SDL_HAVE_YUV
   1451 static int
   1452 SDL_UpdateTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
   1453                      const void *pixels, int pitch)
   1454 {
   1455     SDL_Texture *native = texture->native;
   1456     SDL_Rect full_rect;
   1457 
   1458     if (SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch) < 0) {
   1459         return -1;
   1460     }
   1461 
   1462     full_rect.x = 0;
   1463     full_rect.y = 0;
   1464     full_rect.w = texture->w;
   1465     full_rect.h = texture->h;
   1466     rect = &full_rect;
   1467 
   1468     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   1469         /* We can lock the texture and copy to it */
   1470         void *native_pixels = NULL;
   1471         int native_pitch = 0;
   1472 
   1473         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
   1474             return -1;
   1475         }
   1476         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
   1477                             rect->w, rect->h, native_pixels, native_pitch);
   1478         SDL_UnlockTexture(native);
   1479     } else {
   1480         /* Use a temporary buffer for updating */
   1481         const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
   1482         const size_t alloclen = rect->h * temp_pitch;
   1483         if (alloclen > 0) {
   1484             void *temp_pixels = SDL_malloc(alloclen);
   1485             if (!temp_pixels) {
   1486                 return SDL_OutOfMemory();
   1487             }
   1488             SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
   1489                                 rect->w, rect->h, temp_pixels, temp_pitch);
   1490             SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
   1491             SDL_free(temp_pixels);
   1492         }
   1493     }
   1494     return 0;
   1495 }
   1496 #endif /* SDL_HAVE_YUV */
   1497 
   1498 static int
   1499 SDL_UpdateTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
   1500                         const void *pixels, int pitch)
   1501 {
   1502     SDL_Texture *native = texture->native;
   1503 
   1504     if (!rect->w || !rect->h) {
   1505         return 0;  /* nothing to do. */
   1506     }
   1507 
   1508     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   1509         /* We can lock the texture and copy to it */
   1510         void *native_pixels = NULL;
   1511         int native_pitch = 0;
   1512 
   1513         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
   1514             return -1;
   1515         }
   1516         SDL_ConvertPixels(rect->w, rect->h,
   1517                           texture->format, pixels, pitch,
   1518                           native->format, native_pixels, native_pitch);
   1519         SDL_UnlockTexture(native);
   1520     } else {
   1521         /* Use a temporary buffer for updating */
   1522         const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
   1523         const size_t alloclen = rect->h * temp_pitch;
   1524         if (alloclen > 0) {
   1525             void *temp_pixels = SDL_malloc(alloclen);
   1526             if (!temp_pixels) {
   1527                 return SDL_OutOfMemory();
   1528             }
   1529             SDL_ConvertPixels(rect->w, rect->h,
   1530                               texture->format, pixels, pitch,
   1531                               native->format, temp_pixels, temp_pitch);
   1532             SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
   1533             SDL_free(temp_pixels);
   1534         }
   1535     }
   1536     return 0;
   1537 }
   1538 
   1539 int
   1540 SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect,
   1541                   const void *pixels, int pitch)
   1542 {
   1543     SDL_Rect full_rect;
   1544 
   1545     CHECK_TEXTURE_MAGIC(texture, -1);
   1546 
   1547     if (!pixels) {
   1548         return SDL_InvalidParamError("pixels");
   1549     }
   1550     if (!pitch) {
   1551         return SDL_InvalidParamError("pitch");
   1552     }
   1553 
   1554     if (!rect) {
   1555         full_rect.x = 0;
   1556         full_rect.y = 0;
   1557         full_rect.w = texture->w;
   1558         full_rect.h = texture->h;
   1559         rect = &full_rect;
   1560     }
   1561 
   1562     if ((rect->w == 0) || (rect->h == 0)) {
   1563         return 0;  /* nothing to do. */
   1564 #if SDL_HAVE_YUV
   1565     } else if (texture->yuv) {
   1566         return SDL_UpdateTextureYUV(texture, rect, pixels, pitch);
   1567 #endif
   1568     } else if (texture->native) {
   1569         return SDL_UpdateTextureNative(texture, rect, pixels, pitch);
   1570     } else {
   1571         SDL_Renderer *renderer = texture->renderer;
   1572         if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
   1573             return -1;
   1574         }
   1575         return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
   1576     }
   1577 }
   1578 
   1579 #if SDL_HAVE_YUV
   1580 static int
   1581 SDL_UpdateTextureYUVPlanar(SDL_Texture * texture, const SDL_Rect * rect,
   1582                            const Uint8 *Yplane, int Ypitch,
   1583                            const Uint8 *Uplane, int Upitch,
   1584                            const Uint8 *Vplane, int Vpitch)
   1585 {
   1586     SDL_Texture *native = texture->native;
   1587     SDL_Rect full_rect;
   1588 
   1589     if (SDL_SW_UpdateYUVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch) < 0) {
   1590         return -1;
   1591     }
   1592 
   1593     full_rect.x = 0;
   1594     full_rect.y = 0;
   1595     full_rect.w = texture->w;
   1596     full_rect.h = texture->h;
   1597     rect = &full_rect;
   1598 
   1599     if (!rect->w || !rect->h) {
   1600         return 0;  /* nothing to do. */
   1601     }
   1602 
   1603     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   1604         /* We can lock the texture and copy to it */
   1605         void *native_pixels = NULL;
   1606         int native_pitch = 0;
   1607 
   1608         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
   1609             return -1;
   1610         }
   1611         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
   1612                             rect->w, rect->h, native_pixels, native_pitch);
   1613         SDL_UnlockTexture(native);
   1614     } else {
   1615         /* Use a temporary buffer for updating */
   1616         const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
   1617         const size_t alloclen = rect->h * temp_pitch;
   1618         if (alloclen > 0) {
   1619             void *temp_pixels = SDL_malloc(alloclen);
   1620             if (!temp_pixels) {
   1621                 return SDL_OutOfMemory();
   1622             }
   1623             SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
   1624                                 rect->w, rect->h, temp_pixels, temp_pitch);
   1625             SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
   1626             SDL_free(temp_pixels);
   1627         }
   1628     }
   1629     return 0;
   1630 }
   1631 #endif /* SDL_HAVE_YUV */
   1632 
   1633 int SDL_UpdateYUVTexture(SDL_Texture * texture, const SDL_Rect * rect,
   1634                          const Uint8 *Yplane, int Ypitch,
   1635                          const Uint8 *Uplane, int Upitch,
   1636                          const Uint8 *Vplane, int Vpitch)
   1637 {
   1638 #if SDL_HAVE_YUV
   1639     SDL_Renderer *renderer;
   1640     SDL_Rect full_rect;
   1641 
   1642     CHECK_TEXTURE_MAGIC(texture, -1);
   1643 
   1644     if (!Yplane) {
   1645         return SDL_InvalidParamError("Yplane");
   1646     }
   1647     if (!Ypitch) {
   1648         return SDL_InvalidParamError("Ypitch");
   1649     }
   1650     if (!Uplane) {
   1651         return SDL_InvalidParamError("Uplane");
   1652     }
   1653     if (!Upitch) {
   1654         return SDL_InvalidParamError("Upitch");
   1655     }
   1656     if (!Vplane) {
   1657         return SDL_InvalidParamError("Vplane");
   1658     }
   1659     if (!Vpitch) {
   1660         return SDL_InvalidParamError("Vpitch");
   1661     }
   1662 
   1663     if (texture->format != SDL_PIXELFORMAT_YV12 &&
   1664         texture->format != SDL_PIXELFORMAT_IYUV) {
   1665         return SDL_SetError("Texture format must by YV12 or IYUV");
   1666     }
   1667 
   1668     if (!rect) {
   1669         full_rect.x = 0;
   1670         full_rect.y = 0;
   1671         full_rect.w = texture->w;
   1672         full_rect.h = texture->h;
   1673         rect = &full_rect;
   1674     }
   1675 
   1676     if (!rect->w || !rect->h) {
   1677         return 0;  /* nothing to do. */
   1678     }
   1679 
   1680     if (texture->yuv) {
   1681         return SDL_UpdateTextureYUVPlanar(texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
   1682     } else {
   1683         SDL_assert(!texture->native);
   1684         renderer = texture->renderer;
   1685         SDL_assert(renderer->UpdateTextureYUV);
   1686         if (renderer->UpdateTextureYUV) {
   1687             if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
   1688                 return -1;
   1689             }
   1690             return renderer->UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
   1691         } else {
   1692             return SDL_Unsupported();
   1693         }
   1694     }
   1695 #else
   1696     return -1;
   1697 #endif
   1698 }
   1699 
   1700 #if SDL_HAVE_YUV
   1701 static int
   1702 SDL_LockTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
   1703                    void **pixels, int *pitch)
   1704 {
   1705     return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch);
   1706 }
   1707 #endif /* SDL_HAVE_YUV */
   1708 
   1709 static int
   1710 SDL_LockTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
   1711                       void **pixels, int *pitch)
   1712 {
   1713     texture->locked_rect = *rect;
   1714     *pixels = (void *) ((Uint8 *) texture->pixels +
   1715                         rect->y * texture->pitch +
   1716                         rect->x * SDL_BYTESPERPIXEL(texture->format));
   1717     *pitch = texture->pitch;
   1718     return 0;
   1719 }
   1720 
   1721 int
   1722 SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect,
   1723                 void **pixels, int *pitch)
   1724 {
   1725     SDL_Rect full_rect;
   1726 
   1727     CHECK_TEXTURE_MAGIC(texture, -1);
   1728 
   1729     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
   1730         return SDL_SetError("SDL_LockTexture(): texture must be streaming");
   1731     }
   1732 
   1733     if (!rect) {
   1734         full_rect.x = 0;
   1735         full_rect.y = 0;
   1736         full_rect.w = texture->w;
   1737         full_rect.h = texture->h;
   1738         rect = &full_rect;
   1739     }
   1740 
   1741 #if SDL_HAVE_YUV
   1742     if (texture->yuv) {
   1743         if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
   1744             return -1;
   1745         }
   1746         return SDL_LockTextureYUV(texture, rect, pixels, pitch);
   1747     } else
   1748 #endif
   1749     if (texture->native) {
   1750         /* Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. */
   1751         return SDL_LockTextureNative(texture, rect, pixels, pitch);
   1752     } else {
   1753         SDL_Renderer *renderer = texture->renderer;
   1754         if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
   1755             return -1;
   1756         }
   1757         return renderer->LockTexture(renderer, texture, rect, pixels, pitch);
   1758     }
   1759 }
   1760 
   1761 int
   1762 SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect,
   1763                          SDL_Surface **surface)
   1764 {
   1765     SDL_Rect real_rect;
   1766     void *pixels = NULL;
   1767     int pitch = 0; /* fix static analysis */
   1768     int ret;
   1769 
   1770     if (texture == NULL || surface == NULL) {
   1771         return -1;
   1772     }
   1773 
   1774     real_rect.x = 0;
   1775     real_rect.y = 0;
   1776     real_rect.w = texture->w;
   1777     real_rect.h = texture->h;
   1778 
   1779     if (rect) {
   1780         SDL_IntersectRect(rect, &real_rect, &real_rect);
   1781     }
   1782 
   1783     ret = SDL_LockTexture(texture, &real_rect, &pixels, &pitch);
   1784     if (ret < 0) {
   1785         return ret;
   1786     }
   1787 
   1788     texture->locked_surface = SDL_CreateRGBSurfaceWithFormatFrom(pixels, real_rect.w, real_rect.h, 0, pitch, texture->format);
   1789     if (texture->locked_surface == NULL) {
   1790         SDL_UnlockTexture(texture);
   1791         return -1;
   1792     }
   1793 
   1794     *surface = texture->locked_surface;
   1795     return 0;
   1796 }
   1797 
   1798 #if SDL_HAVE_YUV
   1799 static void
   1800 SDL_UnlockTextureYUV(SDL_Texture * texture)
   1801 {
   1802     SDL_Texture *native = texture->native;
   1803     void *native_pixels = NULL;
   1804     int native_pitch = 0;
   1805     SDL_Rect rect;
   1806 
   1807     rect.x = 0;
   1808     rect.y = 0;
   1809     rect.w = texture->w;
   1810     rect.h = texture->h;
   1811 
   1812     if (SDL_LockTexture(native, &rect, &native_pixels, &native_pitch) < 0) {
   1813         return;
   1814     }
   1815     SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format,
   1816                         rect.w, rect.h, native_pixels, native_pitch);
   1817     SDL_UnlockTexture(native);
   1818 }
   1819 #endif /* SDL_HAVE_YUV */
   1820 
   1821 static void
   1822 SDL_UnlockTextureNative(SDL_Texture * texture)
   1823 {
   1824     SDL_Texture *native = texture->native;
   1825     void *native_pixels = NULL;
   1826     int native_pitch = 0;
   1827     const SDL_Rect *rect = &texture->locked_rect;
   1828     const void* pixels = (void *) ((Uint8 *) texture->pixels +
   1829                         rect->y * texture->pitch +
   1830                         rect->x * SDL_BYTESPERPIXEL(texture->format));
   1831     int pitch = texture->pitch;
   1832 
   1833     if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
   1834         return;
   1835     }
   1836     SDL_ConvertPixels(rect->w, rect->h,
   1837                       texture->format, pixels, pitch,
   1838                       native->format, native_pixels, native_pitch);
   1839     SDL_UnlockTexture(native);
   1840 }
   1841 
   1842 void
   1843 SDL_UnlockTexture(SDL_Texture * texture)
   1844 {
   1845     CHECK_TEXTURE_MAGIC(texture, );
   1846 
   1847     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
   1848         return;
   1849     }
   1850 #if SDL_HAVE_YUV
   1851     if (texture->yuv) {
   1852         SDL_UnlockTextureYUV(texture);
   1853     } else
   1854 #endif
   1855     if (texture->native) {
   1856         SDL_UnlockTextureNative(texture);
   1857     } else {
   1858         SDL_Renderer *renderer = texture->renderer;
   1859         renderer->UnlockTexture(renderer, texture);
   1860     }
   1861 
   1862     SDL_FreeSurface(texture->locked_surface);
   1863     texture->locked_surface = NULL;
   1864 }
   1865 
   1866 SDL_bool
   1867 SDL_RenderTargetSupported(SDL_Renderer *renderer)
   1868 {
   1869     if (!renderer || !renderer->SetRenderTarget) {
   1870         return SDL_FALSE;
   1871     }
   1872     return (renderer->info.flags & SDL_RENDERER_TARGETTEXTURE) != 0;
   1873 }
   1874 
   1875 int
   1876 SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
   1877 {
   1878     if (!SDL_RenderTargetSupported(renderer)) {
   1879         return SDL_Unsupported();
   1880     }
   1881 
   1882     /* texture == NULL is valid and means reset the target to the window */
   1883     if (texture) {
   1884         CHECK_TEXTURE_MAGIC(texture, -1);
   1885         if (renderer != texture->renderer) {
   1886             return SDL_SetError("Texture was not created with this renderer");
   1887         }
   1888         if (texture->access != SDL_TEXTUREACCESS_TARGET) {
   1889             return SDL_SetError("Texture not created with SDL_TEXTUREACCESS_TARGET");
   1890         }
   1891         if (texture->native) {
   1892             /* Always render to the native texture */
   1893             texture = texture->native;
   1894         }
   1895     }
   1896 
   1897     if (texture == renderer->target) {
   1898         /* Nothing to do! */
   1899         return 0;
   1900     }
   1901 
   1902     FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
   1903 
   1904     SDL_LockMutex(renderer->target_mutex);
   1905 
   1906     if (texture && !renderer->target) {
   1907         /* Make a backup of the viewport */
   1908         renderer->viewport_backup = renderer->viewport;
   1909         renderer->clip_rect_backup = renderer->clip_rect;
   1910         renderer->clipping_enabled_backup = renderer->clipping_enabled;
   1911         renderer->scale_backup = renderer->scale;
   1912         renderer->logical_w_backup = renderer->logical_w;
   1913         renderer->logical_h_backup = renderer->logical_h;
   1914     }
   1915     renderer->target = texture;
   1916 
   1917     if (renderer->SetRenderTarget(renderer, texture) < 0) {
   1918         SDL_UnlockMutex(renderer->target_mutex);
   1919         return -1;
   1920     }
   1921 
   1922     if (texture) {
   1923         renderer->viewport.x = 0;
   1924         renderer->viewport.y = 0;
   1925         renderer->viewport.w = texture->w;
   1926         renderer->viewport.h = texture->h;
   1927         SDL_zero(renderer->clip_rect);
   1928         renderer->clipping_enabled = SDL_FALSE;
   1929         renderer->scale.x = 1.0f;
   1930         renderer->scale.y = 1.0f;
   1931         renderer->logical_w = texture->w;
   1932         renderer->logical_h = texture->h;
   1933     } else {
   1934         renderer->viewport = renderer->viewport_backup;
   1935         renderer->clip_rect = renderer->clip_rect_backup;
   1936         renderer->clipping_enabled = renderer->clipping_enabled_backup;
   1937         renderer->scale = renderer->scale_backup;
   1938         renderer->logical_w = renderer->logical_w_backup;
   1939         renderer->logical_h = renderer->logical_h_backup;
   1940     }
   1941 
   1942     SDL_UnlockMutex(renderer->target_mutex);
   1943 
   1944     if (QueueCmdSetViewport(renderer) < 0) {
   1945         return -1;
   1946     }
   1947     if (QueueCmdSetClipRect(renderer) < 0) {
   1948         return -1;
   1949     }
   1950 
   1951     /* All set! */
   1952     return FlushRenderCommandsIfNotBatching(renderer);
   1953 }
   1954 
   1955 SDL_Texture *
   1956 SDL_GetRenderTarget(SDL_Renderer *renderer)
   1957 {
   1958     return renderer->target;
   1959 }
   1960 
   1961 static int
   1962 UpdateLogicalSize(SDL_Renderer *renderer)
   1963 {
   1964     int w = 1, h = 1;
   1965     float want_aspect;
   1966     float real_aspect;
   1967     float scale;
   1968     SDL_Rect viewport;
   1969     /* 0 is for letterbox, 1 is for overscan */
   1970     int scale_policy = 0;
   1971     const char *hint;
   1972 
   1973     if (!renderer->logical_w || !renderer->logical_h) {
   1974         return 0;
   1975     }
   1976     if (SDL_GetRendererOutputSize(renderer, &w, &h) < 0) {
   1977         return -1;
   1978     }
   1979 
   1980     hint = SDL_GetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE);
   1981     if (hint && (*hint == '1' || SDL_strcasecmp(hint, "overscan") == 0))  {
   1982 #if SDL_VIDEO_RENDER_D3D
   1983         SDL_bool overscan_supported = SDL_TRUE;
   1984         /* Unfortunately, Direct3D 9 doesn't support negative viewport numbers
   1985            which the overscan implementation relies on.
   1986         */
   1987         if (SDL_strcasecmp(SDL_GetCurrentVideoDriver(), "direct3d") == 0) {
   1988             overscan_supported = SDL_FALSE;
   1989         }
   1990         if (overscan_supported) {
   1991             scale_policy = 1;
   1992         }
   1993 #else
   1994         scale_policy = 1;
   1995 #endif
   1996     }
   1997 
   1998     want_aspect = (float)renderer->logical_w / renderer->logical_h;
   1999     real_aspect = (float)w / h;
   2000 
   2001     /* Clear the scale because we're setting viewport in output coordinates */
   2002     SDL_RenderSetScale(renderer, 1.0f, 1.0f);
   2003 
   2004     if (renderer->integer_scale) {
   2005         if (want_aspect > real_aspect) {
   2006             scale = (float)(w / renderer->logical_w);
   2007         } else {
   2008             scale = (float)(h / renderer->logical_h);
   2009         }
   2010         viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
   2011         viewport.x = (w - viewport.w) / 2;
   2012         viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
   2013         viewport.y = (h - viewport.h) / 2;
   2014 
   2015         SDL_RenderSetViewport(renderer, &viewport);
   2016     } else if (SDL_fabs(want_aspect-real_aspect) < 0.0001) {
   2017         /* The aspect ratios are the same, just scale appropriately */
   2018         scale = (float)w / renderer->logical_w;
   2019         SDL_RenderSetViewport(renderer, NULL);
   2020     } else if (want_aspect > real_aspect) {
   2021         if (scale_policy == 1) {
   2022             /* We want a wider aspect ratio than is available - 
   2023              zoom so logical height matches the real height 
   2024              and the width will grow off the screen 
   2025              */
   2026             scale = (float)h / renderer->logical_h;
   2027             viewport.y = 0;
   2028             viewport.h = h;
   2029             viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
   2030             viewport.x = (w - viewport.w) / 2;
   2031             SDL_RenderSetViewport(renderer, &viewport);
   2032         } else {
   2033             /* We want a wider aspect ratio than is available - letterbox it */
   2034             scale = (float)w / renderer->logical_w;
   2035             viewport.x = 0;
   2036             viewport.w = w;
   2037             viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
   2038             viewport.y = (h - viewport.h) / 2;
   2039             SDL_RenderSetViewport(renderer, &viewport);
   2040         }
   2041     } else {
   2042         if (scale_policy == 1) {
   2043             /* We want a narrower aspect ratio than is available -
   2044              zoom so logical width matches the real width
   2045              and the height will grow off the screen
   2046              */
   2047             scale = (float)w / renderer->logical_w;
   2048             viewport.x = 0;
   2049             viewport.w = w;
   2050             viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
   2051             viewport.y = (h - viewport.h) / 2;
   2052             SDL_RenderSetViewport(renderer, &viewport);
   2053         } else {
   2054             /* We want a narrower aspect ratio than is available - use side-bars */
   2055              scale = (float)h / renderer->logical_h;
   2056              viewport.y = 0;
   2057              viewport.h = h;
   2058              viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
   2059              viewport.x = (w - viewport.w) / 2;
   2060              SDL_RenderSetViewport(renderer, &viewport);
   2061         }
   2062     }
   2063 
   2064     /* Set the new scale */
   2065     SDL_RenderSetScale(renderer, scale, scale);
   2066 
   2067     return 0;
   2068 }
   2069 
   2070 int
   2071 SDL_RenderSetLogicalSize(SDL_Renderer * renderer, int w, int h)
   2072 {
   2073     CHECK_RENDERER_MAGIC(renderer, -1);
   2074 
   2075     if (!w || !h) {
   2076         /* Clear any previous logical resolution */
   2077         renderer->logical_w = 0;
   2078         renderer->logical_h = 0;
   2079         SDL_RenderSetViewport(renderer, NULL);
   2080         SDL_RenderSetScale(renderer, 1.0f, 1.0f);
   2081         return 0;
   2082     }
   2083 
   2084     renderer->logical_w = w;
   2085     renderer->logical_h = h;
   2086 
   2087     return UpdateLogicalSize(renderer);
   2088 }
   2089 
   2090 void
   2091 SDL_RenderGetLogicalSize(SDL_Renderer * renderer, int *w, int *h)
   2092 {
   2093     CHECK_RENDERER_MAGIC(renderer, );
   2094 
   2095     if (w) {
   2096         *w = renderer->logical_w;
   2097     }
   2098     if (h) {
   2099         *h = renderer->logical_h;
   2100     }
   2101 }
   2102 
   2103 int
   2104 SDL_RenderSetIntegerScale(SDL_Renderer * renderer, SDL_bool enable)
   2105 {
   2106     CHECK_RENDERER_MAGIC(renderer, -1);
   2107 
   2108     renderer->integer_scale = enable;
   2109 
   2110     return UpdateLogicalSize(renderer);
   2111 }
   2112 
   2113 SDL_bool
   2114 SDLCALL SDL_RenderGetIntegerScale(SDL_Renderer * renderer)
   2115 {
   2116     CHECK_RENDERER_MAGIC(renderer, SDL_FALSE);
   2117 
   2118     return renderer->integer_scale;
   2119 }
   2120 
   2121 int
   2122 SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect)
   2123 {
   2124     int retval;
   2125     CHECK_RENDERER_MAGIC(renderer, -1);
   2126 
   2127     if (rect) {
   2128         renderer->viewport.x = (int)SDL_floor(rect->x * renderer->scale.x);
   2129         renderer->viewport.y = (int)SDL_floor(rect->y * renderer->scale.y);
   2130         renderer->viewport.w = (int)SDL_ceil(rect->w * renderer->scale.x);
   2131         renderer->viewport.h = (int)SDL_ceil(rect->h * renderer->scale.y);
   2132     } else {
   2133         renderer->viewport.x = 0;
   2134         renderer->viewport.y = 0;
   2135         if (SDL_GetRendererOutputSize(renderer, &renderer->viewport.w, &renderer->viewport.h) < 0) {
   2136             return -1;
   2137         }
   2138     }
   2139     retval = QueueCmdSetViewport(renderer);
   2140     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2141 }
   2142 
   2143 void
   2144 SDL_RenderGetViewport(SDL_Renderer * renderer, SDL_Rect * rect)
   2145 {
   2146     CHECK_RENDERER_MAGIC(renderer, );
   2147 
   2148     if (rect) {
   2149         rect->x = (int)(renderer->viewport.x / renderer->scale.x);
   2150         rect->y = (int)(renderer->viewport.y / renderer->scale.y);
   2151         rect->w = (int)(renderer->viewport.w / renderer->scale.x);
   2152         rect->h = (int)(renderer->viewport.h / renderer->scale.y);
   2153     }
   2154 }
   2155 
   2156 int
   2157 SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect)
   2158 {
   2159     int retval;
   2160     CHECK_RENDERER_MAGIC(renderer, -1)
   2161 
   2162     if (rect) {
   2163         renderer->clipping_enabled = SDL_TRUE;
   2164         renderer->clip_rect.x = (int)SDL_floor(rect->x * renderer->scale.x);
   2165         renderer->clip_rect.y = (int)SDL_floor(rect->y * renderer->scale.y);
   2166         renderer->clip_rect.w = (int)SDL_ceil(rect->w * renderer->scale.x);
   2167         renderer->clip_rect.h = (int)SDL_ceil(rect->h * renderer->scale.y);
   2168     } else {
   2169         renderer->clipping_enabled = SDL_FALSE;
   2170         SDL_zero(renderer->clip_rect);
   2171     }
   2172 
   2173     retval = QueueCmdSetClipRect(renderer);
   2174     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2175 }
   2176 
   2177 void
   2178 SDL_RenderGetClipRect(SDL_Renderer * renderer, SDL_Rect * rect)
   2179 {
   2180     CHECK_RENDERER_MAGIC(renderer, )
   2181 
   2182     if (rect) {
   2183         rect->x = (int)(renderer->clip_rect.x / renderer->scale.x);
   2184         rect->y = (int)(renderer->clip_rect.y / renderer->scale.y);
   2185         rect->w = (int)(renderer->clip_rect.w / renderer->scale.x);
   2186         rect->h = (int)(renderer->clip_rect.h / renderer->scale.y);
   2187     }
   2188 }
   2189 
   2190 SDL_bool
   2191 SDL_RenderIsClipEnabled(SDL_Renderer * renderer)
   2192 {
   2193     CHECK_RENDERER_MAGIC(renderer, SDL_FALSE)
   2194     return renderer->clipping_enabled;
   2195 }
   2196 
   2197 int
   2198 SDL_RenderSetScale(SDL_Renderer * renderer, float scaleX, float scaleY)
   2199 {
   2200     CHECK_RENDERER_MAGIC(renderer, -1);
   2201 
   2202     renderer->scale.x = scaleX;
   2203     renderer->scale.y = scaleY;
   2204     return 0;
   2205 }
   2206 
   2207 void
   2208 SDL_RenderGetScale(SDL_Renderer * renderer, float *scaleX, float *scaleY)
   2209 {
   2210     CHECK_RENDERER_MAGIC(renderer, );
   2211 
   2212     if (scaleX) {
   2213         *scaleX = renderer->scale.x;
   2214     }
   2215     if (scaleY) {
   2216         *scaleY = renderer->scale.y;
   2217     }
   2218 }
   2219 
   2220 int
   2221 SDL_SetRenderDrawColor(SDL_Renderer * renderer,
   2222                        Uint8 r, Uint8 g, Uint8 b, Uint8 a)
   2223 {
   2224     CHECK_RENDERER_MAGIC(renderer, -1);
   2225 
   2226     renderer->r = r;
   2227     renderer->g = g;
   2228     renderer->b = b;
   2229     renderer->a = a;
   2230     return 0;
   2231 }
   2232 
   2233 int
   2234 SDL_GetRenderDrawColor(SDL_Renderer * renderer,
   2235                        Uint8 * r, Uint8 * g, Uint8 * b, Uint8 * a)
   2236 {
   2237     CHECK_RENDERER_MAGIC(renderer, -1);
   2238 
   2239     if (r) {
   2240         *r = renderer->r;
   2241     }
   2242     if (g) {
   2243         *g = renderer->g;
   2244     }
   2245     if (b) {
   2246         *b = renderer->b;
   2247     }
   2248     if (a) {
   2249         *a = renderer->a;
   2250     }
   2251     return 0;
   2252 }
   2253 
   2254 int
   2255 SDL_SetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
   2256 {
   2257     CHECK_RENDERER_MAGIC(renderer, -1);
   2258 
   2259     if (!IsSupportedBlendMode(renderer, blendMode)) {
   2260         return SDL_Unsupported();
   2261     }
   2262     renderer->blendMode = blendMode;
   2263     return 0;
   2264 }
   2265 
   2266 int
   2267 SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode *blendMode)
   2268 {
   2269     CHECK_RENDERER_MAGIC(renderer, -1);
   2270 
   2271     *blendMode = renderer->blendMode;
   2272     return 0;
   2273 }
   2274 
   2275 int
   2276 SDL_RenderClear(SDL_Renderer * renderer)
   2277 {
   2278     int retval;
   2279     CHECK_RENDERER_MAGIC(renderer, -1);
   2280     retval = QueueCmdClear(renderer);
   2281     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2282 }
   2283 
   2284 
   2285 /* !!! FIXME: delete all the duplicate code for the integer versions in 2.1,
   2286    !!! FIXME:  making the floating point versions the only available APIs. */
   2287 
   2288 int
   2289 SDL_RenderDrawPoint(SDL_Renderer * renderer, int x, int y)
   2290 {
   2291     SDL_FPoint fpoint;
   2292     fpoint.x = (float) x;
   2293     fpoint.y = (float) y;
   2294     return SDL_RenderDrawPointsF(renderer, &fpoint, 1);
   2295 }
   2296 
   2297 int
   2298 SDL_RenderDrawPointF(SDL_Renderer * renderer, float x, float y)
   2299 {
   2300     SDL_FPoint fpoint;
   2301     fpoint.x = x;
   2302     fpoint.y = y;
   2303     return SDL_RenderDrawPointsF(renderer, &fpoint, 1);
   2304 }
   2305 
   2306 static int
   2307 RenderDrawPointsWithRects(SDL_Renderer * renderer,
   2308                           const SDL_Point * points, const int count)
   2309 {
   2310     int retval = -1;
   2311     SDL_bool isstack;
   2312     SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack);
   2313     int i;
   2314 
   2315     if (!frects) {
   2316         return SDL_OutOfMemory();
   2317     }
   2318 
   2319     for (i = 0; i < count; ++i) {
   2320         frects[i].x = points[i].x * renderer->scale.x;
   2321         frects[i].y = points[i].y * renderer->scale.y;
   2322         frects[i].w = renderer->scale.x;
   2323         frects[i].h = renderer->scale.y;
   2324     }
   2325 
   2326     retval = QueueCmdFillRects(renderer, frects, count);
   2327 
   2328     SDL_small_free(frects, isstack);
   2329 
   2330     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2331 }
   2332 
   2333 int
   2334 SDL_RenderDrawPoints(SDL_Renderer * renderer,
   2335                      const SDL_Point * points, int count)
   2336 {
   2337     SDL_FPoint *fpoints;
   2338     int i;
   2339     int retval;
   2340     SDL_bool isstack;
   2341 
   2342     CHECK_RENDERER_MAGIC(renderer, -1);
   2343 
   2344     if (!points) {
   2345         return SDL_SetError("SDL_RenderDrawPoints(): Passed NULL points");
   2346     }
   2347     if (count < 1) {
   2348         return 0;
   2349     }
   2350 
   2351     /* Don't draw while we're hidden */
   2352     if (renderer->hidden) {
   2353         return 0;
   2354     }
   2355 
   2356     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
   2357         return RenderDrawPointsWithRects(renderer, points, count);
   2358     }
   2359 
   2360     fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
   2361     if (!fpoints) {
   2362         return SDL_OutOfMemory();
   2363     }
   2364     for (i = 0; i < count; ++i) {
   2365         fpoints[i].x = points[i].x * renderer->scale.x;
   2366         fpoints[i].y = points[i].y * renderer->scale.y;
   2367     }
   2368 
   2369     retval = QueueCmdDrawPoints(renderer, fpoints, count);
   2370 
   2371     SDL_small_free(fpoints, isstack);
   2372 
   2373     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2374 }
   2375 
   2376 static int
   2377 RenderDrawPointsWithRectsF(SDL_Renderer * renderer,
   2378                            const SDL_FPoint * fpoints, const int count)
   2379 {
   2380     int retval = -1;
   2381     SDL_bool isstack;
   2382     SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack);
   2383     int i;
   2384 
   2385     if (!frects) {
   2386         return SDL_OutOfMemory();
   2387     }
   2388 
   2389     for (i = 0; i < count; ++i) {
   2390         frects[i].x = fpoints[i].x * renderer->scale.x;
   2391         frects[i].y = fpoints[i].y * renderer->scale.y;
   2392         frects[i].w = renderer->scale.x;
   2393         frects[i].h = renderer->scale.y;
   2394     }
   2395 
   2396     retval = QueueCmdFillRects(renderer, frects, count);
   2397 
   2398     SDL_small_free(frects, isstack);
   2399 
   2400     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2401 }
   2402 
   2403 int
   2404 SDL_RenderDrawPointsF(SDL_Renderer * renderer,
   2405                       const SDL_FPoint * points, int count)
   2406 {
   2407     SDL_FPoint *fpoints;
   2408     int i;
   2409     int retval;
   2410     SDL_bool isstack;
   2411 
   2412     CHECK_RENDERER_MAGIC(renderer, -1);
   2413 
   2414     if (!points) {
   2415         return SDL_SetError("SDL_RenderDrawFPoints(): Passed NULL points");
   2416     }
   2417     if (count < 1) {
   2418         return 0;
   2419     }
   2420 
   2421     /* Don't draw while we're hidden */
   2422     if (renderer->hidden) {
   2423         return 0;
   2424     }
   2425 
   2426     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
   2427         return RenderDrawPointsWithRectsF(renderer, points, count);
   2428     }
   2429 
   2430     fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
   2431     if (!fpoints) {
   2432         return SDL_OutOfMemory();
   2433     }
   2434     for (i = 0; i < count; ++i) {
   2435         fpoints[i].x = points[i].x * renderer->scale.x;
   2436         fpoints[i].y = points[i].y * renderer->scale.y;
   2437     }
   2438 
   2439     retval = QueueCmdDrawPoints(renderer, fpoints, count);
   2440 
   2441     SDL_small_free(fpoints, isstack);
   2442 
   2443     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2444 }
   2445 
   2446 int
   2447 SDL_RenderDrawLine(SDL_Renderer * renderer, int x1, int y1, int x2, int y2)
   2448 {
   2449     SDL_FPoint points[2];
   2450     points[0].x = (float) x1;
   2451     points[0].y = (float) y1;
   2452     points[1].x = (float) x2;
   2453     points[1].y = (float) y2;
   2454     return SDL_RenderDrawLinesF(renderer, points, 2);
   2455 }
   2456 
   2457 int
   2458 SDL_RenderDrawLineF(SDL_Renderer * renderer, float x1, float y1, float x2, float y2)
   2459 {
   2460     SDL_FPoint points[2];
   2461     points[0].x = x1;
   2462     points[0].y = y1;
   2463     points[1].x = x2;
   2464     points[1].y = y2;
   2465     return SDL_RenderDrawLinesF(renderer, points, 2);
   2466 }
   2467 
   2468 static int
   2469 RenderDrawLinesWithRects(SDL_Renderer * renderer,
   2470                      const SDL_Point * points, const int count)
   2471 {
   2472     SDL_FRect *frect;
   2473     SDL_FRect *frects;
   2474     SDL_FPoint fpoints[2];
   2475     int i, nrects = 0;
   2476     int retval = 0;
   2477     SDL_bool isstack;
   2478 
   2479     frects = SDL_small_alloc(SDL_FRect, count-1, &isstack);
   2480     if (!frects) {
   2481         return SDL_OutOfMemory();
   2482     }
   2483 
   2484     for (i = 0; i < count-1; ++i) {
   2485         if (points[i].x == points[i+1].x) {
   2486             const int minY = SDL_min(points[i].y, points[i+1].y);
   2487             const int maxY = SDL_max(points[i].y, points[i+1].y);
   2488 
   2489             frect = &frects[nrects++];
   2490             frect->x = points[i].x * renderer->scale.x;
   2491             frect->y = minY * renderer->scale.y;
   2492             frect->w = renderer->scale.x;
   2493             frect->h = (maxY - minY + 1) * renderer->scale.y;
   2494         } else if (points[i].y == points[i+1].y) {
   2495             const int minX = SDL_min(points[i].x, points[i+1].x);
   2496             const int maxX = SDL_max(points[i].x, points[i+1].x);
   2497 
   2498             frect = &frects[nrects++];
   2499             frect->x = minX * renderer->scale.x;
   2500             frect->y = points[i].y * renderer->scale.y;
   2501             frect->w = (maxX - minX + 1) * renderer->scale.x;
   2502             frect->h = renderer->scale.y;
   2503         } else {
   2504             /* FIXME: We can't use a rect for this line... */
   2505             fpoints[0].x = points[i].x * renderer->scale.x;
   2506             fpoints[0].y = points[i].y * renderer->scale.y;
   2507             fpoints[1].x = points[i+1].x * renderer->scale.x;
   2508             fpoints[1].y = points[i+1].y * renderer->scale.y;
   2509             retval += QueueCmdDrawLines(renderer, fpoints, 2);
   2510         }
   2511     }
   2512 
   2513     retval += QueueCmdFillRects(renderer, frects, nrects);
   2514 
   2515     SDL_small_free(frects, isstack);
   2516 
   2517     if (retval < 0) {
   2518         retval = -1;
   2519     }
   2520     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2521 }
   2522 
   2523 static int
   2524 RenderDrawLinesWithRectsF(SDL_Renderer * renderer,
   2525                           const SDL_FPoint * points, const int count)
   2526 {
   2527     SDL_FRect *frect;
   2528     SDL_FRect *frects;
   2529     SDL_FPoint fpoints[2];
   2530     int i, nrects = 0;
   2531     int retval = 0;
   2532     SDL_bool isstack;
   2533 
   2534     frects = SDL_small_alloc(SDL_FRect, count-1, &isstack);
   2535     if (!frects) {
   2536         return SDL_OutOfMemory();
   2537     }
   2538 
   2539     for (i = 0; i < count-1; ++i) {
   2540         if (points[i].x == points[i+1].x) {
   2541             const int minY = (int)SDL_min(points[i].y, points[i+1].y);
   2542             const int maxY = (int)SDL_max(points[i].y, points[i+1].y);
   2543 
   2544             frect = &frects[nrects++];
   2545             frect->x = points[i].x * renderer->scale.x;
   2546             frect->y = minY * renderer->scale.y;
   2547             frect->w = renderer->scale.x;
   2548             frect->h = (maxY - minY + 1) * renderer->scale.y;
   2549         } else if (points[i].y == points[i+1].y) {
   2550             const int minX = (int)SDL_min(points[i].x, points[i+1].x);
   2551             const int maxX = (int)SDL_max(points[i].x, points[i+1].x);
   2552 
   2553             frect = &frects[nrects++];
   2554             frect->x = minX * renderer->scale.x;
   2555             frect->y = points[i].y * renderer->scale.y;
   2556             frect->w = (maxX - minX + 1) * renderer->scale.x;
   2557             frect->h = renderer->scale.y;
   2558         } else {
   2559             /* FIXME: We can't use a rect for this line... */
   2560             fpoints[0].x = points[i].x * renderer->scale.x;
   2561             fpoints[0].y = points[i].y * renderer->scale.y;
   2562             fpoints[1].x = points[i+1].x * renderer->scale.x;
   2563             fpoints[1].y = points[i+1].y * renderer->scale.y;
   2564             retval += QueueCmdDrawLines(renderer, fpoints, 2);
   2565         }
   2566     }
   2567 
   2568     retval += QueueCmdFillRects(renderer, frects, nrects);
   2569 
   2570     SDL_small_free(frects, isstack);
   2571 
   2572     if (retval < 0) {
   2573         retval = -1;
   2574     }
   2575     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2576 }
   2577 
   2578 int
   2579 SDL_RenderDrawLines(SDL_Renderer * renderer,
   2580                     const SDL_Point * points, int count)
   2581 {
   2582     SDL_FPoint *fpoints;
   2583     int i;
   2584     int retval;
   2585     SDL_bool isstack;
   2586 
   2587     CHECK_RENDERER_MAGIC(renderer, -1);
   2588 
   2589     if (!points) {
   2590         return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points");
   2591     }
   2592     if (count < 2) {
   2593         return 0;
   2594     }
   2595 
   2596     /* Don't draw while we're hidden */
   2597     if (renderer->hidden) {
   2598         return 0;
   2599     }
   2600 
   2601     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
   2602         return RenderDrawLinesWithRects(renderer, points, count);
   2603     }
   2604 
   2605     fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
   2606     if (!fpoints) {
   2607         return SDL_OutOfMemory();
   2608     }
   2609     for (i = 0; i < count; ++i) {
   2610         fpoints[i].x = points[i].x * renderer->scale.x;
   2611         fpoints[i].y = points[i].y * renderer->scale.y;
   2612     }
   2613 
   2614     retval = QueueCmdDrawLines(renderer, fpoints, count);
   2615 
   2616     SDL_small_free(fpoints, isstack);
   2617 
   2618     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2619 }
   2620 
   2621 int
   2622 SDL_RenderDrawLinesF(SDL_Renderer * renderer,
   2623                      const SDL_FPoint * points, int count)
   2624 {
   2625     SDL_FPoint *fpoints;
   2626     int i;
   2627     int retval;
   2628     SDL_bool isstack;
   2629 
   2630     CHECK_RENDERER_MAGIC(renderer, -1);
   2631 
   2632     if (!points) {
   2633         return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points");
   2634     }
   2635     if (count < 2) {
   2636         return 0;
   2637     }
   2638 
   2639     /* Don't draw while we're hidden */
   2640     if (renderer->hidden) {
   2641         return 0;
   2642     }
   2643 
   2644     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
   2645         return RenderDrawLinesWithRectsF(renderer, points, count);
   2646     }
   2647 
   2648     fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
   2649     if (!fpoints) {
   2650         return SDL_OutOfMemory();
   2651     }
   2652     for (i = 0; i < count; ++i) {
   2653         fpoints[i].x = points[i].x * renderer->scale.x;
   2654         fpoints[i].y = points[i].y * renderer->scale.y;
   2655     }
   2656 
   2657     retval = QueueCmdDrawLines(renderer, fpoints, count);
   2658 
   2659     SDL_small_free(fpoints, isstack);
   2660 
   2661     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2662 }
   2663 
   2664 int
   2665 SDL_RenderDrawRect(SDL_Renderer * renderer, const SDL_Rect * rect)
   2666 {
   2667     SDL_FRect frect;
   2668     SDL_FRect *prect = NULL;
   2669 
   2670     if (rect) {
   2671         frect.x = (float) rect->x;
   2672         frect.y = (float) rect->y;
   2673         frect.w = (float) rect->w;
   2674         frect.h = (float) rect->h;
   2675         prect = &frect;
   2676     }
   2677 
   2678     return SDL_RenderDrawRectF(renderer, prect);
   2679 }
   2680 
   2681 int
   2682 SDL_RenderDrawRectF(SDL_Renderer * renderer, const SDL_FRect * rect)
   2683 {
   2684     SDL_FRect frect;
   2685     SDL_FPoint points[5];
   2686 
   2687     CHECK_RENDERER_MAGIC(renderer, -1);
   2688 
   2689     /* If 'rect' == NULL, then outline the whole surface */
   2690     if (!rect) {
   2691         SDL_Rect r;
   2692         SDL_RenderGetViewport(renderer, &r);
   2693         frect.x = 0.0f;
   2694         frect.y = 0.0f;
   2695         frect.w = (float) r.w;
   2696         frect.h = (float) r.h;
   2697         rect = &frect;
   2698     }
   2699 
   2700     points[0].x = rect->x;
   2701     points[0].y = rect->y;
   2702     points[1].x = rect->x+rect->w-1;
   2703     points[1].y = rect->y;
   2704     points[2].x = rect->x+rect->w-1;
   2705     points[2].y = rect->y+rect->h-1;
   2706     points[3].x = rect->x;
   2707     points[3].y = rect->y+rect->h-1;
   2708     points[4].x = rect->x;
   2709     points[4].y = rect->y;
   2710     return SDL_RenderDrawLinesF(renderer, points, 5);
   2711 }
   2712 
   2713 int
   2714 SDL_RenderDrawRects(SDL_Renderer * renderer,
   2715                     const SDL_Rect * rects, int count)
   2716 {
   2717     int i;
   2718 
   2719     CHECK_RENDERER_MAGIC(renderer, -1);
   2720 
   2721     if (!rects) {
   2722         return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects");
   2723     }
   2724     if (count < 1) {
   2725         return 0;
   2726     }
   2727 
   2728     /* Don't draw while we're hidden */
   2729     if (renderer->hidden) {
   2730         return 0;
   2731     }
   2732 
   2733     for (i = 0; i < count; ++i) {
   2734         if (SDL_RenderDrawRect(renderer, &rects[i]) < 0) {
   2735             return -1;
   2736         }
   2737     }
   2738     return 0;
   2739 }
   2740 
   2741 int
   2742 SDL_RenderDrawRectsF(SDL_Renderer * renderer,
   2743                      const SDL_FRect * rects, int count)
   2744 {
   2745     int i;
   2746 
   2747     CHECK_RENDERER_MAGIC(renderer, -1);
   2748 
   2749     if (!rects) {
   2750         return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects");
   2751     }
   2752     if (count < 1) {
   2753         return 0;
   2754     }
   2755 
   2756     /* Don't draw while we're hidden */
   2757     if (renderer->hidden) {
   2758         return 0;
   2759     }
   2760 
   2761     for (i = 0; i < count; ++i) {
   2762         if (SDL_RenderDrawRectF(renderer, &rects[i]) < 0) {
   2763             return -1;
   2764         }
   2765     }
   2766     return 0;
   2767 }
   2768 
   2769 int
   2770 SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect)
   2771 {
   2772     SDL_FRect frect;
   2773 
   2774     CHECK_RENDERER_MAGIC(renderer, -1);
   2775 
   2776     /* If 'rect' == NULL, then outline the whole surface */
   2777     if (rect) {
   2778         frect.x = (float) rect->x;
   2779         frect.y = (float) rect->y;
   2780         frect.w = (float) rect->w;
   2781         frect.h = (float) rect->h;
   2782     } else {
   2783         SDL_Rect r;
   2784         SDL_zero(r);
   2785         SDL_RenderGetViewport(renderer, &r);
   2786         frect.x = 0.0f;
   2787         frect.y = 0.0f;
   2788         frect.w = (float) r.w;
   2789         frect.h = (float) r.h;
   2790     }
   2791     return SDL_RenderFillRectsF(renderer, &frect, 1);
   2792 }
   2793 
   2794 int
   2795 SDL_RenderFillRectF(SDL_Renderer * renderer, const SDL_FRect * rect)
   2796 {
   2797     SDL_FRect frect;
   2798 
   2799     CHECK_RENDERER_MAGIC(renderer, -1);
   2800 
   2801     /* If 'rect' == NULL, then outline the whole surface */
   2802     if (!rect) {
   2803         SDL_Rect r;
   2804         SDL_zero(r);
   2805         SDL_RenderGetViewport(renderer, &r);
   2806         frect.x = 0.0f;
   2807         frect.y = 0.0f;
   2808         frect.w = (float) r.w;
   2809         frect.h = (float) r.h;
   2810         rect = &frect;
   2811     }
   2812     return SDL_RenderFillRectsF(renderer, rect, 1);
   2813 }
   2814 
   2815 int
   2816 SDL_RenderFillRects(SDL_Renderer * renderer,
   2817                     const SDL_Rect * rects, int count)
   2818 {
   2819     SDL_FRect *frects;
   2820     int i;
   2821     int retval;
   2822     SDL_bool isstack;
   2823 
   2824     CHECK_RENDERER_MAGIC(renderer, -1);
   2825 
   2826     if (!rects) {
   2827         return SDL_SetError("SDL_RenderFillRects(): Passed NULL rects");
   2828     }
   2829     if (count < 1) {
   2830         return 0;
   2831     }
   2832 
   2833     /* Don't draw while we're hidden */
   2834     if (renderer->hidden) {
   2835         return 0;
   2836     }
   2837 
   2838     frects = SDL_small_alloc(SDL_FRect, count, &isstack);
   2839     if (!frects) {
   2840         return SDL_OutOfMemory();
   2841     }
   2842     for (i = 0; i < count; ++i) {
   2843         frects[i].x = rects[i].x * renderer->scale.x;
   2844         frects[i].y = rects[i].y * renderer->scale.y;
   2845         frects[i].w = rects[i].w * renderer->scale.x;
   2846         frects[i].h = rects[i].h * renderer->scale.y;
   2847     }
   2848 
   2849     retval = QueueCmdFillRects(renderer, frects, count);
   2850 
   2851     SDL_small_free(frects, isstack);
   2852 
   2853     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2854 }
   2855 
   2856 int
   2857 SDL_RenderFillRectsF(SDL_Renderer * renderer,
   2858                      const SDL_FRect * rects, int count)
   2859 {
   2860     SDL_FRect *frects;
   2861     int i;
   2862     int retval;
   2863     SDL_bool isstack;
   2864 
   2865     CHECK_RENDERER_MAGIC(renderer, -1);
   2866 
   2867     if (!rects) {
   2868         return SDL_SetError("SDL_RenderFillFRects(): Passed NULL rects");
   2869     }
   2870     if (count < 1) {
   2871         return 0;
   2872     }
   2873 
   2874     /* Don't draw while we're hidden */
   2875     if (renderer->hidden) {
   2876         return 0;
   2877     }
   2878 
   2879     frects = SDL_small_alloc(SDL_FRect, count, &isstack);
   2880     if (!frects) {
   2881         return SDL_OutOfMemory();
   2882     }
   2883     for (i = 0; i < count; ++i) {
   2884         frects[i].x = rects[i].x * renderer->scale.x;
   2885         frects[i].y = rects[i].y * renderer->scale.y;
   2886         frects[i].w = rects[i].w * renderer->scale.x;
   2887         frects[i].h = rects[i].h * renderer->scale.y;
   2888     }
   2889 
   2890     retval = QueueCmdFillRects(renderer, frects, count);
   2891 
   2892     SDL_small_free(frects, isstack);
   2893 
   2894     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2895 }
   2896 
   2897 /* !!! FIXME: move this to a public API if we want to do float versions of all of these later */
   2898 SDL_FORCE_INLINE SDL_bool SDL_FRectEmpty(const SDL_FRect *r)
   2899 {
   2900     return ((!r) || (r->w <= 0.0f) || (r->h <= 0.0f)) ? SDL_TRUE : SDL_FALSE;
   2901 }
   2902 
   2903 /* !!! FIXME: move this to a public API if we want to do float versions of all of these later */
   2904 static SDL_bool
   2905 SDL_HasIntersectionF(const SDL_FRect * A, const SDL_FRect * B)
   2906 {
   2907     float Amin, Amax, Bmin, Bmax;
   2908 
   2909     if (!A) {
   2910         SDL_InvalidParamError("A");
   2911         return SDL_FALSE;
   2912     }
   2913 
   2914     if (!B) {
   2915         SDL_InvalidParamError("B");
   2916         return SDL_FALSE;
   2917     }
   2918 
   2919     /* Special cases for empty rects */
   2920     if (SDL_FRectEmpty(A) || SDL_FRectEmpty(B)) {
   2921         return SDL_FALSE;
   2922     }
   2923 
   2924     /* Horizontal intersection */
   2925     Amin = A->x;
   2926     Amax = Amin + A->w;
   2927     Bmin = B->x;
   2928     Bmax = Bmin + B->w;
   2929     if (Bmin > Amin)
   2930         Amin = Bmin;
   2931     if (Bmax < Amax)
   2932         Amax = Bmax;
   2933     if (Amax <= Amin)
   2934         return SDL_FALSE;
   2935 
   2936     /* Vertical intersection */
   2937     Amin = A->y;
   2938     Amax = Amin + A->h;
   2939     Bmin = B->y;
   2940     Bmax = Bmin + B->h;
   2941     if (Bmin > Amin)
   2942         Amin = Bmin;
   2943     if (Bmax < Amax)
   2944         Amax = Bmax;
   2945     if (Amax <= Amin)
   2946         return SDL_FALSE;
   2947 
   2948     return SDL_TRUE;
   2949 }
   2950 
   2951 int
   2952 SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
   2953                const SDL_Rect * srcrect, const SDL_Rect * dstrect)
   2954 {
   2955     SDL_FRect dstfrect;
   2956     SDL_FRect *pdstfrect = NULL;
   2957     if (dstrect) {
   2958         dstfrect.x = (float) dstrect->x;
   2959         dstfrect.y = (float) dstrect->y;
   2960         dstfrect.w = (float) dstrect->w;
   2961         dstfrect.h = (float) dstrect->h;
   2962         pdstfrect = &dstfrect;
   2963     }
   2964     return SDL_RenderCopyF(renderer, texture, srcrect, pdstfrect);
   2965 }
   2966 
   2967 int
   2968 SDL_RenderCopyF(SDL_Renderer * renderer, SDL_Texture * texture,
   2969                 const SDL_Rect * srcrect, const SDL_FRect * dstrect)
   2970 {
   2971     SDL_Rect real_srcrect;
   2972     SDL_FRect real_dstrect;
   2973     SDL_Rect r;
   2974     int retval;
   2975 
   2976     CHECK_RENDERER_MAGIC(renderer, -1);
   2977     CHECK_TEXTURE_MAGIC(texture, -1);
   2978 
   2979     if (renderer != texture->renderer) {
   2980         return SDL_SetError("Texture was not created with this renderer");
   2981     }
   2982 
   2983     /* Don't draw while we're hidden */
   2984     if (renderer->hidden) {
   2985         return 0;
   2986     }
   2987 
   2988     real_srcrect.x = 0;
   2989     real_srcrect.y = 0;
   2990     real_srcrect.w = texture->w;
   2991     real_srcrect.h = texture->h;
   2992     if (srcrect) {
   2993         if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
   2994             return 0;
   2995         }
   2996     }
   2997 
   2998     SDL_zero(r);
   2999     SDL_RenderGetViewport(renderer, &r);
   3000     real_dstrect.x = 0.0f;
   3001     real_dstrect.y = 0.0f;
   3002     real_dstrect.w = (float) r.w;
   3003     real_dstrect.h = (float) r.h;
   3004     if (dstrect) {
   3005         if (!SDL_HasIntersectionF(dstrect, &real_dstrect)) {
   3006             return 0;
   3007         }
   3008         real_dstrect = *dstrect;
   3009     }
   3010 
   3011     if (texture->native) {
   3012         texture = texture->native;
   3013     }
   3014 
   3015     real_dstrect.x *= renderer->scale.x;
   3016     real_dstrect.y *= renderer->scale.y;
   3017     real_dstrect.w *= renderer->scale.x;
   3018     real_dstrect.h *= renderer->scale.y;
   3019 
   3020     texture->last_command_generation = renderer->render_command_generation;
   3021 
   3022     retval = QueueCmdCopy(renderer, texture, &real_srcrect, &real_dstrect);
   3023     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   3024 }
   3025 
   3026 int
   3027 SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
   3028                const SDL_Rect * srcrect, const SDL_Rect * dstrect,
   3029                const double angle, const SDL_Point *center, const SDL_RendererFlip flip)
   3030 {
   3031     SDL_FRect dstfrect;
   3032     SDL_FRect *pdstfrect = NULL;
   3033     SDL_FPoint fcenter;
   3034     SDL_FPoint *pfcenter = NULL;
   3035 
   3036     if (dstrect) {
   3037         dstfrect.x = (float) dstrect->x;
   3038         dstfrect.y = (float) dstrect->y;
   3039         dstfrect.w = (float) dstrect->w;
   3040         dstfrect.h = (float) dstrect->h;
   3041         pdstfrect = &dstfrect;
   3042     }
   3043 
   3044     if (center) {
   3045         fcenter.x = (float) center->x;
   3046         fcenter.y = (float) center->y;
   3047         pfcenter = &fcenter;
   3048     }
   3049 
   3050     return SDL_RenderCopyExF(renderer, texture, srcrect, pdstfrect, angle, pfcenter, flip);
   3051 }
   3052 
   3053 int
   3054 SDL_RenderCopyExF(SDL_Renderer * renderer, SDL_Texture * texture,
   3055                const SDL_Rect * srcrect, const SDL_FRect * dstrect,
   3056                const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
   3057 {
   3058     SDL_Rect real_srcrect;
   3059     SDL_FRect real_dstrect;
   3060     SDL_FPoint real_center;
   3061     int retval;
   3062 
   3063     if (flip == SDL_FLIP_NONE && (int)(angle/360) == angle/360) { /* fast path when we don't need rotation or flipping */
   3064         return SDL_RenderCopyF(renderer, texture, srcrect, dstrect);
   3065     }
   3066 
   3067     CHECK_RENDERER_MAGIC(renderer, -1);
   3068     CHECK_TEXTURE_MAGIC(texture, -1);
   3069 
   3070     if (renderer != texture->renderer) {
   3071         return SDL_SetError("Texture was not created with this renderer");
   3072     }
   3073     if (!renderer->QueueCopyEx) {
   3074         return SDL_SetError("Renderer does not support RenderCopyEx");
   3075     }
   3076 
   3077     /* Don't draw while we're hidden */
   3078     if (renderer->hidden) {
   3079         return 0;
   3080     }
   3081 
   3082     real_srcrect.x = 0;
   3083     real_srcrect.y = 0;
   3084     real_srcrect.w = texture->w;
   3085     real_srcrect.h = texture->h;
   3086     if (srcrect) {
   3087         if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
   3088             return 0;
   3089         }
   3090     }
   3091 
   3092     /* We don't intersect the dstrect with the viewport as RenderCopy does because of potential rotation clipping issues... TODO: should we? */
   3093     if (dstrect) {
   3094         real_dstrect = *dstrect;
   3095     } else {
   3096         SDL_Rect r;
   3097         SDL_zero(r);
   3098         SDL_RenderGetViewport(renderer, &r);
   3099         real_dstrect.x = 0.0f;
   3100         real_dstrect.y = 0.0f;
   3101         real_dstrect.w = (float) r.w;
   3102         real_dstrect.h = (float) r.h;
   3103     }
   3104 
   3105     if (texture->native) {
   3106         texture = texture->native;
   3107     }
   3108 
   3109     if (center) {
   3110         real_center = *center;
   3111     } else {
   3112         real_center.x = real_dstrect.w / 2.0f;
   3113         real_center.y = real_dstrect.h / 2.0f;
   3114     }
   3115 
   3116     real_dstrect.x *= renderer->scale.x;
   3117     real_dstrect.y *= renderer->scale.y;
   3118     real_dstrect.w *= renderer->scale.x;
   3119     real_dstrect.h *= renderer->scale.y;
   3120 
   3121     real_center.x *= renderer->scale.x;
   3122     real_center.y *= renderer->scale.y;
   3123 
   3124     texture->last_command_generation = renderer->render_command_generation;
   3125 
   3126     retval = QueueCmdCopyEx(renderer, texture, &real_srcrect, &real_dstrect, angle, &real_center, flip);
   3127     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   3128 }
   3129 
   3130 int
   3131 SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   3132                      Uint32 format, void * pixels, int pitch)
   3133 {
   3134     SDL_Rect real_rect;
   3135 
   3136     CHECK_RENDERER_MAGIC(renderer, -1);
   3137 
   3138     if (!renderer->RenderReadPixels) {
   3139         return SDL_Unsupported();
   3140     }
   3141 
   3142     FlushRenderCommands(renderer);  /* we need to render before we read the results. */
   3143 
   3144     if (!format) {
   3145         format = SDL_GetWindowPixelFormat(renderer->window);
   3146     }
   3147 
   3148     real_rect.x = renderer->viewport.x;
   3149     real_rect.y = renderer->viewport.y;
   3150     real_rect.w = renderer->viewport.w;
   3151     real_rect.h = renderer->viewport.h;
   3152     if (rect) {
   3153         if (!SDL_IntersectRect(rect, &real_rect, &real_rect)) {
   3154             return 0;
   3155         }
   3156         if (real_rect.y > rect->y) {
   3157             pixels = (Uint8 *)pixels + pitch * (real_rect.y - rect->y);
   3158         }
   3159         if (real_rect.x > rect->x) {
   3160             int bpp = SDL_BYTESPERPIXEL(format);
   3161             pixels = (Uint8 *)pixels + bpp * (real_rect.x - rect->x);
   3162         }
   3163     }
   3164 
   3165     return renderer->RenderReadPixels(renderer, &real_rect,
   3166                                       format, pixels, pitch);
   3167 }
   3168 
   3169 void
   3170 SDL_RenderPresent(SDL_Renderer * renderer)
   3171 {
   3172     CHECK_RENDERER_MAGIC(renderer, );
   3173 
   3174     FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
   3175 
   3176     /* Don't present while we're hidden */
   3177     if (renderer->hidden) {
   3178         return;
   3179     }
   3180     renderer->RenderPresent(renderer);
   3181 }
   3182 
   3183 void
   3184 SDL_DestroyTexture(SDL_Texture * texture)
   3185 {
   3186     SDL_Renderer *renderer;
   3187 
   3188     CHECK_TEXTURE_MAGIC(texture, );
   3189 
   3190     renderer = texture->renderer;
   3191     if (texture == renderer->target) {
   3192         SDL_SetRenderTarget(renderer, NULL);  /* implies command queue flush */
   3193     } else {
   3194         FlushRenderCommandsIfTextureNeeded(texture);
   3195     }
   3196 
   3197     texture->magic = NULL;
   3198 
   3199     if (texture->next) {
   3200         texture->next->prev = texture->prev;
   3201     }
   3202     if (texture->prev) {
   3203         texture->prev->next = texture->next;
   3204     } else {
   3205         renderer->textures = texture->next;
   3206     }
   3207 
   3208     if (texture->native) {
   3209         SDL_DestroyTexture(texture->native);
   3210     }
   3211 #if SDL_HAVE_YUV
   3212     if (texture->yuv) {
   3213         SDL_SW_DestroyYUVTexture(texture->yuv);
   3214     }
   3215 #endif
   3216     SDL_free(texture->pixels);
   3217 
   3218     renderer->DestroyTexture(renderer, texture);
   3219 
   3220     SDL_FreeSurface(texture->locked_surface);
   3221     texture->locked_surface = NULL;
   3222 
   3223     SDL_free(texture);
   3224 }
   3225 
   3226 void
   3227 SDL_DestroyRenderer(SDL_Renderer * renderer)
   3228 {
   3229     SDL_RenderCommand *cmd;
   3230 
   3231     CHECK_RENDERER_MAGIC(renderer, );
   3232 
   3233     SDL_DelEventWatch(SDL_RendererEventWatch, renderer);
   3234 
   3235     if (renderer->render_commands_tail != NULL) {
   3236         renderer->render_commands_tail->next = renderer->render_commands_pool;
   3237         cmd = renderer->render_commands;
   3238     } else {
   3239         cmd = renderer->render_commands_pool;
   3240     }
   3241 
   3242     renderer->render_commands_pool = NULL;
   3243     renderer->render_commands_tail = NULL;
   3244     renderer->render_commands = NULL;
   3245 
   3246     while (cmd != NULL) {
   3247         SDL_RenderCommand *next = cmd->next;
   3248         SDL_free(cmd);
   3249         cmd = next;
   3250     }
   3251 
   3252     SDL_free(renderer->vertex_data);
   3253 
   3254     /* Free existing textures for this renderer */
   3255     while (renderer->textures) {
   3256         SDL_Texture *tex = renderer->textures; (void) tex;
   3257         SDL_DestroyTexture(renderer->textures);
   3258         SDL_assert(tex != renderer->textures);  /* satisfy static analysis. */
   3259     }
   3260 
   3261     if (renderer->window) {
   3262         SDL_SetWindowData(renderer->window, SDL_WINDOWRENDERDATA, NULL);
   3263     }
   3264 
   3265     /* It's no longer magical... */
   3266     renderer->magic = NULL;
   3267 
   3268     /* Free the target mutex */
   3269     SDL_DestroyMutex(renderer->target_mutex);
   3270     renderer->target_mutex = NULL;
   3271 
   3272     /* Free the renderer instance */
   3273     renderer->DestroyRenderer(renderer);
   3274 }
   3275 
   3276 int SDL_GL_BindTexture(SDL_Texture *texture, float *texw, float *texh)
   3277 {
   3278     SDL_Renderer *renderer;
   3279 
   3280     CHECK_TEXTURE_MAGIC(texture, -1);
   3281     renderer = texture->renderer;
   3282     if (texture->native) {
   3283         return SDL_GL_BindTexture(texture->native, texw, texh);
   3284     } else if (renderer && renderer->GL_BindTexture) {
   3285         FlushRenderCommandsIfTextureNeeded(texture);  /* in case the app is going to mess with it. */
   3286         return renderer->GL_BindTexture(renderer, texture, texw, texh);
   3287     } else {
   3288         return SDL_Unsupported();
   3289     }
   3290 }
   3291 
   3292 int SDL_GL_UnbindTexture(SDL_Texture *texture)
   3293 {
   3294     SDL_Renderer *renderer;
   3295 
   3296     CHECK_TEXTURE_MAGIC(texture, -1);
   3297     renderer = texture->renderer;
   3298     if (texture->native) {
   3299         return SDL_GL_UnbindTexture(texture->native);
   3300     } else if (renderer && renderer->GL_UnbindTexture) {
   3301         FlushRenderCommandsIfTextureNeeded(texture);  /* in case the app messed with it. */
   3302         return renderer->GL_UnbindTexture(renderer, texture);
   3303     }
   3304 
   3305     return SDL_Unsupported();
   3306 }
   3307 
   3308 void *
   3309 SDL_RenderGetMetalLayer(SDL_Renderer * renderer)
   3310 {
   3311     CHECK_RENDERER_MAGIC(renderer, NULL);
   3312 
   3313     if (renderer->GetMetalLayer) {
   3314         FlushRenderCommands(renderer);  /* in case the app is going to mess with it. */
   3315         return renderer->GetMetalLayer(renderer);
   3316     }
   3317     return NULL;
   3318 }
   3319 
   3320 void *
   3321 SDL_RenderGetMetalCommandEncoder(SDL_Renderer * renderer)
   3322 {
   3323     CHECK_RENDERER_MAGIC(renderer, NULL);
   3324 
   3325     if (renderer->GetMetalCommandEncoder) {
   3326         FlushRenderCommands(renderer);  /* in case the app is going to mess with it. */
   3327         return renderer->GetMetalCommandEncoder(renderer);
   3328     }
   3329     return NULL;
   3330 }
   3331 
   3332 static SDL_BlendMode
   3333 SDL_GetShortBlendMode(SDL_BlendMode blendMode)
   3334 {
   3335     if (blendMode == SDL_BLENDMODE_NONE_FULL) {
   3336         return SDL_BLENDMODE_NONE;
   3337     }
   3338     if (blendMode == SDL_BLENDMODE_BLEND_FULL) {
   3339         return SDL_BLENDMODE_BLEND;
   3340     }
   3341     if (blendMode == SDL_BLENDMODE_ADD_FULL) {
   3342         return SDL_BLENDMODE_ADD;
   3343     }
   3344     if (blendMode == SDL_BLENDMODE_MOD_FULL) {
   3345         return SDL_BLENDMODE_MOD;
   3346     }
   3347     if (blendMode == SDL_BLENDMODE_MUL_FULL) {
   3348         return SDL_BLENDMODE_MUL;
   3349     }
   3350     return blendMode;
   3351 }
   3352 
   3353 static SDL_BlendMode
   3354 SDL_GetLongBlendMode(SDL_BlendMode blendMode)
   3355 {
   3356     if (blendMode == SDL_BLENDMODE_NONE) {
   3357         return SDL_BLENDMODE_NONE_FULL;
   3358     }
   3359     if (blendMode == SDL_BLENDMODE_BLEND) {
   3360         return SDL_BLENDMODE_BLEND_FULL;
   3361     }
   3362     if (blendMode == SDL_BLENDMODE_ADD) {
   3363         return SDL_BLENDMODE_ADD_FULL;
   3364     }
   3365     if (blendMode == SDL_BLENDMODE_MOD) {
   3366         return SDL_BLENDMODE_MOD_FULL;
   3367     }
   3368     if (blendMode == SDL_BLENDMODE_MUL) {
   3369         return SDL_BLENDMODE_MUL_FULL;
   3370     }
   3371     return blendMode;
   3372 }
   3373 
   3374 SDL_BlendMode
   3375 SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor,
   3376                            SDL_BlendOperation colorOperation,
   3377                            SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor,
   3378                            SDL_BlendOperation alphaOperation)
   3379 {
   3380     SDL_BlendMode blendMode = SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation,
   3381                                                     srcAlphaFactor, dstAlphaFactor, alphaOperation);
   3382     return SDL_GetShortBlendMode(blendMode);
   3383 }
   3384 
   3385 SDL_BlendFactor
   3386 SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode)
   3387 {
   3388     blendMode = SDL_GetLongBlendMode(blendMode);
   3389     return (SDL_BlendFactor)(((Uint32)blendMode >> 4) & 0xF);
   3390 }
   3391 
   3392 SDL_BlendFactor
   3393 SDL_GetBlendModeDstColorFactor(SDL_BlendMode blendMode)
   3394 {
   3395     blendMode = SDL_GetLongBlendMode(blendMode);
   3396     return (SDL_BlendFactor)(((Uint32)blendMode >> 8) & 0xF);
   3397 }
   3398 
   3399 SDL_BlendOperation
   3400 SDL_GetBlendModeColorOperation(SDL_BlendMode blendMode)
   3401 {
   3402     blendMode = SDL_GetLongBlendMode(blendMode);
   3403     return (SDL_BlendOperation)(((Uint32)blendMode >> 0) & 0xF);
   3404 }
   3405 
   3406 SDL_BlendFactor
   3407 SDL_GetBlendModeSrcAlphaFactor(SDL_BlendMode blendMode)
   3408 {
   3409     blendMode = SDL_GetLongBlendMode(blendMode);
   3410     return (SDL_BlendFactor)(((Uint32)blendMode >> 20) & 0xF);
   3411 }
   3412 
   3413 SDL_BlendFactor
   3414 SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode)
   3415 {
   3416     blendMode = SDL_GetLongBlendMode(blendMode);
   3417     return (SDL_BlendFactor)(((Uint32)blendMode >> 24) & 0xF);
   3418 }
   3419 
   3420 SDL_BlendOperation
   3421 SDL_GetBlendModeAlphaOperation(SDL_BlendMode blendMode)
   3422 {
   3423     blendMode = SDL_GetLongBlendMode(blendMode);
   3424     return (SDL_BlendOperation)(((Uint32)blendMode >> 16) & 0xF);
   3425 }
   3426 
   3427 /* vi: set ts=4 sw=4 expandtab: */