src/render/SDL_render.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 16 Feb 2020 00:08:36 -0800
changeset 13532 fab2cd7dee5b
parent 13440 ac297b67f6d9
permissions -rw-r--r--
Further improvements for bug 4128 - CMAKE: Generated target import file contains incorrect include path

Mohamed

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