src/render/SDL_render.c
author Ozkan Sezer <sezeroz@gmail.com>
Thu, 01 Nov 2018 20:04:24 +0300
changeset 12383 f6430feceeda
parent 12382 03d0bddca61b
child 12447 0844007ea1c2
permissions -rw-r--r--
fix build using Watcom :

./src/render/SDL_render.c(2168): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2168): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2175): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2175): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2322): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2322): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2322): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2322): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2329): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2329): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2329): Error! E1054: Expression must be constant
./src/render/SDL_render.c(2329): Error! E1054: Expression must be constant

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