src/render/SDL_render.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 28 May 2015 12:18:05 -0700
changeset 9673 aea5a2032785
parent 9636 ac8cd6c6d966
child 9762 5c4a85c5b648
child 11074 3d681a44723a
permissions -rw-r--r--
Fixed bug 2367 - Bad mouse motion coordinates with two windows where one has changed logical size

Andreas Ragnerstam

I have two windows where one has a renderer where the logical size has been changed with SDL_RenderSetLogicalSize. When I get SDL_MOUSEMOTION events belonging to the non-scaled window these will have been scaled with the factor of the scaled window, which is not expected.

Adding some printf debugging to SDL_RendererEventWatch of SDL_render.c, where (event->type == SDL_MOUSEMOTION), I found that for every mouse motion SDL_RendererEventWatch is called twice and the event->motion.x and event.motion.y are set twice for the event, once for each renderer where only the last one set will be saved to the event struct. This will work fine if both renderers have the same scale, but otherwise the motion coordinates will be scaled for the renderer belonging to another window than the mouse was moved in.

I guess one solution would be to check that window == renderer->window for SDL_MOUSEMOTION events, similar to what is done for when SDL_WINDOWEVENT events.

I get the same error on both X11 and Windows.
The same problem also exists for SDL_MOUSEBUTTONDOWN and SDL_MOUSEBUTTONUP events.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2015 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     if (!renderer || renderer->magic != &renderer_magic) { \
    37         SDL_SetError("Invalid renderer"); \
    38         return retval; \
    39     }
    40 
    41 #define CHECK_TEXTURE_MAGIC(texture, retval) \
    42     if (!texture || texture->magic != &texture_magic) { \
    43         SDL_SetError("Invalid texture"); \
    44         return retval; \
    45     }
    46 
    47 
    48 #if !SDL_RENDER_DISABLED
    49 static const SDL_RenderDriver *render_drivers[] = {
    50 #if SDL_VIDEO_RENDER_D3D
    51     &D3D_RenderDriver,
    52 #endif
    53 #if SDL_VIDEO_RENDER_D3D11
    54     &D3D11_RenderDriver,
    55 #endif
    56 #if SDL_VIDEO_RENDER_OGL
    57     &GL_RenderDriver,
    58 #endif
    59 #if SDL_VIDEO_RENDER_OGL_ES2
    60     &GLES2_RenderDriver,
    61 #endif
    62 #if SDL_VIDEO_RENDER_OGL_ES
    63     &GLES_RenderDriver,
    64 #endif
    65 #if SDL_VIDEO_RENDER_DIRECTFB
    66     &DirectFB_RenderDriver,
    67 #endif
    68 #if SDL_VIDEO_RENDER_PSP
    69     &PSP_RenderDriver,
    70 #endif
    71     &SW_RenderDriver
    72 };
    73 #endif /* !SDL_RENDER_DISABLED */
    74 
    75 static char renderer_magic;
    76 static char texture_magic;
    77 
    78 static int UpdateLogicalSize(SDL_Renderer *renderer);
    79 
    80 int
    81 SDL_GetNumRenderDrivers(void)
    82 {
    83 #if !SDL_RENDER_DISABLED
    84     return SDL_arraysize(render_drivers);
    85 #else
    86     return 0;
    87 #endif
    88 }
    89 
    90 int
    91 SDL_GetRenderDriverInfo(int index, SDL_RendererInfo * info)
    92 {
    93 #if !SDL_RENDER_DISABLED
    94     if (index < 0 || index >= SDL_GetNumRenderDrivers()) {
    95         return SDL_SetError("index must be in the range of 0 - %d",
    96                             SDL_GetNumRenderDrivers() - 1);
    97     }
    98     *info = render_drivers[index]->info;
    99     return 0;
   100 #else
   101     return SDL_SetError("SDL not built with rendering support");
   102 #endif
   103 }
   104 
   105 static int
   106 SDL_RendererEventWatch(void *userdata, SDL_Event *event)
   107 {
   108     SDL_Renderer *renderer = (SDL_Renderer *)userdata;
   109 
   110     if (event->type == SDL_WINDOWEVENT) {
   111         SDL_Window *window = SDL_GetWindowFromID(event->window.windowID);
   112         if (window == renderer->window) {
   113             if (renderer->WindowEvent) {
   114                 renderer->WindowEvent(renderer, &event->window);
   115             }
   116 
   117             if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
   118                 /* Make sure we're operating on the default render target */
   119                 SDL_Texture *saved_target = SDL_GetRenderTarget(renderer);
   120                 if (saved_target) {
   121                     SDL_SetRenderTarget(renderer, NULL);
   122                 }
   123 
   124                 if (renderer->logical_w) {
   125                     UpdateLogicalSize(renderer);
   126                 } else {
   127                     /* Window was resized, reset viewport */
   128                     int w, h;
   129 
   130                     if (renderer->GetOutputSize) {
   131                         renderer->GetOutputSize(renderer, &w, &h);
   132                     } else {
   133                         SDL_GetWindowSize(renderer->window, &w, &h);
   134                     }
   135 
   136                     if (renderer->target) {
   137                         renderer->viewport_backup.x = 0;
   138                         renderer->viewport_backup.y = 0;
   139                         renderer->viewport_backup.w = w;
   140                         renderer->viewport_backup.h = h;
   141                     } else {
   142                         renderer->viewport.x = 0;
   143                         renderer->viewport.y = 0;
   144                         renderer->viewport.w = w;
   145                         renderer->viewport.h = h;
   146                         renderer->UpdateViewport(renderer);
   147                     }
   148                 }
   149 
   150                 if (saved_target) {
   151                     SDL_SetRenderTarget(renderer, saved_target);
   152                 }
   153             } else if (event->window.event == SDL_WINDOWEVENT_HIDDEN) {
   154                 renderer->hidden = SDL_TRUE;
   155             } else if (event->window.event == SDL_WINDOWEVENT_SHOWN) {
   156                 if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)) {
   157                     renderer->hidden = SDL_FALSE;
   158                 }
   159             } else if (event->window.event == SDL_WINDOWEVENT_MINIMIZED) {
   160                 renderer->hidden = SDL_TRUE;
   161             } else if (event->window.event == SDL_WINDOWEVENT_RESTORED) {
   162                 if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN)) {
   163                     renderer->hidden = SDL_FALSE;
   164                 }
   165             }
   166         }
   167     } else if (event->type == SDL_MOUSEMOTION) {
   168         SDL_Window *window = SDL_GetWindowFromID(event->motion.windowID);
   169         if (renderer->logical_w && window == renderer->window) {
   170             event->motion.x -= renderer->viewport.x;
   171             event->motion.y -= renderer->viewport.y;
   172             event->motion.x = (int)(event->motion.x / renderer->scale.x);
   173             event->motion.y = (int)(event->motion.y / renderer->scale.y);
   174             if (event->motion.xrel > 0) {
   175                 event->motion.xrel = SDL_max(1, (int)(event->motion.xrel / renderer->scale.x));
   176             } else if (event->motion.xrel < 0) {
   177                 event->motion.xrel = SDL_min(-1, (int)(event->motion.xrel / renderer->scale.x));
   178             }
   179             if (event->motion.yrel > 0) {
   180                 event->motion.yrel = SDL_max(1, (int)(event->motion.yrel / renderer->scale.y));
   181             } else if (event->motion.yrel < 0) {
   182                 event->motion.yrel = SDL_min(-1, (int)(event->motion.yrel / renderer->scale.y));
   183             }
   184         }
   185     } else if (event->type == SDL_MOUSEBUTTONDOWN ||
   186                event->type == SDL_MOUSEBUTTONUP) {
   187         SDL_Window *window = SDL_GetWindowFromID(event->button.windowID);
   188         if (renderer->logical_w && window == renderer->window) {
   189             event->button.x -= renderer->viewport.x;
   190             event->button.y -= renderer->viewport.y;
   191             event->button.x = (int)(event->button.x / renderer->scale.x);
   192             event->button.y = (int)(event->button.y / renderer->scale.y);
   193         }
   194     }
   195     return 0;
   196 }
   197 
   198 int
   199 SDL_CreateWindowAndRenderer(int width, int height, Uint32 window_flags,
   200                             SDL_Window **window, SDL_Renderer **renderer)
   201 {
   202     *window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED,
   203                                      SDL_WINDOWPOS_UNDEFINED,
   204                                      width, height, window_flags);
   205     if (!*window) {
   206         *renderer = NULL;
   207         return -1;
   208     }
   209 
   210     *renderer = SDL_CreateRenderer(*window, -1, 0);
   211     if (!*renderer) {
   212         return -1;
   213     }
   214 
   215     return 0;
   216 }
   217 
   218 SDL_Renderer *
   219 SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags)
   220 {
   221 #if !SDL_RENDER_DISABLED
   222     SDL_Renderer *renderer = NULL;
   223     int n = SDL_GetNumRenderDrivers();
   224     const char *hint;
   225 
   226     if (!window) {
   227         SDL_SetError("Invalid window");
   228         return NULL;
   229     }
   230 
   231     if (SDL_GetRenderer(window)) {
   232         SDL_SetError("Renderer already associated with window");
   233         return NULL;
   234     }
   235 
   236     hint = SDL_GetHint(SDL_HINT_RENDER_VSYNC);
   237     if (hint) {
   238         if (*hint == '0') {
   239             flags &= ~SDL_RENDERER_PRESENTVSYNC;
   240         } else {
   241             flags |= SDL_RENDERER_PRESENTVSYNC;
   242         }
   243     }
   244 
   245     if (index < 0) {
   246         hint = SDL_GetHint(SDL_HINT_RENDER_DRIVER);
   247         if (hint) {
   248             for (index = 0; index < n; ++index) {
   249                 const SDL_RenderDriver *driver = render_drivers[index];
   250 
   251                 if (SDL_strcasecmp(hint, driver->info.name) == 0) {
   252                     /* Create a new renderer instance */
   253                     renderer = driver->CreateRenderer(window, flags);
   254                     break;
   255                 }
   256             }
   257         }
   258 
   259         if (!renderer) {
   260             for (index = 0; index < n; ++index) {
   261                 const SDL_RenderDriver *driver = render_drivers[index];
   262 
   263                 if ((driver->info.flags & flags) == flags) {
   264                     /* Create a new renderer instance */
   265                     renderer = driver->CreateRenderer(window, flags);
   266                     if (renderer) {
   267                         /* Yay, we got one! */
   268                         break;
   269                     }
   270                 }
   271             }
   272         }
   273         if (index == n) {
   274             SDL_SetError("Couldn't find matching render driver");
   275             return NULL;
   276         }
   277     } else {
   278         if (index >= SDL_GetNumRenderDrivers()) {
   279             SDL_SetError("index must be -1 or in the range of 0 - %d",
   280                          SDL_GetNumRenderDrivers() - 1);
   281             return NULL;
   282         }
   283         /* Create a new renderer instance */
   284         renderer = render_drivers[index]->CreateRenderer(window, flags);
   285     }
   286 
   287     if (renderer) {
   288         renderer->magic = &renderer_magic;
   289         renderer->window = window;
   290         renderer->scale.x = 1.0f;
   291         renderer->scale.y = 1.0f;
   292 
   293         if (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN|SDL_WINDOW_MINIMIZED)) {
   294             renderer->hidden = SDL_TRUE;
   295         } else {
   296             renderer->hidden = SDL_FALSE;
   297         }
   298 
   299         SDL_SetWindowData(window, SDL_WINDOWRENDERDATA, renderer);
   300 
   301         SDL_RenderSetViewport(renderer, NULL);
   302 
   303         SDL_AddEventWatch(SDL_RendererEventWatch, renderer);
   304 
   305         SDL_LogInfo(SDL_LOG_CATEGORY_RENDER,
   306                     "Created renderer: %s", renderer->info.name);
   307     }
   308     return renderer;
   309 #else
   310     SDL_SetError("SDL not built with rendering support");
   311     return NULL;
   312 #endif
   313 }
   314 
   315 SDL_Renderer *
   316 SDL_CreateSoftwareRenderer(SDL_Surface * surface)
   317 {
   318 #if !SDL_RENDER_DISABLED
   319     SDL_Renderer *renderer;
   320 
   321     renderer = SW_CreateRendererForSurface(surface);
   322 
   323     if (renderer) {
   324         renderer->magic = &renderer_magic;
   325         renderer->scale.x = 1.0f;
   326         renderer->scale.y = 1.0f;
   327 
   328         SDL_RenderSetViewport(renderer, NULL);
   329     }
   330     return renderer;
   331 #else
   332     SDL_SetError("SDL not built with rendering support");
   333     return NULL;
   334 #endif /* !SDL_RENDER_DISABLED */
   335 }
   336 
   337 SDL_Renderer *
   338 SDL_GetRenderer(SDL_Window * window)
   339 {
   340     return (SDL_Renderer *)SDL_GetWindowData(window, SDL_WINDOWRENDERDATA);
   341 }
   342 
   343 int
   344 SDL_GetRendererInfo(SDL_Renderer * renderer, SDL_RendererInfo * info)
   345 {
   346     CHECK_RENDERER_MAGIC(renderer, -1);
   347 
   348     *info = renderer->info;
   349     return 0;
   350 }
   351 
   352 int
   353 SDL_GetRendererOutputSize(SDL_Renderer * renderer, int *w, int *h)
   354 {
   355     CHECK_RENDERER_MAGIC(renderer, -1);
   356 
   357     if (renderer->target) {
   358         return SDL_QueryTexture(renderer->target, NULL, NULL, w, h);
   359     } else if (renderer->GetOutputSize) {
   360         return renderer->GetOutputSize(renderer, w, h);
   361     } else if (renderer->window) {
   362         SDL_GetWindowSize(renderer->window, w, h);
   363         return 0;
   364     } else {
   365         SDL_assert(0 && "This should never happen");
   366         return SDL_SetError("Renderer doesn't support querying output size");
   367     }
   368 }
   369 
   370 static SDL_bool
   371 IsSupportedFormat(SDL_Renderer * renderer, Uint32 format)
   372 {
   373     Uint32 i;
   374 
   375     for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   376         if (renderer->info.texture_formats[i] == format) {
   377             return SDL_TRUE;
   378         }
   379     }
   380     return SDL_FALSE;
   381 }
   382 
   383 static Uint32
   384 GetClosestSupportedFormat(SDL_Renderer * renderer, Uint32 format)
   385 {
   386     Uint32 i;
   387 
   388     if (SDL_ISPIXELFORMAT_FOURCC(format)) {
   389         /* Look for an exact match */
   390         for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   391             if (renderer->info.texture_formats[i] == format) {
   392                 return renderer->info.texture_formats[i];
   393             }
   394         }
   395     } else {
   396         SDL_bool hasAlpha = SDL_ISPIXELFORMAT_ALPHA(format);
   397 
   398         /* We just want to match the first format that has the same channels */
   399         for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   400             if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) &&
   401                 SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == hasAlpha) {
   402                 return renderer->info.texture_formats[i];
   403             }
   404         }
   405     }
   406     return renderer->info.texture_formats[0];
   407 }
   408 
   409 SDL_Texture *
   410 SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h)
   411 {
   412     SDL_Texture *texture;
   413 
   414     CHECK_RENDERER_MAGIC(renderer, NULL);
   415 
   416     if (!format) {
   417         format = renderer->info.texture_formats[0];
   418     }
   419     if (SDL_BYTESPERPIXEL(format) == 0) {
   420         SDL_SetError("Invalid texture format");
   421         return NULL;
   422     }
   423     if (SDL_ISPIXELFORMAT_INDEXED(format)) {
   424         SDL_SetError("Palettized textures are not supported");
   425         return NULL;
   426     }
   427     if (w <= 0 || h <= 0) {
   428         SDL_SetError("Texture dimensions can't be 0");
   429         return NULL;
   430     }
   431     if ((renderer->info.max_texture_width && w > renderer->info.max_texture_width) ||
   432         (renderer->info.max_texture_height && h > renderer->info.max_texture_height)) {
   433         SDL_SetError("Texture dimensions are limited to %dx%d", renderer->info.max_texture_width, renderer->info.max_texture_height);
   434         return NULL;
   435     }
   436     texture = (SDL_Texture *) SDL_calloc(1, sizeof(*texture));
   437     if (!texture) {
   438         SDL_OutOfMemory();
   439         return NULL;
   440     }
   441     texture->magic = &texture_magic;
   442     texture->format = format;
   443     texture->access = access;
   444     texture->w = w;
   445     texture->h = h;
   446     texture->r = 255;
   447     texture->g = 255;
   448     texture->b = 255;
   449     texture->a = 255;
   450     texture->renderer = renderer;
   451     texture->next = renderer->textures;
   452     if (renderer->textures) {
   453         renderer->textures->prev = texture;
   454     }
   455     renderer->textures = texture;
   456 
   457     if (IsSupportedFormat(renderer, format)) {
   458         if (renderer->CreateTexture(renderer, texture) < 0) {
   459             SDL_DestroyTexture(texture);
   460             return 0;
   461         }
   462     } else {
   463         texture->native = SDL_CreateTexture(renderer,
   464                                 GetClosestSupportedFormat(renderer, format),
   465                                 access, w, h);
   466         if (!texture->native) {
   467             SDL_DestroyTexture(texture);
   468             return NULL;
   469         }
   470 
   471         /* Swap textures to have texture before texture->native in the list */
   472         texture->native->next = texture->next;
   473         if (texture->native->next) {
   474             texture->native->next->prev = texture->native;
   475         }
   476         texture->prev = texture->native->prev;
   477         if (texture->prev) {
   478             texture->prev->next = texture;
   479         }
   480         texture->native->prev = texture;
   481         texture->next = texture->native;
   482         renderer->textures = texture;
   483 
   484         if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
   485             texture->yuv = SDL_SW_CreateYUVTexture(format, w, h);
   486             if (!texture->yuv) {
   487                 SDL_DestroyTexture(texture);
   488                 return NULL;
   489             }
   490         } else if (access == SDL_TEXTUREACCESS_STREAMING) {
   491             /* The pitch is 4 byte aligned */
   492             texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3);
   493             texture->pixels = SDL_calloc(1, texture->pitch * h);
   494             if (!texture->pixels) {
   495                 SDL_DestroyTexture(texture);
   496                 return NULL;
   497             }
   498         }
   499     }
   500     return texture;
   501 }
   502 
   503 SDL_Texture *
   504 SDL_CreateTextureFromSurface(SDL_Renderer * renderer, SDL_Surface * surface)
   505 {
   506     const SDL_PixelFormat *fmt;
   507     SDL_bool needAlpha;
   508     Uint32 i;
   509     Uint32 format;
   510     SDL_Texture *texture;
   511 
   512     CHECK_RENDERER_MAGIC(renderer, NULL);
   513 
   514     if (!surface) {
   515         SDL_SetError("SDL_CreateTextureFromSurface() passed NULL surface");
   516         return NULL;
   517     }
   518 
   519     /* See what the best texture format is */
   520     fmt = surface->format;
   521     if (fmt->Amask || SDL_GetColorKey(surface, NULL) == 0) {
   522         needAlpha = SDL_TRUE;
   523     } else {
   524         needAlpha = SDL_FALSE;
   525     }
   526     format = renderer->info.texture_formats[0];
   527     for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   528         if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) &&
   529             SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == needAlpha) {
   530             format = renderer->info.texture_formats[i];
   531             break;
   532         }
   533     }
   534 
   535     texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STATIC,
   536                                 surface->w, surface->h);
   537     if (!texture) {
   538         return NULL;
   539     }
   540 
   541     if (format == surface->format->format) {
   542         if (SDL_MUSTLOCK(surface)) {
   543             SDL_LockSurface(surface);
   544             SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
   545             SDL_UnlockSurface(surface);
   546         } else {
   547             SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
   548         }
   549     } else {
   550         SDL_PixelFormat *dst_fmt;
   551         SDL_Surface *temp = NULL;
   552 
   553         /* Set up a destination surface for the texture update */
   554         dst_fmt = SDL_AllocFormat(format);
   555         if (!dst_fmt) {
   556            SDL_DestroyTexture(texture);
   557            return NULL;
   558         }
   559         temp = SDL_ConvertSurface(surface, dst_fmt, 0);
   560         SDL_FreeFormat(dst_fmt);
   561         if (temp) {
   562             SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
   563             SDL_FreeSurface(temp);
   564         } else {
   565             SDL_DestroyTexture(texture);
   566             return NULL;
   567         }
   568     }
   569 
   570     {
   571         Uint8 r, g, b, a;
   572         SDL_BlendMode blendMode;
   573 
   574         SDL_GetSurfaceColorMod(surface, &r, &g, &b);
   575         SDL_SetTextureColorMod(texture, r, g, b);
   576 
   577         SDL_GetSurfaceAlphaMod(surface, &a);
   578         SDL_SetTextureAlphaMod(texture, a);
   579 
   580         if (SDL_GetColorKey(surface, NULL) == 0) {
   581             /* We converted to a texture with alpha format */
   582             SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
   583         } else {
   584             SDL_GetSurfaceBlendMode(surface, &blendMode);
   585             SDL_SetTextureBlendMode(texture, blendMode);
   586         }
   587     }
   588     return texture;
   589 }
   590 
   591 int
   592 SDL_QueryTexture(SDL_Texture * texture, Uint32 * format, int *access,
   593                  int *w, int *h)
   594 {
   595     CHECK_TEXTURE_MAGIC(texture, -1);
   596 
   597     if (format) {
   598         *format = texture->format;
   599     }
   600     if (access) {
   601         *access = texture->access;
   602     }
   603     if (w) {
   604         *w = texture->w;
   605     }
   606     if (h) {
   607         *h = texture->h;
   608     }
   609     return 0;
   610 }
   611 
   612 int
   613 SDL_SetTextureColorMod(SDL_Texture * texture, Uint8 r, Uint8 g, Uint8 b)
   614 {
   615     SDL_Renderer *renderer;
   616 
   617     CHECK_TEXTURE_MAGIC(texture, -1);
   618 
   619     renderer = texture->renderer;
   620     if (r < 255 || g < 255 || b < 255) {
   621         texture->modMode |= SDL_TEXTUREMODULATE_COLOR;
   622     } else {
   623         texture->modMode &= ~SDL_TEXTUREMODULATE_COLOR;
   624     }
   625     texture->r = r;
   626     texture->g = g;
   627     texture->b = b;
   628     if (texture->native) {
   629         return SDL_SetTextureColorMod(texture->native, r, g, b);
   630     } else if (renderer->SetTextureColorMod) {
   631         return renderer->SetTextureColorMod(renderer, texture);
   632     } else {
   633         return 0;
   634     }
   635 }
   636 
   637 int
   638 SDL_GetTextureColorMod(SDL_Texture * texture, Uint8 * r, Uint8 * g,
   639                        Uint8 * b)
   640 {
   641     CHECK_TEXTURE_MAGIC(texture, -1);
   642 
   643     if (r) {
   644         *r = texture->r;
   645     }
   646     if (g) {
   647         *g = texture->g;
   648     }
   649     if (b) {
   650         *b = texture->b;
   651     }
   652     return 0;
   653 }
   654 
   655 int
   656 SDL_SetTextureAlphaMod(SDL_Texture * texture, Uint8 alpha)
   657 {
   658     SDL_Renderer *renderer;
   659 
   660     CHECK_TEXTURE_MAGIC(texture, -1);
   661 
   662     renderer = texture->renderer;
   663     if (alpha < 255) {
   664         texture->modMode |= SDL_TEXTUREMODULATE_ALPHA;
   665     } else {
   666         texture->modMode &= ~SDL_TEXTUREMODULATE_ALPHA;
   667     }
   668     texture->a = alpha;
   669     if (texture->native) {
   670         return SDL_SetTextureAlphaMod(texture->native, alpha);
   671     } else if (renderer->SetTextureAlphaMod) {
   672         return renderer->SetTextureAlphaMod(renderer, texture);
   673     } else {
   674         return 0;
   675     }
   676 }
   677 
   678 int
   679 SDL_GetTextureAlphaMod(SDL_Texture * texture, Uint8 * alpha)
   680 {
   681     CHECK_TEXTURE_MAGIC(texture, -1);
   682 
   683     if (alpha) {
   684         *alpha = texture->a;
   685     }
   686     return 0;
   687 }
   688 
   689 int
   690 SDL_SetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode blendMode)
   691 {
   692     SDL_Renderer *renderer;
   693 
   694     CHECK_TEXTURE_MAGIC(texture, -1);
   695 
   696     renderer = texture->renderer;
   697     texture->blendMode = blendMode;
   698     if (texture->native) {
   699         return SDL_SetTextureBlendMode(texture->native, blendMode);
   700     } else if (renderer->SetTextureBlendMode) {
   701         return renderer->SetTextureBlendMode(renderer, texture);
   702     } else {
   703         return 0;
   704     }
   705 }
   706 
   707 int
   708 SDL_GetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode *blendMode)
   709 {
   710     CHECK_TEXTURE_MAGIC(texture, -1);
   711 
   712     if (blendMode) {
   713         *blendMode = texture->blendMode;
   714     }
   715     return 0;
   716 }
   717 
   718 static int
   719 SDL_UpdateTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
   720                      const void *pixels, int pitch)
   721 {
   722     SDL_Texture *native = texture->native;
   723     SDL_Rect full_rect;
   724 
   725     if (SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch) < 0) {
   726         return -1;
   727     }
   728 
   729     full_rect.x = 0;
   730     full_rect.y = 0;
   731     full_rect.w = texture->w;
   732     full_rect.h = texture->h;
   733     rect = &full_rect;
   734 
   735     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   736         /* We can lock the texture and copy to it */
   737         void *native_pixels;
   738         int native_pitch;
   739 
   740         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
   741             return -1;
   742         }
   743         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
   744                             rect->w, rect->h, native_pixels, native_pitch);
   745         SDL_UnlockTexture(native);
   746     } else {
   747         /* Use a temporary buffer for updating */
   748         void *temp_pixels;
   749         int temp_pitch;
   750 
   751         temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
   752         temp_pixels = SDL_malloc(rect->h * temp_pitch);
   753         if (!temp_pixels) {
   754             return SDL_OutOfMemory();
   755         }
   756         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
   757                             rect->w, rect->h, temp_pixels, temp_pitch);
   758         SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
   759         SDL_free(temp_pixels);
   760     }
   761     return 0;
   762 }
   763 
   764 static int
   765 SDL_UpdateTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
   766                         const void *pixels, int pitch)
   767 {
   768     SDL_Texture *native = texture->native;
   769 
   770     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   771         /* We can lock the texture and copy to it */
   772         void *native_pixels;
   773         int native_pitch;
   774 
   775         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
   776             return -1;
   777         }
   778         SDL_ConvertPixels(rect->w, rect->h,
   779                           texture->format, pixels, pitch,
   780                           native->format, native_pixels, native_pitch);
   781         SDL_UnlockTexture(native);
   782     } else {
   783         /* Use a temporary buffer for updating */
   784         void *temp_pixels;
   785         int temp_pitch;
   786 
   787         temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
   788         temp_pixels = SDL_malloc(rect->h * temp_pitch);
   789         if (!temp_pixels) {
   790             return SDL_OutOfMemory();
   791         }
   792         SDL_ConvertPixels(rect->w, rect->h,
   793                           texture->format, pixels, pitch,
   794                           native->format, temp_pixels, temp_pitch);
   795         SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
   796         SDL_free(temp_pixels);
   797     }
   798     return 0;
   799 }
   800 
   801 int
   802 SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect,
   803                   const void *pixels, int pitch)
   804 {
   805     SDL_Renderer *renderer;
   806     SDL_Rect full_rect;
   807 
   808     CHECK_TEXTURE_MAGIC(texture, -1);
   809 
   810     if (!pixels) {
   811         return SDL_InvalidParamError("pixels");
   812     }
   813     if (!pitch) {
   814         return SDL_InvalidParamError("pitch");
   815     }
   816 
   817     if (!rect) {
   818         full_rect.x = 0;
   819         full_rect.y = 0;
   820         full_rect.w = texture->w;
   821         full_rect.h = texture->h;
   822         rect = &full_rect;
   823     }
   824 
   825     if ((rect->w == 0) || (rect->h == 0)) {
   826         return 0;  /* nothing to do. */
   827     } else if (texture->yuv) {
   828         return SDL_UpdateTextureYUV(texture, rect, pixels, pitch);
   829     } else if (texture->native) {
   830         return SDL_UpdateTextureNative(texture, rect, pixels, pitch);
   831     } else {
   832         renderer = texture->renderer;
   833         return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
   834     }
   835 }
   836 
   837 static int
   838 SDL_UpdateTextureYUVPlanar(SDL_Texture * texture, const SDL_Rect * rect,
   839                            const Uint8 *Yplane, int Ypitch,
   840                            const Uint8 *Uplane, int Upitch,
   841                            const Uint8 *Vplane, int Vpitch)
   842 {
   843     SDL_Texture *native = texture->native;
   844     SDL_Rect full_rect;
   845 
   846     if (SDL_SW_UpdateYUVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch) < 0) {
   847         return -1;
   848     }
   849 
   850     full_rect.x = 0;
   851     full_rect.y = 0;
   852     full_rect.w = texture->w;
   853     full_rect.h = texture->h;
   854     rect = &full_rect;
   855 
   856     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   857         /* We can lock the texture and copy to it */
   858         void *native_pixels;
   859         int native_pitch;
   860 
   861         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
   862             return -1;
   863         }
   864         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
   865                             rect->w, rect->h, native_pixels, native_pitch);
   866         SDL_UnlockTexture(native);
   867     } else {
   868         /* Use a temporary buffer for updating */
   869         void *temp_pixels;
   870         int temp_pitch;
   871 
   872         temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
   873         temp_pixels = SDL_malloc(rect->h * temp_pitch);
   874         if (!temp_pixels) {
   875             return SDL_OutOfMemory();
   876         }
   877         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
   878                             rect->w, rect->h, temp_pixels, temp_pitch);
   879         SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
   880         SDL_free(temp_pixels);
   881     }
   882     return 0;
   883 }
   884 
   885 int SDL_UpdateYUVTexture(SDL_Texture * texture, const SDL_Rect * rect,
   886                          const Uint8 *Yplane, int Ypitch,
   887                          const Uint8 *Uplane, int Upitch,
   888                          const Uint8 *Vplane, int Vpitch)
   889 {
   890     SDL_Renderer *renderer;
   891     SDL_Rect full_rect;
   892 
   893     CHECK_TEXTURE_MAGIC(texture, -1);
   894 
   895     if (!Yplane) {
   896         return SDL_InvalidParamError("Yplane");
   897     }
   898     if (!Ypitch) {
   899         return SDL_InvalidParamError("Ypitch");
   900     }
   901     if (!Uplane) {
   902         return SDL_InvalidParamError("Uplane");
   903     }
   904     if (!Upitch) {
   905         return SDL_InvalidParamError("Upitch");
   906     }
   907     if (!Vplane) {
   908         return SDL_InvalidParamError("Vplane");
   909     }
   910     if (!Vpitch) {
   911         return SDL_InvalidParamError("Vpitch");
   912     }
   913 
   914     if (texture->format != SDL_PIXELFORMAT_YV12 &&
   915         texture->format != SDL_PIXELFORMAT_IYUV) {
   916         return SDL_SetError("Texture format must by YV12 or IYUV");
   917     }
   918 
   919     if (!rect) {
   920         full_rect.x = 0;
   921         full_rect.y = 0;
   922         full_rect.w = texture->w;
   923         full_rect.h = texture->h;
   924         rect = &full_rect;
   925     }
   926 
   927     if (texture->yuv) {
   928         return SDL_UpdateTextureYUVPlanar(texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
   929     } else {
   930         SDL_assert(!texture->native);
   931         renderer = texture->renderer;
   932         SDL_assert(renderer->UpdateTextureYUV);
   933         if (renderer->UpdateTextureYUV) {
   934             return renderer->UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
   935         } else {
   936             return SDL_Unsupported();
   937         }
   938     }
   939 }
   940 
   941 static int
   942 SDL_LockTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
   943                    void **pixels, int *pitch)
   944 {
   945     return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch);
   946 }
   947 
   948 static int
   949 SDL_LockTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
   950                       void **pixels, int *pitch)
   951 {
   952     texture->locked_rect = *rect;
   953     *pixels = (void *) ((Uint8 *) texture->pixels +
   954                         rect->y * texture->pitch +
   955                         rect->x * SDL_BYTESPERPIXEL(texture->format));
   956     *pitch = texture->pitch;
   957     return 0;
   958 }
   959 
   960 int
   961 SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect,
   962                 void **pixels, int *pitch)
   963 {
   964     SDL_Renderer *renderer;
   965     SDL_Rect full_rect;
   966 
   967     CHECK_TEXTURE_MAGIC(texture, -1);
   968 
   969     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
   970         return SDL_SetError("SDL_LockTexture(): texture must be streaming");
   971     }
   972 
   973     if (!rect) {
   974         full_rect.x = 0;
   975         full_rect.y = 0;
   976         full_rect.w = texture->w;
   977         full_rect.h = texture->h;
   978         rect = &full_rect;
   979     }
   980 
   981     if (texture->yuv) {
   982         return SDL_LockTextureYUV(texture, rect, pixels, pitch);
   983     } else if (texture->native) {
   984         return SDL_LockTextureNative(texture, rect, pixels, pitch);
   985     } else {
   986         renderer = texture->renderer;
   987         return renderer->LockTexture(renderer, texture, rect, pixels, pitch);
   988     }
   989 }
   990 
   991 static void
   992 SDL_UnlockTextureYUV(SDL_Texture * texture)
   993 {
   994     SDL_Texture *native = texture->native;
   995     void *native_pixels = NULL;
   996     int native_pitch = 0;
   997     SDL_Rect rect;
   998 
   999     rect.x = 0;
  1000     rect.y = 0;
  1001     rect.w = texture->w;
  1002     rect.h = texture->h;
  1003 
  1004     if (SDL_LockTexture(native, &rect, &native_pixels, &native_pitch) < 0) {
  1005         return;
  1006     }
  1007     SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format,
  1008                         rect.w, rect.h, native_pixels, native_pitch);
  1009     SDL_UnlockTexture(native);
  1010 }
  1011 
  1012 static void
  1013 SDL_UnlockTextureNative(SDL_Texture * texture)
  1014 {
  1015     SDL_Texture *native = texture->native;
  1016     void *native_pixels = NULL;
  1017     int native_pitch = 0;
  1018     const SDL_Rect *rect = &texture->locked_rect;
  1019     const void* pixels = (void *) ((Uint8 *) texture->pixels +
  1020                         rect->y * texture->pitch +
  1021                         rect->x * SDL_BYTESPERPIXEL(texture->format));
  1022     int pitch = texture->pitch;
  1023 
  1024     if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
  1025         return;
  1026     }
  1027     SDL_ConvertPixels(rect->w, rect->h,
  1028                       texture->format, pixels, pitch,
  1029                       native->format, native_pixels, native_pitch);
  1030     SDL_UnlockTexture(native);
  1031 }
  1032 
  1033 void
  1034 SDL_UnlockTexture(SDL_Texture * texture)
  1035 {
  1036     SDL_Renderer *renderer;
  1037 
  1038     CHECK_TEXTURE_MAGIC(texture, );
  1039 
  1040     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
  1041         return;
  1042     }
  1043     if (texture->yuv) {
  1044         SDL_UnlockTextureYUV(texture);
  1045     } else if (texture->native) {
  1046         SDL_UnlockTextureNative(texture);
  1047     } else {
  1048         renderer = texture->renderer;
  1049         renderer->UnlockTexture(renderer, texture);
  1050     }
  1051 }
  1052 
  1053 SDL_bool
  1054 SDL_RenderTargetSupported(SDL_Renderer *renderer)
  1055 {
  1056     if (!renderer || !renderer->SetRenderTarget) {
  1057         return SDL_FALSE;
  1058     }
  1059     return (renderer->info.flags & SDL_RENDERER_TARGETTEXTURE) != 0;
  1060 }
  1061 
  1062 int
  1063 SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
  1064 {
  1065     if (!SDL_RenderTargetSupported(renderer)) {
  1066         return SDL_Unsupported();
  1067     }
  1068     if (texture == renderer->target) {
  1069         /* Nothing to do! */
  1070         return 0;
  1071     }
  1072 
  1073     /* texture == NULL is valid and means reset the target to the window */
  1074     if (texture) {
  1075         CHECK_TEXTURE_MAGIC(texture, -1);
  1076         if (renderer != texture->renderer) {
  1077             return SDL_SetError("Texture was not created with this renderer");
  1078         }
  1079         if (texture->access != SDL_TEXTUREACCESS_TARGET) {
  1080             return SDL_SetError("Texture not created with SDL_TEXTUREACCESS_TARGET");
  1081         }
  1082         if (texture->native) {
  1083             /* Always render to the native texture */
  1084             texture = texture->native;
  1085         }
  1086     }
  1087 
  1088     if (texture && !renderer->target) {
  1089         /* Make a backup of the viewport */
  1090         renderer->viewport_backup = renderer->viewport;
  1091         renderer->clip_rect_backup = renderer->clip_rect;
  1092         renderer->clipping_enabled_backup = renderer->clipping_enabled;
  1093         renderer->scale_backup = renderer->scale;
  1094         renderer->logical_w_backup = renderer->logical_w;
  1095         renderer->logical_h_backup = renderer->logical_h;
  1096     }
  1097     renderer->target = texture;
  1098 
  1099     if (renderer->SetRenderTarget(renderer, texture) < 0) {
  1100         return -1;
  1101     }
  1102 
  1103     if (texture) {
  1104         renderer->viewport.x = 0;
  1105         renderer->viewport.y = 0;
  1106         renderer->viewport.w = texture->w;
  1107         renderer->viewport.h = texture->h;
  1108         renderer->scale.x = 1.0f;
  1109         renderer->scale.y = 1.0f;
  1110         renderer->logical_w = texture->w;
  1111         renderer->logical_h = texture->h;
  1112     } else {
  1113         renderer->viewport = renderer->viewport_backup;
  1114         renderer->clip_rect = renderer->clip_rect_backup;
  1115         renderer->clipping_enabled = renderer->clipping_enabled_backup;
  1116         renderer->scale = renderer->scale_backup;
  1117         renderer->logical_w = renderer->logical_w_backup;
  1118         renderer->logical_h = renderer->logical_h_backup;
  1119     }
  1120     if (renderer->UpdateViewport(renderer) < 0) {
  1121         return -1;
  1122     }
  1123     if (renderer->UpdateClipRect(renderer) < 0) {
  1124         return -1;
  1125     }
  1126 
  1127     /* All set! */
  1128     return 0;
  1129 }
  1130 
  1131 SDL_Texture *
  1132 SDL_GetRenderTarget(SDL_Renderer *renderer)
  1133 {
  1134     return renderer->target;
  1135 }
  1136 
  1137 static int
  1138 UpdateLogicalSize(SDL_Renderer *renderer)
  1139 {
  1140     int w = 1, h = 1;
  1141     float want_aspect;
  1142     float real_aspect;
  1143     float scale;
  1144     SDL_Rect viewport;
  1145 
  1146     if (SDL_GetRendererOutputSize(renderer, &w, &h) < 0) {
  1147         return -1;
  1148     }
  1149 
  1150     want_aspect = (float)renderer->logical_w / renderer->logical_h;
  1151     real_aspect = (float)w / h;
  1152 
  1153     /* Clear the scale because we're setting viewport in output coordinates */
  1154     SDL_RenderSetScale(renderer, 1.0f, 1.0f);
  1155 
  1156     if (SDL_fabs(want_aspect-real_aspect) < 0.0001) {
  1157         /* The aspect ratios are the same, just scale appropriately */
  1158         scale = (float)w / renderer->logical_w;
  1159         SDL_RenderSetViewport(renderer, NULL);
  1160     } else if (want_aspect > real_aspect) {
  1161         /* We want a wider aspect ratio than is available - letterbox it */
  1162         scale = (float)w / renderer->logical_w;
  1163         viewport.x = 0;
  1164         viewport.w = w;
  1165         viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
  1166         viewport.y = (h - viewport.h) / 2;
  1167         SDL_RenderSetViewport(renderer, &viewport);
  1168     } else {
  1169         /* We want a narrower aspect ratio than is available - use side-bars */
  1170         scale = (float)h / renderer->logical_h;
  1171         viewport.y = 0;
  1172         viewport.h = h;
  1173         viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
  1174         viewport.x = (w - viewport.w) / 2;
  1175         SDL_RenderSetViewport(renderer, &viewport);
  1176     }
  1177 
  1178     /* Set the new scale */
  1179     SDL_RenderSetScale(renderer, scale, scale);
  1180 
  1181     return 0;
  1182 }
  1183 
  1184 int
  1185 SDL_RenderSetLogicalSize(SDL_Renderer * renderer, int w, int h)
  1186 {
  1187     CHECK_RENDERER_MAGIC(renderer, -1);
  1188 
  1189     if (!w || !h) {
  1190         /* Clear any previous logical resolution */
  1191         renderer->logical_w = 0;
  1192         renderer->logical_h = 0;
  1193         SDL_RenderSetViewport(renderer, NULL);
  1194         SDL_RenderSetScale(renderer, 1.0f, 1.0f);
  1195         return 0;
  1196     }
  1197 
  1198     renderer->logical_w = w;
  1199     renderer->logical_h = h;
  1200 
  1201     return UpdateLogicalSize(renderer);
  1202 }
  1203 
  1204 void
  1205 SDL_RenderGetLogicalSize(SDL_Renderer * renderer, int *w, int *h)
  1206 {
  1207     CHECK_RENDERER_MAGIC(renderer, );
  1208 
  1209     if (w) {
  1210         *w = renderer->logical_w;
  1211     }
  1212     if (h) {
  1213         *h = renderer->logical_h;
  1214     }
  1215 }
  1216 
  1217 int
  1218 SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect)
  1219 {
  1220     CHECK_RENDERER_MAGIC(renderer, -1);
  1221 
  1222     if (rect) {
  1223         renderer->viewport.x = (int)SDL_floor(rect->x * renderer->scale.x);
  1224         renderer->viewport.y = (int)SDL_floor(rect->y * renderer->scale.y);
  1225         renderer->viewport.w = (int)SDL_ceil(rect->w * renderer->scale.x);
  1226         renderer->viewport.h = (int)SDL_ceil(rect->h * renderer->scale.y);
  1227     } else {
  1228         renderer->viewport.x = 0;
  1229         renderer->viewport.y = 0;
  1230         if (SDL_GetRendererOutputSize(renderer, &renderer->viewport.w, &renderer->viewport.h) < 0) {
  1231             return -1;
  1232         }
  1233     }
  1234     return renderer->UpdateViewport(renderer);
  1235 }
  1236 
  1237 void
  1238 SDL_RenderGetViewport(SDL_Renderer * renderer, SDL_Rect * rect)
  1239 {
  1240     CHECK_RENDERER_MAGIC(renderer, );
  1241 
  1242     if (rect) {
  1243         rect->x = (int)(renderer->viewport.x / renderer->scale.x);
  1244         rect->y = (int)(renderer->viewport.y / renderer->scale.y);
  1245         rect->w = (int)(renderer->viewport.w / renderer->scale.x);
  1246         rect->h = (int)(renderer->viewport.h / renderer->scale.y);
  1247     }
  1248 }
  1249 
  1250 int
  1251 SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect)
  1252 {
  1253     CHECK_RENDERER_MAGIC(renderer, -1)
  1254 
  1255     if (rect) {
  1256         renderer->clipping_enabled = SDL_TRUE;
  1257         renderer->clip_rect.x = (int)SDL_floor(rect->x * renderer->scale.x);
  1258         renderer->clip_rect.y = (int)SDL_floor(rect->y * renderer->scale.y);
  1259         renderer->clip_rect.w = (int)SDL_ceil(rect->w * renderer->scale.x);
  1260         renderer->clip_rect.h = (int)SDL_ceil(rect->h * renderer->scale.y);
  1261     } else {
  1262         renderer->clipping_enabled = SDL_FALSE;
  1263         SDL_zero(renderer->clip_rect);
  1264     }
  1265     return renderer->UpdateClipRect(renderer);
  1266 }
  1267 
  1268 void
  1269 SDL_RenderGetClipRect(SDL_Renderer * renderer, SDL_Rect * rect)
  1270 {
  1271     CHECK_RENDERER_MAGIC(renderer, )
  1272 
  1273     if (rect) {
  1274         rect->x = (int)(renderer->clip_rect.x / renderer->scale.x);
  1275         rect->y = (int)(renderer->clip_rect.y / renderer->scale.y);
  1276         rect->w = (int)(renderer->clip_rect.w / renderer->scale.x);
  1277         rect->h = (int)(renderer->clip_rect.h / renderer->scale.y);
  1278     }
  1279 }
  1280 
  1281 SDL_bool
  1282 SDL_RenderIsClipEnabled(SDL_Renderer * renderer)
  1283 {
  1284     CHECK_RENDERER_MAGIC(renderer, SDL_FALSE)
  1285     return renderer->clipping_enabled;
  1286 }
  1287 
  1288 int
  1289 SDL_RenderSetScale(SDL_Renderer * renderer, float scaleX, float scaleY)
  1290 {
  1291     CHECK_RENDERER_MAGIC(renderer, -1);
  1292 
  1293     renderer->scale.x = scaleX;
  1294     renderer->scale.y = scaleY;
  1295     return 0;
  1296 }
  1297 
  1298 void
  1299 SDL_RenderGetScale(SDL_Renderer * renderer, float *scaleX, float *scaleY)
  1300 {
  1301     CHECK_RENDERER_MAGIC(renderer, );
  1302 
  1303     if (scaleX) {
  1304         *scaleX = renderer->scale.x;
  1305     }
  1306     if (scaleY) {
  1307         *scaleY = renderer->scale.y;
  1308     }
  1309 }
  1310 
  1311 int
  1312 SDL_SetRenderDrawColor(SDL_Renderer * renderer,
  1313                        Uint8 r, Uint8 g, Uint8 b, Uint8 a)
  1314 {
  1315     CHECK_RENDERER_MAGIC(renderer, -1);
  1316 
  1317     renderer->r = r;
  1318     renderer->g = g;
  1319     renderer->b = b;
  1320     renderer->a = a;
  1321     return 0;
  1322 }
  1323 
  1324 int
  1325 SDL_GetRenderDrawColor(SDL_Renderer * renderer,
  1326                        Uint8 * r, Uint8 * g, Uint8 * b, Uint8 * a)
  1327 {
  1328     CHECK_RENDERER_MAGIC(renderer, -1);
  1329 
  1330     if (r) {
  1331         *r = renderer->r;
  1332     }
  1333     if (g) {
  1334         *g = renderer->g;
  1335     }
  1336     if (b) {
  1337         *b = renderer->b;
  1338     }
  1339     if (a) {
  1340         *a = renderer->a;
  1341     }
  1342     return 0;
  1343 }
  1344 
  1345 int
  1346 SDL_SetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
  1347 {
  1348     CHECK_RENDERER_MAGIC(renderer, -1);
  1349 
  1350     renderer->blendMode = blendMode;
  1351     return 0;
  1352 }
  1353 
  1354 int
  1355 SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode *blendMode)
  1356 {
  1357     CHECK_RENDERER_MAGIC(renderer, -1);
  1358 
  1359     *blendMode = renderer->blendMode;
  1360     return 0;
  1361 }
  1362 
  1363 int
  1364 SDL_RenderClear(SDL_Renderer * renderer)
  1365 {
  1366     CHECK_RENDERER_MAGIC(renderer, -1);
  1367 
  1368     /* Don't draw while we're hidden */
  1369     if (renderer->hidden) {
  1370         return 0;
  1371     }
  1372     return renderer->RenderClear(renderer);
  1373 }
  1374 
  1375 int
  1376 SDL_RenderDrawPoint(SDL_Renderer * renderer, int x, int y)
  1377 {
  1378     SDL_Point point;
  1379 
  1380     point.x = x;
  1381     point.y = y;
  1382     return SDL_RenderDrawPoints(renderer, &point, 1);
  1383 }
  1384 
  1385 static int
  1386 RenderDrawPointsWithRects(SDL_Renderer * renderer,
  1387                      const SDL_Point * points, int count)
  1388 {
  1389     SDL_FRect *frects;
  1390     int i;
  1391     int status;
  1392 
  1393     frects = SDL_stack_alloc(SDL_FRect, count);
  1394     if (!frects) {
  1395         return SDL_OutOfMemory();
  1396     }
  1397     for (i = 0; i < count; ++i) {
  1398         frects[i].x = points[i].x * renderer->scale.x;
  1399         frects[i].y = points[i].y * renderer->scale.y;
  1400         frects[i].w = renderer->scale.x;
  1401         frects[i].h = renderer->scale.y;
  1402     }
  1403 
  1404     status = renderer->RenderFillRects(renderer, frects, count);
  1405 
  1406     SDL_stack_free(frects);
  1407 
  1408     return status;
  1409 }
  1410 
  1411 int
  1412 SDL_RenderDrawPoints(SDL_Renderer * renderer,
  1413                      const SDL_Point * points, int count)
  1414 {
  1415     SDL_FPoint *fpoints;
  1416     int i;
  1417     int status;
  1418 
  1419     CHECK_RENDERER_MAGIC(renderer, -1);
  1420 
  1421     if (!points) {
  1422         return SDL_SetError("SDL_RenderDrawPoints(): Passed NULL points");
  1423     }
  1424     if (count < 1) {
  1425         return 0;
  1426     }
  1427     /* Don't draw while we're hidden */
  1428     if (renderer->hidden) {
  1429         return 0;
  1430     }
  1431 
  1432     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
  1433         return RenderDrawPointsWithRects(renderer, points, count);
  1434     }
  1435 
  1436     fpoints = SDL_stack_alloc(SDL_FPoint, count);
  1437     if (!fpoints) {
  1438         return SDL_OutOfMemory();
  1439     }
  1440     for (i = 0; i < count; ++i) {
  1441         fpoints[i].x = points[i].x * renderer->scale.x;
  1442         fpoints[i].y = points[i].y * renderer->scale.y;
  1443     }
  1444 
  1445     status = renderer->RenderDrawPoints(renderer, fpoints, count);
  1446 
  1447     SDL_stack_free(fpoints);
  1448 
  1449     return status;
  1450 }
  1451 
  1452 int
  1453 SDL_RenderDrawLine(SDL_Renderer * renderer, int x1, int y1, int x2, int y2)
  1454 {
  1455     SDL_Point points[2];
  1456 
  1457     points[0].x = x1;
  1458     points[0].y = y1;
  1459     points[1].x = x2;
  1460     points[1].y = y2;
  1461     return SDL_RenderDrawLines(renderer, points, 2);
  1462 }
  1463 
  1464 static int
  1465 RenderDrawLinesWithRects(SDL_Renderer * renderer,
  1466                      const SDL_Point * points, int count)
  1467 {
  1468     SDL_FRect *frect;
  1469     SDL_FRect *frects;
  1470     SDL_FPoint fpoints[2];
  1471     int i, nrects;
  1472     int status;
  1473 
  1474     frects = SDL_stack_alloc(SDL_FRect, count-1);
  1475     if (!frects) {
  1476         return SDL_OutOfMemory();
  1477     }
  1478 
  1479     status = 0;
  1480     nrects = 0;
  1481     for (i = 0; i < count-1; ++i) {
  1482         if (points[i].x == points[i+1].x) {
  1483             int minY = SDL_min(points[i].y, points[i+1].y);
  1484             int maxY = SDL_max(points[i].y, points[i+1].y);
  1485 
  1486             frect = &frects[nrects++];
  1487             frect->x = points[i].x * renderer->scale.x;
  1488             frect->y = minY * renderer->scale.y;
  1489             frect->w = renderer->scale.x;
  1490             frect->h = (maxY - minY + 1) * renderer->scale.y;
  1491         } else if (points[i].y == points[i+1].y) {
  1492             int minX = SDL_min(points[i].x, points[i+1].x);
  1493             int maxX = SDL_max(points[i].x, points[i+1].x);
  1494 
  1495             frect = &frects[nrects++];
  1496             frect->x = minX * renderer->scale.x;
  1497             frect->y = points[i].y * renderer->scale.y;
  1498             frect->w = (maxX - minX + 1) * renderer->scale.x;
  1499             frect->h = renderer->scale.y;
  1500         } else {
  1501             /* FIXME: We can't use a rect for this line... */
  1502             fpoints[0].x = points[i].x * renderer->scale.x;
  1503             fpoints[0].y = points[i].y * renderer->scale.y;
  1504             fpoints[1].x = points[i+1].x * renderer->scale.x;
  1505             fpoints[1].y = points[i+1].y * renderer->scale.y;
  1506             status += renderer->RenderDrawLines(renderer, fpoints, 2);
  1507         }
  1508     }
  1509 
  1510     status += renderer->RenderFillRects(renderer, frects, nrects);
  1511 
  1512     SDL_stack_free(frects);
  1513 
  1514     if (status < 0) {
  1515         status = -1;
  1516     }
  1517     return status;
  1518 }
  1519 
  1520 int
  1521 SDL_RenderDrawLines(SDL_Renderer * renderer,
  1522                     const SDL_Point * points, int count)
  1523 {
  1524     SDL_FPoint *fpoints;
  1525     int i;
  1526     int status;
  1527 
  1528     CHECK_RENDERER_MAGIC(renderer, -1);
  1529 
  1530     if (!points) {
  1531         return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points");
  1532     }
  1533     if (count < 2) {
  1534         return 0;
  1535     }
  1536     /* Don't draw while we're hidden */
  1537     if (renderer->hidden) {
  1538         return 0;
  1539     }
  1540 
  1541     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
  1542         return RenderDrawLinesWithRects(renderer, points, count);
  1543     }
  1544 
  1545     fpoints = SDL_stack_alloc(SDL_FPoint, count);
  1546     if (!fpoints) {
  1547         return SDL_OutOfMemory();
  1548     }
  1549     for (i = 0; i < count; ++i) {
  1550         fpoints[i].x = points[i].x * renderer->scale.x;
  1551         fpoints[i].y = points[i].y * renderer->scale.y;
  1552     }
  1553 
  1554     status = renderer->RenderDrawLines(renderer, fpoints, count);
  1555 
  1556     SDL_stack_free(fpoints);
  1557 
  1558     return status;
  1559 }
  1560 
  1561 int
  1562 SDL_RenderDrawRect(SDL_Renderer * renderer, const SDL_Rect * rect)
  1563 {
  1564     SDL_Rect full_rect;
  1565     SDL_Point points[5];
  1566 
  1567     CHECK_RENDERER_MAGIC(renderer, -1);
  1568 
  1569     /* If 'rect' == NULL, then outline the whole surface */
  1570     if (!rect) {
  1571         SDL_RenderGetViewport(renderer, &full_rect);
  1572         full_rect.x = 0;
  1573         full_rect.y = 0;
  1574         rect = &full_rect;
  1575     }
  1576 
  1577     points[0].x = rect->x;
  1578     points[0].y = rect->y;
  1579     points[1].x = rect->x+rect->w-1;
  1580     points[1].y = rect->y;
  1581     points[2].x = rect->x+rect->w-1;
  1582     points[2].y = rect->y+rect->h-1;
  1583     points[3].x = rect->x;
  1584     points[3].y = rect->y+rect->h-1;
  1585     points[4].x = rect->x;
  1586     points[4].y = rect->y;
  1587     return SDL_RenderDrawLines(renderer, points, 5);
  1588 }
  1589 
  1590 int
  1591 SDL_RenderDrawRects(SDL_Renderer * renderer,
  1592                     const SDL_Rect * rects, int count)
  1593 {
  1594     int i;
  1595 
  1596     CHECK_RENDERER_MAGIC(renderer, -1);
  1597 
  1598     if (!rects) {
  1599         return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects");
  1600     }
  1601     if (count < 1) {
  1602         return 0;
  1603     }
  1604 
  1605     /* Don't draw while we're hidden */
  1606     if (renderer->hidden) {
  1607         return 0;
  1608     }
  1609     for (i = 0; i < count; ++i) {
  1610         if (SDL_RenderDrawRect(renderer, &rects[i]) < 0) {
  1611             return -1;
  1612         }
  1613     }
  1614     return 0;
  1615 }
  1616 
  1617 int
  1618 SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect)
  1619 {
  1620     SDL_Rect full_rect = { 0, 0, 0, 0 };
  1621 
  1622     CHECK_RENDERER_MAGIC(renderer, -1);
  1623 
  1624     /* If 'rect' == NULL, then outline the whole surface */
  1625     if (!rect) {
  1626         SDL_RenderGetViewport(renderer, &full_rect);
  1627         full_rect.x = 0;
  1628         full_rect.y = 0;
  1629         rect = &full_rect;
  1630     }
  1631     return SDL_RenderFillRects(renderer, rect, 1);
  1632 }
  1633 
  1634 int
  1635 SDL_RenderFillRects(SDL_Renderer * renderer,
  1636                     const SDL_Rect * rects, int count)
  1637 {
  1638     SDL_FRect *frects;
  1639     int i;
  1640     int status;
  1641 
  1642     CHECK_RENDERER_MAGIC(renderer, -1);
  1643 
  1644     if (!rects) {
  1645         return SDL_SetError("SDL_RenderFillRects(): Passed NULL rects");
  1646     }
  1647     if (count < 1) {
  1648         return 0;
  1649     }
  1650     /* Don't draw while we're hidden */
  1651     if (renderer->hidden) {
  1652         return 0;
  1653     }
  1654 
  1655     frects = SDL_stack_alloc(SDL_FRect, count);
  1656     if (!frects) {
  1657         return SDL_OutOfMemory();
  1658     }
  1659     for (i = 0; i < count; ++i) {
  1660         frects[i].x = rects[i].x * renderer->scale.x;
  1661         frects[i].y = rects[i].y * renderer->scale.y;
  1662         frects[i].w = rects[i].w * renderer->scale.x;
  1663         frects[i].h = rects[i].h * renderer->scale.y;
  1664     }
  1665 
  1666     status = renderer->RenderFillRects(renderer, frects, count);
  1667 
  1668     SDL_stack_free(frects);
  1669 
  1670     return status;
  1671 }
  1672 
  1673 int
  1674 SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
  1675                const SDL_Rect * srcrect, const SDL_Rect * dstrect)
  1676 {
  1677     SDL_Rect real_srcrect = { 0, 0, 0, 0 };
  1678     SDL_Rect real_dstrect = { 0, 0, 0, 0 };
  1679     SDL_FRect frect;
  1680 
  1681     CHECK_RENDERER_MAGIC(renderer, -1);
  1682     CHECK_TEXTURE_MAGIC(texture, -1);
  1683 
  1684     if (renderer != texture->renderer) {
  1685         return SDL_SetError("Texture was not created with this renderer");
  1686     }
  1687 
  1688     real_srcrect.x = 0;
  1689     real_srcrect.y = 0;
  1690     real_srcrect.w = texture->w;
  1691     real_srcrect.h = texture->h;
  1692     if (srcrect) {
  1693         if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
  1694             return 0;
  1695         }
  1696     }
  1697 
  1698     SDL_RenderGetViewport(renderer, &real_dstrect);
  1699     real_dstrect.x = 0;
  1700     real_dstrect.y = 0;
  1701     if (dstrect) {
  1702         if (!SDL_HasIntersection(dstrect, &real_dstrect)) {
  1703             return 0;
  1704         }
  1705         real_dstrect = *dstrect;
  1706     }
  1707 
  1708     if (texture->native) {
  1709         texture = texture->native;
  1710     }
  1711 
  1712     /* Don't draw while we're hidden */
  1713     if (renderer->hidden) {
  1714         return 0;
  1715     }
  1716 
  1717     frect.x = real_dstrect.x * renderer->scale.x;
  1718     frect.y = real_dstrect.y * renderer->scale.y;
  1719     frect.w = real_dstrect.w * renderer->scale.x;
  1720     frect.h = real_dstrect.h * renderer->scale.y;
  1721 
  1722     return renderer->RenderCopy(renderer, texture, &real_srcrect, &frect);
  1723 }
  1724 
  1725 
  1726 int
  1727 SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
  1728                const SDL_Rect * srcrect, const SDL_Rect * dstrect,
  1729                const double angle, const SDL_Point *center, const SDL_RendererFlip flip)
  1730 {
  1731     SDL_Rect real_srcrect = { 0, 0, 0, 0 };
  1732     SDL_Rect real_dstrect = { 0, 0, 0, 0 };
  1733     SDL_Point real_center;
  1734     SDL_FRect frect;
  1735     SDL_FPoint fcenter;
  1736 
  1737     CHECK_RENDERER_MAGIC(renderer, -1);
  1738     CHECK_TEXTURE_MAGIC(texture, -1);
  1739 
  1740     if (renderer != texture->renderer) {
  1741         return SDL_SetError("Texture was not created with this renderer");
  1742     }
  1743     if (!renderer->RenderCopyEx) {
  1744         return SDL_SetError("Renderer does not support RenderCopyEx");
  1745     }
  1746 
  1747     real_srcrect.x = 0;
  1748     real_srcrect.y = 0;
  1749     real_srcrect.w = texture->w;
  1750     real_srcrect.h = texture->h;
  1751     if (srcrect) {
  1752         if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
  1753             return 0;
  1754         }
  1755     }
  1756 
  1757     /* We don't intersect the dstrect with the viewport as RenderCopy does because of potential rotation clipping issues... TODO: should we? */
  1758     if (dstrect) {
  1759         real_dstrect = *dstrect;
  1760     } else {
  1761         SDL_RenderGetViewport(renderer, &real_dstrect);
  1762         real_dstrect.x = 0;
  1763         real_dstrect.y = 0;
  1764     }
  1765 
  1766     if (texture->native) {
  1767         texture = texture->native;
  1768     }
  1769 
  1770     if(center) real_center = *center;
  1771     else {
  1772         real_center.x = real_dstrect.w/2;
  1773         real_center.y = real_dstrect.h/2;
  1774     }
  1775 
  1776     frect.x = real_dstrect.x * renderer->scale.x;
  1777     frect.y = real_dstrect.y * renderer->scale.y;
  1778     frect.w = real_dstrect.w * renderer->scale.x;
  1779     frect.h = real_dstrect.h * renderer->scale.y;
  1780 
  1781     fcenter.x = real_center.x * renderer->scale.x;
  1782     fcenter.y = real_center.y * renderer->scale.y;
  1783 
  1784     return renderer->RenderCopyEx(renderer, texture, &real_srcrect, &frect, angle, &fcenter, flip);
  1785 }
  1786 
  1787 int
  1788 SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
  1789                      Uint32 format, void * pixels, int pitch)
  1790 {
  1791     SDL_Rect real_rect;
  1792 
  1793     CHECK_RENDERER_MAGIC(renderer, -1);
  1794 
  1795     if (!renderer->RenderReadPixels) {
  1796         return SDL_Unsupported();
  1797     }
  1798 
  1799     if (!format) {
  1800         format = SDL_GetWindowPixelFormat(renderer->window);
  1801     }
  1802 
  1803     real_rect.x = renderer->viewport.x;
  1804     real_rect.y = renderer->viewport.y;
  1805     real_rect.w = renderer->viewport.w;
  1806     real_rect.h = renderer->viewport.h;
  1807     if (rect) {
  1808         if (!SDL_IntersectRect(rect, &real_rect, &real_rect)) {
  1809             return 0;
  1810         }
  1811         if (real_rect.y > rect->y) {
  1812             pixels = (Uint8 *)pixels + pitch * (real_rect.y - rect->y);
  1813         }
  1814         if (real_rect.x > rect->x) {
  1815             int bpp = SDL_BYTESPERPIXEL(format);
  1816             pixels = (Uint8 *)pixels + bpp * (real_rect.x - rect->x);
  1817         }
  1818     }
  1819 
  1820     return renderer->RenderReadPixels(renderer, &real_rect,
  1821                                       format, pixels, pitch);
  1822 }
  1823 
  1824 void
  1825 SDL_RenderPresent(SDL_Renderer * renderer)
  1826 {
  1827     CHECK_RENDERER_MAGIC(renderer, );
  1828 
  1829     /* Don't draw while we're hidden */
  1830     if (renderer->hidden) {
  1831         return;
  1832     }
  1833     renderer->RenderPresent(renderer);
  1834 }
  1835 
  1836 void
  1837 SDL_DestroyTexture(SDL_Texture * texture)
  1838 {
  1839     SDL_Renderer *renderer;
  1840 
  1841     CHECK_TEXTURE_MAGIC(texture, );
  1842 
  1843     renderer = texture->renderer;
  1844     if (texture == renderer->target) {
  1845         SDL_SetRenderTarget(renderer, NULL);
  1846     }
  1847 
  1848     texture->magic = NULL;
  1849 
  1850     if (texture->next) {
  1851         texture->next->prev = texture->prev;
  1852     }
  1853     if (texture->prev) {
  1854         texture->prev->next = texture->next;
  1855     } else {
  1856         renderer->textures = texture->next;
  1857     }
  1858 
  1859     if (texture->native) {
  1860         SDL_DestroyTexture(texture->native);
  1861     }
  1862     if (texture->yuv) {
  1863         SDL_SW_DestroyYUVTexture(texture->yuv);
  1864     }
  1865     SDL_free(texture->pixels);
  1866 
  1867     renderer->DestroyTexture(renderer, texture);
  1868     SDL_free(texture);
  1869 }
  1870 
  1871 void
  1872 SDL_DestroyRenderer(SDL_Renderer * renderer)
  1873 {
  1874     CHECK_RENDERER_MAGIC(renderer, );
  1875 
  1876     SDL_DelEventWatch(SDL_RendererEventWatch, renderer);
  1877 
  1878     /* Free existing textures for this renderer */
  1879     while (renderer->textures) {
  1880         SDL_DestroyTexture(renderer->textures);
  1881     }
  1882 
  1883     if (renderer->window) {
  1884         SDL_SetWindowData(renderer->window, SDL_WINDOWRENDERDATA, NULL);
  1885     }
  1886 
  1887     /* It's no longer magical... */
  1888     renderer->magic = NULL;
  1889 
  1890     /* Free the renderer instance */
  1891     renderer->DestroyRenderer(renderer);
  1892 }
  1893 
  1894 int SDL_GL_BindTexture(SDL_Texture *texture, float *texw, float *texh)
  1895 {
  1896     SDL_Renderer *renderer;
  1897 
  1898     CHECK_TEXTURE_MAGIC(texture, -1);
  1899     renderer = texture->renderer;
  1900     if (texture->native) {
  1901         return SDL_GL_BindTexture(texture->native, texw, texh);
  1902     } else if (renderer && renderer->GL_BindTexture) {
  1903         return renderer->GL_BindTexture(renderer, texture, texw, texh);
  1904     } else {
  1905         return SDL_Unsupported();
  1906     }
  1907 }
  1908 
  1909 int SDL_GL_UnbindTexture(SDL_Texture *texture)
  1910 {
  1911     SDL_Renderer *renderer;
  1912 
  1913     CHECK_TEXTURE_MAGIC(texture, -1);
  1914     renderer = texture->renderer;
  1915     if (texture->native) {
  1916         return SDL_GL_UnbindTexture(texture->native);
  1917     } else if (renderer && renderer->GL_UnbindTexture) {
  1918         return renderer->GL_UnbindTexture(renderer, texture);
  1919     }
  1920 
  1921     return SDL_Unsupported();
  1922 }
  1923 
  1924 /* vi: set ts=4 sw=4 expandtab: */