src/render/SDL_render.c
author David Ludwig <dludwig@pobox.com>
Sun, 27 Dec 2015 17:55:45 -0500
changeset 9968 0a420ec24b6a
parent 9849 1305bfa0214e
child 9998 f67cf37e9cd4
permissions -rw-r--r--
Fixed bug 3202 - Fix renderer visibility on a window maximized directly from the minimized state

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