src/render/SDL_render.c
author Sylvain Becker <sylvain.becker@gmail.com>
Fri, 28 Jun 2019 16:38:42 +0200
changeset 12910 dd9169424181
parent 12909 8e27bc5f03d8
child 13019 ecad0a0684ad
permissions -rw-r--r--
Android: concurrency issues, make sure Activity is in running State when calling
functions like SDL_CreateWindow, SDL_CreateRenderer, Android_GLES_CreateContext

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