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