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