src/render/SDL_render.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 26 May 2015 16:42:36 -0400
changeset 9636 ac8cd6c6d966
parent 9619 b94b6d0bff0f
child 9673 aea5a2032785
permissions -rw-r--r--
Drop out of SDL_UpdateTexture() early if the rectangle is zero pixels.

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