src/render/SDL_render.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 20 Sep 2018 15:46:02 -0400
branchSDL-ryan-batching-renderer
changeset 12211 0d8f33ad5fbb
parent 12021 3239c8ad2986
child 12214 d377f3ccade7
permissions -rw-r--r--
render: Move to a batching system for rendering (work in progress).
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../SDL_internal.h"
    22 
    23 /* The SDL 2D rendering system */
    24 
    25 #include "SDL_assert.h"
    26 #include "SDL_hints.h"
    27 #include "SDL_log.h"
    28 #include "SDL_render.h"
    29 #include "SDL_sysrender.h"
    30 #include "software/SDL_render_sw_c.h"
    31 
    32 
    33 #define SDL_WINDOWRENDERDATA    "_SDL_WindowRenderData"
    34 
    35 #define CHECK_RENDERER_MAGIC(renderer, retval) \
    36     SDL_assert(renderer && renderer->magic == &renderer_magic); \
    37     if (!renderer || renderer->magic != &renderer_magic) { \
    38         SDL_SetError("Invalid renderer"); \
    39         return retval; \
    40     }
    41 
    42 #define CHECK_TEXTURE_MAGIC(texture, retval) \
    43     SDL_assert(texture && texture->magic == &texture_magic); \
    44     if (!texture || texture->magic != &texture_magic) { \
    45         SDL_SetError("Invalid texture"); \
    46         return retval; \
    47     }
    48 
    49 /* Predefined blend modes */
    50 #define SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation, \
    51                               srcAlphaFactor, dstAlphaFactor, alphaOperation) \
    52     (SDL_BlendMode)(((Uint32)colorOperation << 0) | \
    53                     ((Uint32)srcColorFactor << 4) | \
    54                     ((Uint32)dstColorFactor << 8) | \
    55                     ((Uint32)alphaOperation << 16) | \
    56                     ((Uint32)srcAlphaFactor << 20) | \
    57                     ((Uint32)dstAlphaFactor << 24))
    58 
    59 #define SDL_BLENDMODE_NONE_FULL \
    60     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD, \
    61                           SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD)
    62 
    63 #define SDL_BLENDMODE_BLEND_FULL \
    64     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \
    65                           SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD)
    66 
    67 #define SDL_BLENDMODE_ADD_FULL \
    68     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, \
    69                           SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD)
    70 
    71 #define SDL_BLENDMODE_MOD_FULL \
    72     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_COLOR, SDL_BLENDOPERATION_ADD, \
    73                           SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD)
    74 
    75 #if !SDL_RENDER_DISABLED
    76 static const SDL_RenderDriver *render_drivers[] = {
    77 #if SDL_VIDEO_RENDER_D3D
    78     &D3D_RenderDriver,
    79 #endif
    80 #if SDL_VIDEO_RENDER_D3D11
    81     &D3D11_RenderDriver,
    82 #endif
    83 #if SDL_VIDEO_RENDER_OGL
    84     &GL_RenderDriver,
    85 #endif
    86 #if SDL_VIDEO_RENDER_OGL_ES2
    87     &GLES2_RenderDriver,
    88 #endif
    89 #if SDL_VIDEO_RENDER_OGL_ES
    90     &GLES_RenderDriver,
    91 #endif
    92 #if SDL_VIDEO_RENDER_DIRECTFB
    93     &DirectFB_RenderDriver,
    94 #endif
    95 #if SDL_VIDEO_RENDER_METAL
    96     &METAL_RenderDriver,
    97 #endif
    98 #if SDL_VIDEO_RENDER_PSP
    99     &PSP_RenderDriver,
   100 #endif
   101     &SW_RenderDriver
   102 };
   103 #endif /* !SDL_RENDER_DISABLED */
   104 
   105 static char renderer_magic;
   106 static char texture_magic;
   107 
   108 
   109 static int
   110 FlushRenderCommands(SDL_Renderer *renderer)
   111 {
   112     int retval;
   113     SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
   114 
   115     if (renderer->render_commands == NULL) {  /* nothing to do! */
   116         SDL_assert(renderer->vertex_data_used == 0);
   117         return 0;
   118     }
   119 
   120     retval = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used);
   121 
   122     /* Move the whole render command queue to the unused pool so we can reuse them next time. */
   123     if (renderer->render_commands_tail != NULL) {
   124         renderer->render_commands_tail->next = renderer->render_commands_pool;
   125         renderer->render_commands_pool = renderer->render_commands;
   126         renderer->render_commands_tail = NULL;
   127         renderer->render_commands = NULL;
   128     }
   129     renderer->vertex_data_used = 0;
   130     renderer->render_command_generation++;
   131     return retval;
   132 }
   133 
   134 static int
   135 FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture)
   136 {
   137     SDL_Renderer *renderer = texture->renderer;
   138     if (texture->last_command_generation == renderer->render_command_generation) {
   139         /* the current command queue depends on this texture, flush the queue now before it changes */
   140         return FlushRenderCommands(renderer);
   141     }
   142     return 0;
   143 }
   144 
   145 static SDL_INLINE int
   146 FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer)
   147 {
   148     return renderer->batching ? 0 : FlushRenderCommands(renderer);
   149 }
   150 
   151 void *
   152 SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, size_t *offset)
   153 {
   154     const size_t needed = renderer->vertex_data_used + numbytes;
   155     void *retval;
   156 
   157     while (needed > renderer->vertex_data_allocation) {
   158         const size_t current_allocation = renderer->vertex_data ? renderer->vertex_data_allocation : 128;
   159         const size_t newsize = current_allocation * 2;
   160         void *ptr = SDL_realloc(renderer->vertex_data, newsize);
   161         if (ptr == NULL) {
   162             SDL_OutOfMemory();
   163             return NULL;
   164         }
   165         renderer->vertex_data = ptr;
   166         renderer->vertex_data_allocation = newsize;
   167     }
   168 
   169     retval = ((Uint8 *) renderer->vertex_data) + renderer->vertex_data_used;
   170     if (offset) {
   171         *offset = renderer->vertex_data_used;
   172     }
   173 
   174     renderer->vertex_data_used += numbytes;
   175     return retval;
   176 }
   177 
   178 static SDL_RenderCommand *
   179 AllocateRenderCommand(SDL_Renderer *renderer)
   180 {
   181     SDL_RenderCommand *retval = NULL;
   182 
   183     /* !!! FIXME: are there threading limitations in SDL's render API? If not, we need to mutex this. */
   184     retval = renderer->render_commands_pool;
   185     if (retval != NULL) {
   186         renderer->render_commands_pool = retval->next;
   187         retval->next = NULL;
   188     } else {
   189         retval = SDL_calloc(1, sizeof (*retval));
   190         if (!retval) {
   191             SDL_OutOfMemory();
   192             return NULL;
   193         }
   194     }
   195 
   196     SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
   197     if (renderer->render_commands_tail != NULL) {
   198         renderer->render_commands_tail->next = retval;
   199     } else {
   200         renderer->render_commands = retval;
   201     }
   202     renderer->render_commands_tail = retval;
   203 
   204     return retval;
   205 }
   206 
   207 static int
   208 QueueCmdUpdateViewport(SDL_Renderer *renderer)
   209 {
   210     SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   211     if (cmd == NULL) {
   212         return -1;
   213     }
   214 
   215     cmd->command = SDL_RENDERCMD_SETVIEWPORT;
   216     SDL_memcpy(&cmd->data.viewport, &renderer->viewport, sizeof (cmd->data.viewport));
   217     return FlushRenderCommandsIfNotBatching(renderer);
   218 }
   219 
   220 static int
   221 QueueCmdUpdateClipRect(SDL_Renderer *renderer)
   222 {
   223     SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   224     if (cmd == NULL) {
   225         return -1;
   226     }
   227 
   228     cmd->command = SDL_RENDERCMD_SETCLIPRECT;
   229     cmd->data.cliprect.enabled = renderer->clipping_enabled;
   230     SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (cmd->data.cliprect.rect));
   231     return FlushRenderCommandsIfNotBatching(renderer);
   232 }
   233 
   234 static int
   235 QueueCmdClear(SDL_Renderer *renderer)
   236 {
   237     SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   238     if (cmd == NULL) {
   239         return -1;
   240     }
   241 
   242     cmd->command = SDL_RENDERCMD_CLEAR;
   243     cmd->data.color.r = renderer->r;
   244     cmd->data.color.g = renderer->g;
   245     cmd->data.color.b = renderer->b;
   246     cmd->data.color.a = renderer->a;
   247     return FlushRenderCommandsIfNotBatching(renderer);
   248 }
   249 
   250 static SDL_RenderCommand *
   251 PrepQueueCmdDrawSolid(SDL_Renderer *renderer, const SDL_RenderCommandType cmdtype)
   252 {
   253     SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   254     if (cmd != NULL) {
   255         cmd->command = cmdtype;
   256         cmd->data.draw.first = 0;  /* render backend will fill this in. */
   257         cmd->data.draw.count = 0;  /* render backend will fill this in. */
   258         cmd->data.draw.r = renderer->r;
   259         cmd->data.draw.g = renderer->g;
   260         cmd->data.draw.b = renderer->b;
   261         cmd->data.draw.a = renderer->a;
   262         cmd->data.draw.blend = renderer->blendMode;
   263         cmd->data.draw.texture = NULL;  /* no texture. */
   264     }
   265     return cmd;
   266 }
   267 
   268 static int
   269 QueueCmdDrawPoints(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
   270 {
   271     SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_POINTS);
   272     int retval = -1;
   273     if (cmd != NULL) {
   274         retval = renderer->QueueDrawPoints(renderer, cmd, points, count);
   275         if (retval < 0) {
   276             cmd->command = SDL_RENDERCMD_NO_OP;
   277         }
   278     }
   279     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   280 }
   281 
   282 static int
   283 QueueCmdDrawLines(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
   284 {
   285     SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_LINES);
   286     int retval = -1;
   287     if (cmd != NULL) {
   288         retval = renderer->QueueDrawLines(renderer, cmd, points, count);
   289         if (retval < 0) {
   290             cmd->command = SDL_RENDERCMD_NO_OP;
   291         }
   292     }
   293     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   294 }
   295 
   296 static int
   297 QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect * rects, const int count)
   298 {
   299     SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_FILL_RECTS);
   300     int retval = -1;
   301     if (cmd != NULL) {
   302         retval = renderer->QueueFillRects(renderer, cmd, rects, count);
   303         if (retval < 0) {
   304             cmd->command = SDL_RENDERCMD_NO_OP;
   305         }
   306     }
   307     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   308 }
   309 
   310 static SDL_RenderCommand *
   311 PrepQueueCmdDrawTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_RenderCommandType cmdtype)
   312 {
   313     SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   314     if (cmd != NULL) {
   315         cmd->command = cmdtype;
   316         cmd->data.draw.first = 0;  /* render backend will fill this in. */
   317         cmd->data.draw.count = 0;  /* render backend will fill this in. */
   318         cmd->data.draw.r = texture->r;
   319         cmd->data.draw.g = texture->g;
   320         cmd->data.draw.b = texture->b;
   321         cmd->data.draw.a = texture->a;
   322         cmd->data.draw.blend = texture->blendMode;
   323         cmd->data.draw.texture = texture;
   324     }
   325     return cmd;
   326 }
   327 
   328 static int
   329 QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_FRect * dstrect)
   330 {
   331     SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY);
   332     int retval = -1;
   333     if (cmd != NULL) {
   334         retval = renderer->QueueCopy(renderer, cmd, texture, srcrect, dstrect);
   335         if (retval < 0) {
   336             cmd->command = SDL_RENDERCMD_NO_OP;
   337         }
   338     }
   339     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   340 }
   341 
   342 static int
   343 QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture * texture,
   344                const SDL_Rect * srcquad, const SDL_FRect * dstrect,
   345                const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
   346 {
   347     SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY_EX);
   348     SDL_assert(renderer->QueueCopyEx != NULL);  /* should have caught at higher level. */
   349     int retval = -1;
   350     if (cmd != NULL) {
   351         retval = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip);
   352         if (retval < 0) {
   353             cmd->command = SDL_RENDERCMD_NO_OP;
   354         }
   355     }
   356     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   357 }
   358 
   359 
   360 static int UpdateLogicalSize(SDL_Renderer *renderer);
   361 
   362 int
   363 SDL_GetNumRenderDrivers(void)
   364 {
   365 #if !SDL_RENDER_DISABLED
   366     return SDL_arraysize(render_drivers);
   367 #else
   368     return 0;
   369 #endif
   370 }
   371 
   372 int
   373 SDL_GetRenderDriverInfo(int index, SDL_RendererInfo * info)
   374 {
   375 #if !SDL_RENDER_DISABLED
   376     if (index < 0 || index >= SDL_GetNumRenderDrivers()) {
   377         return SDL_SetError("index must be in the range of 0 - %d",
   378                             SDL_GetNumRenderDrivers() - 1);
   379     }
   380     *info = render_drivers[index]->info;
   381     return 0;
   382 #else
   383     return SDL_SetError("SDL not built with rendering support");
   384 #endif
   385 }
   386 
   387 static void GetWindowViewportValues(SDL_Renderer *renderer, int *logical_w, int *logical_h, SDL_Rect *viewport, SDL_FPoint *scale)
   388 {
   389     SDL_LockMutex(renderer->target_mutex);
   390     *logical_w = renderer->target ? renderer->logical_w_backup : renderer->logical_w;
   391     *logical_h = renderer->target ? renderer->logical_h_backup : renderer->logical_h;
   392     *viewport = renderer->target ? renderer->viewport_backup : renderer->viewport;
   393     *scale = renderer->target ? renderer->scale_backup : renderer->scale;
   394     SDL_UnlockMutex(renderer->target_mutex);
   395 }
   396 
   397 static int SDLCALL
   398 SDL_RendererEventWatch(void *userdata, SDL_Event *event)
   399 {
   400     SDL_Renderer *renderer = (SDL_Renderer *)userdata;
   401 
   402     if (event->type == SDL_WINDOWEVENT) {
   403         SDL_Window *window = SDL_GetWindowFromID(event->window.windowID);
   404         if (window == renderer->window) {
   405             if (renderer->WindowEvent) {
   406                 renderer->WindowEvent(renderer, &event->window);
   407             }
   408 
   409             if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
   410                 /* Make sure we're operating on the default render target */
   411                 SDL_Texture *saved_target = SDL_GetRenderTarget(renderer);
   412                 if (saved_target) {
   413                     SDL_SetRenderTarget(renderer, NULL);
   414                 }
   415 
   416                 if (renderer->logical_w) {
   417                     UpdateLogicalSize(renderer);
   418                 } else {
   419                     /* Window was resized, reset viewport */
   420                     int w, h;
   421 
   422                     if (renderer->GetOutputSize) {
   423                         renderer->GetOutputSize(renderer, &w, &h);
   424                     } else {
   425                         SDL_GetWindowSize(renderer->window, &w, &h);
   426                     }
   427 
   428                     if (renderer->target) {
   429                         renderer->viewport_backup.x = 0;
   430                         renderer->viewport_backup.y = 0;
   431                         renderer->viewport_backup.w = w;
   432                         renderer->viewport_backup.h = h;
   433                     } else {
   434                         renderer->viewport.x = 0;
   435                         renderer->viewport.y = 0;
   436                         renderer->viewport.w = w;
   437                         renderer->viewport.h = h;
   438                         QueueCmdUpdateViewport(renderer);
   439                     }
   440                 }
   441 
   442                 if (saved_target) {
   443                     SDL_SetRenderTarget(renderer, saved_target);
   444                 }
   445             } else if (event->window.event == SDL_WINDOWEVENT_HIDDEN) {
   446                 renderer->hidden = SDL_TRUE;
   447             } else if (event->window.event == SDL_WINDOWEVENT_SHOWN) {
   448                 if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)) {
   449                     renderer->hidden = SDL_FALSE;
   450                 }
   451             } else if (event->window.event == SDL_WINDOWEVENT_MINIMIZED) {
   452                 renderer->hidden = SDL_TRUE;
   453             } else if (event->window.event == SDL_WINDOWEVENT_RESTORED || 
   454                        event->window.event == SDL_WINDOWEVENT_MAXIMIZED) {
   455                 if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN)) {
   456                     renderer->hidden = SDL_FALSE;
   457                 }
   458             }
   459         }
   460     } else if (event->type == SDL_MOUSEMOTION) {
   461         SDL_Window *window = SDL_GetWindowFromID(event->motion.windowID);
   462         if (window == renderer->window) {
   463             int logical_w, logical_h;
   464             SDL_Rect viewport;
   465             SDL_FPoint scale;
   466             GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
   467             if (logical_w) {
   468                 event->motion.x -= (int)(viewport.x * renderer->dpi_scale.x);
   469                 event->motion.y -= (int)(viewport.y * renderer->dpi_scale.y);
   470                 event->motion.x = (int)(event->motion.x / (scale.x * renderer->dpi_scale.x));
   471                 event->motion.y = (int)(event->motion.y / (scale.y * renderer->dpi_scale.y));
   472                 if (event->motion.xrel > 0) {
   473                     event->motion.xrel = SDL_max(1, (int)(event->motion.xrel / (scale.x * renderer->dpi_scale.x)));
   474                 } else if (event->motion.xrel < 0) {
   475                     event->motion.xrel = SDL_min(-1, (int)(event->motion.xrel / (scale.x * renderer->dpi_scale.x)));
   476                 }
   477                 if (event->motion.yrel > 0) {
   478                     event->motion.yrel = SDL_max(1, (int)(event->motion.yrel / (scale.y * renderer->dpi_scale.y)));
   479                 } else if (event->motion.yrel < 0) {
   480                     event->motion.yrel = SDL_min(-1, (int)(event->motion.yrel / (scale.y * renderer->dpi_scale.y)));
   481                 }
   482             }
   483         }
   484     } else if (event->type == SDL_MOUSEBUTTONDOWN ||
   485                event->type == SDL_MOUSEBUTTONUP) {
   486         SDL_Window *window = SDL_GetWindowFromID(event->button.windowID);
   487         if (window == renderer->window) {
   488             int logical_w, logical_h;
   489             SDL_Rect viewport;
   490             SDL_FPoint scale;
   491             GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
   492             if (logical_w) {
   493                 event->button.x -= (int)(viewport.x * renderer->dpi_scale.x);
   494                 event->button.y -= (int)(viewport.y * renderer->dpi_scale.y);
   495                 event->button.x = (int)(event->button.x / (scale.x * renderer->dpi_scale.x));
   496                 event->button.y = (int)(event->button.y / (scale.y * renderer->dpi_scale.y));
   497             }
   498         }
   499     } else if (event->type == SDL_FINGERDOWN ||
   500                event->type == SDL_FINGERUP ||
   501                event->type == SDL_FINGERMOTION) {
   502         int logical_w, logical_h;
   503         SDL_Rect viewport;
   504         SDL_FPoint scale;
   505         GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
   506         if (logical_w) {
   507             int w = 1;
   508             int h = 1;
   509             SDL_GetRendererOutputSize(renderer, &w, &h);
   510 
   511             event->tfinger.x *= (w - 1);
   512             event->tfinger.y *= (h - 1);
   513 
   514             event->tfinger.x -= (viewport.x * renderer->dpi_scale.x);
   515             event->tfinger.y -= (viewport.y * renderer->dpi_scale.y);
   516             event->tfinger.x = (event->tfinger.x / (scale.x * renderer->dpi_scale.x));
   517             event->tfinger.y = (event->tfinger.y / (scale.y * renderer->dpi_scale.y));
   518 
   519             if (logical_w > 1) {
   520                 event->tfinger.x = event->tfinger.x / (logical_w - 1);
   521             } else {
   522                 event->tfinger.x = 0.5f;
   523             }
   524             if (logical_h > 1) {
   525                 event->tfinger.y = event->tfinger.y / (logical_h - 1);
   526             } else {
   527                 event->tfinger.y = 0.5f;
   528             }
   529         }
   530     }
   531 
   532     return 0;
   533 }
   534 
   535 int
   536 SDL_CreateWindowAndRenderer(int width, int height, Uint32 window_flags,
   537                             SDL_Window **window, SDL_Renderer **renderer)
   538 {
   539     *window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED,
   540                                      SDL_WINDOWPOS_UNDEFINED,
   541                                      width, height, window_flags);
   542     if (!*window) {
   543         *renderer = NULL;
   544         return -1;
   545     }
   546 
   547     *renderer = SDL_CreateRenderer(*window, -1, 0);
   548     if (!*renderer) {
   549         return -1;
   550     }
   551 
   552     return 0;
   553 }
   554 
   555 static SDL_INLINE
   556 void VerifyDrawQueueFunctions(const SDL_Renderer *renderer)
   557 {
   558     /* all of these functions are required to be implemented, even as no-ops, so we don't
   559         have to check that they aren't NULL over and over. */
   560     SDL_assert(renderer->QueueDrawPoints != NULL);
   561     SDL_assert(renderer->QueueDrawLines != NULL);
   562     SDL_assert(renderer->QueueFillRects != NULL);
   563     SDL_assert(renderer->QueueCopy != NULL);
   564     SDL_assert(renderer->RunCommandQueue != NULL);
   565 }
   566 
   567 SDL_Renderer *
   568 SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags)
   569 {
   570 #if !SDL_RENDER_DISABLED
   571     SDL_Renderer *renderer = NULL;
   572     int n = SDL_GetNumRenderDrivers();
   573     SDL_bool batching = SDL_TRUE;
   574     const char *hint;
   575 
   576     if (!window) {
   577         SDL_SetError("Invalid window");
   578         return NULL;
   579     }
   580 
   581     if (SDL_GetRenderer(window)) {
   582         SDL_SetError("Renderer already associated with window");
   583         return NULL;
   584     }
   585 
   586     if (SDL_GetHint(SDL_HINT_RENDER_VSYNC)) {
   587         if (SDL_GetHintBoolean(SDL_HINT_RENDER_VSYNC, SDL_TRUE)) {
   588             flags |= SDL_RENDERER_PRESENTVSYNC;
   589         } else {
   590             flags &= ~SDL_RENDERER_PRESENTVSYNC;
   591         }
   592     }
   593 
   594     if (index < 0) {
   595         hint = SDL_GetHint(SDL_HINT_RENDER_DRIVER);
   596         if (hint) {
   597             for (index = 0; index < n; ++index) {
   598                 const SDL_RenderDriver *driver = render_drivers[index];
   599 
   600                 if (SDL_strcasecmp(hint, driver->info.name) == 0) {
   601                     /* Create a new renderer instance */
   602                     renderer = driver->CreateRenderer(window, flags);
   603                     if (renderer) {
   604                         batching = SDL_FALSE;
   605                     }
   606                     break;
   607                 }
   608             }
   609         }
   610 
   611         if (!renderer) {
   612             for (index = 0; index < n; ++index) {
   613                 const SDL_RenderDriver *driver = render_drivers[index];
   614 
   615                 if ((driver->info.flags & flags) == flags) {
   616                     /* Create a new renderer instance */
   617                     renderer = driver->CreateRenderer(window, flags);
   618                     if (renderer) {
   619                         /* Yay, we got one! */
   620                         break;
   621                     }
   622                 }
   623             }
   624         }
   625         if (index == n) {
   626             SDL_SetError("Couldn't find matching render driver");
   627             return NULL;
   628         }
   629     } else {
   630         if (index >= SDL_GetNumRenderDrivers()) {
   631             SDL_SetError("index must be -1 or in the range of 0 - %d",
   632                          SDL_GetNumRenderDrivers() - 1);
   633             return NULL;
   634         }
   635         /* Create a new renderer instance */
   636         renderer = render_drivers[index]->CreateRenderer(window, flags);
   637         batching = SDL_FALSE;
   638     }
   639 
   640     if (renderer) {
   641         VerifyDrawQueueFunctions(renderer);
   642 
   643         /* let app/user override batching decisions. */
   644         if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) {
   645             batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE);
   646         }
   647 
   648         renderer->batching = batching;
   649         renderer->magic = &renderer_magic;
   650         renderer->window = window;
   651         renderer->target_mutex = SDL_CreateMutex();
   652         renderer->scale.x = 1.0f;
   653         renderer->scale.y = 1.0f;
   654         renderer->dpi_scale.x = 1.0f;
   655         renderer->dpi_scale.y = 1.0f;
   656 
   657         /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
   658         renderer->render_command_generation = 1;
   659 
   660         if (window && renderer->GetOutputSize) {
   661             int window_w, window_h;
   662             int output_w, output_h;
   663             if (renderer->GetOutputSize(renderer, &output_w, &output_h) == 0) {
   664                 SDL_GetWindowSize(renderer->window, &window_w, &window_h);
   665                 renderer->dpi_scale.x = (float)window_w / output_w;
   666                 renderer->dpi_scale.y = (float)window_h / output_h;
   667             }
   668         }
   669 
   670         if (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN|SDL_WINDOW_MINIMIZED)) {
   671             renderer->hidden = SDL_TRUE;
   672         } else {
   673             renderer->hidden = SDL_FALSE;
   674         }
   675 
   676         SDL_SetWindowData(window, SDL_WINDOWRENDERDATA, renderer);
   677 
   678         SDL_RenderSetViewport(renderer, NULL);
   679 
   680         SDL_AddEventWatch(SDL_RendererEventWatch, renderer);
   681 
   682         SDL_LogInfo(SDL_LOG_CATEGORY_RENDER,
   683                     "Created renderer: %s", renderer->info.name);
   684     }
   685     return renderer;
   686 #else
   687     SDL_SetError("SDL not built with rendering support");
   688     return NULL;
   689 #endif
   690 }
   691 
   692 SDL_Renderer *
   693 SDL_CreateSoftwareRenderer(SDL_Surface * surface)
   694 {
   695 #if !SDL_RENDER_DISABLED
   696     SDL_Renderer *renderer;
   697 
   698     renderer = SW_CreateRendererForSurface(surface);
   699 
   700     if (renderer) {
   701         VerifyDrawQueueFunctions(renderer);
   702         renderer->magic = &renderer_magic;
   703         renderer->target_mutex = SDL_CreateMutex();
   704         renderer->scale.x = 1.0f;
   705         renderer->scale.y = 1.0f;
   706 
   707         /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
   708         renderer->render_command_generation = 1;
   709 
   710         SDL_RenderSetViewport(renderer, NULL);
   711     }
   712     return renderer;
   713 #else
   714     SDL_SetError("SDL not built with rendering support");
   715     return NULL;
   716 #endif /* !SDL_RENDER_DISABLED */
   717 }
   718 
   719 SDL_Renderer *
   720 SDL_GetRenderer(SDL_Window * window)
   721 {
   722     return (SDL_Renderer *)SDL_GetWindowData(window, SDL_WINDOWRENDERDATA);
   723 }
   724 
   725 int
   726 SDL_GetRendererInfo(SDL_Renderer * renderer, SDL_RendererInfo * info)
   727 {
   728     CHECK_RENDERER_MAGIC(renderer, -1);
   729 
   730     *info = renderer->info;
   731     return 0;
   732 }
   733 
   734 int
   735 SDL_GetRendererOutputSize(SDL_Renderer * renderer, int *w, int *h)
   736 {
   737     CHECK_RENDERER_MAGIC(renderer, -1);
   738 
   739     if (renderer->target) {
   740         return SDL_QueryTexture(renderer->target, NULL, NULL, w, h);
   741     } else if (renderer->GetOutputSize) {
   742         return renderer->GetOutputSize(renderer, w, h);
   743     } else if (renderer->window) {
   744         SDL_GetWindowSize(renderer->window, w, h);
   745         return 0;
   746     } else {
   747         SDL_assert(0 && "This should never happen");
   748         return SDL_SetError("Renderer doesn't support querying output size");
   749     }
   750 }
   751 
   752 static SDL_bool
   753 IsSupportedBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
   754 {
   755     switch (blendMode)
   756     {
   757     /* These are required to be supported by all renderers */
   758     case SDL_BLENDMODE_NONE:
   759     case SDL_BLENDMODE_BLEND:
   760     case SDL_BLENDMODE_ADD:
   761     case SDL_BLENDMODE_MOD:
   762         return SDL_TRUE;
   763 
   764     default:
   765         return renderer->SupportsBlendMode && renderer->SupportsBlendMode(renderer, blendMode);
   766     }
   767 }
   768 
   769 static SDL_bool
   770 IsSupportedFormat(SDL_Renderer * renderer, Uint32 format)
   771 {
   772     Uint32 i;
   773 
   774     for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   775         if (renderer->info.texture_formats[i] == format) {
   776             return SDL_TRUE;
   777         }
   778     }
   779     return SDL_FALSE;
   780 }
   781 
   782 static Uint32
   783 GetClosestSupportedFormat(SDL_Renderer * renderer, Uint32 format)
   784 {
   785     Uint32 i;
   786 
   787     if (SDL_ISPIXELFORMAT_FOURCC(format)) {
   788         /* Look for an exact match */
   789         for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   790             if (renderer->info.texture_formats[i] == format) {
   791                 return renderer->info.texture_formats[i];
   792             }
   793         }
   794     } else {
   795         SDL_bool hasAlpha = SDL_ISPIXELFORMAT_ALPHA(format);
   796 
   797         /* We just want to match the first format that has the same channels */
   798         for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   799             if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) &&
   800                 SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == hasAlpha) {
   801                 return renderer->info.texture_formats[i];
   802             }
   803         }
   804     }
   805     return renderer->info.texture_formats[0];
   806 }
   807 
   808 SDL_ScaleMode SDL_GetScaleMode(void)
   809 {
   810     const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
   811 
   812     if (!hint || SDL_strcasecmp(hint, "nearest") == 0) {
   813         return SDL_ScaleModeNearest;
   814     } else if (SDL_strcasecmp(hint, "linear") == 0) {
   815         return SDL_ScaleModeLinear;
   816     } else if (SDL_strcasecmp(hint, "best") == 0) {
   817         return SDL_ScaleModeBest;
   818     } else {
   819         return (SDL_ScaleMode)SDL_atoi(hint);
   820     }
   821 }
   822 
   823 SDL_Texture *
   824 SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h)
   825 {
   826     SDL_Texture *texture;
   827 
   828     CHECK_RENDERER_MAGIC(renderer, NULL);
   829 
   830     if (!format) {
   831         format = renderer->info.texture_formats[0];
   832     }
   833     if (SDL_BYTESPERPIXEL(format) == 0) {
   834         SDL_SetError("Invalid texture format");
   835         return NULL;
   836     }
   837     if (SDL_ISPIXELFORMAT_INDEXED(format)) {
   838         SDL_SetError("Palettized textures are not supported");
   839         return NULL;
   840     }
   841     if (w <= 0 || h <= 0) {
   842         SDL_SetError("Texture dimensions can't be 0");
   843         return NULL;
   844     }
   845     if ((renderer->info.max_texture_width && w > renderer->info.max_texture_width) ||
   846         (renderer->info.max_texture_height && h > renderer->info.max_texture_height)) {
   847         SDL_SetError("Texture dimensions are limited to %dx%d", renderer->info.max_texture_width, renderer->info.max_texture_height);
   848         return NULL;
   849     }
   850     texture = (SDL_Texture *) SDL_calloc(1, sizeof(*texture));
   851     if (!texture) {
   852         SDL_OutOfMemory();
   853         return NULL;
   854     }
   855     texture->magic = &texture_magic;
   856     texture->format = format;
   857     texture->access = access;
   858     texture->w = w;
   859     texture->h = h;
   860     texture->r = 255;
   861     texture->g = 255;
   862     texture->b = 255;
   863     texture->a = 255;
   864     texture->scaleMode = SDL_GetScaleMode();
   865     texture->renderer = renderer;
   866     texture->next = renderer->textures;
   867     if (renderer->textures) {
   868         renderer->textures->prev = texture;
   869     }
   870     renderer->textures = texture;
   871 
   872     if (IsSupportedFormat(renderer, format)) {
   873         if (renderer->CreateTexture(renderer, texture) < 0) {
   874             SDL_DestroyTexture(texture);
   875             return NULL;
   876         }
   877     } else {
   878         texture->native = SDL_CreateTexture(renderer,
   879                                 GetClosestSupportedFormat(renderer, format),
   880                                 access, w, h);
   881         if (!texture->native) {
   882             SDL_DestroyTexture(texture);
   883             return NULL;
   884         }
   885 
   886         /* Swap textures to have texture before texture->native in the list */
   887         texture->native->next = texture->next;
   888         if (texture->native->next) {
   889             texture->native->next->prev = texture->native;
   890         }
   891         texture->prev = texture->native->prev;
   892         if (texture->prev) {
   893             texture->prev->next = texture;
   894         }
   895         texture->native->prev = texture;
   896         texture->next = texture->native;
   897         renderer->textures = texture;
   898 
   899         if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
   900             texture->yuv = SDL_SW_CreateYUVTexture(format, w, h);
   901             if (!texture->yuv) {
   902                 SDL_DestroyTexture(texture);
   903                 return NULL;
   904             }
   905         } else if (access == SDL_TEXTUREACCESS_STREAMING) {
   906             /* The pitch is 4 byte aligned */
   907             texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3);
   908             texture->pixels = SDL_calloc(1, texture->pitch * h);
   909             if (!texture->pixels) {
   910                 SDL_DestroyTexture(texture);
   911                 return NULL;
   912             }
   913         }
   914     }
   915     return texture;
   916 }
   917 
   918 SDL_Texture *
   919 SDL_CreateTextureFromSurface(SDL_Renderer * renderer, SDL_Surface * surface)
   920 {
   921     const SDL_PixelFormat *fmt;
   922     SDL_bool needAlpha;
   923     Uint32 i;
   924     Uint32 format;
   925     SDL_Texture *texture;
   926 
   927     CHECK_RENDERER_MAGIC(renderer, NULL);
   928 
   929     if (!surface) {
   930         SDL_SetError("SDL_CreateTextureFromSurface() passed NULL surface");
   931         return NULL;
   932     }
   933 
   934     /* See what the best texture format is */
   935     fmt = surface->format;
   936     if (fmt->Amask || SDL_GetColorKey(surface, NULL) == 0) {
   937         needAlpha = SDL_TRUE;
   938     } else {
   939         needAlpha = SDL_FALSE;
   940     }
   941     format = renderer->info.texture_formats[0];
   942     for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   943         if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) &&
   944             SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == needAlpha) {
   945             format = renderer->info.texture_formats[i];
   946             break;
   947         }
   948     }
   949 
   950     texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STATIC,
   951                                 surface->w, surface->h);
   952     if (!texture) {
   953         return NULL;
   954     }
   955 
   956     if (format == surface->format->format) {
   957         if (SDL_MUSTLOCK(surface)) {
   958             SDL_LockSurface(surface);
   959             SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
   960             SDL_UnlockSurface(surface);
   961         } else {
   962             SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
   963         }
   964     } else {
   965         SDL_PixelFormat *dst_fmt;
   966         SDL_Surface *temp = NULL;
   967 
   968         /* Set up a destination surface for the texture update */
   969         dst_fmt = SDL_AllocFormat(format);
   970         if (!dst_fmt) {
   971            SDL_DestroyTexture(texture);
   972            return NULL;
   973         }
   974         temp = SDL_ConvertSurface(surface, dst_fmt, 0);
   975         SDL_FreeFormat(dst_fmt);
   976         if (temp) {
   977             SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
   978             SDL_FreeSurface(temp);
   979         } else {
   980             SDL_DestroyTexture(texture);
   981             return NULL;
   982         }
   983     }
   984 
   985     {
   986         Uint8 r, g, b, a;
   987         SDL_BlendMode blendMode;
   988 
   989         SDL_GetSurfaceColorMod(surface, &r, &g, &b);
   990         SDL_SetTextureColorMod(texture, r, g, b);
   991 
   992         SDL_GetSurfaceAlphaMod(surface, &a);
   993         SDL_SetTextureAlphaMod(texture, a);
   994 
   995         if (SDL_GetColorKey(surface, NULL) == 0) {
   996             /* We converted to a texture with alpha format */
   997             SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
   998         } else {
   999             SDL_GetSurfaceBlendMode(surface, &blendMode);
  1000             SDL_SetTextureBlendMode(texture, blendMode);
  1001         }
  1002     }
  1003     return texture;
  1004 }
  1005 
  1006 int
  1007 SDL_QueryTexture(SDL_Texture * texture, Uint32 * format, int *access,
  1008                  int *w, int *h)
  1009 {
  1010     CHECK_TEXTURE_MAGIC(texture, -1);
  1011 
  1012     if (format) {
  1013         *format = texture->format;
  1014     }
  1015     if (access) {
  1016         *access = texture->access;
  1017     }
  1018     if (w) {
  1019         *w = texture->w;
  1020     }
  1021     if (h) {
  1022         *h = texture->h;
  1023     }
  1024     return 0;
  1025 }
  1026 
  1027 int
  1028 SDL_SetTextureColorMod(SDL_Texture * texture, Uint8 r, Uint8 g, Uint8 b)
  1029 {
  1030     SDL_Renderer *renderer;
  1031 
  1032     CHECK_TEXTURE_MAGIC(texture, -1);
  1033 
  1034     renderer = texture->renderer;
  1035     if (r < 255 || g < 255 || b < 255) {
  1036         texture->modMode |= SDL_TEXTUREMODULATE_COLOR;
  1037     } else {
  1038         texture->modMode &= ~SDL_TEXTUREMODULATE_COLOR;
  1039     }
  1040     texture->r = r;
  1041     texture->g = g;
  1042     texture->b = b;
  1043     if (texture->native) {
  1044         return SDL_SetTextureColorMod(texture->native, r, g, b);
  1045     }
  1046     return 0;
  1047 }
  1048 
  1049 int
  1050 SDL_GetTextureColorMod(SDL_Texture * texture, Uint8 * r, Uint8 * g,
  1051                        Uint8 * b)
  1052 {
  1053     CHECK_TEXTURE_MAGIC(texture, -1);
  1054 
  1055     if (r) {
  1056         *r = texture->r;
  1057     }
  1058     if (g) {
  1059         *g = texture->g;
  1060     }
  1061     if (b) {
  1062         *b = texture->b;
  1063     }
  1064     return 0;
  1065 }
  1066 
  1067 int
  1068 SDL_SetTextureAlphaMod(SDL_Texture * texture, Uint8 alpha)
  1069 {
  1070     SDL_Renderer *renderer;
  1071 
  1072     CHECK_TEXTURE_MAGIC(texture, -1);
  1073 
  1074     renderer = texture->renderer;
  1075     if (alpha < 255) {
  1076         texture->modMode |= SDL_TEXTUREMODULATE_ALPHA;
  1077     } else {
  1078         texture->modMode &= ~SDL_TEXTUREMODULATE_ALPHA;
  1079     }
  1080     texture->a = alpha;
  1081     if (texture->native) {
  1082         return SDL_SetTextureAlphaMod(texture->native, alpha);
  1083     }
  1084     return 0;
  1085 }
  1086 
  1087 int
  1088 SDL_GetTextureAlphaMod(SDL_Texture * texture, Uint8 * alpha)
  1089 {
  1090     CHECK_TEXTURE_MAGIC(texture, -1);
  1091 
  1092     if (alpha) {
  1093         *alpha = texture->a;
  1094     }
  1095     return 0;
  1096 }
  1097 
  1098 int
  1099 SDL_SetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode blendMode)
  1100 {
  1101     SDL_Renderer *renderer;
  1102 
  1103     CHECK_TEXTURE_MAGIC(texture, -1);
  1104 
  1105     renderer = texture->renderer;
  1106     if (!IsSupportedBlendMode(renderer, blendMode)) {
  1107         return SDL_Unsupported();
  1108     }
  1109     texture->blendMode = blendMode;
  1110     if (texture->native) {
  1111         return SDL_SetTextureBlendMode(texture->native, blendMode);
  1112     }
  1113     return 0;
  1114 }
  1115 
  1116 int
  1117 SDL_GetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode *blendMode)
  1118 {
  1119     CHECK_TEXTURE_MAGIC(texture, -1);
  1120 
  1121     if (blendMode) {
  1122         *blendMode = texture->blendMode;
  1123     }
  1124     return 0;
  1125 }
  1126 
  1127 static int
  1128 SDL_UpdateTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
  1129                      const void *pixels, int pitch)
  1130 {
  1131     SDL_Texture *native = texture->native;
  1132     SDL_Rect full_rect;
  1133 
  1134     if (SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch) < 0) {
  1135         return -1;
  1136     }
  1137 
  1138     full_rect.x = 0;
  1139     full_rect.y = 0;
  1140     full_rect.w = texture->w;
  1141     full_rect.h = texture->h;
  1142     rect = &full_rect;
  1143 
  1144     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  1145         /* We can lock the texture and copy to it */
  1146         void *native_pixels = NULL;
  1147         int native_pitch = 0;
  1148 
  1149         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
  1150             return -1;
  1151         }
  1152         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
  1153                             rect->w, rect->h, native_pixels, native_pitch);
  1154         SDL_UnlockTexture(native);
  1155     } else {
  1156         /* Use a temporary buffer for updating */
  1157         const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
  1158         const size_t alloclen = rect->h * temp_pitch;
  1159         if (alloclen > 0) {
  1160             void *temp_pixels = SDL_malloc(alloclen);
  1161             if (!temp_pixels) {
  1162                 return SDL_OutOfMemory();
  1163             }
  1164             SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
  1165                                 rect->w, rect->h, temp_pixels, temp_pitch);
  1166             SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
  1167             SDL_free(temp_pixels);
  1168         }
  1169     }
  1170     return 0;
  1171 }
  1172 
  1173 static int
  1174 SDL_UpdateTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
  1175                         const void *pixels, int pitch)
  1176 {
  1177     SDL_Texture *native = texture->native;
  1178 
  1179     if (!rect->w || !rect->h) {
  1180         return 0;  /* nothing to do. */
  1181     }
  1182 
  1183     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  1184         /* We can lock the texture and copy to it */
  1185         void *native_pixels = NULL;
  1186         int native_pitch = 0;
  1187 
  1188         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
  1189             return -1;
  1190         }
  1191         SDL_ConvertPixels(rect->w, rect->h,
  1192                           texture->format, pixels, pitch,
  1193                           native->format, native_pixels, native_pitch);
  1194         SDL_UnlockTexture(native);
  1195     } else {
  1196         /* Use a temporary buffer for updating */
  1197         const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
  1198         const size_t alloclen = rect->h * temp_pitch;
  1199         if (alloclen > 0) {
  1200             void *temp_pixels = SDL_malloc(alloclen);
  1201             if (!temp_pixels) {
  1202                 return SDL_OutOfMemory();
  1203             }
  1204             SDL_ConvertPixels(rect->w, rect->h,
  1205                               texture->format, pixels, pitch,
  1206                               native->format, temp_pixels, temp_pitch);
  1207             SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
  1208             SDL_free(temp_pixels);
  1209         }
  1210     }
  1211     return 0;
  1212 }
  1213 
  1214 int
  1215 SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect,
  1216                   const void *pixels, int pitch)
  1217 {
  1218     SDL_Rect full_rect;
  1219 
  1220     CHECK_TEXTURE_MAGIC(texture, -1);
  1221 
  1222     if (!pixels) {
  1223         return SDL_InvalidParamError("pixels");
  1224     }
  1225     if (!pitch) {
  1226         return SDL_InvalidParamError("pitch");
  1227     }
  1228 
  1229     if (!rect) {
  1230         full_rect.x = 0;
  1231         full_rect.y = 0;
  1232         full_rect.w = texture->w;
  1233         full_rect.h = texture->h;
  1234         rect = &full_rect;
  1235     }
  1236 
  1237     if ((rect->w == 0) || (rect->h == 0)) {
  1238         return 0;  /* nothing to do. */
  1239     } else if (texture->yuv) {
  1240         return SDL_UpdateTextureYUV(texture, rect, pixels, pitch);
  1241     } else if (texture->native) {
  1242         return SDL_UpdateTextureNative(texture, rect, pixels, pitch);
  1243     } else {
  1244         SDL_Renderer *renderer = texture->renderer;
  1245         if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
  1246             return -1;
  1247         }
  1248         return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
  1249     }
  1250 }
  1251 
  1252 static int
  1253 SDL_UpdateTextureYUVPlanar(SDL_Texture * texture, const SDL_Rect * rect,
  1254                            const Uint8 *Yplane, int Ypitch,
  1255                            const Uint8 *Uplane, int Upitch,
  1256                            const Uint8 *Vplane, int Vpitch)
  1257 {
  1258     SDL_Texture *native = texture->native;
  1259     SDL_Rect full_rect;
  1260 
  1261     if (SDL_SW_UpdateYUVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch) < 0) {
  1262         return -1;
  1263     }
  1264 
  1265     full_rect.x = 0;
  1266     full_rect.y = 0;
  1267     full_rect.w = texture->w;
  1268     full_rect.h = texture->h;
  1269     rect = &full_rect;
  1270 
  1271     if (!rect->w || !rect->h) {
  1272         return 0;  /* nothing to do. */
  1273     }
  1274 
  1275     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
  1276         /* We can lock the texture and copy to it */
  1277         void *native_pixels = NULL;
  1278         int native_pitch = 0;
  1279 
  1280         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
  1281             return -1;
  1282         }
  1283         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
  1284                             rect->w, rect->h, native_pixels, native_pitch);
  1285         SDL_UnlockTexture(native);
  1286     } else {
  1287         /* Use a temporary buffer for updating */
  1288         const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
  1289         const size_t alloclen = rect->h * temp_pitch;
  1290         if (alloclen > 0) {
  1291             void *temp_pixels = SDL_malloc(alloclen);
  1292             if (!temp_pixels) {
  1293                 return SDL_OutOfMemory();
  1294             }
  1295             SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
  1296                                 rect->w, rect->h, temp_pixels, temp_pitch);
  1297             SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
  1298             SDL_free(temp_pixels);
  1299         }
  1300     }
  1301     return 0;
  1302 }
  1303 
  1304 int SDL_UpdateYUVTexture(SDL_Texture * texture, const SDL_Rect * rect,
  1305                          const Uint8 *Yplane, int Ypitch,
  1306                          const Uint8 *Uplane, int Upitch,
  1307                          const Uint8 *Vplane, int Vpitch)
  1308 {
  1309     SDL_Renderer *renderer;
  1310     SDL_Rect full_rect;
  1311 
  1312     CHECK_TEXTURE_MAGIC(texture, -1);
  1313 
  1314     if (!Yplane) {
  1315         return SDL_InvalidParamError("Yplane");
  1316     }
  1317     if (!Ypitch) {
  1318         return SDL_InvalidParamError("Ypitch");
  1319     }
  1320     if (!Uplane) {
  1321         return SDL_InvalidParamError("Uplane");
  1322     }
  1323     if (!Upitch) {
  1324         return SDL_InvalidParamError("Upitch");
  1325     }
  1326     if (!Vplane) {
  1327         return SDL_InvalidParamError("Vplane");
  1328     }
  1329     if (!Vpitch) {
  1330         return SDL_InvalidParamError("Vpitch");
  1331     }
  1332 
  1333     if (texture->format != SDL_PIXELFORMAT_YV12 &&
  1334         texture->format != SDL_PIXELFORMAT_IYUV) {
  1335         return SDL_SetError("Texture format must by YV12 or IYUV");
  1336     }
  1337 
  1338     if (!rect) {
  1339         full_rect.x = 0;
  1340         full_rect.y = 0;
  1341         full_rect.w = texture->w;
  1342         full_rect.h = texture->h;
  1343         rect = &full_rect;
  1344     }
  1345 
  1346     if (!rect->w || !rect->h) {
  1347         return 0;  /* nothing to do. */
  1348     }
  1349 
  1350     if (texture->yuv) {
  1351         return SDL_UpdateTextureYUVPlanar(texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
  1352     } else {
  1353         SDL_assert(!texture->native);
  1354         renderer = texture->renderer;
  1355         SDL_assert(renderer->UpdateTextureYUV);
  1356         if (renderer->UpdateTextureYUV) {
  1357             if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
  1358                 return -1;
  1359             }
  1360             return renderer->UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
  1361         } else {
  1362             return SDL_Unsupported();
  1363         }
  1364     }
  1365 }
  1366 
  1367 static int
  1368 SDL_LockTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
  1369                    void **pixels, int *pitch)
  1370 {
  1371     return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch);
  1372 }
  1373 
  1374 static int
  1375 SDL_LockTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
  1376                       void **pixels, int *pitch)
  1377 {
  1378     texture->locked_rect = *rect;
  1379     *pixels = (void *) ((Uint8 *) texture->pixels +
  1380                         rect->y * texture->pitch +
  1381                         rect->x * SDL_BYTESPERPIXEL(texture->format));
  1382     *pitch = texture->pitch;
  1383     return 0;
  1384 }
  1385 
  1386 int
  1387 SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect,
  1388                 void **pixels, int *pitch)
  1389 {
  1390     SDL_Rect full_rect;
  1391 
  1392     CHECK_TEXTURE_MAGIC(texture, -1);
  1393 
  1394     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
  1395         return SDL_SetError("SDL_LockTexture(): texture must be streaming");
  1396     }
  1397 
  1398     if (!rect) {
  1399         full_rect.x = 0;
  1400         full_rect.y = 0;
  1401         full_rect.w = texture->w;
  1402         full_rect.h = texture->h;
  1403         rect = &full_rect;
  1404     }
  1405 
  1406     if (texture->yuv) {
  1407         if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
  1408             return -1;
  1409         }
  1410         return SDL_LockTextureYUV(texture, rect, pixels, pitch);
  1411     } else if (texture->native) {
  1412         /* Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. */
  1413         return SDL_LockTextureNative(texture, rect, pixels, pitch);
  1414     } else {
  1415         SDL_Renderer *renderer = texture->renderer;
  1416         if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
  1417             return -1;
  1418         }
  1419         return renderer->LockTexture(renderer, texture, rect, pixels, pitch);
  1420     }
  1421 }
  1422 
  1423 static void
  1424 SDL_UnlockTextureYUV(SDL_Texture * texture)
  1425 {
  1426     SDL_Texture *native = texture->native;
  1427     void *native_pixels = NULL;
  1428     int native_pitch = 0;
  1429     SDL_Rect rect;
  1430 
  1431     rect.x = 0;
  1432     rect.y = 0;
  1433     rect.w = texture->w;
  1434     rect.h = texture->h;
  1435 
  1436     if (SDL_LockTexture(native, &rect, &native_pixels, &native_pitch) < 0) {
  1437         return;
  1438     }
  1439     SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format,
  1440                         rect.w, rect.h, native_pixels, native_pitch);
  1441     SDL_UnlockTexture(native);
  1442 }
  1443 
  1444 static void
  1445 SDL_UnlockTextureNative(SDL_Texture * texture)
  1446 {
  1447     SDL_Texture *native = texture->native;
  1448     void *native_pixels = NULL;
  1449     int native_pitch = 0;
  1450     const SDL_Rect *rect = &texture->locked_rect;
  1451     const void* pixels = (void *) ((Uint8 *) texture->pixels +
  1452                         rect->y * texture->pitch +
  1453                         rect->x * SDL_BYTESPERPIXEL(texture->format));
  1454     int pitch = texture->pitch;
  1455 
  1456     if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
  1457         return;
  1458     }
  1459     SDL_ConvertPixels(rect->w, rect->h,
  1460                       texture->format, pixels, pitch,
  1461                       native->format, native_pixels, native_pitch);
  1462     SDL_UnlockTexture(native);
  1463 }
  1464 
  1465 void
  1466 SDL_UnlockTexture(SDL_Texture * texture)
  1467 {
  1468     CHECK_TEXTURE_MAGIC(texture, );
  1469 
  1470     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
  1471         return;
  1472     }
  1473     if (texture->yuv) {
  1474         SDL_UnlockTextureYUV(texture);
  1475     } else if (texture->native) {
  1476         SDL_UnlockTextureNative(texture);
  1477     } else {
  1478         SDL_Renderer *renderer = texture->renderer;
  1479         renderer->UnlockTexture(renderer, texture);
  1480     }
  1481 }
  1482 
  1483 SDL_bool
  1484 SDL_RenderTargetSupported(SDL_Renderer *renderer)
  1485 {
  1486     if (!renderer || !renderer->SetRenderTarget) {
  1487         return SDL_FALSE;
  1488     }
  1489     return (renderer->info.flags & SDL_RENDERER_TARGETTEXTURE) != 0;
  1490 }
  1491 
  1492 int
  1493 SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
  1494 {
  1495     if (!SDL_RenderTargetSupported(renderer)) {
  1496         return SDL_Unsupported();
  1497     }
  1498     if (texture == renderer->target) {
  1499         /* Nothing to do! */
  1500         return 0;
  1501     }
  1502 
  1503     FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
  1504 
  1505     /* texture == NULL is valid and means reset the target to the window */
  1506     if (texture) {
  1507         CHECK_TEXTURE_MAGIC(texture, -1);
  1508         if (renderer != texture->renderer) {
  1509             return SDL_SetError("Texture was not created with this renderer");
  1510         }
  1511         if (texture->access != SDL_TEXTUREACCESS_TARGET) {
  1512             return SDL_SetError("Texture not created with SDL_TEXTUREACCESS_TARGET");
  1513         }
  1514         if (texture->native) {
  1515             /* Always render to the native texture */
  1516             texture = texture->native;
  1517         }
  1518     }
  1519 
  1520     SDL_LockMutex(renderer->target_mutex);
  1521 
  1522     if (texture && !renderer->target) {
  1523         /* Make a backup of the viewport */
  1524         renderer->viewport_backup = renderer->viewport;
  1525         renderer->clip_rect_backup = renderer->clip_rect;
  1526         renderer->clipping_enabled_backup = renderer->clipping_enabled;
  1527         renderer->scale_backup = renderer->scale;
  1528         renderer->logical_w_backup = renderer->logical_w;
  1529         renderer->logical_h_backup = renderer->logical_h;
  1530     }
  1531     renderer->target = texture;
  1532 
  1533     if (renderer->SetRenderTarget(renderer, texture) < 0) {
  1534         SDL_UnlockMutex(renderer->target_mutex);
  1535         return -1;
  1536     }
  1537 
  1538     if (texture) {
  1539         renderer->viewport.x = 0;
  1540         renderer->viewport.y = 0;
  1541         renderer->viewport.w = texture->w;
  1542         renderer->viewport.h = texture->h;
  1543         SDL_zero(renderer->clip_rect);
  1544         renderer->clipping_enabled = SDL_FALSE;
  1545         renderer->scale.x = 1.0f;
  1546         renderer->scale.y = 1.0f;
  1547         renderer->logical_w = texture->w;
  1548         renderer->logical_h = texture->h;
  1549     } else {
  1550         renderer->viewport = renderer->viewport_backup;
  1551         renderer->clip_rect = renderer->clip_rect_backup;
  1552         renderer->clipping_enabled = renderer->clipping_enabled_backup;
  1553         renderer->scale = renderer->scale_backup;
  1554         renderer->logical_w = renderer->logical_w_backup;
  1555         renderer->logical_h = renderer->logical_h_backup;
  1556     }
  1557 
  1558     SDL_UnlockMutex(renderer->target_mutex);
  1559 
  1560     if (QueueCmdUpdateViewport(renderer) < 0) {
  1561         return -1;
  1562     }
  1563     if (QueueCmdUpdateClipRect(renderer) < 0) {
  1564         return -1;
  1565     }
  1566 
  1567     /* All set! */
  1568     return 0;
  1569 }
  1570 
  1571 SDL_Texture *
  1572 SDL_GetRenderTarget(SDL_Renderer *renderer)
  1573 {
  1574     return renderer->target;
  1575 }
  1576 
  1577 static int
  1578 UpdateLogicalSize(SDL_Renderer *renderer)
  1579 {
  1580     int w = 1, h = 1;
  1581     float want_aspect;
  1582     float real_aspect;
  1583     float scale;
  1584     SDL_Rect viewport;
  1585     /* 0 is for letterbox, 1 is for overscan */
  1586     int scale_policy = 0;
  1587     const char *hint;
  1588 
  1589     if (!renderer->logical_w || !renderer->logical_h) {
  1590         return 0;
  1591     }
  1592     if (SDL_GetRendererOutputSize(renderer, &w, &h) < 0) {
  1593         return -1;
  1594     }
  1595 
  1596     hint = SDL_GetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE);
  1597     if (hint && (*hint == '1' || SDL_strcasecmp(hint, "overscan") == 0))  {
  1598 #if SDL_VIDEO_RENDER_D3D
  1599         SDL_bool overscan_supported = SDL_TRUE;
  1600         /* Unfortunately, Direct3D 9 doesn't support negative viewport numbers
  1601            which the overscan implementation relies on.
  1602         */
  1603         if (SDL_strcasecmp(SDL_GetCurrentVideoDriver(), "direct3d") == 0) {
  1604             overscan_supported = SDL_FALSE;
  1605         }
  1606         if (overscan_supported) {
  1607             scale_policy = 1;
  1608         }
  1609 #else
  1610         scale_policy = 1;
  1611 #endif
  1612     }
  1613 
  1614     want_aspect = (float)renderer->logical_w / renderer->logical_h;
  1615     real_aspect = (float)w / h;
  1616 
  1617     /* Clear the scale because we're setting viewport in output coordinates */
  1618     SDL_RenderSetScale(renderer, 1.0f, 1.0f);
  1619 
  1620     if (renderer->integer_scale) {
  1621         if (want_aspect > real_aspect) {
  1622             scale = (float)(w / renderer->logical_w);
  1623         } else {
  1624             scale = (float)(h / renderer->logical_h);
  1625         }
  1626         viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
  1627         viewport.x = (w - viewport.w) / 2;
  1628         viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
  1629         viewport.y = (h - viewport.h) / 2;
  1630 
  1631         SDL_RenderSetViewport(renderer, &viewport);
  1632     } else if (SDL_fabs(want_aspect-real_aspect) < 0.0001) {
  1633         /* The aspect ratios are the same, just scale appropriately */
  1634         scale = (float)w / renderer->logical_w;
  1635         SDL_RenderSetViewport(renderer, NULL);
  1636     } else if (want_aspect > real_aspect) {
  1637         if (scale_policy == 1) {
  1638             /* We want a wider aspect ratio than is available - 
  1639              zoom so logical height matches the real height 
  1640              and the width will grow off the screen 
  1641              */
  1642             scale = (float)h / renderer->logical_h;
  1643             viewport.y = 0;
  1644             viewport.h = h;
  1645             viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
  1646             viewport.x = (w - viewport.w) / 2;
  1647             SDL_RenderSetViewport(renderer, &viewport);
  1648         } else {
  1649             /* We want a wider aspect ratio than is available - letterbox it */
  1650             scale = (float)w / renderer->logical_w;
  1651             viewport.x = 0;
  1652             viewport.w = w;
  1653             viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
  1654             viewport.y = (h - viewport.h) / 2;
  1655             SDL_RenderSetViewport(renderer, &viewport);
  1656         }
  1657     } else {
  1658         if (scale_policy == 1) {
  1659             /* We want a narrower aspect ratio than is available -
  1660              zoom so logical width matches the real width
  1661              and the height will grow off the screen
  1662              */
  1663             scale = (float)w / renderer->logical_w;
  1664             viewport.x = 0;
  1665             viewport.w = w;
  1666             viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
  1667             viewport.y = (h - viewport.h) / 2;
  1668             SDL_RenderSetViewport(renderer, &viewport);
  1669         } else {
  1670             /* We want a narrower aspect ratio than is available - use side-bars */
  1671              scale = (float)h / renderer->logical_h;
  1672              viewport.y = 0;
  1673              viewport.h = h;
  1674              viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
  1675              viewport.x = (w - viewport.w) / 2;
  1676              SDL_RenderSetViewport(renderer, &viewport);
  1677         }
  1678     }
  1679 
  1680     /* Set the new scale */
  1681     SDL_RenderSetScale(renderer, scale, scale);
  1682 
  1683     return 0;
  1684 }
  1685 
  1686 int
  1687 SDL_RenderSetLogicalSize(SDL_Renderer * renderer, int w, int h)
  1688 {
  1689     CHECK_RENDERER_MAGIC(renderer, -1);
  1690 
  1691     if (!w || !h) {
  1692         /* Clear any previous logical resolution */
  1693         renderer->logical_w = 0;
  1694         renderer->logical_h = 0;
  1695         SDL_RenderSetViewport(renderer, NULL);
  1696         SDL_RenderSetScale(renderer, 1.0f, 1.0f);
  1697         return 0;
  1698     }
  1699 
  1700     renderer->logical_w = w;
  1701     renderer->logical_h = h;
  1702 
  1703     return UpdateLogicalSize(renderer);
  1704 }
  1705 
  1706 void
  1707 SDL_RenderGetLogicalSize(SDL_Renderer * renderer, int *w, int *h)
  1708 {
  1709     CHECK_RENDERER_MAGIC(renderer, );
  1710 
  1711     if (w) {
  1712         *w = renderer->logical_w;
  1713     }
  1714     if (h) {
  1715         *h = renderer->logical_h;
  1716     }
  1717 }
  1718 
  1719 int
  1720 SDL_RenderSetIntegerScale(SDL_Renderer * renderer, SDL_bool enable)
  1721 {
  1722     CHECK_RENDERER_MAGIC(renderer, -1);
  1723 
  1724     renderer->integer_scale = enable;
  1725 
  1726     return UpdateLogicalSize(renderer);
  1727 }
  1728 
  1729 SDL_bool
  1730 SDLCALL SDL_RenderGetIntegerScale(SDL_Renderer * renderer)
  1731 {
  1732     CHECK_RENDERER_MAGIC(renderer, SDL_FALSE);
  1733 
  1734     return renderer->integer_scale;
  1735 }
  1736 
  1737 int
  1738 SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect)
  1739 {
  1740     CHECK_RENDERER_MAGIC(renderer, -1);
  1741 
  1742     if (rect) {
  1743         renderer->viewport.x = (int)SDL_floor(rect->x * renderer->scale.x);
  1744         renderer->viewport.y = (int)SDL_floor(rect->y * renderer->scale.y);
  1745         renderer->viewport.w = (int)SDL_ceil(rect->w * renderer->scale.x);
  1746         renderer->viewport.h = (int)SDL_ceil(rect->h * renderer->scale.y);
  1747     } else {
  1748         renderer->viewport.x = 0;
  1749         renderer->viewport.y = 0;
  1750         if (SDL_GetRendererOutputSize(renderer, &renderer->viewport.w, &renderer->viewport.h) < 0) {
  1751             return -1;
  1752         }
  1753     }
  1754     return QueueCmdUpdateViewport(renderer);
  1755 }
  1756 
  1757 void
  1758 SDL_RenderGetViewport(SDL_Renderer * renderer, SDL_Rect * rect)
  1759 {
  1760     CHECK_RENDERER_MAGIC(renderer, );
  1761 
  1762     if (rect) {
  1763         rect->x = (int)(renderer->viewport.x / renderer->scale.x);
  1764         rect->y = (int)(renderer->viewport.y / renderer->scale.y);
  1765         rect->w = (int)(renderer->viewport.w / renderer->scale.x);
  1766         rect->h = (int)(renderer->viewport.h / renderer->scale.y);
  1767     }
  1768 }
  1769 
  1770 int
  1771 SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect)
  1772 {
  1773     CHECK_RENDERER_MAGIC(renderer, -1)
  1774 
  1775     if (rect) {
  1776         renderer->clipping_enabled = SDL_TRUE;
  1777         renderer->clip_rect.x = (int)SDL_floor(rect->x * renderer->scale.x);
  1778         renderer->clip_rect.y = (int)SDL_floor(rect->y * renderer->scale.y);
  1779         renderer->clip_rect.w = (int)SDL_ceil(rect->w * renderer->scale.x);
  1780         renderer->clip_rect.h = (int)SDL_ceil(rect->h * renderer->scale.y);
  1781     } else {
  1782         renderer->clipping_enabled = SDL_FALSE;
  1783         SDL_zero(renderer->clip_rect);
  1784     }
  1785     return QueueCmdUpdateClipRect(renderer);
  1786 }
  1787 
  1788 void
  1789 SDL_RenderGetClipRect(SDL_Renderer * renderer, SDL_Rect * rect)
  1790 {
  1791     CHECK_RENDERER_MAGIC(renderer, )
  1792 
  1793     if (rect) {
  1794         rect->x = (int)(renderer->clip_rect.x / renderer->scale.x);
  1795         rect->y = (int)(renderer->clip_rect.y / renderer->scale.y);
  1796         rect->w = (int)(renderer->clip_rect.w / renderer->scale.x);
  1797         rect->h = (int)(renderer->clip_rect.h / renderer->scale.y);
  1798     }
  1799 }
  1800 
  1801 SDL_bool
  1802 SDL_RenderIsClipEnabled(SDL_Renderer * renderer)
  1803 {
  1804     CHECK_RENDERER_MAGIC(renderer, SDL_FALSE)
  1805     return renderer->clipping_enabled;
  1806 }
  1807 
  1808 int
  1809 SDL_RenderSetScale(SDL_Renderer * renderer, float scaleX, float scaleY)
  1810 {
  1811     CHECK_RENDERER_MAGIC(renderer, -1);
  1812 
  1813     renderer->scale.x = scaleX;
  1814     renderer->scale.y = scaleY;
  1815     return 0;
  1816 }
  1817 
  1818 void
  1819 SDL_RenderGetScale(SDL_Renderer * renderer, float *scaleX, float *scaleY)
  1820 {
  1821     CHECK_RENDERER_MAGIC(renderer, );
  1822 
  1823     if (scaleX) {
  1824         *scaleX = renderer->scale.x;
  1825     }
  1826     if (scaleY) {
  1827         *scaleY = renderer->scale.y;
  1828     }
  1829 }
  1830 
  1831 int
  1832 SDL_SetRenderDrawColor(SDL_Renderer * renderer,
  1833                        Uint8 r, Uint8 g, Uint8 b, Uint8 a)
  1834 {
  1835     CHECK_RENDERER_MAGIC(renderer, -1);
  1836 
  1837     renderer->r = r;
  1838     renderer->g = g;
  1839     renderer->b = b;
  1840     renderer->a = a;
  1841     return 0;
  1842 }
  1843 
  1844 int
  1845 SDL_GetRenderDrawColor(SDL_Renderer * renderer,
  1846                        Uint8 * r, Uint8 * g, Uint8 * b, Uint8 * a)
  1847 {
  1848     CHECK_RENDERER_MAGIC(renderer, -1);
  1849 
  1850     if (r) {
  1851         *r = renderer->r;
  1852     }
  1853     if (g) {
  1854         *g = renderer->g;
  1855     }
  1856     if (b) {
  1857         *b = renderer->b;
  1858     }
  1859     if (a) {
  1860         *a = renderer->a;
  1861     }
  1862     return 0;
  1863 }
  1864 
  1865 int
  1866 SDL_SetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
  1867 {
  1868     CHECK_RENDERER_MAGIC(renderer, -1);
  1869 
  1870     if (!IsSupportedBlendMode(renderer, blendMode)) {
  1871         return SDL_Unsupported();
  1872     }
  1873     renderer->blendMode = blendMode;
  1874     return 0;
  1875 }
  1876 
  1877 int
  1878 SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode *blendMode)
  1879 {
  1880     CHECK_RENDERER_MAGIC(renderer, -1);
  1881 
  1882     *blendMode = renderer->blendMode;
  1883     return 0;
  1884 }
  1885 
  1886 int
  1887 SDL_RenderClear(SDL_Renderer * renderer)
  1888 {
  1889     CHECK_RENDERER_MAGIC(renderer, -1);
  1890     return QueueCmdClear(renderer);
  1891 }
  1892 
  1893 int
  1894 SDL_RenderDrawPoint(SDL_Renderer * renderer, int x, int y)
  1895 {
  1896     SDL_Point point;
  1897 
  1898     point.x = x;
  1899     point.y = y;
  1900     return SDL_RenderDrawPoints(renderer, &point, 1);
  1901 }
  1902 
  1903 static int
  1904 RenderDrawPointsWithRects(SDL_Renderer * renderer,
  1905                      const SDL_Point * points, int count)
  1906 {
  1907     SDL_FRect *frects;
  1908     int i;
  1909     int status = -1;
  1910 
  1911     frects = SDL_stack_alloc(SDL_FRect, count);
  1912     if (!frects) {
  1913         return SDL_OutOfMemory();
  1914     }
  1915     for (i = 0; i < count; ++i) {
  1916         frects[i].x = points[i].x * renderer->scale.x;
  1917         frects[i].y = points[i].y * renderer->scale.y;
  1918         frects[i].w = renderer->scale.x;
  1919         frects[i].h = renderer->scale.y;
  1920     }
  1921 
  1922     status = QueueCmdFillRects(renderer, frects, count);
  1923 
  1924     SDL_stack_free(frects);
  1925 
  1926     return status;
  1927 }
  1928 
  1929 int
  1930 SDL_RenderDrawPoints(SDL_Renderer * renderer,
  1931                      const SDL_Point * points, int count)
  1932 {
  1933     SDL_FPoint *fpoints;
  1934     int i;
  1935     int status;
  1936 
  1937     CHECK_RENDERER_MAGIC(renderer, -1);
  1938 
  1939     if (!points) {
  1940         return SDL_SetError("SDL_RenderDrawPoints(): Passed NULL points");
  1941     }
  1942     if (count < 1) {
  1943         return 0;
  1944     }
  1945 
  1946     /* Don't draw while we're hidden */
  1947     if (renderer->hidden) {
  1948         return 0;
  1949     }
  1950 
  1951     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
  1952         return RenderDrawPointsWithRects(renderer, points, count);
  1953     }
  1954 
  1955     fpoints = SDL_stack_alloc(SDL_FPoint, count);
  1956     if (!fpoints) {
  1957         return SDL_OutOfMemory();
  1958     }
  1959     for (i = 0; i < count; ++i) {
  1960         fpoints[i].x = points[i].x * renderer->scale.x;
  1961         fpoints[i].y = points[i].y * renderer->scale.y;
  1962     }
  1963 
  1964     status = QueueCmdDrawPoints(renderer, fpoints, count);
  1965 
  1966     SDL_stack_free(fpoints);
  1967 
  1968     return status;
  1969 }
  1970 
  1971 int
  1972 SDL_RenderDrawLine(SDL_Renderer * renderer, int x1, int y1, int x2, int y2)
  1973 {
  1974     SDL_Point points[2];
  1975 
  1976     points[0].x = x1;
  1977     points[0].y = y1;
  1978     points[1].x = x2;
  1979     points[1].y = y2;
  1980     return SDL_RenderDrawLines(renderer, points, 2);
  1981 }
  1982 
  1983 static int
  1984 RenderDrawLinesWithRects(SDL_Renderer * renderer,
  1985                      const SDL_Point * points, int count)
  1986 {
  1987     SDL_FRect *frect;
  1988     SDL_FRect *frects;
  1989     SDL_FPoint fpoints[2];
  1990     int i, nrects = 0;
  1991     int status = 0;
  1992 
  1993     frects = SDL_stack_alloc(SDL_FRect, count-1);
  1994     if (!frects) {
  1995         return SDL_OutOfMemory();
  1996     }
  1997 
  1998     for (i = 0; i < count-1; ++i) {
  1999         if (points[i].x == points[i+1].x) {
  2000             const int minY = SDL_min(points[i].y, points[i+1].y);
  2001             const int maxY = SDL_max(points[i].y, points[i+1].y);
  2002 
  2003             frect = &frects[nrects++];
  2004             frect->x = points[i].x * renderer->scale.x;
  2005             frect->y = minY * renderer->scale.y;
  2006             frect->w = renderer->scale.x;
  2007             frect->h = (maxY - minY + 1) * renderer->scale.y;
  2008         } else if (points[i].y == points[i+1].y) {
  2009             const int minX = SDL_min(points[i].x, points[i+1].x);
  2010             const int maxX = SDL_max(points[i].x, points[i+1].x);
  2011 
  2012             frect = &frects[nrects++];
  2013             frect->x = minX * renderer->scale.x;
  2014             frect->y = points[i].y * renderer->scale.y;
  2015             frect->w = (maxX - minX + 1) * renderer->scale.x;
  2016             frect->h = renderer->scale.y;
  2017         } else {
  2018             /* FIXME: We can't use a rect for this line... */
  2019             fpoints[0].x = points[i].x * renderer->scale.x;
  2020             fpoints[0].y = points[i].y * renderer->scale.y;
  2021             fpoints[1].x = points[i+1].x * renderer->scale.x;
  2022             fpoints[1].y = points[i+1].y * renderer->scale.y;
  2023             status += QueueCmdDrawLines(renderer, fpoints, 2);
  2024         }
  2025     }
  2026 
  2027     status += QueueCmdFillRects(renderer, frects, nrects);
  2028 
  2029     SDL_stack_free(frects);
  2030 
  2031     if (status < 0) {
  2032         status = -1;
  2033     }
  2034     return status;
  2035 }
  2036 
  2037 int
  2038 SDL_RenderDrawLines(SDL_Renderer * renderer,
  2039                     const SDL_Point * points, int count)
  2040 {
  2041     SDL_FPoint *fpoints;
  2042     int i;
  2043     int status;
  2044 
  2045     CHECK_RENDERER_MAGIC(renderer, -1);
  2046 
  2047     if (!points) {
  2048         return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points");
  2049     }
  2050     if (count < 2) {
  2051         return 0;
  2052     }
  2053 
  2054     /* Don't draw while we're hidden */
  2055     if (renderer->hidden) {
  2056         return 0;
  2057     }
  2058 
  2059     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
  2060         return RenderDrawLinesWithRects(renderer, points, count);
  2061     }
  2062 
  2063     fpoints = SDL_stack_alloc(SDL_FPoint, count);
  2064     if (!fpoints) {
  2065         return SDL_OutOfMemory();
  2066     }
  2067     for (i = 0; i < count; ++i) {
  2068         fpoints[i].x = points[i].x * renderer->scale.x;
  2069         fpoints[i].y = points[i].y * renderer->scale.y;
  2070     }
  2071 
  2072     status = QueueCmdDrawLines(renderer, fpoints, count);
  2073 
  2074     SDL_stack_free(fpoints);
  2075 
  2076     return status;
  2077 }
  2078 
  2079 int
  2080 SDL_RenderDrawRect(SDL_Renderer * renderer, const SDL_Rect * rect)
  2081 {
  2082     SDL_Rect full_rect;
  2083     SDL_Point points[5];
  2084 
  2085     CHECK_RENDERER_MAGIC(renderer, -1);
  2086 
  2087     /* If 'rect' == NULL, then outline the whole surface */
  2088     if (!rect) {
  2089         SDL_RenderGetViewport(renderer, &full_rect);
  2090         full_rect.x = 0;
  2091         full_rect.y = 0;
  2092         rect = &full_rect;
  2093     }
  2094 
  2095     points[0].x = rect->x;
  2096     points[0].y = rect->y;
  2097     points[1].x = rect->x+rect->w-1;
  2098     points[1].y = rect->y;
  2099     points[2].x = rect->x+rect->w-1;
  2100     points[2].y = rect->y+rect->h-1;
  2101     points[3].x = rect->x;
  2102     points[3].y = rect->y+rect->h-1;
  2103     points[4].x = rect->x;
  2104     points[4].y = rect->y;
  2105     return SDL_RenderDrawLines(renderer, points, 5);
  2106 }
  2107 
  2108 int
  2109 SDL_RenderDrawRects(SDL_Renderer * renderer,
  2110                     const SDL_Rect * rects, int count)
  2111 {
  2112     int i;
  2113 
  2114     CHECK_RENDERER_MAGIC(renderer, -1);
  2115 
  2116     if (!rects) {
  2117         return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects");
  2118     }
  2119     if (count < 1) {
  2120         return 0;
  2121     }
  2122 
  2123     /* Don't draw while we're hidden */
  2124     if (renderer->hidden) {
  2125         return 0;
  2126     }
  2127 
  2128     for (i = 0; i < count; ++i) {
  2129         if (SDL_RenderDrawRect(renderer, &rects[i]) < 0) {
  2130             return -1;
  2131         }
  2132     }
  2133     return 0;
  2134 }
  2135 
  2136 int
  2137 SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect)
  2138 {
  2139     SDL_Rect full_rect = { 0, 0, 0, 0 };
  2140 
  2141     CHECK_RENDERER_MAGIC(renderer, -1);
  2142 
  2143     /* If 'rect' == NULL, then outline the whole surface */
  2144     if (!rect) {
  2145         SDL_RenderGetViewport(renderer, &full_rect);
  2146         full_rect.x = 0;
  2147         full_rect.y = 0;
  2148         rect = &full_rect;
  2149     }
  2150     return SDL_RenderFillRects(renderer, rect, 1);
  2151 }
  2152 
  2153 int
  2154 SDL_RenderFillRects(SDL_Renderer * renderer,
  2155                     const SDL_Rect * rects, int count)
  2156 {
  2157     SDL_FRect *frects;
  2158     int i;
  2159     int status;
  2160 
  2161     CHECK_RENDERER_MAGIC(renderer, -1);
  2162 
  2163     if (!rects) {
  2164         return SDL_SetError("SDL_RenderFillRects(): Passed NULL rects");
  2165     }
  2166     if (count < 1) {
  2167         return 0;
  2168     }
  2169 
  2170     /* Don't draw while we're hidden */
  2171     if (renderer->hidden) {
  2172         return 0;
  2173     }
  2174 
  2175     frects = SDL_stack_alloc(SDL_FRect, count);
  2176     if (!frects) {
  2177         return SDL_OutOfMemory();
  2178     }
  2179     for (i = 0; i < count; ++i) {
  2180         frects[i].x = rects[i].x * renderer->scale.x;
  2181         frects[i].y = rects[i].y * renderer->scale.y;
  2182         frects[i].w = rects[i].w * renderer->scale.x;
  2183         frects[i].h = rects[i].h * renderer->scale.y;
  2184     }
  2185 
  2186     status = QueueCmdFillRects(renderer, frects, count);
  2187 
  2188     SDL_stack_free(frects);
  2189 
  2190     return status;
  2191 }
  2192 
  2193 int
  2194 SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
  2195                const SDL_Rect * srcrect, const SDL_Rect * dstrect)
  2196 {
  2197     SDL_Rect real_srcrect = { 0, 0, 0, 0 };
  2198     SDL_Rect real_dstrect = { 0, 0, 0, 0 };
  2199     SDL_FRect frect;
  2200 
  2201     CHECK_RENDERER_MAGIC(renderer, -1);
  2202     CHECK_TEXTURE_MAGIC(texture, -1);
  2203 
  2204     if (renderer != texture->renderer) {
  2205         return SDL_SetError("Texture was not created with this renderer");
  2206     }
  2207 
  2208     /* Don't draw while we're hidden */
  2209     if (renderer->hidden) {
  2210         return 0;
  2211     }
  2212 
  2213     real_srcrect.x = 0;
  2214     real_srcrect.y = 0;
  2215     real_srcrect.w = texture->w;
  2216     real_srcrect.h = texture->h;
  2217     if (srcrect) {
  2218         if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
  2219             return 0;
  2220         }
  2221     }
  2222 
  2223     SDL_RenderGetViewport(renderer, &real_dstrect);
  2224     real_dstrect.x = 0;
  2225     real_dstrect.y = 0;
  2226     if (dstrect) {
  2227         if (!SDL_HasIntersection(dstrect, &real_dstrect)) {
  2228             return 0;
  2229         }
  2230         real_dstrect = *dstrect;
  2231     }
  2232 
  2233     if (texture->native) {
  2234         texture = texture->native;
  2235     }
  2236 
  2237     frect.x = real_dstrect.x * renderer->scale.x;
  2238     frect.y = real_dstrect.y * renderer->scale.y;
  2239     frect.w = real_dstrect.w * renderer->scale.x;
  2240     frect.h = real_dstrect.h * renderer->scale.y;
  2241 
  2242     texture->last_command_generation = renderer->render_command_generation;
  2243 
  2244     return QueueCmdCopy(renderer, texture, &real_srcrect, &frect);
  2245 }
  2246 
  2247 
  2248 int
  2249 SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
  2250                const SDL_Rect * srcrect, const SDL_Rect * dstrect,
  2251                const double angle, const SDL_Point *center, const SDL_RendererFlip flip)
  2252 {
  2253     SDL_Rect real_srcrect = { 0, 0, 0, 0 };
  2254     SDL_Rect real_dstrect = { 0, 0, 0, 0 };
  2255     SDL_Point real_center;
  2256     SDL_FRect frect;
  2257     SDL_FPoint fcenter;
  2258 
  2259     if (flip == SDL_FLIP_NONE && (int)(angle/360) == angle/360) { /* fast path when we don't need rotation or flipping */
  2260         return SDL_RenderCopy(renderer, texture, srcrect, dstrect);
  2261     }
  2262 
  2263     CHECK_RENDERER_MAGIC(renderer, -1);
  2264     CHECK_TEXTURE_MAGIC(texture, -1);
  2265 
  2266     if (renderer != texture->renderer) {
  2267         return SDL_SetError("Texture was not created with this renderer");
  2268     }
  2269     if (!renderer->QueueCopyEx) {
  2270         return SDL_SetError("Renderer does not support RenderCopyEx");
  2271     }
  2272 
  2273     /* Don't draw while we're hidden */
  2274     if (renderer->hidden) {
  2275         return 0;
  2276     }
  2277 
  2278     real_srcrect.x = 0;
  2279     real_srcrect.y = 0;
  2280     real_srcrect.w = texture->w;
  2281     real_srcrect.h = texture->h;
  2282     if (srcrect) {
  2283         if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
  2284             return 0;
  2285         }
  2286     }
  2287 
  2288     /* We don't intersect the dstrect with the viewport as RenderCopy does because of potential rotation clipping issues... TODO: should we? */
  2289     if (dstrect) {
  2290         real_dstrect = *dstrect;
  2291     } else {
  2292         SDL_RenderGetViewport(renderer, &real_dstrect);
  2293         real_dstrect.x = 0;
  2294         real_dstrect.y = 0;
  2295     }
  2296 
  2297     if (texture->native) {
  2298         texture = texture->native;
  2299     }
  2300 
  2301     if (center) {
  2302         real_center = *center;
  2303     } else {
  2304         real_center.x = real_dstrect.w/2;
  2305         real_center.y = real_dstrect.h/2;
  2306     }
  2307 
  2308     frect.x = real_dstrect.x * renderer->scale.x;
  2309     frect.y = real_dstrect.y * renderer->scale.y;
  2310     frect.w = real_dstrect.w * renderer->scale.x;
  2311     frect.h = real_dstrect.h * renderer->scale.y;
  2312 
  2313     fcenter.x = real_center.x * renderer->scale.x;
  2314     fcenter.y = real_center.y * renderer->scale.y;
  2315 
  2316     texture->last_command_generation = renderer->render_command_generation;
  2317 
  2318     return QueueCmdCopyEx(renderer, texture, &real_srcrect, &frect, angle, &fcenter, flip);
  2319 }
  2320 
  2321 int
  2322 SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
  2323                      Uint32 format, void * pixels, int pitch)
  2324 {
  2325     SDL_Rect real_rect;
  2326 
  2327     CHECK_RENDERER_MAGIC(renderer, -1);
  2328 
  2329     if (!renderer->RenderReadPixels) {
  2330         return SDL_Unsupported();
  2331     }
  2332 
  2333     FlushRenderCommands(renderer);  /* we need to render before we read the results. */
  2334 
  2335     if (!format) {
  2336         format = SDL_GetWindowPixelFormat(renderer->window);
  2337     }
  2338 
  2339     real_rect.x = renderer->viewport.x;
  2340     real_rect.y = renderer->viewport.y;
  2341     real_rect.w = renderer->viewport.w;
  2342     real_rect.h = renderer->viewport.h;
  2343     if (rect) {
  2344         if (!SDL_IntersectRect(rect, &real_rect, &real_rect)) {
  2345             return 0;
  2346         }
  2347         if (real_rect.y > rect->y) {
  2348             pixels = (Uint8 *)pixels + pitch * (real_rect.y - rect->y);
  2349         }
  2350         if (real_rect.x > rect->x) {
  2351             int bpp = SDL_BYTESPERPIXEL(format);
  2352             pixels = (Uint8 *)pixels + bpp * (real_rect.x - rect->x);
  2353         }
  2354     }
  2355 
  2356     return renderer->RenderReadPixels(renderer, &real_rect,
  2357                                       format, pixels, pitch);
  2358 }
  2359 
  2360 void
  2361 SDL_RenderPresent(SDL_Renderer * renderer)
  2362 {
  2363     CHECK_RENDERER_MAGIC(renderer, );
  2364 
  2365     FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
  2366 
  2367     /* Don't present while we're hidden */
  2368     if (renderer->hidden) {
  2369         return;
  2370     }
  2371     renderer->RenderPresent(renderer);
  2372 }
  2373 
  2374 void
  2375 SDL_DestroyTexture(SDL_Texture * texture)
  2376 {
  2377     SDL_Renderer *renderer;
  2378 
  2379     CHECK_TEXTURE_MAGIC(texture, );
  2380 
  2381     renderer = texture->renderer;
  2382     if (texture == renderer->target) {
  2383         SDL_SetRenderTarget(renderer, NULL);  /* implies command queue flush */
  2384     } else {
  2385         FlushRenderCommandsIfTextureNeeded(texture);
  2386     }
  2387 
  2388     texture->magic = NULL;
  2389 
  2390     if (texture->next) {
  2391         texture->next->prev = texture->prev;
  2392     }
  2393     if (texture->prev) {
  2394         texture->prev->next = texture->next;
  2395     } else {
  2396         renderer->textures = texture->next;
  2397     }
  2398 
  2399     if (texture->native) {
  2400         SDL_DestroyTexture(texture->native);
  2401     }
  2402     if (texture->yuv) {
  2403         SDL_SW_DestroyYUVTexture(texture->yuv);
  2404     }
  2405     SDL_free(texture->pixels);
  2406 
  2407     renderer->DestroyTexture(renderer, texture);
  2408     SDL_free(texture);
  2409 }
  2410 
  2411 void
  2412 SDL_DestroyRenderer(SDL_Renderer * renderer)
  2413 {
  2414     SDL_RenderCommand *cmd;
  2415 
  2416     CHECK_RENDERER_MAGIC(renderer, );
  2417 
  2418     SDL_DelEventWatch(SDL_RendererEventWatch, renderer);
  2419 
  2420     if (renderer->render_commands_tail != NULL) {
  2421         renderer->render_commands_tail->next = renderer->render_commands_pool;
  2422         cmd = renderer->render_commands;
  2423     } else {
  2424         cmd = renderer->render_commands_pool;
  2425     }
  2426 
  2427     renderer->render_commands_pool = NULL;
  2428     renderer->render_commands_tail = NULL;
  2429     renderer->render_commands = NULL;
  2430 
  2431     while (cmd != NULL) {
  2432         SDL_RenderCommand *next = cmd->next;
  2433         SDL_free(cmd);
  2434         cmd = next;
  2435     }
  2436 
  2437     SDL_free(renderer->vertex_data);
  2438 
  2439     /* Free existing textures for this renderer */
  2440     while (renderer->textures) {
  2441         SDL_Texture *tex = renderer->textures; (void) tex;
  2442         SDL_DestroyTexture(renderer->textures);
  2443         SDL_assert(tex != renderer->textures);  /* satisfy static analysis. */
  2444     }
  2445 
  2446     if (renderer->window) {
  2447         SDL_SetWindowData(renderer->window, SDL_WINDOWRENDERDATA, NULL);
  2448     }
  2449 
  2450     /* It's no longer magical... */
  2451     renderer->magic = NULL;
  2452 
  2453     /* Free the target mutex */
  2454     SDL_DestroyMutex(renderer->target_mutex);
  2455     renderer->target_mutex = NULL;
  2456 
  2457     /* Free the renderer instance */
  2458     renderer->DestroyRenderer(renderer);
  2459 }
  2460 
  2461 int SDL_GL_BindTexture(SDL_Texture *texture, float *texw, float *texh)
  2462 {
  2463     SDL_Renderer *renderer;
  2464 
  2465     CHECK_TEXTURE_MAGIC(texture, -1);
  2466     renderer = texture->renderer;
  2467     if (texture->native) {
  2468         return SDL_GL_BindTexture(texture->native, texw, texh);
  2469     } else if (renderer && renderer->GL_BindTexture) {
  2470         FlushRenderCommandsIfTextureNeeded(texture);  /* in case the app is going to mess with it. */
  2471         return renderer->GL_BindTexture(renderer, texture, texw, texh);
  2472     } else {
  2473         return SDL_Unsupported();
  2474     }
  2475 }
  2476 
  2477 int SDL_GL_UnbindTexture(SDL_Texture *texture)
  2478 {
  2479     SDL_Renderer *renderer;
  2480 
  2481     CHECK_TEXTURE_MAGIC(texture, -1);
  2482     renderer = texture->renderer;
  2483     if (texture->native) {
  2484         return SDL_GL_UnbindTexture(texture->native);
  2485     } else if (renderer && renderer->GL_UnbindTexture) {
  2486         FlushRenderCommandsIfTextureNeeded(texture);  /* in case the app messed with it. */
  2487         return renderer->GL_UnbindTexture(renderer, texture);
  2488     }
  2489 
  2490     return SDL_Unsupported();
  2491 }
  2492 
  2493 void *
  2494 SDL_RenderGetMetalLayer(SDL_Renderer * renderer)
  2495 {
  2496     CHECK_RENDERER_MAGIC(renderer, NULL);
  2497 
  2498     if (renderer->GetMetalLayer) {
  2499         FlushRenderCommands(renderer);  /* in case the app is going to mess with it. */
  2500         return renderer->GetMetalLayer(renderer);
  2501     }
  2502     return NULL;
  2503 }
  2504 
  2505 void *
  2506 SDL_RenderGetMetalCommandEncoder(SDL_Renderer * renderer)
  2507 {
  2508     CHECK_RENDERER_MAGIC(renderer, NULL);
  2509 
  2510     if (renderer->GetMetalCommandEncoder) {
  2511         FlushRenderCommands(renderer);  /* in case the app is going to mess with it. */
  2512         return renderer->GetMetalCommandEncoder(renderer);
  2513     }
  2514     return NULL;
  2515 }
  2516 
  2517 static SDL_BlendMode
  2518 SDL_GetShortBlendMode(SDL_BlendMode blendMode)
  2519 {
  2520     if (blendMode == SDL_BLENDMODE_NONE_FULL) {
  2521         return SDL_BLENDMODE_NONE;
  2522     }
  2523     if (blendMode == SDL_BLENDMODE_BLEND_FULL) {
  2524         return SDL_BLENDMODE_BLEND;
  2525     }
  2526     if (blendMode == SDL_BLENDMODE_ADD_FULL) {
  2527         return SDL_BLENDMODE_ADD;
  2528     }
  2529     if (blendMode == SDL_BLENDMODE_MOD_FULL) {
  2530         return SDL_BLENDMODE_MOD;
  2531     }
  2532     return blendMode;
  2533 }
  2534 
  2535 static SDL_BlendMode
  2536 SDL_GetLongBlendMode(SDL_BlendMode blendMode)
  2537 {
  2538     if (blendMode == SDL_BLENDMODE_NONE) {
  2539         return SDL_BLENDMODE_NONE_FULL;
  2540     }
  2541     if (blendMode == SDL_BLENDMODE_BLEND) {
  2542         return SDL_BLENDMODE_BLEND_FULL;
  2543     }
  2544     if (blendMode == SDL_BLENDMODE_ADD) {
  2545         return SDL_BLENDMODE_ADD_FULL;
  2546     }
  2547     if (blendMode == SDL_BLENDMODE_MOD) {
  2548         return SDL_BLENDMODE_MOD_FULL;
  2549     }
  2550     return blendMode;
  2551 }
  2552 
  2553 SDL_BlendMode
  2554 SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor,
  2555                            SDL_BlendOperation colorOperation,
  2556                            SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor,
  2557                            SDL_BlendOperation alphaOperation)
  2558 {
  2559     SDL_BlendMode blendMode = SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation,
  2560                                                     srcAlphaFactor, dstAlphaFactor, alphaOperation);
  2561     return SDL_GetShortBlendMode(blendMode);
  2562 }
  2563 
  2564 SDL_BlendFactor
  2565 SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode)
  2566 {
  2567     blendMode = SDL_GetLongBlendMode(blendMode);
  2568     return (SDL_BlendFactor)(((Uint32)blendMode >> 4) & 0xF);
  2569 }
  2570 
  2571 SDL_BlendFactor
  2572 SDL_GetBlendModeDstColorFactor(SDL_BlendMode blendMode)
  2573 {
  2574     blendMode = SDL_GetLongBlendMode(blendMode);
  2575     return (SDL_BlendFactor)(((Uint32)blendMode >> 8) & 0xF);
  2576 }
  2577 
  2578 SDL_BlendOperation
  2579 SDL_GetBlendModeColorOperation(SDL_BlendMode blendMode)
  2580 {
  2581     blendMode = SDL_GetLongBlendMode(blendMode);
  2582     return (SDL_BlendOperation)(((Uint32)blendMode >> 0) & 0xF);
  2583 }
  2584 
  2585 SDL_BlendFactor
  2586 SDL_GetBlendModeSrcAlphaFactor(SDL_BlendMode blendMode)
  2587 {
  2588     blendMode = SDL_GetLongBlendMode(blendMode);
  2589     return (SDL_BlendFactor)(((Uint32)blendMode >> 20) & 0xF);
  2590 }
  2591 
  2592 SDL_BlendFactor
  2593 SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode)
  2594 {
  2595     blendMode = SDL_GetLongBlendMode(blendMode);
  2596     return (SDL_BlendFactor)(((Uint32)blendMode >> 24) & 0xF);
  2597 }
  2598 
  2599 SDL_BlendOperation
  2600 SDL_GetBlendModeAlphaOperation(SDL_BlendMode blendMode)
  2601 {
  2602     blendMode = SDL_GetLongBlendMode(blendMode);
  2603     return (SDL_BlendOperation)(((Uint32)blendMode >> 16) & 0xF);
  2604 }
  2605 
  2606 /* vi: set ts=4 sw=4 expandtab: */