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