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