cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_render.c (54178B)


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