src/render/SDL_render.c
author Jørgen P. Tjernø <jorgenpt@gmail.com>
Sat, 19 Apr 2014 13:15:41 -0700
changeset 8728 c7174f961388
parent 8654 275c4c82803b
child 9078 230e7558f76a
permissions -rw-r--r--
Render: Allow empty cliprect.

This fixes an issue where an empty cliprect is treated the same as a NULL
cliprect, causing the render backends to disable clipping.

Also adds a new API, SDL_RenderIsClipEnabled(render) that allows you to
differentiate between:
- SDL_RenderSetClipRect(render, NULL)
- SDL_Rect r = {0,0,0,0}; SDL_RenderSetClipRect(render, &r);

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