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