src/render/SDL_render.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 14 Jun 2019 13:56:42 -0700
changeset 12860 0f52dd40abe5
parent 12808 8a4b1beb4f6e
child 12909 8e27bc5f03d8
permissions -rw-r--r--
Worked around "Undefined symbol: ___isPlatformVersionAtLeast()" link error on Xcode 11 beta
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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_METAL
    84     &METAL_RenderDriver,
    85 #endif
    86 #if SDL_VIDEO_RENDER_OGL
    87     &GL_RenderDriver,
    88 #endif
    89 #if SDL_VIDEO_RENDER_OGL_ES2
    90     &GLES2_RenderDriver,
    91 #endif
    92 #if SDL_VIDEO_RENDER_OGL_ES
    93     &GLES_RenderDriver,
    94 #endif
    95 #if SDL_VIDEO_RENDER_DIRECTFB
    96     &DirectFB_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     SDL_bool direct_update;
  1191     int i;
  1192     Uint32 format = SDL_PIXELFORMAT_UNKNOWN;
  1193     SDL_Texture *texture;
  1194 
  1195     CHECK_RENDERER_MAGIC(renderer, NULL);
  1196 
  1197     if (!surface) {
  1198         SDL_SetError("SDL_CreateTextureFromSurface() passed NULL surface");
  1199         return NULL;
  1200     }
  1201 
  1202     /* See what the best texture format is */
  1203     fmt = surface->format;
  1204     if (fmt->Amask || SDL_HasColorKey(surface)) {
  1205         needAlpha = SDL_TRUE;
  1206     } else {
  1207         needAlpha = SDL_FALSE;
  1208     }
  1209 
  1210     /* If Palette contains alpha values, promotes to alpha format */
  1211     if (fmt->palette) {
  1212         for (i = 0; i < fmt->palette->ncolors; i++) {
  1213             Uint8 alpha_value = fmt->palette->colors[i].a;
  1214             if (alpha_value != 0 || alpha_value != SDL_ALPHA_OPAQUE) {
  1215                 needAlpha = SDL_TRUE;
  1216                 break;
  1217             }
  1218         }
  1219     }
  1220 
  1221     /* Try to have the best pixel format for the texture */
  1222     /* No alpha, but a colorkey => promote to alpha */
  1223     if (!fmt->Amask && SDL_HasColorKey(surface)) {
  1224         if (fmt->format == SDL_PIXELFORMAT_RGB888) {
  1225             for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
  1226                 if (renderer->info.texture_formats[i] == SDL_PIXELFORMAT_ARGB8888) {
  1227                     format = SDL_PIXELFORMAT_ARGB8888;
  1228                     break;
  1229                 }
  1230             }
  1231         } else if (fmt->format == SDL_PIXELFORMAT_BGR888) {
  1232             for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
  1233                 if (renderer->info.texture_formats[i] == SDL_PIXELFORMAT_ABGR8888) {
  1234                     format = SDL_PIXELFORMAT_ABGR8888;
  1235                     break;
  1236                 }
  1237             }
  1238         }
  1239     } else {
  1240         /* Exact match would be fine */
  1241         for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
  1242             if (renderer->info.texture_formats[i] == fmt->format) {
  1243                 format = fmt->format;
  1244                 break;
  1245             }
  1246         }
  1247     }
  1248 
  1249     /* Fallback, choose a valid pixel format */
  1250     if (format == SDL_PIXELFORMAT_UNKNOWN) {
  1251         format = renderer->info.texture_formats[0];
  1252         for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
  1253             if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) &&
  1254                     SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == needAlpha) {
  1255                 format = renderer->info.texture_formats[i];
  1256                 break;
  1257             }
  1258         }
  1259     }
  1260 
  1261     texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STATIC,
  1262                                 surface->w, surface->h);
  1263     if (!texture) {
  1264         return NULL;
  1265     }
  1266 
  1267     if (format == surface->format->format) {
  1268         if (surface->format->Amask && SDL_HasColorKey(surface)) {
  1269             /* Surface and Renderer formats are identicals. 
  1270              * Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */
  1271             direct_update = SDL_FALSE;
  1272         } else {
  1273             /* Update Texture directly */
  1274             direct_update = SDL_TRUE;
  1275         }
  1276     } else {
  1277         /* Surface and Renderer formats are differents, it needs an intermediate conversion. */
  1278         direct_update = SDL_FALSE;
  1279     }
  1280 
  1281     if (direct_update) {
  1282         if (SDL_MUSTLOCK(surface)) {
  1283             SDL_LockSurface(surface);
  1284             SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
  1285             SDL_UnlockSurface(surface);
  1286         } else {
  1287             SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
  1288         }
  1289     } else {
  1290         SDL_PixelFormat *dst_fmt;
  1291         SDL_Surface *temp = NULL;
  1292 
  1293         /* Set up a destination surface for the texture update */
  1294         dst_fmt = SDL_AllocFormat(format);
  1295         if (!dst_fmt) {
  1296            SDL_DestroyTexture(texture);
  1297            return NULL;
  1298         }
  1299         temp = SDL_ConvertSurface(surface, dst_fmt, 0);
  1300         SDL_FreeFormat(dst_fmt);
  1301         if (temp) {
  1302             SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
  1303             SDL_FreeSurface(temp);
  1304         } else {
  1305             SDL_DestroyTexture(texture);
  1306             return NULL;
  1307         }
  1308     }
  1309 
  1310     {
  1311         Uint8 r, g, b, a;
  1312         SDL_BlendMode blendMode;
  1313 
  1314         SDL_GetSurfaceColorMod(surface, &r, &g, &b);
  1315         SDL_SetTextureColorMod(texture, r, g, b);
  1316 
  1317         SDL_GetSurfaceAlphaMod(surface, &a);
  1318         SDL_SetTextureAlphaMod(texture, a);
  1319 
  1320         if (SDL_HasColorKey(surface)) {
  1321             /* We converted to a texture with alpha format */
  1322             SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
  1323         } else {
  1324             SDL_GetSurfaceBlendMode(surface, &blendMode);
  1325             SDL_SetTextureBlendMode(texture, blendMode);
  1326         }
  1327     }
  1328     return texture;
  1329 }
  1330 
  1331 int
  1332 SDL_QueryTexture(SDL_Texture * texture, Uint32 * format, int *access,
  1333                  int *w, int *h)
  1334 {
  1335     CHECK_TEXTURE_MAGIC(texture, -1);
  1336 
  1337     if (format) {
  1338         *format = texture->format;
  1339     }
  1340     if (access) {
  1341         *access = texture->access;
  1342     }
  1343     if (w) {
  1344         *w = texture->w;
  1345     }
  1346     if (h) {
  1347         *h = texture->h;
  1348     }
  1349     return 0;
  1350 }
  1351 
  1352 int
  1353 SDL_SetTextureColorMod(SDL_Texture * texture, Uint8 r, Uint8 g, Uint8 b)
  1354 {
  1355     CHECK_TEXTURE_MAGIC(texture, -1);
  1356 
  1357     if (r < 255 || g < 255 || b < 255) {
  1358         texture->modMode |= SDL_TEXTUREMODULATE_COLOR;
  1359     } else {
  1360         texture->modMode &= ~SDL_TEXTUREMODULATE_COLOR;
  1361     }
  1362     texture->r = r;
  1363     texture->g = g;
  1364     texture->b = b;
  1365     if (texture->native) {
  1366         return SDL_SetTextureColorMod(texture->native, r, g, b);
  1367     }
  1368     return 0;
  1369 }
  1370 
  1371 int
  1372 SDL_GetTextureColorMod(SDL_Texture * texture, Uint8 * r, Uint8 * g,
  1373                        Uint8 * b)
  1374 {
  1375     CHECK_TEXTURE_MAGIC(texture, -1);
  1376 
  1377     if (r) {
  1378         *r = texture->r;
  1379     }
  1380     if (g) {
  1381         *g = texture->g;
  1382     }
  1383     if (b) {
  1384         *b = texture->b;
  1385     }
  1386     return 0;
  1387 }
  1388 
  1389 int
  1390 SDL_SetTextureAlphaMod(SDL_Texture * texture, Uint8 alpha)
  1391 {
  1392     CHECK_TEXTURE_MAGIC(texture, -1);
  1393 
  1394     if (alpha < 255) {
  1395         texture->modMode |= SDL_TEXTUREMODULATE_ALPHA;
  1396     } else {
  1397         texture->modMode &= ~SDL_TEXTUREMODULATE_ALPHA;
  1398     }
  1399     texture->a = alpha;
  1400     if (texture->native) {
  1401         return SDL_SetTextureAlphaMod(texture->native, alpha);
  1402     }
  1403     return 0;
  1404 }
  1405 
  1406 int
  1407 SDL_GetTextureAlphaMod(SDL_Texture * texture, Uint8 * alpha)
  1408 {
  1409     CHECK_TEXTURE_MAGIC(texture, -1);
  1410 
  1411     if (alpha) {
  1412         *alpha = texture->a;
  1413     }
  1414     return 0;
  1415 }
  1416 
  1417 int
  1418 SDL_SetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode blendMode)
  1419 {
  1420     SDL_Renderer *renderer;
  1421 
  1422     CHECK_TEXTURE_MAGIC(texture, -1);
  1423 
  1424     renderer = texture->renderer;
  1425     if (!IsSupportedBlendMode(renderer, blendMode)) {
  1426         return SDL_Unsupported();
  1427     }
  1428     texture->blendMode = blendMode;
  1429     if (texture->native) {
  1430         return SDL_SetTextureBlendMode(texture->native, blendMode);
  1431     }
  1432     return 0;
  1433 }
  1434 
  1435 int
  1436 SDL_GetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode *blendMode)
  1437 {
  1438     CHECK_TEXTURE_MAGIC(texture, -1);
  1439 
  1440     if (blendMode) {
  1441         *blendMode = texture->blendMode;
  1442     }
  1443     return 0;
  1444 }
  1445 
  1446 static int
  1447 SDL_UpdateTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
  1448                      const void *pixels, int pitch)
  1449 {
  1450     SDL_Texture *native = texture->native;
  1451     SDL_Rect full_rect;
  1452 
  1453     if (SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch) < 0) {
  1454         return -1;
  1455     }
  1456 
  1457     full_rect.x = 0;
  1458     full_rect.y = 0;
  1459     full_rect.w = texture->w;
  1460     full_rect.h = texture->h;
  1461     rect = &full_rect;
  1462 
  1463     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  1464         /* We can lock the texture and copy to it */
  1465         void *native_pixels = NULL;
  1466         int native_pitch = 0;
  1467 
  1468         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
  1469             return -1;
  1470         }
  1471         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
  1472                             rect->w, rect->h, native_pixels, native_pitch);
  1473         SDL_UnlockTexture(native);
  1474     } else {
  1475         /* Use a temporary buffer for updating */
  1476         const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
  1477         const size_t alloclen = rect->h * temp_pitch;
  1478         if (alloclen > 0) {
  1479             void *temp_pixels = SDL_malloc(alloclen);
  1480             if (!temp_pixels) {
  1481                 return SDL_OutOfMemory();
  1482             }
  1483             SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
  1484                                 rect->w, rect->h, temp_pixels, temp_pitch);
  1485             SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
  1486             SDL_free(temp_pixels);
  1487         }
  1488     }
  1489     return 0;
  1490 }
  1491 
  1492 static int
  1493 SDL_UpdateTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
  1494                         const void *pixels, int pitch)
  1495 {
  1496     SDL_Texture *native = texture->native;
  1497 
  1498     if (!rect->w || !rect->h) {
  1499         return 0;  /* nothing to do. */
  1500     }
  1501 
  1502     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  1503         /* We can lock the texture and copy to it */
  1504         void *native_pixels = NULL;
  1505         int native_pitch = 0;
  1506 
  1507         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
  1508             return -1;
  1509         }
  1510         SDL_ConvertPixels(rect->w, rect->h,
  1511                           texture->format, pixels, pitch,
  1512                           native->format, native_pixels, native_pitch);
  1513         SDL_UnlockTexture(native);
  1514     } else {
  1515         /* Use a temporary buffer for updating */
  1516         const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
  1517         const size_t alloclen = rect->h * temp_pitch;
  1518         if (alloclen > 0) {
  1519             void *temp_pixels = SDL_malloc(alloclen);
  1520             if (!temp_pixels) {
  1521                 return SDL_OutOfMemory();
  1522             }
  1523             SDL_ConvertPixels(rect->w, rect->h,
  1524                               texture->format, pixels, pitch,
  1525                               native->format, temp_pixels, temp_pitch);
  1526             SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
  1527             SDL_free(temp_pixels);
  1528         }
  1529     }
  1530     return 0;
  1531 }
  1532 
  1533 int
  1534 SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect,
  1535                   const void *pixels, int pitch)
  1536 {
  1537     SDL_Rect full_rect;
  1538 
  1539     CHECK_TEXTURE_MAGIC(texture, -1);
  1540 
  1541     if (!pixels) {
  1542         return SDL_InvalidParamError("pixels");
  1543     }
  1544     if (!pitch) {
  1545         return SDL_InvalidParamError("pitch");
  1546     }
  1547 
  1548     if (!rect) {
  1549         full_rect.x = 0;
  1550         full_rect.y = 0;
  1551         full_rect.w = texture->w;
  1552         full_rect.h = texture->h;
  1553         rect = &full_rect;
  1554     }
  1555 
  1556     if ((rect->w == 0) || (rect->h == 0)) {
  1557         return 0;  /* nothing to do. */
  1558     } else if (texture->yuv) {
  1559         return SDL_UpdateTextureYUV(texture, rect, pixels, pitch);
  1560     } else if (texture->native) {
  1561         return SDL_UpdateTextureNative(texture, rect, pixels, pitch);
  1562     } else {
  1563         SDL_Renderer *renderer = texture->renderer;
  1564         if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
  1565             return -1;
  1566         }
  1567         return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
  1568     }
  1569 }
  1570 
  1571 static int
  1572 SDL_UpdateTextureYUVPlanar(SDL_Texture * texture, const SDL_Rect * rect,
  1573                            const Uint8 *Yplane, int Ypitch,
  1574                            const Uint8 *Uplane, int Upitch,
  1575                            const Uint8 *Vplane, int Vpitch)
  1576 {
  1577     SDL_Texture *native = texture->native;
  1578     SDL_Rect full_rect;
  1579 
  1580     if (SDL_SW_UpdateYUVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch) < 0) {
  1581         return -1;
  1582     }
  1583 
  1584     full_rect.x = 0;
  1585     full_rect.y = 0;
  1586     full_rect.w = texture->w;
  1587     full_rect.h = texture->h;
  1588     rect = &full_rect;
  1589 
  1590     if (!rect->w || !rect->h) {
  1591         return 0;  /* nothing to do. */
  1592     }
  1593 
  1594     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  1595         /* We can lock the texture and copy to it */
  1596         void *native_pixels = NULL;
  1597         int native_pitch = 0;
  1598 
  1599         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
  1600             return -1;
  1601         }
  1602         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
  1603                             rect->w, rect->h, native_pixels, native_pitch);
  1604         SDL_UnlockTexture(native);
  1605     } else {
  1606         /* Use a temporary buffer for updating */
  1607         const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
  1608         const size_t alloclen = rect->h * temp_pitch;
  1609         if (alloclen > 0) {
  1610             void *temp_pixels = SDL_malloc(alloclen);
  1611             if (!temp_pixels) {
  1612                 return SDL_OutOfMemory();
  1613             }
  1614             SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
  1615                                 rect->w, rect->h, temp_pixels, temp_pitch);
  1616             SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
  1617             SDL_free(temp_pixels);
  1618         }
  1619     }
  1620     return 0;
  1621 }
  1622 
  1623 int SDL_UpdateYUVTexture(SDL_Texture * texture, const SDL_Rect * rect,
  1624                          const Uint8 *Yplane, int Ypitch,
  1625                          const Uint8 *Uplane, int Upitch,
  1626                          const Uint8 *Vplane, int Vpitch)
  1627 {
  1628     SDL_Renderer *renderer;
  1629     SDL_Rect full_rect;
  1630 
  1631     CHECK_TEXTURE_MAGIC(texture, -1);
  1632 
  1633     if (!Yplane) {
  1634         return SDL_InvalidParamError("Yplane");
  1635     }
  1636     if (!Ypitch) {
  1637         return SDL_InvalidParamError("Ypitch");
  1638     }
  1639     if (!Uplane) {
  1640         return SDL_InvalidParamError("Uplane");
  1641     }
  1642     if (!Upitch) {
  1643         return SDL_InvalidParamError("Upitch");
  1644     }
  1645     if (!Vplane) {
  1646         return SDL_InvalidParamError("Vplane");
  1647     }
  1648     if (!Vpitch) {
  1649         return SDL_InvalidParamError("Vpitch");
  1650     }
  1651 
  1652     if (texture->format != SDL_PIXELFORMAT_YV12 &&
  1653         texture->format != SDL_PIXELFORMAT_IYUV) {
  1654         return SDL_SetError("Texture format must by YV12 or IYUV");
  1655     }
  1656 
  1657     if (!rect) {
  1658         full_rect.x = 0;
  1659         full_rect.y = 0;
  1660         full_rect.w = texture->w;
  1661         full_rect.h = texture->h;
  1662         rect = &full_rect;
  1663     }
  1664 
  1665     if (!rect->w || !rect->h) {
  1666         return 0;  /* nothing to do. */
  1667     }
  1668 
  1669     if (texture->yuv) {
  1670         return SDL_UpdateTextureYUVPlanar(texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
  1671     } else {
  1672         SDL_assert(!texture->native);
  1673         renderer = texture->renderer;
  1674         SDL_assert(renderer->UpdateTextureYUV);
  1675         if (renderer->UpdateTextureYUV) {
  1676             if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
  1677                 return -1;
  1678             }
  1679             return renderer->UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
  1680         } else {
  1681             return SDL_Unsupported();
  1682         }
  1683     }
  1684 }
  1685 
  1686 static int
  1687 SDL_LockTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
  1688                    void **pixels, int *pitch)
  1689 {
  1690     return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch);
  1691 }
  1692 
  1693 static int
  1694 SDL_LockTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
  1695                       void **pixels, int *pitch)
  1696 {
  1697     texture->locked_rect = *rect;
  1698     *pixels = (void *) ((Uint8 *) texture->pixels +
  1699                         rect->y * texture->pitch +
  1700                         rect->x * SDL_BYTESPERPIXEL(texture->format));
  1701     *pitch = texture->pitch;
  1702     return 0;
  1703 }
  1704 
  1705 int
  1706 SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect,
  1707                 void **pixels, int *pitch)
  1708 {
  1709     SDL_Rect full_rect;
  1710 
  1711     CHECK_TEXTURE_MAGIC(texture, -1);
  1712 
  1713     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
  1714         return SDL_SetError("SDL_LockTexture(): texture must be streaming");
  1715     }
  1716 
  1717     if (!rect) {
  1718         full_rect.x = 0;
  1719         full_rect.y = 0;
  1720         full_rect.w = texture->w;
  1721         full_rect.h = texture->h;
  1722         rect = &full_rect;
  1723     }
  1724 
  1725     if (texture->yuv) {
  1726         if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
  1727             return -1;
  1728         }
  1729         return SDL_LockTextureYUV(texture, rect, pixels, pitch);
  1730     } else if (texture->native) {
  1731         /* Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. */
  1732         return SDL_LockTextureNative(texture, rect, pixels, pitch);
  1733     } else {
  1734         SDL_Renderer *renderer = texture->renderer;
  1735         if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
  1736             return -1;
  1737         }
  1738         return renderer->LockTexture(renderer, texture, rect, pixels, pitch);
  1739     }
  1740 }
  1741 
  1742 static void
  1743 SDL_UnlockTextureYUV(SDL_Texture * texture)
  1744 {
  1745     SDL_Texture *native = texture->native;
  1746     void *native_pixels = NULL;
  1747     int native_pitch = 0;
  1748     SDL_Rect rect;
  1749 
  1750     rect.x = 0;
  1751     rect.y = 0;
  1752     rect.w = texture->w;
  1753     rect.h = texture->h;
  1754 
  1755     if (SDL_LockTexture(native, &rect, &native_pixels, &native_pitch) < 0) {
  1756         return;
  1757     }
  1758     SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format,
  1759                         rect.w, rect.h, native_pixels, native_pitch);
  1760     SDL_UnlockTexture(native);
  1761 }
  1762 
  1763 static void
  1764 SDL_UnlockTextureNative(SDL_Texture * texture)
  1765 {
  1766     SDL_Texture *native = texture->native;
  1767     void *native_pixels = NULL;
  1768     int native_pitch = 0;
  1769     const SDL_Rect *rect = &texture->locked_rect;
  1770     const void* pixels = (void *) ((Uint8 *) texture->pixels +
  1771                         rect->y * texture->pitch +
  1772                         rect->x * SDL_BYTESPERPIXEL(texture->format));
  1773     int pitch = texture->pitch;
  1774 
  1775     if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
  1776         return;
  1777     }
  1778     SDL_ConvertPixels(rect->w, rect->h,
  1779                       texture->format, pixels, pitch,
  1780                       native->format, native_pixels, native_pitch);
  1781     SDL_UnlockTexture(native);
  1782 }
  1783 
  1784 void
  1785 SDL_UnlockTexture(SDL_Texture * texture)
  1786 {
  1787     CHECK_TEXTURE_MAGIC(texture, );
  1788 
  1789     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
  1790         return;
  1791     }
  1792     if (texture->yuv) {
  1793         SDL_UnlockTextureYUV(texture);
  1794     } else if (texture->native) {
  1795         SDL_UnlockTextureNative(texture);
  1796     } else {
  1797         SDL_Renderer *renderer = texture->renderer;
  1798         renderer->UnlockTexture(renderer, texture);
  1799     }
  1800 }
  1801 
  1802 SDL_bool
  1803 SDL_RenderTargetSupported(SDL_Renderer *renderer)
  1804 {
  1805     if (!renderer || !renderer->SetRenderTarget) {
  1806         return SDL_FALSE;
  1807     }
  1808     return (renderer->info.flags & SDL_RENDERER_TARGETTEXTURE) != 0;
  1809 }
  1810 
  1811 int
  1812 SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
  1813 {
  1814     if (!SDL_RenderTargetSupported(renderer)) {
  1815         return SDL_Unsupported();
  1816     }
  1817     if (texture == renderer->target) {
  1818         /* Nothing to do! */
  1819         return 0;
  1820     }
  1821 
  1822     FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
  1823 
  1824     /* texture == NULL is valid and means reset the target to the window */
  1825     if (texture) {
  1826         CHECK_TEXTURE_MAGIC(texture, -1);
  1827         if (renderer != texture->renderer) {
  1828             return SDL_SetError("Texture was not created with this renderer");
  1829         }
  1830         if (texture->access != SDL_TEXTUREACCESS_TARGET) {
  1831             return SDL_SetError("Texture not created with SDL_TEXTUREACCESS_TARGET");
  1832         }
  1833         if (texture->native) {
  1834             /* Always render to the native texture */
  1835             texture = texture->native;
  1836         }
  1837     }
  1838 
  1839     SDL_LockMutex(renderer->target_mutex);
  1840 
  1841     if (texture && !renderer->target) {
  1842         /* Make a backup of the viewport */
  1843         renderer->viewport_backup = renderer->viewport;
  1844         renderer->clip_rect_backup = renderer->clip_rect;
  1845         renderer->clipping_enabled_backup = renderer->clipping_enabled;
  1846         renderer->scale_backup = renderer->scale;
  1847         renderer->logical_w_backup = renderer->logical_w;
  1848         renderer->logical_h_backup = renderer->logical_h;
  1849     }
  1850     renderer->target = texture;
  1851 
  1852     if (renderer->SetRenderTarget(renderer, texture) < 0) {
  1853         SDL_UnlockMutex(renderer->target_mutex);
  1854         return -1;
  1855     }
  1856 
  1857     if (texture) {
  1858         renderer->viewport.x = 0;
  1859         renderer->viewport.y = 0;
  1860         renderer->viewport.w = texture->w;
  1861         renderer->viewport.h = texture->h;
  1862         SDL_zero(renderer->clip_rect);
  1863         renderer->clipping_enabled = SDL_FALSE;
  1864         renderer->scale.x = 1.0f;
  1865         renderer->scale.y = 1.0f;
  1866         renderer->logical_w = texture->w;
  1867         renderer->logical_h = texture->h;
  1868     } else {
  1869         renderer->viewport = renderer->viewport_backup;
  1870         renderer->clip_rect = renderer->clip_rect_backup;
  1871         renderer->clipping_enabled = renderer->clipping_enabled_backup;
  1872         renderer->scale = renderer->scale_backup;
  1873         renderer->logical_w = renderer->logical_w_backup;
  1874         renderer->logical_h = renderer->logical_h_backup;
  1875     }
  1876 
  1877     SDL_UnlockMutex(renderer->target_mutex);
  1878 
  1879     if (QueueCmdSetViewport(renderer) < 0) {
  1880         return -1;
  1881     }
  1882     if (QueueCmdSetClipRect(renderer) < 0) {
  1883         return -1;
  1884     }
  1885 
  1886     /* All set! */
  1887     return FlushRenderCommandsIfNotBatching(renderer);
  1888 }
  1889 
  1890 SDL_Texture *
  1891 SDL_GetRenderTarget(SDL_Renderer *renderer)
  1892 {
  1893     return renderer->target;
  1894 }
  1895 
  1896 static int
  1897 UpdateLogicalSize(SDL_Renderer *renderer)
  1898 {
  1899     int w = 1, h = 1;
  1900     float want_aspect;
  1901     float real_aspect;
  1902     float scale;
  1903     SDL_Rect viewport;
  1904     /* 0 is for letterbox, 1 is for overscan */
  1905     int scale_policy = 0;
  1906     const char *hint;
  1907 
  1908     if (!renderer->logical_w || !renderer->logical_h) {
  1909         return 0;
  1910     }
  1911     if (SDL_GetRendererOutputSize(renderer, &w, &h) < 0) {
  1912         return -1;
  1913     }
  1914 
  1915     hint = SDL_GetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE);
  1916     if (hint && (*hint == '1' || SDL_strcasecmp(hint, "overscan") == 0))  {
  1917 #if SDL_VIDEO_RENDER_D3D
  1918         SDL_bool overscan_supported = SDL_TRUE;
  1919         /* Unfortunately, Direct3D 9 doesn't support negative viewport numbers
  1920            which the overscan implementation relies on.
  1921         */
  1922         if (SDL_strcasecmp(SDL_GetCurrentVideoDriver(), "direct3d") == 0) {
  1923             overscan_supported = SDL_FALSE;
  1924         }
  1925         if (overscan_supported) {
  1926             scale_policy = 1;
  1927         }
  1928 #else
  1929         scale_policy = 1;
  1930 #endif
  1931     }
  1932 
  1933     want_aspect = (float)renderer->logical_w / renderer->logical_h;
  1934     real_aspect = (float)w / h;
  1935 
  1936     /* Clear the scale because we're setting viewport in output coordinates */
  1937     SDL_RenderSetScale(renderer, 1.0f, 1.0f);
  1938 
  1939     if (renderer->integer_scale) {
  1940         if (want_aspect > real_aspect) {
  1941             scale = (float)(w / renderer->logical_w);
  1942         } else {
  1943             scale = (float)(h / renderer->logical_h);
  1944         }
  1945         viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
  1946         viewport.x = (w - viewport.w) / 2;
  1947         viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
  1948         viewport.y = (h - viewport.h) / 2;
  1949 
  1950         SDL_RenderSetViewport(renderer, &viewport);
  1951     } else if (SDL_fabs(want_aspect-real_aspect) < 0.0001) {
  1952         /* The aspect ratios are the same, just scale appropriately */
  1953         scale = (float)w / renderer->logical_w;
  1954         SDL_RenderSetViewport(renderer, NULL);
  1955     } else if (want_aspect > real_aspect) {
  1956         if (scale_policy == 1) {
  1957             /* We want a wider aspect ratio than is available - 
  1958              zoom so logical height matches the real height 
  1959              and the width will grow off the screen 
  1960              */
  1961             scale = (float)h / renderer->logical_h;
  1962             viewport.y = 0;
  1963             viewport.h = h;
  1964             viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
  1965             viewport.x = (w - viewport.w) / 2;
  1966             SDL_RenderSetViewport(renderer, &viewport);
  1967         } else {
  1968             /* We want a wider aspect ratio than is available - letterbox it */
  1969             scale = (float)w / renderer->logical_w;
  1970             viewport.x = 0;
  1971             viewport.w = w;
  1972             viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
  1973             viewport.y = (h - viewport.h) / 2;
  1974             SDL_RenderSetViewport(renderer, &viewport);
  1975         }
  1976     } else {
  1977         if (scale_policy == 1) {
  1978             /* We want a narrower aspect ratio than is available -
  1979              zoom so logical width matches the real width
  1980              and the height will grow off the screen
  1981              */
  1982             scale = (float)w / renderer->logical_w;
  1983             viewport.x = 0;
  1984             viewport.w = w;
  1985             viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
  1986             viewport.y = (h - viewport.h) / 2;
  1987             SDL_RenderSetViewport(renderer, &viewport);
  1988         } else {
  1989             /* We want a narrower aspect ratio than is available - use side-bars */
  1990              scale = (float)h / renderer->logical_h;
  1991              viewport.y = 0;
  1992              viewport.h = h;
  1993              viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
  1994              viewport.x = (w - viewport.w) / 2;
  1995              SDL_RenderSetViewport(renderer, &viewport);
  1996         }
  1997     }
  1998 
  1999     /* Set the new scale */
  2000     SDL_RenderSetScale(renderer, scale, scale);
  2001 
  2002     return 0;
  2003 }
  2004 
  2005 int
  2006 SDL_RenderSetLogicalSize(SDL_Renderer * renderer, int w, int h)
  2007 {
  2008     CHECK_RENDERER_MAGIC(renderer, -1);
  2009 
  2010     if (!w || !h) {
  2011         /* Clear any previous logical resolution */
  2012         renderer->logical_w = 0;
  2013         renderer->logical_h = 0;
  2014         SDL_RenderSetViewport(renderer, NULL);
  2015         SDL_RenderSetScale(renderer, 1.0f, 1.0f);
  2016         return 0;
  2017     }
  2018 
  2019     renderer->logical_w = w;
  2020     renderer->logical_h = h;
  2021 
  2022     return UpdateLogicalSize(renderer);
  2023 }
  2024 
  2025 void
  2026 SDL_RenderGetLogicalSize(SDL_Renderer * renderer, int *w, int *h)
  2027 {
  2028     CHECK_RENDERER_MAGIC(renderer, );
  2029 
  2030     if (w) {
  2031         *w = renderer->logical_w;
  2032     }
  2033     if (h) {
  2034         *h = renderer->logical_h;
  2035     }
  2036 }
  2037 
  2038 int
  2039 SDL_RenderSetIntegerScale(SDL_Renderer * renderer, SDL_bool enable)
  2040 {
  2041     CHECK_RENDERER_MAGIC(renderer, -1);
  2042 
  2043     renderer->integer_scale = enable;
  2044 
  2045     return UpdateLogicalSize(renderer);
  2046 }
  2047 
  2048 SDL_bool
  2049 SDLCALL SDL_RenderGetIntegerScale(SDL_Renderer * renderer)
  2050 {
  2051     CHECK_RENDERER_MAGIC(renderer, SDL_FALSE);
  2052 
  2053     return renderer->integer_scale;
  2054 }
  2055 
  2056 int
  2057 SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect)
  2058 {
  2059     int retval;
  2060     CHECK_RENDERER_MAGIC(renderer, -1);
  2061 
  2062     if (rect) {
  2063         renderer->viewport.x = (int)SDL_floor(rect->x * renderer->scale.x);
  2064         renderer->viewport.y = (int)SDL_floor(rect->y * renderer->scale.y);
  2065         renderer->viewport.w = (int)SDL_ceil(rect->w * renderer->scale.x);
  2066         renderer->viewport.h = (int)SDL_ceil(rect->h * renderer->scale.y);
  2067     } else {
  2068         renderer->viewport.x = 0;
  2069         renderer->viewport.y = 0;
  2070         if (SDL_GetRendererOutputSize(renderer, &renderer->viewport.w, &renderer->viewport.h) < 0) {
  2071             return -1;
  2072         }
  2073     }
  2074     retval = QueueCmdSetViewport(renderer);
  2075     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2076 }
  2077 
  2078 void
  2079 SDL_RenderGetViewport(SDL_Renderer * renderer, SDL_Rect * rect)
  2080 {
  2081     CHECK_RENDERER_MAGIC(renderer, );
  2082 
  2083     if (rect) {
  2084         rect->x = (int)(renderer->viewport.x / renderer->scale.x);
  2085         rect->y = (int)(renderer->viewport.y / renderer->scale.y);
  2086         rect->w = (int)(renderer->viewport.w / renderer->scale.x);
  2087         rect->h = (int)(renderer->viewport.h / renderer->scale.y);
  2088     }
  2089 }
  2090 
  2091 int
  2092 SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect)
  2093 {
  2094     int retval;
  2095     CHECK_RENDERER_MAGIC(renderer, -1)
  2096 
  2097     if (rect) {
  2098         renderer->clipping_enabled = SDL_TRUE;
  2099         renderer->clip_rect.x = (int)SDL_floor(rect->x * renderer->scale.x);
  2100         renderer->clip_rect.y = (int)SDL_floor(rect->y * renderer->scale.y);
  2101         renderer->clip_rect.w = (int)SDL_ceil(rect->w * renderer->scale.x);
  2102         renderer->clip_rect.h = (int)SDL_ceil(rect->h * renderer->scale.y);
  2103     } else {
  2104         renderer->clipping_enabled = SDL_FALSE;
  2105         SDL_zero(renderer->clip_rect);
  2106     }
  2107 
  2108     retval = QueueCmdSetClipRect(renderer);
  2109     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2110 }
  2111 
  2112 void
  2113 SDL_RenderGetClipRect(SDL_Renderer * renderer, SDL_Rect * rect)
  2114 {
  2115     CHECK_RENDERER_MAGIC(renderer, )
  2116 
  2117     if (rect) {
  2118         rect->x = (int)(renderer->clip_rect.x / renderer->scale.x);
  2119         rect->y = (int)(renderer->clip_rect.y / renderer->scale.y);
  2120         rect->w = (int)(renderer->clip_rect.w / renderer->scale.x);
  2121         rect->h = (int)(renderer->clip_rect.h / renderer->scale.y);
  2122     }
  2123 }
  2124 
  2125 SDL_bool
  2126 SDL_RenderIsClipEnabled(SDL_Renderer * renderer)
  2127 {
  2128     CHECK_RENDERER_MAGIC(renderer, SDL_FALSE)
  2129     return renderer->clipping_enabled;
  2130 }
  2131 
  2132 int
  2133 SDL_RenderSetScale(SDL_Renderer * renderer, float scaleX, float scaleY)
  2134 {
  2135     CHECK_RENDERER_MAGIC(renderer, -1);
  2136 
  2137     renderer->scale.x = scaleX;
  2138     renderer->scale.y = scaleY;
  2139     return 0;
  2140 }
  2141 
  2142 void
  2143 SDL_RenderGetScale(SDL_Renderer * renderer, float *scaleX, float *scaleY)
  2144 {
  2145     CHECK_RENDERER_MAGIC(renderer, );
  2146 
  2147     if (scaleX) {
  2148         *scaleX = renderer->scale.x;
  2149     }
  2150     if (scaleY) {
  2151         *scaleY = renderer->scale.y;
  2152     }
  2153 }
  2154 
  2155 int
  2156 SDL_SetRenderDrawColor(SDL_Renderer * renderer,
  2157                        Uint8 r, Uint8 g, Uint8 b, Uint8 a)
  2158 {
  2159     CHECK_RENDERER_MAGIC(renderer, -1);
  2160 
  2161     renderer->r = r;
  2162     renderer->g = g;
  2163     renderer->b = b;
  2164     renderer->a = a;
  2165     return 0;
  2166 }
  2167 
  2168 int
  2169 SDL_GetRenderDrawColor(SDL_Renderer * renderer,
  2170                        Uint8 * r, Uint8 * g, Uint8 * b, Uint8 * a)
  2171 {
  2172     CHECK_RENDERER_MAGIC(renderer, -1);
  2173 
  2174     if (r) {
  2175         *r = renderer->r;
  2176     }
  2177     if (g) {
  2178         *g = renderer->g;
  2179     }
  2180     if (b) {
  2181         *b = renderer->b;
  2182     }
  2183     if (a) {
  2184         *a = renderer->a;
  2185     }
  2186     return 0;
  2187 }
  2188 
  2189 int
  2190 SDL_SetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
  2191 {
  2192     CHECK_RENDERER_MAGIC(renderer, -1);
  2193 
  2194     if (!IsSupportedBlendMode(renderer, blendMode)) {
  2195         return SDL_Unsupported();
  2196     }
  2197     renderer->blendMode = blendMode;
  2198     return 0;
  2199 }
  2200 
  2201 int
  2202 SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode *blendMode)
  2203 {
  2204     CHECK_RENDERER_MAGIC(renderer, -1);
  2205 
  2206     *blendMode = renderer->blendMode;
  2207     return 0;
  2208 }
  2209 
  2210 int
  2211 SDL_RenderClear(SDL_Renderer * renderer)
  2212 {
  2213     int retval;
  2214     CHECK_RENDERER_MAGIC(renderer, -1);
  2215     retval = QueueCmdClear(renderer);
  2216     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2217 }
  2218 
  2219 
  2220 /* !!! FIXME: delete all the duplicate code for the integer versions in 2.1,
  2221    !!! FIXME:  making the floating point versions the only available APIs. */
  2222 
  2223 int
  2224 SDL_RenderDrawPoint(SDL_Renderer * renderer, int x, int y)
  2225 {
  2226     SDL_FPoint fpoint;
  2227     fpoint.x = (float) x;
  2228     fpoint.y = (float) y;
  2229     return SDL_RenderDrawPointsF(renderer, &fpoint, 1);
  2230 }
  2231 
  2232 int
  2233 SDL_RenderDrawPointF(SDL_Renderer * renderer, float x, float y)
  2234 {
  2235     SDL_FPoint fpoint;
  2236     fpoint.x = x;
  2237     fpoint.y = y;
  2238     return SDL_RenderDrawPointsF(renderer, &fpoint, 1);
  2239 }
  2240 
  2241 static int
  2242 RenderDrawPointsWithRects(SDL_Renderer * renderer,
  2243                           const SDL_Point * points, const int count)
  2244 {
  2245     int retval = -1;
  2246     SDL_bool isstack;
  2247     SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack);
  2248     int i;
  2249 
  2250     if (!frects) {
  2251         return SDL_OutOfMemory();
  2252     }
  2253 
  2254     for (i = 0; i < count; ++i) {
  2255         frects[i].x = points[i].x * renderer->scale.x;
  2256         frects[i].y = points[i].y * renderer->scale.y;
  2257         frects[i].w = renderer->scale.x;
  2258         frects[i].h = renderer->scale.y;
  2259     }
  2260 
  2261     retval = QueueCmdFillRects(renderer, frects, count);
  2262 
  2263     SDL_small_free(frects, isstack);
  2264 
  2265     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2266 }
  2267 
  2268 int
  2269 SDL_RenderDrawPoints(SDL_Renderer * renderer,
  2270                      const SDL_Point * points, int count)
  2271 {
  2272     SDL_FPoint *fpoints;
  2273     int i;
  2274     int retval;
  2275     SDL_bool isstack;
  2276 
  2277     CHECK_RENDERER_MAGIC(renderer, -1);
  2278 
  2279     if (!points) {
  2280         return SDL_SetError("SDL_RenderDrawPoints(): Passed NULL points");
  2281     }
  2282     if (count < 1) {
  2283         return 0;
  2284     }
  2285 
  2286     /* Don't draw while we're hidden */
  2287     if (renderer->hidden) {
  2288         return 0;
  2289     }
  2290 
  2291     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
  2292         return RenderDrawPointsWithRects(renderer, points, count);
  2293     }
  2294 
  2295     fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
  2296     if (!fpoints) {
  2297         return SDL_OutOfMemory();
  2298     }
  2299     for (i = 0; i < count; ++i) {
  2300         fpoints[i].x = points[i].x * renderer->scale.x;
  2301         fpoints[i].y = points[i].y * renderer->scale.y;
  2302     }
  2303 
  2304     retval = QueueCmdDrawPoints(renderer, fpoints, count);
  2305 
  2306     SDL_small_free(fpoints, isstack);
  2307 
  2308     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2309 }
  2310 
  2311 static int
  2312 RenderDrawPointsWithRectsF(SDL_Renderer * renderer,
  2313                            const SDL_FPoint * fpoints, const int count)
  2314 {
  2315     int retval = -1;
  2316     SDL_bool isstack;
  2317     SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack);
  2318     int i;
  2319 
  2320     if (!frects) {
  2321         return SDL_OutOfMemory();
  2322     }
  2323 
  2324     for (i = 0; i < count; ++i) {
  2325         frects[i].x = fpoints[i].x * renderer->scale.x;
  2326         frects[i].y = fpoints[i].y * renderer->scale.y;
  2327         frects[i].w = renderer->scale.x;
  2328         frects[i].h = renderer->scale.y;
  2329     }
  2330 
  2331     retval = QueueCmdFillRects(renderer, frects, count);
  2332 
  2333     SDL_small_free(frects, isstack);
  2334 
  2335     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2336 }
  2337 
  2338 int
  2339 SDL_RenderDrawPointsF(SDL_Renderer * renderer,
  2340                       const SDL_FPoint * points, int count)
  2341 {
  2342     SDL_FPoint *fpoints;
  2343     int i;
  2344     int retval;
  2345     SDL_bool isstack;
  2346 
  2347     CHECK_RENDERER_MAGIC(renderer, -1);
  2348 
  2349     if (!points) {
  2350         return SDL_SetError("SDL_RenderDrawFPoints(): Passed NULL points");
  2351     }
  2352     if (count < 1) {
  2353         return 0;
  2354     }
  2355 
  2356     /* Don't draw while we're hidden */
  2357     if (renderer->hidden) {
  2358         return 0;
  2359     }
  2360 
  2361     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
  2362         return RenderDrawPointsWithRectsF(renderer, points, count);
  2363     }
  2364 
  2365     fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
  2366     if (!fpoints) {
  2367         return SDL_OutOfMemory();
  2368     }
  2369     for (i = 0; i < count; ++i) {
  2370         fpoints[i].x = points[i].x * renderer->scale.x;
  2371         fpoints[i].y = points[i].y * renderer->scale.y;
  2372     }
  2373 
  2374     retval = QueueCmdDrawPoints(renderer, fpoints, count);
  2375 
  2376     SDL_small_free(fpoints, isstack);
  2377 
  2378     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2379 }
  2380 
  2381 int
  2382 SDL_RenderDrawLine(SDL_Renderer * renderer, int x1, int y1, int x2, int y2)
  2383 {
  2384     SDL_FPoint points[2];
  2385     points[0].x = (float) x1;
  2386     points[0].y = (float) y1;
  2387     points[1].x = (float) x2;
  2388     points[1].y = (float) y2;
  2389     return SDL_RenderDrawLinesF(renderer, points, 2);
  2390 }
  2391 
  2392 int
  2393 SDL_RenderDrawLineF(SDL_Renderer * renderer, float x1, float y1, float x2, float y2)
  2394 {
  2395     SDL_FPoint points[2];
  2396     points[0].x = x1;
  2397     points[0].y = y1;
  2398     points[1].x = x2;
  2399     points[1].y = y2;
  2400     return SDL_RenderDrawLinesF(renderer, points, 2);
  2401 }
  2402 
  2403 static int
  2404 RenderDrawLinesWithRects(SDL_Renderer * renderer,
  2405                      const SDL_Point * points, const int count)
  2406 {
  2407     SDL_FRect *frect;
  2408     SDL_FRect *frects;
  2409     SDL_FPoint fpoints[2];
  2410     int i, nrects = 0;
  2411     int retval = 0;
  2412     SDL_bool isstack;
  2413 
  2414     frects = SDL_small_alloc(SDL_FRect, count-1, &isstack);
  2415     if (!frects) {
  2416         return SDL_OutOfMemory();
  2417     }
  2418 
  2419     for (i = 0; i < count-1; ++i) {
  2420         if (points[i].x == points[i+1].x) {
  2421             const int minY = SDL_min(points[i].y, points[i+1].y);
  2422             const int maxY = SDL_max(points[i].y, points[i+1].y);
  2423 
  2424             frect = &frects[nrects++];
  2425             frect->x = points[i].x * renderer->scale.x;
  2426             frect->y = minY * renderer->scale.y;
  2427             frect->w = renderer->scale.x;
  2428             frect->h = (maxY - minY + 1) * renderer->scale.y;
  2429         } else if (points[i].y == points[i+1].y) {
  2430             const int minX = SDL_min(points[i].x, points[i+1].x);
  2431             const int maxX = SDL_max(points[i].x, points[i+1].x);
  2432 
  2433             frect = &frects[nrects++];
  2434             frect->x = minX * renderer->scale.x;
  2435             frect->y = points[i].y * renderer->scale.y;
  2436             frect->w = (maxX - minX + 1) * renderer->scale.x;
  2437             frect->h = renderer->scale.y;
  2438         } else {
  2439             /* FIXME: We can't use a rect for this line... */
  2440             fpoints[0].x = points[i].x * renderer->scale.x;
  2441             fpoints[0].y = points[i].y * renderer->scale.y;
  2442             fpoints[1].x = points[i+1].x * renderer->scale.x;
  2443             fpoints[1].y = points[i+1].y * renderer->scale.y;
  2444             retval += QueueCmdDrawLines(renderer, fpoints, 2);
  2445         }
  2446     }
  2447 
  2448     retval += QueueCmdFillRects(renderer, frects, nrects);
  2449 
  2450     SDL_small_free(frects, isstack);
  2451 
  2452     if (retval < 0) {
  2453         retval = -1;
  2454     }
  2455     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2456 }
  2457 
  2458 static int
  2459 RenderDrawLinesWithRectsF(SDL_Renderer * renderer,
  2460                           const SDL_FPoint * points, const int count)
  2461 {
  2462     SDL_FRect *frect;
  2463     SDL_FRect *frects;
  2464     SDL_FPoint fpoints[2];
  2465     int i, nrects = 0;
  2466     int retval = 0;
  2467     SDL_bool isstack;
  2468 
  2469     frects = SDL_small_alloc(SDL_FRect, count-1, &isstack);
  2470     if (!frects) {
  2471         return SDL_OutOfMemory();
  2472     }
  2473 
  2474     for (i = 0; i < count-1; ++i) {
  2475         if (points[i].x == points[i+1].x) {
  2476             const int minY = (int)SDL_min(points[i].y, points[i+1].y);
  2477             const int maxY = (int)SDL_max(points[i].y, points[i+1].y);
  2478 
  2479             frect = &frects[nrects++];
  2480             frect->x = points[i].x * renderer->scale.x;
  2481             frect->y = minY * renderer->scale.y;
  2482             frect->w = renderer->scale.x;
  2483             frect->h = (maxY - minY + 1) * renderer->scale.y;
  2484         } else if (points[i].y == points[i+1].y) {
  2485             const int minX = (int)SDL_min(points[i].x, points[i+1].x);
  2486             const int maxX = (int)SDL_max(points[i].x, points[i+1].x);
  2487 
  2488             frect = &frects[nrects++];
  2489             frect->x = minX * renderer->scale.x;
  2490             frect->y = points[i].y * renderer->scale.y;
  2491             frect->w = (maxX - minX + 1) * renderer->scale.x;
  2492             frect->h = renderer->scale.y;
  2493         } else {
  2494             /* FIXME: We can't use a rect for this line... */
  2495             fpoints[0].x = points[i].x * renderer->scale.x;
  2496             fpoints[0].y = points[i].y * renderer->scale.y;
  2497             fpoints[1].x = points[i+1].x * renderer->scale.x;
  2498             fpoints[1].y = points[i+1].y * renderer->scale.y;
  2499             retval += QueueCmdDrawLines(renderer, fpoints, 2);
  2500         }
  2501     }
  2502 
  2503     retval += QueueCmdFillRects(renderer, frects, nrects);
  2504 
  2505     SDL_small_free(frects, isstack);
  2506 
  2507     if (retval < 0) {
  2508         retval = -1;
  2509     }
  2510     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2511 }
  2512 
  2513 int
  2514 SDL_RenderDrawLines(SDL_Renderer * renderer,
  2515                     const SDL_Point * points, int count)
  2516 {
  2517     SDL_FPoint *fpoints;
  2518     int i;
  2519     int retval;
  2520     SDL_bool isstack;
  2521 
  2522     CHECK_RENDERER_MAGIC(renderer, -1);
  2523 
  2524     if (!points) {
  2525         return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points");
  2526     }
  2527     if (count < 2) {
  2528         return 0;
  2529     }
  2530 
  2531     /* Don't draw while we're hidden */
  2532     if (renderer->hidden) {
  2533         return 0;
  2534     }
  2535 
  2536     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
  2537         return RenderDrawLinesWithRects(renderer, points, count);
  2538     }
  2539 
  2540     fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
  2541     if (!fpoints) {
  2542         return SDL_OutOfMemory();
  2543     }
  2544     for (i = 0; i < count; ++i) {
  2545         fpoints[i].x = points[i].x * renderer->scale.x;
  2546         fpoints[i].y = points[i].y * renderer->scale.y;
  2547     }
  2548 
  2549     retval = QueueCmdDrawLines(renderer, fpoints, count);
  2550 
  2551     SDL_small_free(fpoints, isstack);
  2552 
  2553     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2554 }
  2555 
  2556 int
  2557 SDL_RenderDrawLinesF(SDL_Renderer * renderer,
  2558                      const SDL_FPoint * points, int count)
  2559 {
  2560     SDL_FPoint *fpoints;
  2561     int i;
  2562     int retval;
  2563     SDL_bool isstack;
  2564 
  2565     CHECK_RENDERER_MAGIC(renderer, -1);
  2566 
  2567     if (!points) {
  2568         return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points");
  2569     }
  2570     if (count < 2) {
  2571         return 0;
  2572     }
  2573 
  2574     /* Don't draw while we're hidden */
  2575     if (renderer->hidden) {
  2576         return 0;
  2577     }
  2578 
  2579     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
  2580         return RenderDrawLinesWithRectsF(renderer, points, count);
  2581     }
  2582 
  2583     fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
  2584     if (!fpoints) {
  2585         return SDL_OutOfMemory();
  2586     }
  2587     for (i = 0; i < count; ++i) {
  2588         fpoints[i].x = points[i].x * renderer->scale.x;
  2589         fpoints[i].y = points[i].y * renderer->scale.y;
  2590     }
  2591 
  2592     retval = QueueCmdDrawLines(renderer, fpoints, count);
  2593 
  2594     SDL_small_free(fpoints, isstack);
  2595 
  2596     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2597 }
  2598 
  2599 int
  2600 SDL_RenderDrawRect(SDL_Renderer * renderer, const SDL_Rect * rect)
  2601 {
  2602     SDL_FRect frect;
  2603     SDL_FRect *prect = NULL;
  2604 
  2605     if (rect) {
  2606         frect.x = (float) rect->x;
  2607         frect.y = (float) rect->y;
  2608         frect.w = (float) rect->w;
  2609         frect.h = (float) rect->h;
  2610         prect = &frect;
  2611     }
  2612 
  2613     return SDL_RenderDrawRectF(renderer, prect);
  2614 }
  2615 
  2616 int
  2617 SDL_RenderDrawRectF(SDL_Renderer * renderer, const SDL_FRect * rect)
  2618 {
  2619     SDL_FRect frect;
  2620     SDL_FPoint points[5];
  2621 
  2622     CHECK_RENDERER_MAGIC(renderer, -1);
  2623 
  2624     /* If 'rect' == NULL, then outline the whole surface */
  2625     if (!rect) {
  2626         SDL_Rect r;
  2627         SDL_RenderGetViewport(renderer, &r);
  2628         frect.x = 0.0f;
  2629         frect.y = 0.0f;
  2630         frect.w = (float) r.w;
  2631         frect.h = (float) r.h;
  2632         rect = &frect;
  2633     }
  2634 
  2635     points[0].x = rect->x;
  2636     points[0].y = rect->y;
  2637     points[1].x = rect->x+rect->w-1;
  2638     points[1].y = rect->y;
  2639     points[2].x = rect->x+rect->w-1;
  2640     points[2].y = rect->y+rect->h-1;
  2641     points[3].x = rect->x;
  2642     points[3].y = rect->y+rect->h-1;
  2643     points[4].x = rect->x;
  2644     points[4].y = rect->y;
  2645     return SDL_RenderDrawLinesF(renderer, points, 5);
  2646 }
  2647 
  2648 int
  2649 SDL_RenderDrawRects(SDL_Renderer * renderer,
  2650                     const SDL_Rect * rects, int count)
  2651 {
  2652     int i;
  2653 
  2654     CHECK_RENDERER_MAGIC(renderer, -1);
  2655 
  2656     if (!rects) {
  2657         return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects");
  2658     }
  2659     if (count < 1) {
  2660         return 0;
  2661     }
  2662 
  2663     /* Don't draw while we're hidden */
  2664     if (renderer->hidden) {
  2665         return 0;
  2666     }
  2667 
  2668     for (i = 0; i < count; ++i) {
  2669         if (SDL_RenderDrawRect(renderer, &rects[i]) < 0) {
  2670             return -1;
  2671         }
  2672     }
  2673     return 0;
  2674 }
  2675 
  2676 int
  2677 SDL_RenderDrawRectsF(SDL_Renderer * renderer,
  2678                      const SDL_FRect * rects, int count)
  2679 {
  2680     int i;
  2681 
  2682     CHECK_RENDERER_MAGIC(renderer, -1);
  2683 
  2684     if (!rects) {
  2685         return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects");
  2686     }
  2687     if (count < 1) {
  2688         return 0;
  2689     }
  2690 
  2691     /* Don't draw while we're hidden */
  2692     if (renderer->hidden) {
  2693         return 0;
  2694     }
  2695 
  2696     for (i = 0; i < count; ++i) {
  2697         if (SDL_RenderDrawRectF(renderer, &rects[i]) < 0) {
  2698             return -1;
  2699         }
  2700     }
  2701     return 0;
  2702 }
  2703 
  2704 int
  2705 SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect)
  2706 {
  2707     SDL_FRect frect;
  2708 
  2709     CHECK_RENDERER_MAGIC(renderer, -1);
  2710 
  2711     /* If 'rect' == NULL, then outline the whole surface */
  2712     if (rect) {
  2713         frect.x = (float) rect->x;
  2714         frect.y = (float) rect->y;
  2715         frect.w = (float) rect->w;
  2716         frect.h = (float) rect->h;
  2717     } else {
  2718         SDL_Rect r;
  2719         SDL_zero(r);
  2720         SDL_RenderGetViewport(renderer, &r);
  2721         frect.x = 0.0f;
  2722         frect.y = 0.0f;
  2723         frect.w = (float) r.w;
  2724         frect.h = (float) r.h;
  2725     }
  2726     return SDL_RenderFillRectsF(renderer, &frect, 1);
  2727 }
  2728 
  2729 int
  2730 SDL_RenderFillRectF(SDL_Renderer * renderer, const SDL_FRect * rect)
  2731 {
  2732     SDL_FRect frect;
  2733 
  2734     CHECK_RENDERER_MAGIC(renderer, -1);
  2735 
  2736     /* If 'rect' == NULL, then outline the whole surface */
  2737     if (!rect) {
  2738         SDL_Rect r;
  2739         SDL_zero(r);
  2740         SDL_RenderGetViewport(renderer, &r);
  2741         frect.x = 0.0f;
  2742         frect.y = 0.0f;
  2743         frect.w = (float) r.w;
  2744         frect.h = (float) r.h;
  2745         rect = &frect;
  2746     }
  2747     return SDL_RenderFillRectsF(renderer, rect, 1);
  2748 }
  2749 
  2750 int
  2751 SDL_RenderFillRects(SDL_Renderer * renderer,
  2752                     const SDL_Rect * rects, int count)
  2753 {
  2754     SDL_FRect *frects;
  2755     int i;
  2756     int retval;
  2757     SDL_bool isstack;
  2758 
  2759     CHECK_RENDERER_MAGIC(renderer, -1);
  2760 
  2761     if (!rects) {
  2762         return SDL_SetError("SDL_RenderFillRects(): Passed NULL rects");
  2763     }
  2764     if (count < 1) {
  2765         return 0;
  2766     }
  2767 
  2768     /* Don't draw while we're hidden */
  2769     if (renderer->hidden) {
  2770         return 0;
  2771     }
  2772 
  2773     frects = SDL_small_alloc(SDL_FRect, count, &isstack);
  2774     if (!frects) {
  2775         return SDL_OutOfMemory();
  2776     }
  2777     for (i = 0; i < count; ++i) {
  2778         frects[i].x = rects[i].x * renderer->scale.x;
  2779         frects[i].y = rects[i].y * renderer->scale.y;
  2780         frects[i].w = rects[i].w * renderer->scale.x;
  2781         frects[i].h = rects[i].h * renderer->scale.y;
  2782     }
  2783 
  2784     retval = QueueCmdFillRects(renderer, frects, count);
  2785 
  2786     SDL_small_free(frects, isstack);
  2787 
  2788     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2789 }
  2790 
  2791 int
  2792 SDL_RenderFillRectsF(SDL_Renderer * renderer,
  2793                      const SDL_FRect * rects, int count)
  2794 {
  2795     SDL_FRect *frects;
  2796     int i;
  2797     int retval;
  2798     SDL_bool isstack;
  2799 
  2800     CHECK_RENDERER_MAGIC(renderer, -1);
  2801 
  2802     if (!rects) {
  2803         return SDL_SetError("SDL_RenderFillFRects(): Passed NULL rects");
  2804     }
  2805     if (count < 1) {
  2806         return 0;
  2807     }
  2808 
  2809     /* Don't draw while we're hidden */
  2810     if (renderer->hidden) {
  2811         return 0;
  2812     }
  2813 
  2814     frects = SDL_small_alloc(SDL_FRect, count, &isstack);
  2815     if (!frects) {
  2816         return SDL_OutOfMemory();
  2817     }
  2818     for (i = 0; i < count; ++i) {
  2819         frects[i].x = rects[i].x * renderer->scale.x;
  2820         frects[i].y = rects[i].y * renderer->scale.y;
  2821         frects[i].w = rects[i].w * renderer->scale.x;
  2822         frects[i].h = rects[i].h * renderer->scale.y;
  2823     }
  2824 
  2825     retval = QueueCmdFillRects(renderer, frects, count);
  2826 
  2827     SDL_small_free(frects, isstack);
  2828 
  2829     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2830 }
  2831 
  2832 /* !!! FIXME: move this to a public API if we want to do float versions of all of these later */
  2833 SDL_FORCE_INLINE SDL_bool SDL_FRectEmpty(const SDL_FRect *r)
  2834 {
  2835     return ((!r) || (r->w <= 0.0f) || (r->h <= 0.0f)) ? SDL_TRUE : SDL_FALSE;
  2836 }
  2837 
  2838 /* !!! FIXME: move this to a public API if we want to do float versions of all of these later */
  2839 static SDL_bool
  2840 SDL_HasIntersectionF(const SDL_FRect * A, const SDL_FRect * B)
  2841 {
  2842     float Amin, Amax, Bmin, Bmax;
  2843 
  2844     if (!A) {
  2845         SDL_InvalidParamError("A");
  2846         return SDL_FALSE;
  2847     }
  2848 
  2849     if (!B) {
  2850         SDL_InvalidParamError("B");
  2851         return SDL_FALSE;
  2852     }
  2853 
  2854     /* Special cases for empty rects */
  2855     if (SDL_FRectEmpty(A) || SDL_FRectEmpty(B)) {
  2856         return SDL_FALSE;
  2857     }
  2858 
  2859     /* Horizontal intersection */
  2860     Amin = A->x;
  2861     Amax = Amin + A->w;
  2862     Bmin = B->x;
  2863     Bmax = Bmin + B->w;
  2864     if (Bmin > Amin)
  2865         Amin = Bmin;
  2866     if (Bmax < Amax)
  2867         Amax = Bmax;
  2868     if (Amax <= Amin)
  2869         return SDL_FALSE;
  2870 
  2871     /* Vertical intersection */
  2872     Amin = A->y;
  2873     Amax = Amin + A->h;
  2874     Bmin = B->y;
  2875     Bmax = Bmin + B->h;
  2876     if (Bmin > Amin)
  2877         Amin = Bmin;
  2878     if (Bmax < Amax)
  2879         Amax = Bmax;
  2880     if (Amax <= Amin)
  2881         return SDL_FALSE;
  2882 
  2883     return SDL_TRUE;
  2884 }
  2885 
  2886 int
  2887 SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
  2888                const SDL_Rect * srcrect, const SDL_Rect * dstrect)
  2889 {
  2890     SDL_FRect dstfrect;
  2891     SDL_FRect *pdstfrect = NULL;
  2892     if (dstrect) {
  2893         dstfrect.x = (float) dstrect->x;
  2894         dstfrect.y = (float) dstrect->y;
  2895         dstfrect.w = (float) dstrect->w;
  2896         dstfrect.h = (float) dstrect->h;
  2897         pdstfrect = &dstfrect;
  2898     }
  2899     return SDL_RenderCopyF(renderer, texture, srcrect, pdstfrect);
  2900 }
  2901 
  2902 int
  2903 SDL_RenderCopyF(SDL_Renderer * renderer, SDL_Texture * texture,
  2904                 const SDL_Rect * srcrect, const SDL_FRect * dstrect)
  2905 {
  2906     SDL_Rect real_srcrect;
  2907     SDL_FRect real_dstrect;
  2908     SDL_Rect r;
  2909     int retval;
  2910 
  2911     CHECK_RENDERER_MAGIC(renderer, -1);
  2912     CHECK_TEXTURE_MAGIC(texture, -1);
  2913 
  2914     if (renderer != texture->renderer) {
  2915         return SDL_SetError("Texture was not created with this renderer");
  2916     }
  2917 
  2918     /* Don't draw while we're hidden */
  2919     if (renderer->hidden) {
  2920         return 0;
  2921     }
  2922 
  2923     real_srcrect.x = 0;
  2924     real_srcrect.y = 0;
  2925     real_srcrect.w = texture->w;
  2926     real_srcrect.h = texture->h;
  2927     if (srcrect) {
  2928         if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
  2929             return 0;
  2930         }
  2931     }
  2932 
  2933     SDL_zero(r);
  2934     SDL_RenderGetViewport(renderer, &r);
  2935     real_dstrect.x = 0.0f;
  2936     real_dstrect.y = 0.0f;
  2937     real_dstrect.w = (float) r.w;
  2938     real_dstrect.h = (float) r.h;
  2939     if (dstrect) {
  2940         if (!SDL_HasIntersectionF(dstrect, &real_dstrect)) {
  2941             return 0;
  2942         }
  2943         real_dstrect = *dstrect;
  2944     }
  2945 
  2946     if (texture->native) {
  2947         texture = texture->native;
  2948     }
  2949 
  2950     real_dstrect.x *= renderer->scale.x;
  2951     real_dstrect.y *= renderer->scale.y;
  2952     real_dstrect.w *= renderer->scale.x;
  2953     real_dstrect.h *= renderer->scale.y;
  2954 
  2955     texture->last_command_generation = renderer->render_command_generation;
  2956 
  2957     retval = QueueCmdCopy(renderer, texture, &real_srcrect, &real_dstrect);
  2958     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  2959 }
  2960 
  2961 int
  2962 SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
  2963                const SDL_Rect * srcrect, const SDL_Rect * dstrect,
  2964                const double angle, const SDL_Point *center, const SDL_RendererFlip flip)
  2965 {
  2966     SDL_FRect dstfrect;
  2967     SDL_FRect *pdstfrect = NULL;
  2968     SDL_FPoint fcenter;
  2969     SDL_FPoint *pfcenter = NULL;
  2970 
  2971     if (dstrect) {
  2972         dstfrect.x = (float) dstrect->x;
  2973         dstfrect.y = (float) dstrect->y;
  2974         dstfrect.w = (float) dstrect->w;
  2975         dstfrect.h = (float) dstrect->h;
  2976         pdstfrect = &dstfrect;
  2977     }
  2978 
  2979     if (center) {
  2980         fcenter.x = (float) center->x;
  2981         fcenter.y = (float) center->y;
  2982         pfcenter = &fcenter;
  2983     }
  2984 
  2985     return SDL_RenderCopyExF(renderer, texture, srcrect, pdstfrect, angle, pfcenter, flip);
  2986 }
  2987 
  2988 int
  2989 SDL_RenderCopyExF(SDL_Renderer * renderer, SDL_Texture * texture,
  2990                const SDL_Rect * srcrect, const SDL_FRect * dstrect,
  2991                const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
  2992 {
  2993     SDL_Rect real_srcrect;
  2994     SDL_FRect real_dstrect;
  2995     SDL_FPoint real_center;
  2996     int retval;
  2997 
  2998     if (flip == SDL_FLIP_NONE && (int)(angle/360) == angle/360) { /* fast path when we don't need rotation or flipping */
  2999         return SDL_RenderCopyF(renderer, texture, srcrect, dstrect);
  3000     }
  3001 
  3002     CHECK_RENDERER_MAGIC(renderer, -1);
  3003     CHECK_TEXTURE_MAGIC(texture, -1);
  3004 
  3005     if (renderer != texture->renderer) {
  3006         return SDL_SetError("Texture was not created with this renderer");
  3007     }
  3008     if (!renderer->QueueCopyEx) {
  3009         return SDL_SetError("Renderer does not support RenderCopyEx");
  3010     }
  3011 
  3012     /* Don't draw while we're hidden */
  3013     if (renderer->hidden) {
  3014         return 0;
  3015     }
  3016 
  3017     real_srcrect.x = 0;
  3018     real_srcrect.y = 0;
  3019     real_srcrect.w = texture->w;
  3020     real_srcrect.h = texture->h;
  3021     if (srcrect) {
  3022         if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
  3023             return 0;
  3024         }
  3025     }
  3026 
  3027     /* We don't intersect the dstrect with the viewport as RenderCopy does because of potential rotation clipping issues... TODO: should we? */
  3028     if (dstrect) {
  3029         real_dstrect = *dstrect;
  3030     } else {
  3031         SDL_Rect r;
  3032         SDL_zero(r);
  3033         SDL_RenderGetViewport(renderer, &r);
  3034         real_dstrect.x = 0.0f;
  3035         real_dstrect.y = 0.0f;
  3036         real_dstrect.w = (float) r.w;
  3037         real_dstrect.h = (float) r.h;
  3038     }
  3039 
  3040     if (texture->native) {
  3041         texture = texture->native;
  3042     }
  3043 
  3044     if (center) {
  3045         real_center = *center;
  3046     } else {
  3047         real_center.x = real_dstrect.w / 2.0f;
  3048         real_center.y = real_dstrect.h / 2.0f;
  3049     }
  3050 
  3051     real_dstrect.x *= renderer->scale.x;
  3052     real_dstrect.y *= renderer->scale.y;
  3053     real_dstrect.w *= renderer->scale.x;
  3054     real_dstrect.h *= renderer->scale.y;
  3055 
  3056     real_center.x *= renderer->scale.x;
  3057     real_center.y *= renderer->scale.y;
  3058 
  3059     texture->last_command_generation = renderer->render_command_generation;
  3060 
  3061     retval = QueueCmdCopyEx(renderer, texture, &real_srcrect, &real_dstrect, angle, &real_center, flip);
  3062     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
  3063 }
  3064 
  3065 int
  3066 SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
  3067                      Uint32 format, void * pixels, int pitch)
  3068 {
  3069     SDL_Rect real_rect;
  3070 
  3071     CHECK_RENDERER_MAGIC(renderer, -1);
  3072 
  3073     if (!renderer->RenderReadPixels) {
  3074         return SDL_Unsupported();
  3075     }
  3076 
  3077     FlushRenderCommands(renderer);  /* we need to render before we read the results. */
  3078 
  3079     if (!format) {
  3080         format = SDL_GetWindowPixelFormat(renderer->window);
  3081     }
  3082 
  3083     real_rect.x = renderer->viewport.x;
  3084     real_rect.y = renderer->viewport.y;
  3085     real_rect.w = renderer->viewport.w;
  3086     real_rect.h = renderer->viewport.h;
  3087     if (rect) {
  3088         if (!SDL_IntersectRect(rect, &real_rect, &real_rect)) {
  3089             return 0;
  3090         }
  3091         if (real_rect.y > rect->y) {
  3092             pixels = (Uint8 *)pixels + pitch * (real_rect.y - rect->y);
  3093         }
  3094         if (real_rect.x > rect->x) {
  3095             int bpp = SDL_BYTESPERPIXEL(format);
  3096             pixels = (Uint8 *)pixels + bpp * (real_rect.x - rect->x);
  3097         }
  3098     }
  3099 
  3100     return renderer->RenderReadPixels(renderer, &real_rect,
  3101                                       format, pixels, pitch);
  3102 }
  3103 
  3104 void
  3105 SDL_RenderPresent(SDL_Renderer * renderer)
  3106 {
  3107     CHECK_RENDERER_MAGIC(renderer, );
  3108 
  3109     FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
  3110 
  3111     /* Don't present while we're hidden */
  3112     if (renderer->hidden) {
  3113         return;
  3114     }
  3115     renderer->RenderPresent(renderer);
  3116 }
  3117 
  3118 void
  3119 SDL_DestroyTexture(SDL_Texture * texture)
  3120 {
  3121     SDL_Renderer *renderer;
  3122 
  3123     CHECK_TEXTURE_MAGIC(texture, );
  3124 
  3125     renderer = texture->renderer;
  3126     if (texture == renderer->target) {
  3127         SDL_SetRenderTarget(renderer, NULL);  /* implies command queue flush */
  3128     } else {
  3129         FlushRenderCommandsIfTextureNeeded(texture);
  3130     }
  3131 
  3132     texture->magic = NULL;
  3133 
  3134     if (texture->next) {
  3135         texture->next->prev = texture->prev;
  3136     }
  3137     if (texture->prev) {
  3138         texture->prev->next = texture->next;
  3139     } else {
  3140         renderer->textures = texture->next;
  3141     }
  3142 
  3143     if (texture->native) {
  3144         SDL_DestroyTexture(texture->native);
  3145     }
  3146     if (texture->yuv) {
  3147         SDL_SW_DestroyYUVTexture(texture->yuv);
  3148     }
  3149     SDL_free(texture->pixels);
  3150 
  3151     renderer->DestroyTexture(renderer, texture);
  3152     SDL_free(texture);
  3153 }
  3154 
  3155 void
  3156 SDL_DestroyRenderer(SDL_Renderer * renderer)
  3157 {
  3158     SDL_RenderCommand *cmd;
  3159     SDL_AllocVertGap *gap;
  3160     SDL_AllocVertGap *nextgap;
  3161 
  3162     CHECK_RENDERER_MAGIC(renderer, );
  3163 
  3164     SDL_DelEventWatch(SDL_RendererEventWatch, renderer);
  3165 
  3166     if (renderer->render_commands_tail != NULL) {
  3167         renderer->render_commands_tail->next = renderer->render_commands_pool;
  3168         cmd = renderer->render_commands;
  3169     } else {
  3170         cmd = renderer->render_commands_pool;
  3171     }
  3172 
  3173     renderer->render_commands_pool = NULL;
  3174     renderer->render_commands_tail = NULL;
  3175     renderer->render_commands = NULL;
  3176 
  3177     while (cmd != NULL) {
  3178         SDL_RenderCommand *next = cmd->next;
  3179         SDL_free(cmd);
  3180         cmd = next;
  3181     }
  3182 
  3183     SDL_free(renderer->vertex_data);
  3184 
  3185     for (gap = renderer->vertex_data_gaps.next; gap; gap = nextgap) {
  3186         nextgap = gap->next;
  3187         SDL_free(gap);
  3188     }
  3189 
  3190     for (gap = renderer->vertex_data_gaps_pool; gap; gap = nextgap) {
  3191         nextgap = gap->next;
  3192         SDL_free(gap);
  3193     }
  3194 
  3195     /* Free existing textures for this renderer */
  3196     while (renderer->textures) {
  3197         SDL_Texture *tex = renderer->textures; (void) tex;
  3198         SDL_DestroyTexture(renderer->textures);
  3199         SDL_assert(tex != renderer->textures);  /* satisfy static analysis. */
  3200     }
  3201 
  3202     if (renderer->window) {
  3203         SDL_SetWindowData(renderer->window, SDL_WINDOWRENDERDATA, NULL);
  3204     }
  3205 
  3206     /* It's no longer magical... */
  3207     renderer->magic = NULL;
  3208 
  3209     /* Free the target mutex */
  3210     SDL_DestroyMutex(renderer->target_mutex);
  3211     renderer->target_mutex = NULL;
  3212 
  3213     /* Free the renderer instance */
  3214     renderer->DestroyRenderer(renderer);
  3215 }
  3216 
  3217 int SDL_GL_BindTexture(SDL_Texture *texture, float *texw, float *texh)
  3218 {
  3219     SDL_Renderer *renderer;
  3220 
  3221     CHECK_TEXTURE_MAGIC(texture, -1);
  3222     renderer = texture->renderer;
  3223     if (texture->native) {
  3224         return SDL_GL_BindTexture(texture->native, texw, texh);
  3225     } else if (renderer && renderer->GL_BindTexture) {
  3226         FlushRenderCommandsIfTextureNeeded(texture);  /* in case the app is going to mess with it. */
  3227         return renderer->GL_BindTexture(renderer, texture, texw, texh);
  3228     } else {
  3229         return SDL_Unsupported();
  3230     }
  3231 }
  3232 
  3233 int SDL_GL_UnbindTexture(SDL_Texture *texture)
  3234 {
  3235     SDL_Renderer *renderer;
  3236 
  3237     CHECK_TEXTURE_MAGIC(texture, -1);
  3238     renderer = texture->renderer;
  3239     if (texture->native) {
  3240         return SDL_GL_UnbindTexture(texture->native);
  3241     } else if (renderer && renderer->GL_UnbindTexture) {
  3242         FlushRenderCommandsIfTextureNeeded(texture);  /* in case the app messed with it. */
  3243         return renderer->GL_UnbindTexture(renderer, texture);
  3244     }
  3245 
  3246     return SDL_Unsupported();
  3247 }
  3248 
  3249 void *
  3250 SDL_RenderGetMetalLayer(SDL_Renderer * renderer)
  3251 {
  3252     CHECK_RENDERER_MAGIC(renderer, NULL);
  3253 
  3254     if (renderer->GetMetalLayer) {
  3255         FlushRenderCommands(renderer);  /* in case the app is going to mess with it. */
  3256         return renderer->GetMetalLayer(renderer);
  3257     }
  3258     return NULL;
  3259 }
  3260 
  3261 void *
  3262 SDL_RenderGetMetalCommandEncoder(SDL_Renderer * renderer)
  3263 {
  3264     CHECK_RENDERER_MAGIC(renderer, NULL);
  3265 
  3266     if (renderer->GetMetalCommandEncoder) {
  3267         FlushRenderCommands(renderer);  /* in case the app is going to mess with it. */
  3268         return renderer->GetMetalCommandEncoder(renderer);
  3269     }
  3270     return NULL;
  3271 }
  3272 
  3273 static SDL_BlendMode
  3274 SDL_GetShortBlendMode(SDL_BlendMode blendMode)
  3275 {
  3276     if (blendMode == SDL_BLENDMODE_NONE_FULL) {
  3277         return SDL_BLENDMODE_NONE;
  3278     }
  3279     if (blendMode == SDL_BLENDMODE_BLEND_FULL) {
  3280         return SDL_BLENDMODE_BLEND;
  3281     }
  3282     if (blendMode == SDL_BLENDMODE_ADD_FULL) {
  3283         return SDL_BLENDMODE_ADD;
  3284     }
  3285     if (blendMode == SDL_BLENDMODE_MOD_FULL) {
  3286         return SDL_BLENDMODE_MOD;
  3287     }
  3288     return blendMode;
  3289 }
  3290 
  3291 static SDL_BlendMode
  3292 SDL_GetLongBlendMode(SDL_BlendMode blendMode)
  3293 {
  3294     if (blendMode == SDL_BLENDMODE_NONE) {
  3295         return SDL_BLENDMODE_NONE_FULL;
  3296     }
  3297     if (blendMode == SDL_BLENDMODE_BLEND) {
  3298         return SDL_BLENDMODE_BLEND_FULL;
  3299     }
  3300     if (blendMode == SDL_BLENDMODE_ADD) {
  3301         return SDL_BLENDMODE_ADD_FULL;
  3302     }
  3303     if (blendMode == SDL_BLENDMODE_MOD) {
  3304         return SDL_BLENDMODE_MOD_FULL;
  3305     }
  3306     return blendMode;
  3307 }
  3308 
  3309 SDL_BlendMode
  3310 SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor,
  3311                            SDL_BlendOperation colorOperation,
  3312                            SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor,
  3313                            SDL_BlendOperation alphaOperation)
  3314 {
  3315     SDL_BlendMode blendMode = SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation,
  3316                                                     srcAlphaFactor, dstAlphaFactor, alphaOperation);
  3317     return SDL_GetShortBlendMode(blendMode);
  3318 }
  3319 
  3320 SDL_BlendFactor
  3321 SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode)
  3322 {
  3323     blendMode = SDL_GetLongBlendMode(blendMode);
  3324     return (SDL_BlendFactor)(((Uint32)blendMode >> 4) & 0xF);
  3325 }
  3326 
  3327 SDL_BlendFactor
  3328 SDL_GetBlendModeDstColorFactor(SDL_BlendMode blendMode)
  3329 {
  3330     blendMode = SDL_GetLongBlendMode(blendMode);
  3331     return (SDL_BlendFactor)(((Uint32)blendMode >> 8) & 0xF);
  3332 }
  3333 
  3334 SDL_BlendOperation
  3335 SDL_GetBlendModeColorOperation(SDL_BlendMode blendMode)
  3336 {
  3337     blendMode = SDL_GetLongBlendMode(blendMode);
  3338     return (SDL_BlendOperation)(((Uint32)blendMode >> 0) & 0xF);
  3339 }
  3340 
  3341 SDL_BlendFactor
  3342 SDL_GetBlendModeSrcAlphaFactor(SDL_BlendMode blendMode)
  3343 {
  3344     blendMode = SDL_GetLongBlendMode(blendMode);
  3345     return (SDL_BlendFactor)(((Uint32)blendMode >> 20) & 0xF);
  3346 }
  3347 
  3348 SDL_BlendFactor
  3349 SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode)
  3350 {
  3351     blendMode = SDL_GetLongBlendMode(blendMode);
  3352     return (SDL_BlendFactor)(((Uint32)blendMode >> 24) & 0xF);
  3353 }
  3354 
  3355 SDL_BlendOperation
  3356 SDL_GetBlendModeAlphaOperation(SDL_BlendMode blendMode)
  3357 {
  3358     blendMode = SDL_GetLongBlendMode(blendMode);
  3359     return (SDL_BlendOperation)(((Uint32)blendMode >> 16) & 0xF);
  3360 }
  3361 
  3362 /* vi: set ts=4 sw=4 expandtab: */