Change std:vector to eina_array
[platform/upstream/SDL.git] / src / render / SDL_render.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 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_hints.h"
26 #include "SDL_render.h"
27 #include "SDL_sysrender.h"
28 #include "software/SDL_render_sw_c.h"
29 #include "../video/SDL_pixels_c.h"
30
31 #if defined(__ANDROID__)
32 #  include "../core/android/SDL_android.h"
33 #endif
34
35 #define SDL_WINDOWRENDERDATA    "_SDL_WindowRenderData"
36
37 #define CHECK_RENDERER_MAGIC(renderer, retval) \
38     SDL_assert(renderer && renderer->magic == &renderer_magic); \
39     if (!renderer || renderer->magic != &renderer_magic) { \
40         SDL_SetError("Invalid renderer"); \
41         return retval; \
42     }
43
44 #define CHECK_TEXTURE_MAGIC(texture, retval) \
45     SDL_assert(texture && texture->magic == &texture_magic); \
46     if (!texture || texture->magic != &texture_magic) { \
47         SDL_SetError("Invalid texture"); \
48         return retval; \
49     }
50
51 /* Predefined blend modes */
52 #define SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation, \
53                               srcAlphaFactor, dstAlphaFactor, alphaOperation) \
54     (SDL_BlendMode)(((Uint32)colorOperation << 0) | \
55                     ((Uint32)srcColorFactor << 4) | \
56                     ((Uint32)dstColorFactor << 8) | \
57                     ((Uint32)alphaOperation << 16) | \
58                     ((Uint32)srcAlphaFactor << 20) | \
59                     ((Uint32)dstAlphaFactor << 24))
60
61 #define SDL_BLENDMODE_NONE_FULL \
62     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD, \
63                           SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD)
64
65 #define SDL_BLENDMODE_BLEND_FULL \
66     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \
67                           SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD)
68
69 #define SDL_BLENDMODE_ADD_FULL \
70     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, \
71                           SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD)
72
73 #define SDL_BLENDMODE_MOD_FULL \
74     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_COLOR, SDL_BLENDOPERATION_ADD, \
75                           SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD)
76
77 #define SDL_BLENDMODE_MUL_FULL \
78     SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_DST_COLOR, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \
79                           SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD)
80
81 #if !SDL_RENDER_DISABLED
82 static const SDL_RenderDriver *render_drivers[] = {
83 #if SDL_VIDEO_RENDER_D3D
84     &D3D_RenderDriver,
85 #endif
86 #if SDL_VIDEO_RENDER_D3D11
87     &D3D11_RenderDriver,
88 #endif
89 #if SDL_VIDEO_RENDER_METAL
90     &METAL_RenderDriver,
91 #endif
92 #if SDL_VIDEO_RENDER_OGL
93     &GL_RenderDriver,
94 #endif
95 #if SDL_VIDEO_RENDER_OGL_ES2
96     &GLES2_RenderDriver,
97 #endif
98 #if SDL_VIDEO_RENDER_OGL_ES
99     &GLES_RenderDriver,
100 #endif
101 #if SDL_VIDEO_RENDER_DIRECTFB
102     &DirectFB_RenderDriver,
103 #endif
104 #if SDL_VIDEO_RENDER_PSP
105     &PSP_RenderDriver,
106 #endif
107 #if SDL_VIDEO_RENDER_SW
108     &SW_RenderDriver
109 #endif
110 };
111 #endif /* !SDL_RENDER_DISABLED */
112
113 static char renderer_magic;
114 static char texture_magic;
115
116 static SDL_INLINE void
117 DebugLogRenderCommands(const SDL_RenderCommand *cmd)
118 {
119 #if 0
120     unsigned int i = 1;
121     SDL_Log("Render commands to flush:");
122     while (cmd) {
123         switch (cmd->command) {
124             case SDL_RENDERCMD_NO_OP:
125                 SDL_Log(" %u. no-op", i++);
126                 break;
127
128             case SDL_RENDERCMD_SETVIEWPORT:
129                 SDL_Log(" %u. set viewport (first=%u, rect={(%d, %d), %dx%d})", i++,
130                         (unsigned int) cmd->data.viewport.first,
131                         cmd->data.viewport.rect.x, cmd->data.viewport.rect.y,
132                         cmd->data.viewport.rect.w, cmd->data.viewport.rect.h);
133                 break;
134
135             case SDL_RENDERCMD_SETCLIPRECT:
136                 SDL_Log(" %u. set cliprect (enabled=%s, rect={(%d, %d), %dx%d})", i++,
137                         cmd->data.cliprect.enabled ? "true" : "false",
138                         cmd->data.cliprect.rect.x, cmd->data.cliprect.rect.y,
139                         cmd->data.cliprect.rect.w, cmd->data.cliprect.rect.h);
140                 break;
141
142             case SDL_RENDERCMD_SETDRAWCOLOR:
143                 SDL_Log(" %u. set draw color (first=%u, r=%d, g=%d, b=%d, a=%d)", i++,
144                         (unsigned int) cmd->data.color.first,
145                         (int) cmd->data.color.r, (int) cmd->data.color.g,
146                         (int) cmd->data.color.b, (int) cmd->data.color.a);
147                 break;
148
149             case SDL_RENDERCMD_CLEAR:
150                 SDL_Log(" %u. clear (first=%u, r=%d, g=%d, b=%d, a=%d)", i++,
151                         (unsigned int) cmd->data.color.first,
152                         (int) cmd->data.color.r, (int) cmd->data.color.g,
153                         (int) cmd->data.color.b, (int) cmd->data.color.a);
154                 break;
155
156             case SDL_RENDERCMD_DRAW_POINTS:
157                 SDL_Log(" %u. draw points (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++,
158                         (unsigned int) cmd->data.draw.first,
159                         (unsigned int) cmd->data.draw.count,
160                         (int) cmd->data.draw.r, (int) cmd->data.draw.g,
161                         (int) cmd->data.draw.b, (int) cmd->data.draw.a,
162                         (int) cmd->data.draw.blend);
163                 break;
164
165             case SDL_RENDERCMD_DRAW_LINES:
166                 SDL_Log(" %u. draw lines (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++,
167                         (unsigned int) cmd->data.draw.first,
168                         (unsigned int) cmd->data.draw.count,
169                         (int) cmd->data.draw.r, (int) cmd->data.draw.g,
170                         (int) cmd->data.draw.b, (int) cmd->data.draw.a,
171                         (int) cmd->data.draw.blend);
172                 break;
173
174             case SDL_RENDERCMD_FILL_RECTS:
175                 SDL_Log(" %u. fill rects (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++,
176                         (unsigned int) cmd->data.draw.first,
177                         (unsigned int) cmd->data.draw.count,
178                         (int) cmd->data.draw.r, (int) cmd->data.draw.g,
179                         (int) cmd->data.draw.b, (int) cmd->data.draw.a,
180                         (int) cmd->data.draw.blend);
181                 break;
182
183             case SDL_RENDERCMD_COPY:
184                 SDL_Log(" %u. copy (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, tex=%p)", i++,
185                         (unsigned int) cmd->data.draw.first,
186                         (unsigned int) cmd->data.draw.count,
187                         (int) cmd->data.draw.r, (int) cmd->data.draw.g,
188                         (int) cmd->data.draw.b, (int) cmd->data.draw.a,
189                         (int) cmd->data.draw.blend, cmd->data.draw.texture);
190                 break;
191
192
193             case SDL_RENDERCMD_COPY_EX:
194                 SDL_Log(" %u. copyex (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, tex=%p)", i++,
195                         (unsigned int) cmd->data.draw.first,
196                         (unsigned int) cmd->data.draw.count,
197                         (int) cmd->data.draw.r, (int) cmd->data.draw.g,
198                         (int) cmd->data.draw.b, (int) cmd->data.draw.a,
199                         (int) cmd->data.draw.blend, cmd->data.draw.texture);
200                 break;
201         }
202         cmd = cmd->next;
203     }
204 #endif
205 }
206
207 static int
208 FlushRenderCommands(SDL_Renderer *renderer)
209 {
210     int retval;
211
212     SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
213
214     if (renderer->render_commands == NULL) {  /* nothing to do! */
215         SDL_assert(renderer->vertex_data_used == 0);
216         return 0;
217     }
218
219     DebugLogRenderCommands(renderer->render_commands);
220
221     retval = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used);
222
223     /* Move the whole render command queue to the unused pool so we can reuse them next time. */
224     if (renderer->render_commands_tail != NULL) {
225         renderer->render_commands_tail->next = renderer->render_commands_pool;
226         renderer->render_commands_pool = renderer->render_commands;
227         renderer->render_commands_tail = NULL;
228         renderer->render_commands = NULL;
229     }
230     renderer->vertex_data_used = 0;
231     renderer->render_command_generation++;
232     renderer->color_queued = SDL_FALSE;
233     renderer->viewport_queued = SDL_FALSE;
234     renderer->cliprect_queued = SDL_FALSE;
235     return retval;
236 }
237
238 static int
239 FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture)
240 {
241     SDL_Renderer *renderer = texture->renderer;
242     if (texture->last_command_generation == renderer->render_command_generation) {
243         /* the current command queue depends on this texture, flush the queue now before it changes */
244         return FlushRenderCommands(renderer);
245     }
246     return 0;
247 }
248
249 static SDL_INLINE int
250 FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer)
251 {
252     return renderer->batching ? 0 : FlushRenderCommands(renderer);
253 }
254
255 int
256 SDL_RenderFlush(SDL_Renderer * renderer)
257 {
258     return FlushRenderCommands(renderer);
259 }
260
261 void *
262 SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, const size_t alignment, size_t *offset)
263 {
264     const size_t needed = renderer->vertex_data_used + numbytes + alignment;
265     size_t current_offset = renderer->vertex_data_used;
266
267     size_t aligner = (alignment && ((current_offset & (alignment - 1)) != 0)) ? (alignment - (current_offset & (alignment - 1))) : 0;
268     size_t aligned = current_offset + aligner;
269
270     if (renderer->vertex_data_allocation < needed) {
271         const size_t current_allocation = renderer->vertex_data ? renderer->vertex_data_allocation : 1024;
272         size_t newsize = current_allocation * 2;
273         void *ptr;
274         while (newsize < needed) {
275             newsize *= 2;
276         }
277         ptr = SDL_realloc(renderer->vertex_data, newsize);
278         if (ptr == NULL) {
279             SDL_OutOfMemory();
280             return NULL;
281         }
282         renderer->vertex_data = ptr;
283         renderer->vertex_data_allocation = newsize;
284     }
285
286     if (offset) {
287         *offset = aligned;
288     }
289
290     renderer->vertex_data_used += aligner + numbytes;
291
292     return ((Uint8 *) renderer->vertex_data) + aligned;
293 }
294
295 static SDL_RenderCommand *
296 AllocateRenderCommand(SDL_Renderer *renderer)
297 {
298     SDL_RenderCommand *retval = NULL;
299
300     /* !!! FIXME: are there threading limitations in SDL's render API? If not, we need to mutex this. */
301     retval = renderer->render_commands_pool;
302     if (retval != NULL) {
303         renderer->render_commands_pool = retval->next;
304         retval->next = NULL;
305     } else {
306         retval = SDL_calloc(1, sizeof (*retval));
307         if (!retval) {
308             SDL_OutOfMemory();
309             return NULL;
310         }
311     }
312
313     SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
314     if (renderer->render_commands_tail != NULL) {
315         renderer->render_commands_tail->next = retval;
316     } else {
317         renderer->render_commands = retval;
318     }
319     renderer->render_commands_tail = retval;
320
321     return retval;
322 }
323
324 static int
325 QueueCmdSetViewport(SDL_Renderer *renderer)
326 {
327     int retval = 0;
328     if (!renderer->viewport_queued || (SDL_memcmp(&renderer->viewport, &renderer->last_queued_viewport, sizeof (SDL_Rect)) != 0)) {
329         SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
330         retval = -1;
331         if (cmd != NULL) {
332             cmd->command = SDL_RENDERCMD_SETVIEWPORT;
333             cmd->data.viewport.first = 0;  /* render backend will fill this in. */
334             SDL_memcpy(&cmd->data.viewport.rect, &renderer->viewport, sizeof (renderer->viewport));
335             retval = renderer->QueueSetViewport(renderer, cmd);
336             if (retval < 0) {
337                 cmd->command = SDL_RENDERCMD_NO_OP;
338             } else {
339                 SDL_memcpy(&renderer->last_queued_viewport, &renderer->viewport, sizeof (SDL_Rect));
340                 renderer->viewport_queued = SDL_TRUE;
341             }
342         }
343     }
344     return retval;
345 }
346
347 static int
348 QueueCmdSetClipRect(SDL_Renderer *renderer)
349 {
350     int retval = 0;
351     if ((!renderer->cliprect_queued) ||
352          (renderer->clipping_enabled != renderer->last_queued_cliprect_enabled) ||
353          (SDL_memcmp(&renderer->clip_rect, &renderer->last_queued_cliprect, sizeof (SDL_Rect)) != 0)) {
354         SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
355         if (cmd == NULL) {
356             retval = -1;
357         } else {
358             cmd->command = SDL_RENDERCMD_SETCLIPRECT;
359             cmd->data.cliprect.enabled = renderer->clipping_enabled;
360             SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (cmd->data.cliprect.rect));
361             SDL_memcpy(&renderer->last_queued_cliprect, &renderer->clip_rect, sizeof (SDL_Rect));
362             renderer->last_queued_cliprect_enabled = renderer->clipping_enabled;
363             renderer->cliprect_queued = SDL_TRUE;
364         }
365     }
366     return retval;
367 }
368
369 static int
370 QueueCmdSetDrawColor(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uint8 b, const Uint8 a)
371 {
372     const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
373     int retval = 0;
374     
375     if (!renderer->color_queued || (color != renderer->last_queued_color)) {
376         SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
377         retval = -1;
378
379         if (cmd != NULL) {
380             cmd->command = SDL_RENDERCMD_SETDRAWCOLOR;
381             cmd->data.color.first = 0;  /* render backend will fill this in. */
382             cmd->data.color.r = r;
383             cmd->data.color.g = g;
384             cmd->data.color.b = b;
385             cmd->data.color.a = a;
386             retval = renderer->QueueSetDrawColor(renderer, cmd);
387             if (retval < 0) {
388                 cmd->command = SDL_RENDERCMD_NO_OP;
389             } else {
390                 renderer->last_queued_color = color;
391                 renderer->color_queued = SDL_TRUE;
392             }
393         }
394     }
395     return retval;
396 }
397
398 static int
399 QueueCmdClear(SDL_Renderer *renderer)
400 {
401     SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
402     if (cmd == NULL) {
403         return -1;
404     }
405
406     cmd->command = SDL_RENDERCMD_CLEAR;
407     cmd->data.color.first = 0;
408     cmd->data.color.r = renderer->r;
409     cmd->data.color.g = renderer->g;
410     cmd->data.color.b = renderer->b;
411     cmd->data.color.a = renderer->a;
412     return 0;
413 }
414
415 static int
416 PrepQueueCmdDraw(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uint8 b, const Uint8 a)
417 {
418     int retval = QueueCmdSetDrawColor(renderer, r, g, b, a);
419
420     /* Set the viewport and clip rect directly before draws, so the backends
421      * don't have to worry about that state not being valid at draw time. */
422     if (retval == 0 && !renderer->viewport_queued) {
423         retval = QueueCmdSetViewport(renderer);
424     }
425     if (retval == 0 && !renderer->cliprect_queued) {
426         retval = QueueCmdSetClipRect(renderer);
427     }
428     return retval;
429 }
430
431 static SDL_RenderCommand *
432 PrepQueueCmdDrawSolid(SDL_Renderer *renderer, const SDL_RenderCommandType cmdtype)
433 {
434     /* !!! FIXME: drop this draw if viewport w or h is zero. */
435     SDL_RenderCommand *cmd = NULL;
436     if (PrepQueueCmdDraw(renderer, renderer->r, renderer->g, renderer->b, renderer->a) == 0) {
437         cmd = AllocateRenderCommand(renderer);
438         if (cmd != NULL) {
439             cmd->command = cmdtype;
440             cmd->data.draw.first = 0;  /* render backend will fill this in. */
441             cmd->data.draw.count = 0;  /* render backend will fill this in. */
442             cmd->data.draw.r = renderer->r;
443             cmd->data.draw.g = renderer->g;
444             cmd->data.draw.b = renderer->b;
445             cmd->data.draw.a = renderer->a;
446             cmd->data.draw.blend = renderer->blendMode;
447             cmd->data.draw.texture = NULL;  /* no texture. */
448         }
449     }
450     return cmd;
451 }
452
453 static int
454 QueueCmdDrawPoints(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
455 {
456     SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_POINTS);
457     int retval = -1;
458     if (cmd != NULL) {
459         retval = renderer->QueueDrawPoints(renderer, cmd, points, count);
460         if (retval < 0) {
461             cmd->command = SDL_RENDERCMD_NO_OP;
462         }
463     }
464     return retval;
465 }
466
467 static int
468 QueueCmdDrawLines(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
469 {
470     SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_LINES);
471     int retval = -1;
472     if (cmd != NULL) {
473         retval = renderer->QueueDrawLines(renderer, cmd, points, count);
474         if (retval < 0) {
475             cmd->command = SDL_RENDERCMD_NO_OP;
476         }
477     }
478     return retval;
479 }
480
481 static int
482 QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect * rects, const int count)
483 {
484     SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_FILL_RECTS);
485     int retval = -1;
486     if (cmd != NULL) {
487         retval = renderer->QueueFillRects(renderer, cmd, rects, count);
488         if (retval < 0) {
489             cmd->command = SDL_RENDERCMD_NO_OP;
490         }
491     }
492     return retval;
493 }
494
495 static SDL_RenderCommand *
496 PrepQueueCmdDrawTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_RenderCommandType cmdtype)
497 {
498     /* !!! FIXME: drop this draw if viewport w or h is zero. */
499     SDL_RenderCommand *cmd = NULL;
500     if (PrepQueueCmdDraw(renderer, texture->r, texture->g, texture->b, texture->a) == 0) {
501         cmd = AllocateRenderCommand(renderer);
502         if (cmd != NULL) {
503             cmd->command = cmdtype;
504             cmd->data.draw.first = 0;  /* render backend will fill this in. */
505             cmd->data.draw.count = 0;  /* render backend will fill this in. */
506             cmd->data.draw.r = texture->r;
507             cmd->data.draw.g = texture->g;
508             cmd->data.draw.b = texture->b;
509             cmd->data.draw.a = texture->a;
510             cmd->data.draw.blend = texture->blendMode;
511             cmd->data.draw.texture = texture;
512         }
513     }
514     return cmd;
515 }
516
517 static int
518 QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_FRect * dstrect)
519 {
520     SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY);
521     int retval = -1;
522     if (cmd != NULL) {
523         retval = renderer->QueueCopy(renderer, cmd, texture, srcrect, dstrect);
524         if (retval < 0) {
525             cmd->command = SDL_RENDERCMD_NO_OP;
526         }
527     }
528     return retval;
529 }
530
531 static int
532 QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture * texture,
533                const SDL_Rect * srcquad, const SDL_FRect * dstrect,
534                const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
535 {
536     SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY_EX);
537     int retval = -1;
538     SDL_assert(renderer->QueueCopyEx != NULL);  /* should have caught at higher level. */
539     if (cmd != NULL) {
540         retval = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip);
541         if (retval < 0) {
542             cmd->command = SDL_RENDERCMD_NO_OP;
543         }
544     }
545     return retval;
546 }
547
548
549 static int UpdateLogicalSize(SDL_Renderer *renderer);
550
551 int
552 SDL_GetNumRenderDrivers(void)
553 {
554 #if !SDL_RENDER_DISABLED
555     return SDL_arraysize(render_drivers);
556 #else
557     return 0;
558 #endif
559 }
560
561 int
562 SDL_GetRenderDriverInfo(int index, SDL_RendererInfo * info)
563 {
564 #if !SDL_RENDER_DISABLED
565     if (index < 0 || index >= SDL_GetNumRenderDrivers()) {
566         return SDL_SetError("index must be in the range of 0 - %d",
567                             SDL_GetNumRenderDrivers() - 1);
568     }
569     *info = render_drivers[index]->info;
570     return 0;
571 #else
572     return SDL_SetError("SDL not built with rendering support");
573 #endif
574 }
575
576 static void GetWindowViewportValues(SDL_Renderer *renderer, int *logical_w, int *logical_h, SDL_Rect *viewport, SDL_FPoint *scale)
577 {
578     SDL_LockMutex(renderer->target_mutex);
579     *logical_w = renderer->target ? renderer->logical_w_backup : renderer->logical_w;
580     *logical_h = renderer->target ? renderer->logical_h_backup : renderer->logical_h;
581     *viewport = renderer->target ? renderer->viewport_backup : renderer->viewport;
582     *scale = renderer->target ? renderer->scale_backup : renderer->scale;
583     SDL_UnlockMutex(renderer->target_mutex);
584 }
585
586 static int SDLCALL
587 SDL_RendererEventWatch(void *userdata, SDL_Event *event)
588 {
589     SDL_Renderer *renderer = (SDL_Renderer *)userdata;
590
591     if (event->type == SDL_WINDOWEVENT) {
592         SDL_Window *window = SDL_GetWindowFromID(event->window.windowID);
593         if (window == renderer->window) {
594             if (renderer->WindowEvent) {
595                 renderer->WindowEvent(renderer, &event->window);
596             }
597
598             if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
599                 /* Make sure we're operating on the default render target */
600                 SDL_Texture *saved_target = SDL_GetRenderTarget(renderer);
601                 if (saved_target) {
602                     SDL_SetRenderTarget(renderer, NULL);
603                 }
604
605                 if (renderer->logical_w) {
606                     UpdateLogicalSize(renderer);
607                 } else {
608                     /* Window was resized, reset viewport */
609                     int w, h;
610
611                     if (renderer->GetOutputSize) {
612                         renderer->GetOutputSize(renderer, &w, &h);
613                     } else {
614                         SDL_GetWindowSize(renderer->window, &w, &h);
615                     }
616
617                     if (renderer->target) {
618                         renderer->viewport_backup.x = 0;
619                         renderer->viewport_backup.y = 0;
620                         renderer->viewport_backup.w = w;
621                         renderer->viewport_backup.h = h;
622                     } else {
623                         renderer->viewport.x = 0;
624                         renderer->viewport.y = 0;
625                         renderer->viewport.w = w;
626                         renderer->viewport.h = h;
627                         QueueCmdSetViewport(renderer);
628                         FlushRenderCommandsIfNotBatching(renderer);
629                     }
630                 }
631
632                 if (saved_target) {
633                     SDL_SetRenderTarget(renderer, saved_target);
634                 }
635             } else if (event->window.event == SDL_WINDOWEVENT_HIDDEN) {
636                 renderer->hidden = SDL_TRUE;
637             } else if (event->window.event == SDL_WINDOWEVENT_SHOWN) {
638                 if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)) {
639                     renderer->hidden = SDL_FALSE;
640                 }
641             } else if (event->window.event == SDL_WINDOWEVENT_MINIMIZED) {
642                 renderer->hidden = SDL_TRUE;
643             } else if (event->window.event == SDL_WINDOWEVENT_RESTORED || 
644                        event->window.event == SDL_WINDOWEVENT_MAXIMIZED) {
645                 if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN)) {
646                     renderer->hidden = SDL_FALSE;
647                 }
648             }
649         }
650     } else if (event->type == SDL_MOUSEMOTION) {
651         SDL_Window *window = SDL_GetWindowFromID(event->motion.windowID);
652         if (window == renderer->window) {
653             int logical_w, logical_h;
654             SDL_Rect viewport;
655             SDL_FPoint scale;
656             GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
657             if (logical_w) {
658                 event->motion.x -= (int)(viewport.x * renderer->dpi_scale.x);
659                 event->motion.y -= (int)(viewport.y * renderer->dpi_scale.y);
660                 event->motion.x = (int)(event->motion.x / (scale.x * renderer->dpi_scale.x));
661                 event->motion.y = (int)(event->motion.y / (scale.y * renderer->dpi_scale.y));
662                 if (event->motion.xrel != 0 && renderer->relative_scaling) {
663                     float rel = renderer->xrel + event->motion.xrel / (scale.x * renderer->dpi_scale.x);
664                     float trunc = SDL_truncf(rel);
665                     renderer->xrel = rel - trunc;
666                     event->motion.xrel = (Sint32) trunc;
667                 }
668                 if (event->motion.yrel != 0 && renderer->relative_scaling) {
669                     float rel = renderer->yrel + event->motion.yrel / (scale.y * renderer->dpi_scale.y);
670                     float trunc = SDL_truncf(rel);
671                     renderer->yrel = rel - trunc;
672                     event->motion.yrel = (Sint32) trunc;
673                 }
674             }
675         }
676     } else if (event->type == SDL_MOUSEBUTTONDOWN ||
677                event->type == SDL_MOUSEBUTTONUP) {
678         SDL_Window *window = SDL_GetWindowFromID(event->button.windowID);
679         if (window == renderer->window) {
680             int logical_w, logical_h;
681             SDL_Rect viewport;
682             SDL_FPoint scale;
683             GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
684             if (logical_w) {
685                 event->button.x -= (int)(viewport.x * renderer->dpi_scale.x);
686                 event->button.y -= (int)(viewport.y * renderer->dpi_scale.y);
687                 event->button.x = (int)(event->button.x / (scale.x * renderer->dpi_scale.x));
688                 event->button.y = (int)(event->button.y / (scale.y * renderer->dpi_scale.y));
689             }
690         }                               
691     } else if (event->type == SDL_FINGERDOWN ||
692                event->type == SDL_FINGERUP ||
693                event->type == SDL_FINGERMOTION) {
694         int logical_w, logical_h;
695         float physical_w, physical_h;
696         SDL_Rect viewport;
697         SDL_FPoint scale;
698         GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
699
700         /* !!! FIXME: we probably should drop events that are outside of the
701            !!! FIXME: viewport, but we can't do that from an event watcher,
702            !!! FIXME: and we would have to track if a touch happened outside
703            !!! FIXME: the viewport and then slid into it to insert extra
704            !!! FIXME: events, which is a mess, so for now we just clamp these
705            !!! FIXME: events to the edge. */
706
707         if (renderer->GetOutputSize) {
708             int w, h;
709             renderer->GetOutputSize(renderer, &w, &h);
710             physical_w = (float) w;
711             physical_h = (float) h;
712         } else {
713             int w, h;
714             SDL_GetWindowSize(renderer->window, &w, &h);
715             physical_w = ((float) w) * renderer->dpi_scale.x;
716             physical_h = ((float) h) * renderer->dpi_scale.y;
717         }
718
719         if (physical_w == 0.0f) {  /* nowhere for the touch to go, avoid division by zero and put it dead center. */
720             event->tfinger.x = 0.5f;
721         } else {
722             const float normalized_viewport_x = ((float) viewport.x) / physical_w;
723             const float normalized_viewport_w = ((float) viewport.w) / physical_w;
724             if (event->tfinger.x <= normalized_viewport_x) {
725                 event->tfinger.x = 0.0f;  /* to the left of the viewport, clamp to the edge. */
726             } else if (event->tfinger.x >= (normalized_viewport_x + normalized_viewport_w)) {
727                 event->tfinger.x = 1.0f;  /* to the right of the viewport, clamp to the edge. */
728             } else {
729                 event->tfinger.x = (event->tfinger.x - normalized_viewport_x) / normalized_viewport_w;
730             }
731         }
732
733         if (physical_h == 0.0f) {  /* nowhere for the touch to go, avoid division by zero and put it dead center. */
734             event->tfinger.y = 0.5f;
735         } else {
736             const float normalized_viewport_y = ((float) viewport.y) / physical_h;
737             const float normalized_viewport_h = ((float) viewport.h) / physical_h;
738             if (event->tfinger.y <= normalized_viewport_y) {
739                 event->tfinger.y = 0.0f;  /* to the left of the viewport, clamp to the edge. */
740             } else if (event->tfinger.y >= (normalized_viewport_y + normalized_viewport_h)) {
741                 event->tfinger.y = 1.0f;  /* to the right of the viewport, clamp to the edge. */
742             } else {
743                 event->tfinger.y = (event->tfinger.y - normalized_viewport_y) / normalized_viewport_h;
744             }
745         }
746     }
747
748     return 0;
749 }
750
751 int
752 SDL_CreateWindowAndRenderer(int width, int height, Uint32 window_flags,
753                             SDL_Window **window, SDL_Renderer **renderer)
754 {
755     *window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED,
756                                      SDL_WINDOWPOS_UNDEFINED,
757                                      width, height, window_flags);
758     if (!*window) {
759         *renderer = NULL;
760         return -1;
761     }
762
763     *renderer = SDL_CreateRenderer(*window, -1, 0);
764     if (!*renderer) {
765         return -1;
766     }
767
768     return 0;
769 }
770
771 static SDL_INLINE
772 void VerifyDrawQueueFunctions(const SDL_Renderer *renderer)
773 {
774     /* all of these functions are required to be implemented, even as no-ops, so we don't
775         have to check that they aren't NULL over and over. */
776     SDL_assert(renderer->QueueSetViewport != NULL);
777     SDL_assert(renderer->QueueSetDrawColor != NULL);
778     SDL_assert(renderer->QueueDrawPoints != NULL);
779     SDL_assert(renderer->QueueDrawLines != NULL);
780     SDL_assert(renderer->QueueFillRects != NULL);
781     SDL_assert(renderer->QueueCopy != NULL);
782     SDL_assert(renderer->RunCommandQueue != NULL);
783 }
784
785 SDL_Renderer *
786 SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags)
787 {
788 #if !SDL_RENDER_DISABLED
789     SDL_Renderer *renderer = NULL;
790     int n = SDL_GetNumRenderDrivers();
791     SDL_bool batching = SDL_TRUE;
792     const char *hint;
793
794 #if defined(__ANDROID__)
795     Android_ActivityMutex_Lock_Running();
796 #endif
797
798     if (!window) {
799         SDL_SetError("Invalid window");
800         goto error;
801     }
802
803     if (SDL_GetRenderer(window)) {
804         SDL_SetError("Renderer already associated with window");
805         goto error;
806     }
807
808     if (SDL_GetHint(SDL_HINT_RENDER_VSYNC)) {
809         if (SDL_GetHintBoolean(SDL_HINT_RENDER_VSYNC, SDL_TRUE)) {
810             flags |= SDL_RENDERER_PRESENTVSYNC;
811         } else {
812             flags &= ~SDL_RENDERER_PRESENTVSYNC;
813         }
814     }
815
816     if (index < 0) {
817         hint = SDL_GetHint(SDL_HINT_RENDER_DRIVER);
818         if (hint) {
819             for (index = 0; index < n; ++index) {
820                 const SDL_RenderDriver *driver = render_drivers[index];
821
822                 if (SDL_strcasecmp(hint, driver->info.name) == 0) {
823                     /* Create a new renderer instance */
824                     renderer = driver->CreateRenderer(window, flags);
825                     if (renderer) {
826                         batching = SDL_FALSE;
827                     }
828                     break;
829                 }
830             }
831         }
832
833         if (!renderer) {
834             for (index = 0; index < n; ++index) {
835                 const SDL_RenderDriver *driver = render_drivers[index];
836
837                 if ((driver->info.flags & flags) == flags) {
838                     /* Create a new renderer instance */
839                     renderer = driver->CreateRenderer(window, flags);
840                     if (renderer) {
841                         /* Yay, we got one! */
842                         break;
843                     }
844                 }
845             }
846         }
847         if (index == n) {
848             SDL_SetError("Couldn't find matching render driver");
849             goto error;
850         }
851     } else {
852         if (index >= SDL_GetNumRenderDrivers()) {
853             SDL_SetError("index must be -1 or in the range of 0 - %d",
854                          SDL_GetNumRenderDrivers() - 1);
855             goto error;
856         }
857         /* Create a new renderer instance */
858         renderer = render_drivers[index]->CreateRenderer(window, flags);
859         batching = SDL_FALSE;
860     }
861
862     if (!renderer) {
863         goto error;
864     }
865
866     VerifyDrawQueueFunctions(renderer);
867
868     /* let app/user override batching decisions. */
869     if (renderer->always_batch) {
870         batching = SDL_TRUE;
871     } else if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) {
872         batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE);
873     }
874
875     renderer->batching = batching;
876     renderer->magic = &renderer_magic;
877     renderer->window = window;
878     renderer->target_mutex = SDL_CreateMutex();
879     renderer->scale.x = 1.0f;
880     renderer->scale.y = 1.0f;
881     renderer->dpi_scale.x = 1.0f;
882     renderer->dpi_scale.y = 1.0f;
883
884     /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
885     renderer->render_command_generation = 1;
886
887     if (window && renderer->GetOutputSize) {
888         int window_w, window_h;
889         int output_w, output_h;
890         if (renderer->GetOutputSize(renderer, &output_w, &output_h) == 0) {
891             SDL_GetWindowSize(renderer->window, &window_w, &window_h);
892             renderer->dpi_scale.x = (float)window_w / output_w;
893             renderer->dpi_scale.y = (float)window_h / output_h;
894         }
895     }
896
897     renderer->relative_scaling = SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_SCALING, SDL_TRUE);
898
899     if (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN|SDL_WINDOW_MINIMIZED)) {
900         renderer->hidden = SDL_TRUE;
901     } else {
902         renderer->hidden = SDL_FALSE;
903     }
904
905     SDL_SetWindowData(window, SDL_WINDOWRENDERDATA, renderer);
906
907     SDL_RenderSetViewport(renderer, NULL);
908
909     SDL_AddEventWatch(SDL_RendererEventWatch, renderer);
910
911     SDL_LogInfo(SDL_LOG_CATEGORY_RENDER,
912                 "Created renderer: %s", renderer->info.name);
913
914 #if defined(__ANDROID__)
915     Android_ActivityMutex_Unlock();
916 #endif
917     return renderer;
918
919 error:
920
921 #if defined(__ANDROID__)
922     Android_ActivityMutex_Unlock();
923 #endif
924     return NULL;
925
926 #else
927     SDL_SetError("SDL not built with rendering support");
928     return NULL;
929 #endif
930 }
931
932 SDL_Renderer *
933 SDL_CreateSoftwareRenderer(SDL_Surface * surface)
934 {
935 #if !SDL_RENDER_DISABLED && SDL_VIDEO_RENDER_SW
936     SDL_Renderer *renderer;
937
938     renderer = SW_CreateRendererForSurface(surface);
939
940     if (renderer) {
941         VerifyDrawQueueFunctions(renderer);
942         renderer->magic = &renderer_magic;
943         renderer->target_mutex = SDL_CreateMutex();
944         renderer->scale.x = 1.0f;
945         renderer->scale.y = 1.0f;
946
947         /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
948         renderer->render_command_generation = 1;
949
950         SDL_RenderSetViewport(renderer, NULL);
951     }
952     return renderer;
953 #else
954     SDL_SetError("SDL not built with rendering support");
955     return NULL;
956 #endif /* !SDL_RENDER_DISABLED */
957 }
958
959 SDL_Renderer *
960 SDL_GetRenderer(SDL_Window * window)
961 {
962     return (SDL_Renderer *)SDL_GetWindowData(window, SDL_WINDOWRENDERDATA);
963 }
964
965 int
966 SDL_GetRendererInfo(SDL_Renderer * renderer, SDL_RendererInfo * info)
967 {
968     CHECK_RENDERER_MAGIC(renderer, -1);
969
970     *info = renderer->info;
971     return 0;
972 }
973
974 int
975 SDL_GetRendererOutputSize(SDL_Renderer * renderer, int *w, int *h)
976 {
977     CHECK_RENDERER_MAGIC(renderer, -1);
978
979     if (renderer->target) {
980         return SDL_QueryTexture(renderer->target, NULL, NULL, w, h);
981     } else if (renderer->GetOutputSize) {
982         return renderer->GetOutputSize(renderer, w, h);
983     } else if (renderer->window) {
984         SDL_GetWindowSize(renderer->window, w, h);
985         return 0;
986     } else {
987         SDL_assert(0 && "This should never happen");
988         return SDL_SetError("Renderer doesn't support querying output size");
989     }
990 }
991
992 static SDL_bool
993 IsSupportedBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
994 {
995     switch (blendMode)
996     {
997     /* These are required to be supported by all renderers */
998     case SDL_BLENDMODE_NONE:
999     case SDL_BLENDMODE_BLEND:
1000     case SDL_BLENDMODE_ADD:
1001     case SDL_BLENDMODE_MOD:
1002     case SDL_BLENDMODE_MUL:
1003         return SDL_TRUE;
1004
1005     default:
1006         return renderer->SupportsBlendMode && renderer->SupportsBlendMode(renderer, blendMode);
1007     }
1008 }
1009
1010 static SDL_bool
1011 IsSupportedFormat(SDL_Renderer * renderer, Uint32 format)
1012 {
1013     Uint32 i;
1014
1015     for (i = 0; i < renderer->info.num_texture_formats; ++i) {
1016         if (renderer->info.texture_formats[i] == format) {
1017             return SDL_TRUE;
1018         }
1019     }
1020     return SDL_FALSE;
1021 }
1022
1023 static Uint32
1024 GetClosestSupportedFormat(SDL_Renderer * renderer, Uint32 format)
1025 {
1026     Uint32 i;
1027
1028     if (SDL_ISPIXELFORMAT_FOURCC(format)) {
1029         /* Look for an exact match */
1030         for (i = 0; i < renderer->info.num_texture_formats; ++i) {
1031             if (renderer->info.texture_formats[i] == format) {
1032                 return renderer->info.texture_formats[i];
1033             }
1034         }
1035     } else {
1036         SDL_bool hasAlpha = SDL_ISPIXELFORMAT_ALPHA(format);
1037
1038         /* We just want to match the first format that has the same channels */
1039         for (i = 0; i < renderer->info.num_texture_formats; ++i) {
1040             if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) &&
1041                 SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == hasAlpha) {
1042                 return renderer->info.texture_formats[i];
1043             }
1044         }
1045     }
1046     return renderer->info.texture_formats[0];
1047 }
1048
1049
1050 static SDL_ScaleMode SDL_GetScaleMode(void)
1051 {
1052     const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
1053
1054     if (!hint || SDL_strcasecmp(hint, "nearest") == 0) {
1055         return SDL_ScaleModeNearest;
1056     } else if (SDL_strcasecmp(hint, "linear") == 0) {
1057         return SDL_ScaleModeLinear;
1058     } else if (SDL_strcasecmp(hint, "best") == 0) {
1059         return SDL_ScaleModeBest;
1060     } else {
1061         return (SDL_ScaleMode)SDL_atoi(hint);
1062     }
1063 }
1064
1065 SDL_Texture *
1066 SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h)
1067 {
1068     SDL_Texture *texture;
1069
1070     CHECK_RENDERER_MAGIC(renderer, NULL);
1071
1072     if (!format) {
1073         format = renderer->info.texture_formats[0];
1074     }
1075     if (SDL_BYTESPERPIXEL(format) == 0) {
1076         SDL_SetError("Invalid texture format");
1077         return NULL;
1078     }
1079     if (SDL_ISPIXELFORMAT_INDEXED(format)) {
1080         SDL_SetError("Palettized textures are not supported");
1081         return NULL;
1082     }
1083     if (w <= 0 || h <= 0) {
1084         SDL_SetError("Texture dimensions can't be 0");
1085         return NULL;
1086     }
1087     if ((renderer->info.max_texture_width && w > renderer->info.max_texture_width) ||
1088         (renderer->info.max_texture_height && h > renderer->info.max_texture_height)) {
1089         SDL_SetError("Texture dimensions are limited to %dx%d", renderer->info.max_texture_width, renderer->info.max_texture_height);
1090         return NULL;
1091     }
1092     texture = (SDL_Texture *) SDL_calloc(1, sizeof(*texture));
1093     if (!texture) {
1094         SDL_OutOfMemory();
1095         return NULL;
1096     }
1097     texture->magic = &texture_magic;
1098     texture->format = format;
1099     texture->access = access;
1100     texture->w = w;
1101     texture->h = h;
1102     texture->r = 255;
1103     texture->g = 255;
1104     texture->b = 255;
1105     texture->a = 255;
1106     texture->scaleMode = SDL_GetScaleMode();
1107     texture->renderer = renderer;
1108     texture->next = renderer->textures;
1109     if (renderer->textures) {
1110         renderer->textures->prev = texture;
1111     }
1112     renderer->textures = texture;
1113
1114     if (IsSupportedFormat(renderer, format)) {
1115         if (renderer->CreateTexture(renderer, texture) < 0) {
1116             SDL_DestroyTexture(texture);
1117             return NULL;
1118         }
1119     } else {
1120         texture->native = SDL_CreateTexture(renderer,
1121                                 GetClosestSupportedFormat(renderer, format),
1122                                 access, w, h);
1123         if (!texture->native) {
1124             SDL_DestroyTexture(texture);
1125             return NULL;
1126         }
1127
1128         /* Swap textures to have texture before texture->native in the list */
1129         texture->native->next = texture->next;
1130         if (texture->native->next) {
1131             texture->native->next->prev = texture->native;
1132         }
1133         texture->prev = texture->native->prev;
1134         if (texture->prev) {
1135             texture->prev->next = texture;
1136         }
1137         texture->native->prev = texture;
1138         texture->next = texture->native;
1139         renderer->textures = texture;
1140
1141         if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
1142 #if SDL_HAVE_YUV
1143             texture->yuv = SDL_SW_CreateYUVTexture(format, w, h);
1144 #else
1145             SDL_SetError("SDL not built with YUV support");
1146 #endif
1147             if (!texture->yuv) {
1148                 SDL_DestroyTexture(texture);
1149                 return NULL;
1150             }
1151         } else if (access == SDL_TEXTUREACCESS_STREAMING) {
1152             /* The pitch is 4 byte aligned */
1153             texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3);
1154             texture->pixels = SDL_calloc(1, texture->pitch * h);
1155             if (!texture->pixels) {
1156                 SDL_DestroyTexture(texture);
1157                 return NULL;
1158             }
1159         }
1160     }
1161     return texture;
1162 }
1163
1164 SDL_Texture *
1165 SDL_CreateTextureFromSurface(SDL_Renderer * renderer, SDL_Surface * surface)
1166 {
1167     const SDL_PixelFormat *fmt;
1168     SDL_bool needAlpha;
1169     SDL_bool direct_update;
1170     int i;
1171     Uint32 format = SDL_PIXELFORMAT_UNKNOWN;
1172     SDL_Texture *texture;
1173
1174     CHECK_RENDERER_MAGIC(renderer, NULL);
1175
1176     if (!surface) {
1177         SDL_SetError("SDL_CreateTextureFromSurface() passed NULL surface");
1178         return NULL;
1179     }
1180
1181     /* See what the best texture format is */
1182     fmt = surface->format;
1183     if (fmt->Amask || SDL_HasColorKey(surface)) {
1184         needAlpha = SDL_TRUE;
1185     } else {
1186         needAlpha = SDL_FALSE;
1187     }
1188
1189     /* If Palette contains alpha values, promotes to alpha format */
1190     if (fmt->palette) {
1191         SDL_bool is_opaque, has_alpha_channel;
1192         SDL_DetectPalette(fmt->palette, &is_opaque, &has_alpha_channel);
1193         if (!is_opaque) {
1194             needAlpha = SDL_TRUE;
1195         }
1196     }
1197
1198     /* Try to have the best pixel format for the texture */
1199     /* No alpha, but a colorkey => promote to alpha */
1200     if (!fmt->Amask && SDL_HasColorKey(surface)) {
1201         if (fmt->format == SDL_PIXELFORMAT_RGB888) {
1202             for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
1203                 if (renderer->info.texture_formats[i] == SDL_PIXELFORMAT_ARGB8888) {
1204                     format = SDL_PIXELFORMAT_ARGB8888;
1205                     break;
1206                 }
1207             }
1208         } else if (fmt->format == SDL_PIXELFORMAT_BGR888) {
1209             for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
1210                 if (renderer->info.texture_formats[i] == SDL_PIXELFORMAT_ABGR8888) {
1211                     format = SDL_PIXELFORMAT_ABGR8888;
1212                     break;
1213                 }
1214             }
1215         }
1216     } else {
1217         /* Exact match would be fine */
1218         for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
1219             if (renderer->info.texture_formats[i] == fmt->format) {
1220                 format = fmt->format;
1221                 break;
1222             }
1223         }
1224     }
1225
1226     /* Fallback, choose a valid pixel format */
1227     if (format == SDL_PIXELFORMAT_UNKNOWN) {
1228         format = renderer->info.texture_formats[0];
1229         for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
1230             if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) &&
1231                     SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == needAlpha) {
1232                 format = renderer->info.texture_formats[i];
1233                 break;
1234             }
1235         }
1236     }
1237
1238     texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STATIC,
1239                                 surface->w, surface->h);
1240     if (!texture) {
1241         return NULL;
1242     }
1243
1244     if (format == surface->format->format) {
1245         if (surface->format->Amask && SDL_HasColorKey(surface)) {
1246             /* Surface and Renderer formats are identicals. 
1247              * Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */
1248             direct_update = SDL_FALSE;
1249         } else {
1250             /* Update Texture directly */
1251             direct_update = SDL_TRUE;
1252         }
1253     } else {
1254         /* Surface and Renderer formats are differents, it needs an intermediate conversion. */
1255         direct_update = SDL_FALSE;
1256     }
1257
1258     if (direct_update) {
1259         if (SDL_MUSTLOCK(surface)) {
1260             SDL_LockSurface(surface);
1261             SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
1262             SDL_UnlockSurface(surface);
1263         } else {
1264             SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
1265         }
1266     } else {
1267         SDL_PixelFormat *dst_fmt;
1268         SDL_Surface *temp = NULL;
1269
1270         /* Set up a destination surface for the texture update */
1271         dst_fmt = SDL_AllocFormat(format);
1272         if (!dst_fmt) {
1273            SDL_DestroyTexture(texture);
1274            return NULL;
1275         }
1276         temp = SDL_ConvertSurface(surface, dst_fmt, 0);
1277         SDL_FreeFormat(dst_fmt);
1278         if (temp) {
1279             SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
1280             SDL_FreeSurface(temp);
1281         } else {
1282             SDL_DestroyTexture(texture);
1283             return NULL;
1284         }
1285     }
1286
1287     {
1288         Uint8 r, g, b, a;
1289         SDL_BlendMode blendMode;
1290
1291         SDL_GetSurfaceColorMod(surface, &r, &g, &b);
1292         SDL_SetTextureColorMod(texture, r, g, b);
1293
1294         SDL_GetSurfaceAlphaMod(surface, &a);
1295         SDL_SetTextureAlphaMod(texture, a);
1296
1297         if (SDL_HasColorKey(surface)) {
1298             /* We converted to a texture with alpha format */
1299             SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
1300         } else {
1301             SDL_GetSurfaceBlendMode(surface, &blendMode);
1302             SDL_SetTextureBlendMode(texture, blendMode);
1303         }
1304     }
1305     return texture;
1306 }
1307
1308 int
1309 SDL_QueryTexture(SDL_Texture * texture, Uint32 * format, int *access,
1310                  int *w, int *h)
1311 {
1312     CHECK_TEXTURE_MAGIC(texture, -1);
1313
1314     if (format) {
1315         *format = texture->format;
1316     }
1317     if (access) {
1318         *access = texture->access;
1319     }
1320     if (w) {
1321         *w = texture->w;
1322     }
1323     if (h) {
1324         *h = texture->h;
1325     }
1326     return 0;
1327 }
1328
1329 int
1330 SDL_SetTextureColorMod(SDL_Texture * texture, Uint8 r, Uint8 g, Uint8 b)
1331 {
1332     CHECK_TEXTURE_MAGIC(texture, -1);
1333
1334     if (r < 255 || g < 255 || b < 255) {
1335         texture->modMode |= SDL_TEXTUREMODULATE_COLOR;
1336     } else {
1337         texture->modMode &= ~SDL_TEXTUREMODULATE_COLOR;
1338     }
1339     texture->r = r;
1340     texture->g = g;
1341     texture->b = b;
1342     if (texture->native) {
1343         return SDL_SetTextureColorMod(texture->native, r, g, b);
1344     }
1345     return 0;
1346 }
1347
1348 int
1349 SDL_GetTextureColorMod(SDL_Texture * texture, Uint8 * r, Uint8 * g,
1350                        Uint8 * b)
1351 {
1352     CHECK_TEXTURE_MAGIC(texture, -1);
1353
1354     if (r) {
1355         *r = texture->r;
1356     }
1357     if (g) {
1358         *g = texture->g;
1359     }
1360     if (b) {
1361         *b = texture->b;
1362     }
1363     return 0;
1364 }
1365
1366 int
1367 SDL_SetTextureAlphaMod(SDL_Texture * texture, Uint8 alpha)
1368 {
1369     CHECK_TEXTURE_MAGIC(texture, -1);
1370
1371     if (alpha < 255) {
1372         texture->modMode |= SDL_TEXTUREMODULATE_ALPHA;
1373     } else {
1374         texture->modMode &= ~SDL_TEXTUREMODULATE_ALPHA;
1375     }
1376     texture->a = alpha;
1377     if (texture->native) {
1378         return SDL_SetTextureAlphaMod(texture->native, alpha);
1379     }
1380     return 0;
1381 }
1382
1383 int
1384 SDL_GetTextureAlphaMod(SDL_Texture * texture, Uint8 * alpha)
1385 {
1386     CHECK_TEXTURE_MAGIC(texture, -1);
1387
1388     if (alpha) {
1389         *alpha = texture->a;
1390     }
1391     return 0;
1392 }
1393
1394 int
1395 SDL_SetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode blendMode)
1396 {
1397     SDL_Renderer *renderer;
1398
1399     CHECK_TEXTURE_MAGIC(texture, -1);
1400
1401     renderer = texture->renderer;
1402     if (!IsSupportedBlendMode(renderer, blendMode)) {
1403         return SDL_Unsupported();
1404     }
1405     texture->blendMode = blendMode;
1406     if (texture->native) {
1407         return SDL_SetTextureBlendMode(texture->native, blendMode);
1408     }
1409     return 0;
1410 }
1411
1412 int
1413 SDL_GetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode *blendMode)
1414 {
1415     CHECK_TEXTURE_MAGIC(texture, -1);
1416
1417     if (blendMode) {
1418         *blendMode = texture->blendMode;
1419     }
1420     return 0;
1421 }
1422
1423 int
1424 SDL_SetTextureScaleMode(SDL_Texture * texture, SDL_ScaleMode scaleMode)
1425 {
1426     SDL_Renderer *renderer;
1427
1428     CHECK_TEXTURE_MAGIC(texture, -1);
1429
1430     renderer = texture->renderer;
1431     renderer->SetTextureScaleMode(renderer, texture, scaleMode);
1432     texture->scaleMode = scaleMode;
1433     if (texture->native) {
1434         return SDL_SetTextureScaleMode(texture->native, scaleMode);
1435     }
1436     return 0;
1437 }
1438
1439 int
1440 SDL_GetTextureScaleMode(SDL_Texture * texture, SDL_ScaleMode *scaleMode)
1441 {
1442     CHECK_TEXTURE_MAGIC(texture, -1);
1443
1444     if (scaleMode) {
1445         *scaleMode = texture->scaleMode;
1446     }
1447     return 0;
1448 }
1449
1450 #if SDL_HAVE_YUV
1451 static int
1452 SDL_UpdateTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
1453                      const void *pixels, int pitch)
1454 {
1455     SDL_Texture *native = texture->native;
1456     SDL_Rect full_rect;
1457
1458     if (SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch) < 0) {
1459         return -1;
1460     }
1461
1462     full_rect.x = 0;
1463     full_rect.y = 0;
1464     full_rect.w = texture->w;
1465     full_rect.h = texture->h;
1466     rect = &full_rect;
1467
1468     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
1469         /* We can lock the texture and copy to it */
1470         void *native_pixels = NULL;
1471         int native_pitch = 0;
1472
1473         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
1474             return -1;
1475         }
1476         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
1477                             rect->w, rect->h, native_pixels, native_pitch);
1478         SDL_UnlockTexture(native);
1479     } else {
1480         /* Use a temporary buffer for updating */
1481         const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
1482         const size_t alloclen = rect->h * temp_pitch;
1483         if (alloclen > 0) {
1484             void *temp_pixels = SDL_malloc(alloclen);
1485             if (!temp_pixels) {
1486                 return SDL_OutOfMemory();
1487             }
1488             SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
1489                                 rect->w, rect->h, temp_pixels, temp_pitch);
1490             SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
1491             SDL_free(temp_pixels);
1492         }
1493     }
1494     return 0;
1495 }
1496 #endif /* SDL_HAVE_YUV */
1497
1498 static int
1499 SDL_UpdateTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
1500                         const void *pixels, int pitch)
1501 {
1502     SDL_Texture *native = texture->native;
1503
1504     if (!rect->w || !rect->h) {
1505         return 0;  /* nothing to do. */
1506     }
1507
1508     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
1509         /* We can lock the texture and copy to it */
1510         void *native_pixels = NULL;
1511         int native_pitch = 0;
1512
1513         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
1514             return -1;
1515         }
1516         SDL_ConvertPixels(rect->w, rect->h,
1517                           texture->format, pixels, pitch,
1518                           native->format, native_pixels, native_pitch);
1519         SDL_UnlockTexture(native);
1520     } else {
1521         /* Use a temporary buffer for updating */
1522         const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
1523         const size_t alloclen = rect->h * temp_pitch;
1524         if (alloclen > 0) {
1525             void *temp_pixels = SDL_malloc(alloclen);
1526             if (!temp_pixels) {
1527                 return SDL_OutOfMemory();
1528             }
1529             SDL_ConvertPixels(rect->w, rect->h,
1530                               texture->format, pixels, pitch,
1531                               native->format, temp_pixels, temp_pitch);
1532             SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
1533             SDL_free(temp_pixels);
1534         }
1535     }
1536     return 0;
1537 }
1538
1539 int
1540 SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect,
1541                   const void *pixels, int pitch)
1542 {
1543     SDL_Rect full_rect;
1544
1545     CHECK_TEXTURE_MAGIC(texture, -1);
1546
1547     if (!pixels) {
1548         return SDL_InvalidParamError("pixels");
1549     }
1550     if (!pitch) {
1551         return SDL_InvalidParamError("pitch");
1552     }
1553
1554     if (!rect) {
1555         full_rect.x = 0;
1556         full_rect.y = 0;
1557         full_rect.w = texture->w;
1558         full_rect.h = texture->h;
1559         rect = &full_rect;
1560     }
1561
1562     if ((rect->w == 0) || (rect->h == 0)) {
1563         return 0;  /* nothing to do. */
1564 #if SDL_HAVE_YUV
1565     } else if (texture->yuv) {
1566         return SDL_UpdateTextureYUV(texture, rect, pixels, pitch);
1567 #endif
1568     } else if (texture->native) {
1569         return SDL_UpdateTextureNative(texture, rect, pixels, pitch);
1570     } else {
1571         SDL_Renderer *renderer = texture->renderer;
1572         if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
1573             return -1;
1574         }
1575         return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
1576     }
1577 }
1578
1579 #if SDL_HAVE_YUV
1580 static int
1581 SDL_UpdateTextureYUVPlanar(SDL_Texture * texture, const SDL_Rect * rect,
1582                            const Uint8 *Yplane, int Ypitch,
1583                            const Uint8 *Uplane, int Upitch,
1584                            const Uint8 *Vplane, int Vpitch)
1585 {
1586     SDL_Texture *native = texture->native;
1587     SDL_Rect full_rect;
1588
1589     if (SDL_SW_UpdateYUVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch) < 0) {
1590         return -1;
1591     }
1592
1593     full_rect.x = 0;
1594     full_rect.y = 0;
1595     full_rect.w = texture->w;
1596     full_rect.h = texture->h;
1597     rect = &full_rect;
1598
1599     if (!rect->w || !rect->h) {
1600         return 0;  /* nothing to do. */
1601     }
1602
1603     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
1604         /* We can lock the texture and copy to it */
1605         void *native_pixels = NULL;
1606         int native_pitch = 0;
1607
1608         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
1609             return -1;
1610         }
1611         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
1612                             rect->w, rect->h, native_pixels, native_pitch);
1613         SDL_UnlockTexture(native);
1614     } else {
1615         /* Use a temporary buffer for updating */
1616         const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
1617         const size_t alloclen = rect->h * temp_pitch;
1618         if (alloclen > 0) {
1619             void *temp_pixels = SDL_malloc(alloclen);
1620             if (!temp_pixels) {
1621                 return SDL_OutOfMemory();
1622             }
1623             SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
1624                                 rect->w, rect->h, temp_pixels, temp_pitch);
1625             SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
1626             SDL_free(temp_pixels);
1627         }
1628     }
1629     return 0;
1630 }
1631 #endif /* SDL_HAVE_YUV */
1632
1633 int SDL_UpdateYUVTexture(SDL_Texture * texture, const SDL_Rect * rect,
1634                          const Uint8 *Yplane, int Ypitch,
1635                          const Uint8 *Uplane, int Upitch,
1636                          const Uint8 *Vplane, int Vpitch)
1637 {
1638 #if SDL_HAVE_YUV
1639     SDL_Renderer *renderer;
1640     SDL_Rect full_rect;
1641
1642     CHECK_TEXTURE_MAGIC(texture, -1);
1643
1644     if (!Yplane) {
1645         return SDL_InvalidParamError("Yplane");
1646     }
1647     if (!Ypitch) {
1648         return SDL_InvalidParamError("Ypitch");
1649     }
1650     if (!Uplane) {
1651         return SDL_InvalidParamError("Uplane");
1652     }
1653     if (!Upitch) {
1654         return SDL_InvalidParamError("Upitch");
1655     }
1656     if (!Vplane) {
1657         return SDL_InvalidParamError("Vplane");
1658     }
1659     if (!Vpitch) {
1660         return SDL_InvalidParamError("Vpitch");
1661     }
1662
1663     if (texture->format != SDL_PIXELFORMAT_YV12 &&
1664         texture->format != SDL_PIXELFORMAT_IYUV) {
1665         return SDL_SetError("Texture format must by YV12 or IYUV");
1666     }
1667
1668     if (!rect) {
1669         full_rect.x = 0;
1670         full_rect.y = 0;
1671         full_rect.w = texture->w;
1672         full_rect.h = texture->h;
1673         rect = &full_rect;
1674     }
1675
1676     if (!rect->w || !rect->h) {
1677         return 0;  /* nothing to do. */
1678     }
1679
1680     if (texture->yuv) {
1681         return SDL_UpdateTextureYUVPlanar(texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
1682     } else {
1683         SDL_assert(!texture->native);
1684         renderer = texture->renderer;
1685         SDL_assert(renderer->UpdateTextureYUV);
1686         if (renderer->UpdateTextureYUV) {
1687             if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
1688                 return -1;
1689             }
1690             return renderer->UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
1691         } else {
1692             return SDL_Unsupported();
1693         }
1694     }
1695 #else
1696     return -1;
1697 #endif
1698 }
1699
1700 #if SDL_HAVE_YUV
1701 static int
1702 SDL_LockTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
1703                    void **pixels, int *pitch)
1704 {
1705     return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch);
1706 }
1707 #endif /* SDL_HAVE_YUV */
1708
1709 static int
1710 SDL_LockTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
1711                       void **pixels, int *pitch)
1712 {
1713     texture->locked_rect = *rect;
1714     *pixels = (void *) ((Uint8 *) texture->pixels +
1715                         rect->y * texture->pitch +
1716                         rect->x * SDL_BYTESPERPIXEL(texture->format));
1717     *pitch = texture->pitch;
1718     return 0;
1719 }
1720
1721 int
1722 SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect,
1723                 void **pixels, int *pitch)
1724 {
1725     SDL_Rect full_rect;
1726
1727     CHECK_TEXTURE_MAGIC(texture, -1);
1728
1729     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
1730         return SDL_SetError("SDL_LockTexture(): texture must be streaming");
1731     }
1732
1733     if (!rect) {
1734         full_rect.x = 0;
1735         full_rect.y = 0;
1736         full_rect.w = texture->w;
1737         full_rect.h = texture->h;
1738         rect = &full_rect;
1739     }
1740
1741 #if SDL_HAVE_YUV
1742     if (texture->yuv) {
1743         if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
1744             return -1;
1745         }
1746         return SDL_LockTextureYUV(texture, rect, pixels, pitch);
1747     } else
1748 #endif
1749     if (texture->native) {
1750         /* Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. */
1751         return SDL_LockTextureNative(texture, rect, pixels, pitch);
1752     } else {
1753         SDL_Renderer *renderer = texture->renderer;
1754         if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
1755             return -1;
1756         }
1757         return renderer->LockTexture(renderer, texture, rect, pixels, pitch);
1758     }
1759 }
1760
1761 int
1762 SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect,
1763                          SDL_Surface **surface)
1764 {
1765     SDL_Rect real_rect;
1766     void *pixels = NULL;
1767     int pitch = 0; /* fix static analysis */
1768     int ret;
1769
1770     if (texture == NULL || surface == NULL) {
1771         return -1;
1772     }
1773
1774     real_rect.x = 0;
1775     real_rect.y = 0;
1776     real_rect.w = texture->w;
1777     real_rect.h = texture->h;
1778
1779     if (rect) {
1780         SDL_IntersectRect(rect, &real_rect, &real_rect);
1781     }
1782
1783     ret = SDL_LockTexture(texture, &real_rect, &pixels, &pitch);
1784     if (ret < 0) {
1785         return ret;
1786     }
1787
1788     texture->locked_surface = SDL_CreateRGBSurfaceWithFormatFrom(pixels, real_rect.w, real_rect.h, 0, pitch, texture->format);
1789     if (texture->locked_surface == NULL) {
1790         SDL_UnlockTexture(texture);
1791         return -1;
1792     }
1793
1794     *surface = texture->locked_surface;
1795     return 0;
1796 }
1797
1798 #if SDL_HAVE_YUV
1799 static void
1800 SDL_UnlockTextureYUV(SDL_Texture * texture)
1801 {
1802     SDL_Texture *native = texture->native;
1803     void *native_pixels = NULL;
1804     int native_pitch = 0;
1805     SDL_Rect rect;
1806
1807     rect.x = 0;
1808     rect.y = 0;
1809     rect.w = texture->w;
1810     rect.h = texture->h;
1811
1812     if (SDL_LockTexture(native, &rect, &native_pixels, &native_pitch) < 0) {
1813         return;
1814     }
1815     SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format,
1816                         rect.w, rect.h, native_pixels, native_pitch);
1817     SDL_UnlockTexture(native);
1818 }
1819 #endif /* SDL_HAVE_YUV */
1820
1821 static void
1822 SDL_UnlockTextureNative(SDL_Texture * texture)
1823 {
1824     SDL_Texture *native = texture->native;
1825     void *native_pixels = NULL;
1826     int native_pitch = 0;
1827     const SDL_Rect *rect = &texture->locked_rect;
1828     const void* pixels = (void *) ((Uint8 *) texture->pixels +
1829                         rect->y * texture->pitch +
1830                         rect->x * SDL_BYTESPERPIXEL(texture->format));
1831     int pitch = texture->pitch;
1832
1833     if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
1834         return;
1835     }
1836     SDL_ConvertPixels(rect->w, rect->h,
1837                       texture->format, pixels, pitch,
1838                       native->format, native_pixels, native_pitch);
1839     SDL_UnlockTexture(native);
1840 }
1841
1842 void
1843 SDL_UnlockTexture(SDL_Texture * texture)
1844 {
1845     CHECK_TEXTURE_MAGIC(texture, );
1846
1847     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
1848         return;
1849     }
1850 #if SDL_HAVE_YUV
1851     if (texture->yuv) {
1852         SDL_UnlockTextureYUV(texture);
1853     } else
1854 #endif
1855     if (texture->native) {
1856         SDL_UnlockTextureNative(texture);
1857     } else {
1858         SDL_Renderer *renderer = texture->renderer;
1859         renderer->UnlockTexture(renderer, texture);
1860     }
1861
1862     SDL_FreeSurface(texture->locked_surface);
1863     texture->locked_surface = NULL;
1864 }
1865
1866 SDL_bool
1867 SDL_RenderTargetSupported(SDL_Renderer *renderer)
1868 {
1869     if (!renderer || !renderer->SetRenderTarget) {
1870         return SDL_FALSE;
1871     }
1872     return (renderer->info.flags & SDL_RENDERER_TARGETTEXTURE) != 0;
1873 }
1874
1875 int
1876 SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
1877 {
1878     if (!SDL_RenderTargetSupported(renderer)) {
1879         return SDL_Unsupported();
1880     }
1881
1882     /* texture == NULL is valid and means reset the target to the window */
1883     if (texture) {
1884         CHECK_TEXTURE_MAGIC(texture, -1);
1885         if (renderer != texture->renderer) {
1886             return SDL_SetError("Texture was not created with this renderer");
1887         }
1888         if (texture->access != SDL_TEXTUREACCESS_TARGET) {
1889             return SDL_SetError("Texture not created with SDL_TEXTUREACCESS_TARGET");
1890         }
1891         if (texture->native) {
1892             /* Always render to the native texture */
1893             texture = texture->native;
1894         }
1895     }
1896
1897     if (texture == renderer->target) {
1898         /* Nothing to do! */
1899         return 0;
1900     }
1901
1902     FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
1903
1904     SDL_LockMutex(renderer->target_mutex);
1905
1906     if (texture && !renderer->target) {
1907         /* Make a backup of the viewport */
1908         renderer->viewport_backup = renderer->viewport;
1909         renderer->clip_rect_backup = renderer->clip_rect;
1910         renderer->clipping_enabled_backup = renderer->clipping_enabled;
1911         renderer->scale_backup = renderer->scale;
1912         renderer->logical_w_backup = renderer->logical_w;
1913         renderer->logical_h_backup = renderer->logical_h;
1914     }
1915     renderer->target = texture;
1916
1917     if (renderer->SetRenderTarget(renderer, texture) < 0) {
1918         SDL_UnlockMutex(renderer->target_mutex);
1919         return -1;
1920     }
1921
1922     if (texture) {
1923         renderer->viewport.x = 0;
1924         renderer->viewport.y = 0;
1925         renderer->viewport.w = texture->w;
1926         renderer->viewport.h = texture->h;
1927         SDL_zero(renderer->clip_rect);
1928         renderer->clipping_enabled = SDL_FALSE;
1929         renderer->scale.x = 1.0f;
1930         renderer->scale.y = 1.0f;
1931         renderer->logical_w = texture->w;
1932         renderer->logical_h = texture->h;
1933     } else {
1934         renderer->viewport = renderer->viewport_backup;
1935         renderer->clip_rect = renderer->clip_rect_backup;
1936         renderer->clipping_enabled = renderer->clipping_enabled_backup;
1937         renderer->scale = renderer->scale_backup;
1938         renderer->logical_w = renderer->logical_w_backup;
1939         renderer->logical_h = renderer->logical_h_backup;
1940     }
1941
1942     SDL_UnlockMutex(renderer->target_mutex);
1943
1944     if (QueueCmdSetViewport(renderer) < 0) {
1945         return -1;
1946     }
1947     if (QueueCmdSetClipRect(renderer) < 0) {
1948         return -1;
1949     }
1950
1951     /* All set! */
1952     return FlushRenderCommandsIfNotBatching(renderer);
1953 }
1954
1955 SDL_Texture *
1956 SDL_GetRenderTarget(SDL_Renderer *renderer)
1957 {
1958     return renderer->target;
1959 }
1960
1961 static int
1962 UpdateLogicalSize(SDL_Renderer *renderer)
1963 {
1964     int w = 1, h = 1;
1965     float want_aspect;
1966     float real_aspect;
1967     float scale;
1968     SDL_Rect viewport;
1969     /* 0 is for letterbox, 1 is for overscan */
1970     int scale_policy = 0;
1971     const char *hint;
1972
1973     if (!renderer->logical_w || !renderer->logical_h) {
1974         return 0;
1975     }
1976     if (SDL_GetRendererOutputSize(renderer, &w, &h) < 0) {
1977         return -1;
1978     }
1979
1980     hint = SDL_GetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE);
1981     if (hint && (*hint == '1' || SDL_strcasecmp(hint, "overscan") == 0))  {
1982 #if SDL_VIDEO_RENDER_D3D
1983         SDL_bool overscan_supported = SDL_TRUE;
1984         /* Unfortunately, Direct3D 9 doesn't support negative viewport numbers
1985            which the overscan implementation relies on.
1986         */
1987         if (SDL_strcasecmp(SDL_GetCurrentVideoDriver(), "direct3d") == 0) {
1988             overscan_supported = SDL_FALSE;
1989         }
1990         if (overscan_supported) {
1991             scale_policy = 1;
1992         }
1993 #else
1994         scale_policy = 1;
1995 #endif
1996     }
1997
1998     want_aspect = (float)renderer->logical_w / renderer->logical_h;
1999     real_aspect = (float)w / h;
2000
2001     /* Clear the scale because we're setting viewport in output coordinates */
2002     SDL_RenderSetScale(renderer, 1.0f, 1.0f);
2003
2004     if (renderer->integer_scale) {
2005         if (want_aspect > real_aspect) {
2006             scale = (float)(w / renderer->logical_w);
2007         } else {
2008             scale = (float)(h / renderer->logical_h);
2009         }
2010         viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
2011         viewport.x = (w - viewport.w) / 2;
2012         viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
2013         viewport.y = (h - viewport.h) / 2;
2014
2015         SDL_RenderSetViewport(renderer, &viewport);
2016     } else if (SDL_fabs(want_aspect-real_aspect) < 0.0001) {
2017         /* The aspect ratios are the same, just scale appropriately */
2018         scale = (float)w / renderer->logical_w;
2019         SDL_RenderSetViewport(renderer, NULL);
2020     } else if (want_aspect > real_aspect) {
2021         if (scale_policy == 1) {
2022             /* We want a wider aspect ratio than is available - 
2023              zoom so logical height matches the real height 
2024              and the width will grow off the screen 
2025              */
2026             scale = (float)h / renderer->logical_h;
2027             viewport.y = 0;
2028             viewport.h = h;
2029             viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
2030             viewport.x = (w - viewport.w) / 2;
2031             SDL_RenderSetViewport(renderer, &viewport);
2032         } else {
2033             /* We want a wider aspect ratio than is available - letterbox it */
2034             scale = (float)w / renderer->logical_w;
2035             viewport.x = 0;
2036             viewport.w = w;
2037             viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
2038             viewport.y = (h - viewport.h) / 2;
2039             SDL_RenderSetViewport(renderer, &viewport);
2040         }
2041     } else {
2042         if (scale_policy == 1) {
2043             /* We want a narrower aspect ratio than is available -
2044              zoom so logical width matches the real width
2045              and the height will grow off the screen
2046              */
2047             scale = (float)w / renderer->logical_w;
2048             viewport.x = 0;
2049             viewport.w = w;
2050             viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
2051             viewport.y = (h - viewport.h) / 2;
2052             SDL_RenderSetViewport(renderer, &viewport);
2053         } else {
2054             /* We want a narrower aspect ratio than is available - use side-bars */
2055              scale = (float)h / renderer->logical_h;
2056              viewport.y = 0;
2057              viewport.h = h;
2058              viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
2059              viewport.x = (w - viewport.w) / 2;
2060              SDL_RenderSetViewport(renderer, &viewport);
2061         }
2062     }
2063
2064     /* Set the new scale */
2065     SDL_RenderSetScale(renderer, scale, scale);
2066
2067     return 0;
2068 }
2069
2070 int
2071 SDL_RenderSetLogicalSize(SDL_Renderer * renderer, int w, int h)
2072 {
2073     CHECK_RENDERER_MAGIC(renderer, -1);
2074
2075     if (!w || !h) {
2076         /* Clear any previous logical resolution */
2077         renderer->logical_w = 0;
2078         renderer->logical_h = 0;
2079         SDL_RenderSetViewport(renderer, NULL);
2080         SDL_RenderSetScale(renderer, 1.0f, 1.0f);
2081         return 0;
2082     }
2083
2084     renderer->logical_w = w;
2085     renderer->logical_h = h;
2086
2087     return UpdateLogicalSize(renderer);
2088 }
2089
2090 void
2091 SDL_RenderGetLogicalSize(SDL_Renderer * renderer, int *w, int *h)
2092 {
2093     CHECK_RENDERER_MAGIC(renderer, );
2094
2095     if (w) {
2096         *w = renderer->logical_w;
2097     }
2098     if (h) {
2099         *h = renderer->logical_h;
2100     }
2101 }
2102
2103 int
2104 SDL_RenderSetIntegerScale(SDL_Renderer * renderer, SDL_bool enable)
2105 {
2106     CHECK_RENDERER_MAGIC(renderer, -1);
2107
2108     renderer->integer_scale = enable;
2109
2110     return UpdateLogicalSize(renderer);
2111 }
2112
2113 SDL_bool
2114 SDLCALL SDL_RenderGetIntegerScale(SDL_Renderer * renderer)
2115 {
2116     CHECK_RENDERER_MAGIC(renderer, SDL_FALSE);
2117
2118     return renderer->integer_scale;
2119 }
2120
2121 int
2122 SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect)
2123 {
2124     int retval;
2125     CHECK_RENDERER_MAGIC(renderer, -1);
2126
2127     if (rect) {
2128         renderer->viewport.x = (int)SDL_floor(rect->x * renderer->scale.x);
2129         renderer->viewport.y = (int)SDL_floor(rect->y * renderer->scale.y);
2130         renderer->viewport.w = (int)SDL_ceil(rect->w * renderer->scale.x);
2131         renderer->viewport.h = (int)SDL_ceil(rect->h * renderer->scale.y);
2132     } else {
2133         renderer->viewport.x = 0;
2134         renderer->viewport.y = 0;
2135         if (SDL_GetRendererOutputSize(renderer, &renderer->viewport.w, &renderer->viewport.h) < 0) {
2136             return -1;
2137         }
2138     }
2139     retval = QueueCmdSetViewport(renderer);
2140     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2141 }
2142
2143 void
2144 SDL_RenderGetViewport(SDL_Renderer * renderer, SDL_Rect * rect)
2145 {
2146     CHECK_RENDERER_MAGIC(renderer, );
2147
2148     if (rect) {
2149         rect->x = (int)(renderer->viewport.x / renderer->scale.x);
2150         rect->y = (int)(renderer->viewport.y / renderer->scale.y);
2151         rect->w = (int)(renderer->viewport.w / renderer->scale.x);
2152         rect->h = (int)(renderer->viewport.h / renderer->scale.y);
2153     }
2154 }
2155
2156 int
2157 SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect)
2158 {
2159     int retval;
2160     CHECK_RENDERER_MAGIC(renderer, -1)
2161
2162     if (rect) {
2163         renderer->clipping_enabled = SDL_TRUE;
2164         renderer->clip_rect.x = (int)SDL_floor(rect->x * renderer->scale.x);
2165         renderer->clip_rect.y = (int)SDL_floor(rect->y * renderer->scale.y);
2166         renderer->clip_rect.w = (int)SDL_ceil(rect->w * renderer->scale.x);
2167         renderer->clip_rect.h = (int)SDL_ceil(rect->h * renderer->scale.y);
2168     } else {
2169         renderer->clipping_enabled = SDL_FALSE;
2170         SDL_zero(renderer->clip_rect);
2171     }
2172
2173     retval = QueueCmdSetClipRect(renderer);
2174     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2175 }
2176
2177 void
2178 SDL_RenderGetClipRect(SDL_Renderer * renderer, SDL_Rect * rect)
2179 {
2180     CHECK_RENDERER_MAGIC(renderer, )
2181
2182     if (rect) {
2183         rect->x = (int)(renderer->clip_rect.x / renderer->scale.x);
2184         rect->y = (int)(renderer->clip_rect.y / renderer->scale.y);
2185         rect->w = (int)(renderer->clip_rect.w / renderer->scale.x);
2186         rect->h = (int)(renderer->clip_rect.h / renderer->scale.y);
2187     }
2188 }
2189
2190 SDL_bool
2191 SDL_RenderIsClipEnabled(SDL_Renderer * renderer)
2192 {
2193     CHECK_RENDERER_MAGIC(renderer, SDL_FALSE)
2194     return renderer->clipping_enabled;
2195 }
2196
2197 int
2198 SDL_RenderSetScale(SDL_Renderer * renderer, float scaleX, float scaleY)
2199 {
2200     CHECK_RENDERER_MAGIC(renderer, -1);
2201
2202     renderer->scale.x = scaleX;
2203     renderer->scale.y = scaleY;
2204     return 0;
2205 }
2206
2207 void
2208 SDL_RenderGetScale(SDL_Renderer * renderer, float *scaleX, float *scaleY)
2209 {
2210     CHECK_RENDERER_MAGIC(renderer, );
2211
2212     if (scaleX) {
2213         *scaleX = renderer->scale.x;
2214     }
2215     if (scaleY) {
2216         *scaleY = renderer->scale.y;
2217     }
2218 }
2219
2220 int
2221 SDL_SetRenderDrawColor(SDL_Renderer * renderer,
2222                        Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2223 {
2224     CHECK_RENDERER_MAGIC(renderer, -1);
2225
2226     renderer->r = r;
2227     renderer->g = g;
2228     renderer->b = b;
2229     renderer->a = a;
2230     return 0;
2231 }
2232
2233 int
2234 SDL_GetRenderDrawColor(SDL_Renderer * renderer,
2235                        Uint8 * r, Uint8 * g, Uint8 * b, Uint8 * a)
2236 {
2237     CHECK_RENDERER_MAGIC(renderer, -1);
2238
2239     if (r) {
2240         *r = renderer->r;
2241     }
2242     if (g) {
2243         *g = renderer->g;
2244     }
2245     if (b) {
2246         *b = renderer->b;
2247     }
2248     if (a) {
2249         *a = renderer->a;
2250     }
2251     return 0;
2252 }
2253
2254 int
2255 SDL_SetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
2256 {
2257     CHECK_RENDERER_MAGIC(renderer, -1);
2258
2259     if (!IsSupportedBlendMode(renderer, blendMode)) {
2260         return SDL_Unsupported();
2261     }
2262     renderer->blendMode = blendMode;
2263     return 0;
2264 }
2265
2266 int
2267 SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode *blendMode)
2268 {
2269     CHECK_RENDERER_MAGIC(renderer, -1);
2270
2271     *blendMode = renderer->blendMode;
2272     return 0;
2273 }
2274
2275 int
2276 SDL_RenderClear(SDL_Renderer * renderer)
2277 {
2278     int retval;
2279     CHECK_RENDERER_MAGIC(renderer, -1);
2280     retval = QueueCmdClear(renderer);
2281     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2282 }
2283
2284
2285 /* !!! FIXME: delete all the duplicate code for the integer versions in 2.1,
2286    !!! FIXME:  making the floating point versions the only available APIs. */
2287
2288 int
2289 SDL_RenderDrawPoint(SDL_Renderer * renderer, int x, int y)
2290 {
2291     SDL_FPoint fpoint;
2292     fpoint.x = (float) x;
2293     fpoint.y = (float) y;
2294     return SDL_RenderDrawPointsF(renderer, &fpoint, 1);
2295 }
2296
2297 int
2298 SDL_RenderDrawPointF(SDL_Renderer * renderer, float x, float y)
2299 {
2300     SDL_FPoint fpoint;
2301     fpoint.x = x;
2302     fpoint.y = y;
2303     return SDL_RenderDrawPointsF(renderer, &fpoint, 1);
2304 }
2305
2306 static int
2307 RenderDrawPointsWithRects(SDL_Renderer * renderer,
2308                           const SDL_Point * points, const int count)
2309 {
2310     int retval = -1;
2311     SDL_bool isstack;
2312     SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack);
2313     int i;
2314
2315     if (!frects) {
2316         return SDL_OutOfMemory();
2317     }
2318
2319     for (i = 0; i < count; ++i) {
2320         frects[i].x = points[i].x * renderer->scale.x;
2321         frects[i].y = points[i].y * renderer->scale.y;
2322         frects[i].w = renderer->scale.x;
2323         frects[i].h = renderer->scale.y;
2324     }
2325
2326     retval = QueueCmdFillRects(renderer, frects, count);
2327
2328     SDL_small_free(frects, isstack);
2329
2330     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2331 }
2332
2333 int
2334 SDL_RenderDrawPoints(SDL_Renderer * renderer,
2335                      const SDL_Point * points, int count)
2336 {
2337     SDL_FPoint *fpoints;
2338     int i;
2339     int retval;
2340     SDL_bool isstack;
2341
2342     CHECK_RENDERER_MAGIC(renderer, -1);
2343
2344     if (!points) {
2345         return SDL_SetError("SDL_RenderDrawPoints(): Passed NULL points");
2346     }
2347     if (count < 1) {
2348         return 0;
2349     }
2350
2351     /* Don't draw while we're hidden */
2352     if (renderer->hidden) {
2353         return 0;
2354     }
2355
2356     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
2357         return RenderDrawPointsWithRects(renderer, points, count);
2358     }
2359
2360     fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
2361     if (!fpoints) {
2362         return SDL_OutOfMemory();
2363     }
2364     for (i = 0; i < count; ++i) {
2365         fpoints[i].x = points[i].x * renderer->scale.x;
2366         fpoints[i].y = points[i].y * renderer->scale.y;
2367     }
2368
2369     retval = QueueCmdDrawPoints(renderer, fpoints, count);
2370
2371     SDL_small_free(fpoints, isstack);
2372
2373     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2374 }
2375
2376 static int
2377 RenderDrawPointsWithRectsF(SDL_Renderer * renderer,
2378                            const SDL_FPoint * fpoints, const int count)
2379 {
2380     int retval = -1;
2381     SDL_bool isstack;
2382     SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack);
2383     int i;
2384
2385     if (!frects) {
2386         return SDL_OutOfMemory();
2387     }
2388
2389     for (i = 0; i < count; ++i) {
2390         frects[i].x = fpoints[i].x * renderer->scale.x;
2391         frects[i].y = fpoints[i].y * renderer->scale.y;
2392         frects[i].w = renderer->scale.x;
2393         frects[i].h = renderer->scale.y;
2394     }
2395
2396     retval = QueueCmdFillRects(renderer, frects, count);
2397
2398     SDL_small_free(frects, isstack);
2399
2400     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2401 }
2402
2403 int
2404 SDL_RenderDrawPointsF(SDL_Renderer * renderer,
2405                       const SDL_FPoint * points, int count)
2406 {
2407     SDL_FPoint *fpoints;
2408     int i;
2409     int retval;
2410     SDL_bool isstack;
2411
2412     CHECK_RENDERER_MAGIC(renderer, -1);
2413
2414     if (!points) {
2415         return SDL_SetError("SDL_RenderDrawFPoints(): Passed NULL points");
2416     }
2417     if (count < 1) {
2418         return 0;
2419     }
2420
2421     /* Don't draw while we're hidden */
2422     if (renderer->hidden) {
2423         return 0;
2424     }
2425
2426     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
2427         return RenderDrawPointsWithRectsF(renderer, points, count);
2428     }
2429
2430     fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
2431     if (!fpoints) {
2432         return SDL_OutOfMemory();
2433     }
2434     for (i = 0; i < count; ++i) {
2435         fpoints[i].x = points[i].x * renderer->scale.x;
2436         fpoints[i].y = points[i].y * renderer->scale.y;
2437     }
2438
2439     retval = QueueCmdDrawPoints(renderer, fpoints, count);
2440
2441     SDL_small_free(fpoints, isstack);
2442
2443     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2444 }
2445
2446 int
2447 SDL_RenderDrawLine(SDL_Renderer * renderer, int x1, int y1, int x2, int y2)
2448 {
2449     SDL_FPoint points[2];
2450     points[0].x = (float) x1;
2451     points[0].y = (float) y1;
2452     points[1].x = (float) x2;
2453     points[1].y = (float) y2;
2454     return SDL_RenderDrawLinesF(renderer, points, 2);
2455 }
2456
2457 int
2458 SDL_RenderDrawLineF(SDL_Renderer * renderer, float x1, float y1, float x2, float y2)
2459 {
2460     SDL_FPoint points[2];
2461     points[0].x = x1;
2462     points[0].y = y1;
2463     points[1].x = x2;
2464     points[1].y = y2;
2465     return SDL_RenderDrawLinesF(renderer, points, 2);
2466 }
2467
2468 static int
2469 RenderDrawLinesWithRects(SDL_Renderer * renderer,
2470                      const SDL_Point * points, const int count)
2471 {
2472     SDL_FRect *frect;
2473     SDL_FRect *frects;
2474     SDL_FPoint fpoints[2];
2475     int i, nrects = 0;
2476     int retval = 0;
2477     SDL_bool isstack;
2478
2479     frects = SDL_small_alloc(SDL_FRect, count-1, &isstack);
2480     if (!frects) {
2481         return SDL_OutOfMemory();
2482     }
2483
2484     for (i = 0; i < count-1; ++i) {
2485         if (points[i].x == points[i+1].x) {
2486             const int minY = SDL_min(points[i].y, points[i+1].y);
2487             const int maxY = SDL_max(points[i].y, points[i+1].y);
2488
2489             frect = &frects[nrects++];
2490             frect->x = points[i].x * renderer->scale.x;
2491             frect->y = minY * renderer->scale.y;
2492             frect->w = renderer->scale.x;
2493             frect->h = (maxY - minY + 1) * renderer->scale.y;
2494         } else if (points[i].y == points[i+1].y) {
2495             const int minX = SDL_min(points[i].x, points[i+1].x);
2496             const int maxX = SDL_max(points[i].x, points[i+1].x);
2497
2498             frect = &frects[nrects++];
2499             frect->x = minX * renderer->scale.x;
2500             frect->y = points[i].y * renderer->scale.y;
2501             frect->w = (maxX - minX + 1) * renderer->scale.x;
2502             frect->h = renderer->scale.y;
2503         } else {
2504             /* FIXME: We can't use a rect for this line... */
2505             fpoints[0].x = points[i].x * renderer->scale.x;
2506             fpoints[0].y = points[i].y * renderer->scale.y;
2507             fpoints[1].x = points[i+1].x * renderer->scale.x;
2508             fpoints[1].y = points[i+1].y * renderer->scale.y;
2509             retval += QueueCmdDrawLines(renderer, fpoints, 2);
2510         }
2511     }
2512
2513     retval += QueueCmdFillRects(renderer, frects, nrects);
2514
2515     SDL_small_free(frects, isstack);
2516
2517     if (retval < 0) {
2518         retval = -1;
2519     }
2520     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2521 }
2522
2523 static int
2524 RenderDrawLinesWithRectsF(SDL_Renderer * renderer,
2525                           const SDL_FPoint * points, const int count)
2526 {
2527     SDL_FRect *frect;
2528     SDL_FRect *frects;
2529     SDL_FPoint fpoints[2];
2530     int i, nrects = 0;
2531     int retval = 0;
2532     SDL_bool isstack;
2533
2534     frects = SDL_small_alloc(SDL_FRect, count-1, &isstack);
2535     if (!frects) {
2536         return SDL_OutOfMemory();
2537     }
2538
2539     for (i = 0; i < count-1; ++i) {
2540         if (points[i].x == points[i+1].x) {
2541             const int minY = (int)SDL_min(points[i].y, points[i+1].y);
2542             const int maxY = (int)SDL_max(points[i].y, points[i+1].y);
2543
2544             frect = &frects[nrects++];
2545             frect->x = points[i].x * renderer->scale.x;
2546             frect->y = minY * renderer->scale.y;
2547             frect->w = renderer->scale.x;
2548             frect->h = (maxY - minY + 1) * renderer->scale.y;
2549         } else if (points[i].y == points[i+1].y) {
2550             const int minX = (int)SDL_min(points[i].x, points[i+1].x);
2551             const int maxX = (int)SDL_max(points[i].x, points[i+1].x);
2552
2553             frect = &frects[nrects++];
2554             frect->x = minX * renderer->scale.x;
2555             frect->y = points[i].y * renderer->scale.y;
2556             frect->w = (maxX - minX + 1) * renderer->scale.x;
2557             frect->h = renderer->scale.y;
2558         } else {
2559             /* FIXME: We can't use a rect for this line... */
2560             fpoints[0].x = points[i].x * renderer->scale.x;
2561             fpoints[0].y = points[i].y * renderer->scale.y;
2562             fpoints[1].x = points[i+1].x * renderer->scale.x;
2563             fpoints[1].y = points[i+1].y * renderer->scale.y;
2564             retval += QueueCmdDrawLines(renderer, fpoints, 2);
2565         }
2566     }
2567
2568     retval += QueueCmdFillRects(renderer, frects, nrects);
2569
2570     SDL_small_free(frects, isstack);
2571
2572     if (retval < 0) {
2573         retval = -1;
2574     }
2575     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2576 }
2577
2578 int
2579 SDL_RenderDrawLines(SDL_Renderer * renderer,
2580                     const SDL_Point * points, int count)
2581 {
2582     SDL_FPoint *fpoints;
2583     int i;
2584     int retval;
2585     SDL_bool isstack;
2586
2587     CHECK_RENDERER_MAGIC(renderer, -1);
2588
2589     if (!points) {
2590         return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points");
2591     }
2592     if (count < 2) {
2593         return 0;
2594     }
2595
2596     /* Don't draw while we're hidden */
2597     if (renderer->hidden) {
2598         return 0;
2599     }
2600
2601     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
2602         return RenderDrawLinesWithRects(renderer, points, count);
2603     }
2604
2605     fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
2606     if (!fpoints) {
2607         return SDL_OutOfMemory();
2608     }
2609     for (i = 0; i < count; ++i) {
2610         fpoints[i].x = points[i].x * renderer->scale.x;
2611         fpoints[i].y = points[i].y * renderer->scale.y;
2612     }
2613
2614     retval = QueueCmdDrawLines(renderer, fpoints, count);
2615
2616     SDL_small_free(fpoints, isstack);
2617
2618     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2619 }
2620
2621 int
2622 SDL_RenderDrawLinesF(SDL_Renderer * renderer,
2623                      const SDL_FPoint * points, int count)
2624 {
2625     SDL_FPoint *fpoints;
2626     int i;
2627     int retval;
2628     SDL_bool isstack;
2629
2630     CHECK_RENDERER_MAGIC(renderer, -1);
2631
2632     if (!points) {
2633         return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points");
2634     }
2635     if (count < 2) {
2636         return 0;
2637     }
2638
2639     /* Don't draw while we're hidden */
2640     if (renderer->hidden) {
2641         return 0;
2642     }
2643
2644     if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
2645         return RenderDrawLinesWithRectsF(renderer, points, count);
2646     }
2647
2648     fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
2649     if (!fpoints) {
2650         return SDL_OutOfMemory();
2651     }
2652     for (i = 0; i < count; ++i) {
2653         fpoints[i].x = points[i].x * renderer->scale.x;
2654         fpoints[i].y = points[i].y * renderer->scale.y;
2655     }
2656
2657     retval = QueueCmdDrawLines(renderer, fpoints, count);
2658
2659     SDL_small_free(fpoints, isstack);
2660
2661     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2662 }
2663
2664 int
2665 SDL_RenderDrawRect(SDL_Renderer * renderer, const SDL_Rect * rect)
2666 {
2667     SDL_FRect frect;
2668     SDL_FRect *prect = NULL;
2669
2670     if (rect) {
2671         frect.x = (float) rect->x;
2672         frect.y = (float) rect->y;
2673         frect.w = (float) rect->w;
2674         frect.h = (float) rect->h;
2675         prect = &frect;
2676     }
2677
2678     return SDL_RenderDrawRectF(renderer, prect);
2679 }
2680
2681 int
2682 SDL_RenderDrawRectF(SDL_Renderer * renderer, const SDL_FRect * rect)
2683 {
2684     SDL_FRect frect;
2685     SDL_FPoint points[5];
2686
2687     CHECK_RENDERER_MAGIC(renderer, -1);
2688
2689     /* If 'rect' == NULL, then outline the whole surface */
2690     if (!rect) {
2691         SDL_Rect r;
2692         SDL_RenderGetViewport(renderer, &r);
2693         frect.x = 0.0f;
2694         frect.y = 0.0f;
2695         frect.w = (float) r.w;
2696         frect.h = (float) r.h;
2697         rect = &frect;
2698     }
2699
2700     points[0].x = rect->x;
2701     points[0].y = rect->y;
2702     points[1].x = rect->x+rect->w-1;
2703     points[1].y = rect->y;
2704     points[2].x = rect->x+rect->w-1;
2705     points[2].y = rect->y+rect->h-1;
2706     points[3].x = rect->x;
2707     points[3].y = rect->y+rect->h-1;
2708     points[4].x = rect->x;
2709     points[4].y = rect->y;
2710     return SDL_RenderDrawLinesF(renderer, points, 5);
2711 }
2712
2713 int
2714 SDL_RenderDrawRects(SDL_Renderer * renderer,
2715                     const SDL_Rect * rects, int count)
2716 {
2717     int i;
2718
2719     CHECK_RENDERER_MAGIC(renderer, -1);
2720
2721     if (!rects) {
2722         return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects");
2723     }
2724     if (count < 1) {
2725         return 0;
2726     }
2727
2728     /* Don't draw while we're hidden */
2729     if (renderer->hidden) {
2730         return 0;
2731     }
2732
2733     for (i = 0; i < count; ++i) {
2734         if (SDL_RenderDrawRect(renderer, &rects[i]) < 0) {
2735             return -1;
2736         }
2737     }
2738     return 0;
2739 }
2740
2741 int
2742 SDL_RenderDrawRectsF(SDL_Renderer * renderer,
2743                      const SDL_FRect * rects, int count)
2744 {
2745     int i;
2746
2747     CHECK_RENDERER_MAGIC(renderer, -1);
2748
2749     if (!rects) {
2750         return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects");
2751     }
2752     if (count < 1) {
2753         return 0;
2754     }
2755
2756     /* Don't draw while we're hidden */
2757     if (renderer->hidden) {
2758         return 0;
2759     }
2760
2761     for (i = 0; i < count; ++i) {
2762         if (SDL_RenderDrawRectF(renderer, &rects[i]) < 0) {
2763             return -1;
2764         }
2765     }
2766     return 0;
2767 }
2768
2769 int
2770 SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect)
2771 {
2772     SDL_FRect frect;
2773
2774     CHECK_RENDERER_MAGIC(renderer, -1);
2775
2776     /* If 'rect' == NULL, then outline the whole surface */
2777     if (rect) {
2778         frect.x = (float) rect->x;
2779         frect.y = (float) rect->y;
2780         frect.w = (float) rect->w;
2781         frect.h = (float) rect->h;
2782     } else {
2783         SDL_Rect r;
2784         SDL_zero(r);
2785         SDL_RenderGetViewport(renderer, &r);
2786         frect.x = 0.0f;
2787         frect.y = 0.0f;
2788         frect.w = (float) r.w;
2789         frect.h = (float) r.h;
2790     }
2791     return SDL_RenderFillRectsF(renderer, &frect, 1);
2792 }
2793
2794 int
2795 SDL_RenderFillRectF(SDL_Renderer * renderer, const SDL_FRect * rect)
2796 {
2797     SDL_FRect frect;
2798
2799     CHECK_RENDERER_MAGIC(renderer, -1);
2800
2801     /* If 'rect' == NULL, then outline the whole surface */
2802     if (!rect) {
2803         SDL_Rect r;
2804         SDL_zero(r);
2805         SDL_RenderGetViewport(renderer, &r);
2806         frect.x = 0.0f;
2807         frect.y = 0.0f;
2808         frect.w = (float) r.w;
2809         frect.h = (float) r.h;
2810         rect = &frect;
2811     }
2812     return SDL_RenderFillRectsF(renderer, rect, 1);
2813 }
2814
2815 int
2816 SDL_RenderFillRects(SDL_Renderer * renderer,
2817                     const SDL_Rect * rects, int count)
2818 {
2819     SDL_FRect *frects;
2820     int i;
2821     int retval;
2822     SDL_bool isstack;
2823
2824     CHECK_RENDERER_MAGIC(renderer, -1);
2825
2826     if (!rects) {
2827         return SDL_SetError("SDL_RenderFillRects(): Passed NULL rects");
2828     }
2829     if (count < 1) {
2830         return 0;
2831     }
2832
2833     /* Don't draw while we're hidden */
2834     if (renderer->hidden) {
2835         return 0;
2836     }
2837
2838     frects = SDL_small_alloc(SDL_FRect, count, &isstack);
2839     if (!frects) {
2840         return SDL_OutOfMemory();
2841     }
2842     for (i = 0; i < count; ++i) {
2843         frects[i].x = rects[i].x * renderer->scale.x;
2844         frects[i].y = rects[i].y * renderer->scale.y;
2845         frects[i].w = rects[i].w * renderer->scale.x;
2846         frects[i].h = rects[i].h * renderer->scale.y;
2847     }
2848
2849     retval = QueueCmdFillRects(renderer, frects, count);
2850
2851     SDL_small_free(frects, isstack);
2852
2853     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2854 }
2855
2856 int
2857 SDL_RenderFillRectsF(SDL_Renderer * renderer,
2858                      const SDL_FRect * rects, int count)
2859 {
2860     SDL_FRect *frects;
2861     int i;
2862     int retval;
2863     SDL_bool isstack;
2864
2865     CHECK_RENDERER_MAGIC(renderer, -1);
2866
2867     if (!rects) {
2868         return SDL_SetError("SDL_RenderFillFRects(): Passed NULL rects");
2869     }
2870     if (count < 1) {
2871         return 0;
2872     }
2873
2874     /* Don't draw while we're hidden */
2875     if (renderer->hidden) {
2876         return 0;
2877     }
2878
2879     frects = SDL_small_alloc(SDL_FRect, count, &isstack);
2880     if (!frects) {
2881         return SDL_OutOfMemory();
2882     }
2883     for (i = 0; i < count; ++i) {
2884         frects[i].x = rects[i].x * renderer->scale.x;
2885         frects[i].y = rects[i].y * renderer->scale.y;
2886         frects[i].w = rects[i].w * renderer->scale.x;
2887         frects[i].h = rects[i].h * renderer->scale.y;
2888     }
2889
2890     retval = QueueCmdFillRects(renderer, frects, count);
2891
2892     SDL_small_free(frects, isstack);
2893
2894     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2895 }
2896
2897 /* !!! FIXME: move this to a public API if we want to do float versions of all of these later */
2898 SDL_FORCE_INLINE SDL_bool SDL_FRectEmpty(const SDL_FRect *r)
2899 {
2900     return ((!r) || (r->w <= 0.0f) || (r->h <= 0.0f)) ? SDL_TRUE : SDL_FALSE;
2901 }
2902
2903 /* !!! FIXME: move this to a public API if we want to do float versions of all of these later */
2904 static SDL_bool
2905 SDL_HasIntersectionF(const SDL_FRect * A, const SDL_FRect * B)
2906 {
2907     float Amin, Amax, Bmin, Bmax;
2908
2909     if (!A) {
2910         SDL_InvalidParamError("A");
2911         return SDL_FALSE;
2912     }
2913
2914     if (!B) {
2915         SDL_InvalidParamError("B");
2916         return SDL_FALSE;
2917     }
2918
2919     /* Special cases for empty rects */
2920     if (SDL_FRectEmpty(A) || SDL_FRectEmpty(B)) {
2921         return SDL_FALSE;
2922     }
2923
2924     /* Horizontal intersection */
2925     Amin = A->x;
2926     Amax = Amin + A->w;
2927     Bmin = B->x;
2928     Bmax = Bmin + B->w;
2929     if (Bmin > Amin)
2930         Amin = Bmin;
2931     if (Bmax < Amax)
2932         Amax = Bmax;
2933     if (Amax <= Amin)
2934         return SDL_FALSE;
2935
2936     /* Vertical intersection */
2937     Amin = A->y;
2938     Amax = Amin + A->h;
2939     Bmin = B->y;
2940     Bmax = Bmin + B->h;
2941     if (Bmin > Amin)
2942         Amin = Bmin;
2943     if (Bmax < Amax)
2944         Amax = Bmax;
2945     if (Amax <= Amin)
2946         return SDL_FALSE;
2947
2948     return SDL_TRUE;
2949 }
2950
2951 int
2952 SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
2953                const SDL_Rect * srcrect, const SDL_Rect * dstrect)
2954 {
2955     SDL_FRect dstfrect;
2956     SDL_FRect *pdstfrect = NULL;
2957     if (dstrect) {
2958         dstfrect.x = (float) dstrect->x;
2959         dstfrect.y = (float) dstrect->y;
2960         dstfrect.w = (float) dstrect->w;
2961         dstfrect.h = (float) dstrect->h;
2962         pdstfrect = &dstfrect;
2963     }
2964     return SDL_RenderCopyF(renderer, texture, srcrect, pdstfrect);
2965 }
2966
2967 int
2968 SDL_RenderCopyF(SDL_Renderer * renderer, SDL_Texture * texture,
2969                 const SDL_Rect * srcrect, const SDL_FRect * dstrect)
2970 {
2971     SDL_Rect real_srcrect;
2972     SDL_FRect real_dstrect;
2973     SDL_Rect r;
2974     int retval;
2975
2976     CHECK_RENDERER_MAGIC(renderer, -1);
2977     CHECK_TEXTURE_MAGIC(texture, -1);
2978
2979     if (renderer != texture->renderer) {
2980         return SDL_SetError("Texture was not created with this renderer");
2981     }
2982
2983     /* Don't draw while we're hidden */
2984     if (renderer->hidden) {
2985         return 0;
2986     }
2987
2988     real_srcrect.x = 0;
2989     real_srcrect.y = 0;
2990     real_srcrect.w = texture->w;
2991     real_srcrect.h = texture->h;
2992     if (srcrect) {
2993         if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
2994             return 0;
2995         }
2996     }
2997
2998     SDL_zero(r);
2999     SDL_RenderGetViewport(renderer, &r);
3000     real_dstrect.x = 0.0f;
3001     real_dstrect.y = 0.0f;
3002     real_dstrect.w = (float) r.w;
3003     real_dstrect.h = (float) r.h;
3004     if (dstrect) {
3005         if (!SDL_HasIntersectionF(dstrect, &real_dstrect)) {
3006             return 0;
3007         }
3008         real_dstrect = *dstrect;
3009     }
3010
3011     if (texture->native) {
3012         texture = texture->native;
3013     }
3014
3015     real_dstrect.x *= renderer->scale.x;
3016     real_dstrect.y *= renderer->scale.y;
3017     real_dstrect.w *= renderer->scale.x;
3018     real_dstrect.h *= renderer->scale.y;
3019
3020     texture->last_command_generation = renderer->render_command_generation;
3021
3022     retval = QueueCmdCopy(renderer, texture, &real_srcrect, &real_dstrect);
3023     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
3024 }
3025
3026 int
3027 SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
3028                const SDL_Rect * srcrect, const SDL_Rect * dstrect,
3029                const double angle, const SDL_Point *center, const SDL_RendererFlip flip)
3030 {
3031     SDL_FRect dstfrect;
3032     SDL_FRect *pdstfrect = NULL;
3033     SDL_FPoint fcenter;
3034     SDL_FPoint *pfcenter = NULL;
3035
3036     if (dstrect) {
3037         dstfrect.x = (float) dstrect->x;
3038         dstfrect.y = (float) dstrect->y;
3039         dstfrect.w = (float) dstrect->w;
3040         dstfrect.h = (float) dstrect->h;
3041         pdstfrect = &dstfrect;
3042     }
3043
3044     if (center) {
3045         fcenter.x = (float) center->x;
3046         fcenter.y = (float) center->y;
3047         pfcenter = &fcenter;
3048     }
3049
3050     return SDL_RenderCopyExF(renderer, texture, srcrect, pdstfrect, angle, pfcenter, flip);
3051 }
3052
3053 int
3054 SDL_RenderCopyExF(SDL_Renderer * renderer, SDL_Texture * texture,
3055                const SDL_Rect * srcrect, const SDL_FRect * dstrect,
3056                const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
3057 {
3058     SDL_Rect real_srcrect;
3059     SDL_FRect real_dstrect;
3060     SDL_FPoint real_center;
3061     int retval;
3062
3063     if (flip == SDL_FLIP_NONE && (int)(angle/360) == angle/360) { /* fast path when we don't need rotation or flipping */
3064         return SDL_RenderCopyF(renderer, texture, srcrect, dstrect);
3065     }
3066
3067     CHECK_RENDERER_MAGIC(renderer, -1);
3068     CHECK_TEXTURE_MAGIC(texture, -1);
3069
3070     if (renderer != texture->renderer) {
3071         return SDL_SetError("Texture was not created with this renderer");
3072     }
3073     if (!renderer->QueueCopyEx) {
3074         return SDL_SetError("Renderer does not support RenderCopyEx");
3075     }
3076
3077     /* Don't draw while we're hidden */
3078     if (renderer->hidden) {
3079         return 0;
3080     }
3081
3082     real_srcrect.x = 0;
3083     real_srcrect.y = 0;
3084     real_srcrect.w = texture->w;
3085     real_srcrect.h = texture->h;
3086     if (srcrect) {
3087         if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
3088             return 0;
3089         }
3090     }
3091
3092     /* We don't intersect the dstrect with the viewport as RenderCopy does because of potential rotation clipping issues... TODO: should we? */
3093     if (dstrect) {
3094         real_dstrect = *dstrect;
3095     } else {
3096         SDL_Rect r;
3097         SDL_zero(r);
3098         SDL_RenderGetViewport(renderer, &r);
3099         real_dstrect.x = 0.0f;
3100         real_dstrect.y = 0.0f;
3101         real_dstrect.w = (float) r.w;
3102         real_dstrect.h = (float) r.h;
3103     }
3104
3105     if (texture->native) {
3106         texture = texture->native;
3107     }
3108
3109     if (center) {
3110         real_center = *center;
3111     } else {
3112         real_center.x = real_dstrect.w / 2.0f;
3113         real_center.y = real_dstrect.h / 2.0f;
3114     }
3115
3116     real_dstrect.x *= renderer->scale.x;
3117     real_dstrect.y *= renderer->scale.y;
3118     real_dstrect.w *= renderer->scale.x;
3119     real_dstrect.h *= renderer->scale.y;
3120
3121     real_center.x *= renderer->scale.x;
3122     real_center.y *= renderer->scale.y;
3123
3124     texture->last_command_generation = renderer->render_command_generation;
3125
3126     retval = QueueCmdCopyEx(renderer, texture, &real_srcrect, &real_dstrect, angle, &real_center, flip);
3127     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
3128 }
3129
3130 int
3131 SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
3132                      Uint32 format, void * pixels, int pitch)
3133 {
3134     SDL_Rect real_rect;
3135
3136     CHECK_RENDERER_MAGIC(renderer, -1);
3137
3138     if (!renderer->RenderReadPixels) {
3139         return SDL_Unsupported();
3140     }
3141
3142     FlushRenderCommands(renderer);  /* we need to render before we read the results. */
3143
3144     if (!format) {
3145         format = SDL_GetWindowPixelFormat(renderer->window);
3146     }
3147
3148     real_rect.x = renderer->viewport.x;
3149     real_rect.y = renderer->viewport.y;
3150     real_rect.w = renderer->viewport.w;
3151     real_rect.h = renderer->viewport.h;
3152     if (rect) {
3153         if (!SDL_IntersectRect(rect, &real_rect, &real_rect)) {
3154             return 0;
3155         }
3156         if (real_rect.y > rect->y) {
3157             pixels = (Uint8 *)pixels + pitch * (real_rect.y - rect->y);
3158         }
3159         if (real_rect.x > rect->x) {
3160             int bpp = SDL_BYTESPERPIXEL(format);
3161             pixels = (Uint8 *)pixels + bpp * (real_rect.x - rect->x);
3162         }
3163     }
3164
3165     return renderer->RenderReadPixels(renderer, &real_rect,
3166                                       format, pixels, pitch);
3167 }
3168
3169 void
3170 SDL_RenderPresent(SDL_Renderer * renderer)
3171 {
3172     CHECK_RENDERER_MAGIC(renderer, );
3173
3174     FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
3175
3176     /* Don't present while we're hidden */
3177     if (renderer->hidden) {
3178         return;
3179     }
3180     renderer->RenderPresent(renderer);
3181 }
3182
3183 void
3184 SDL_DestroyTexture(SDL_Texture * texture)
3185 {
3186     SDL_Renderer *renderer;
3187
3188     CHECK_TEXTURE_MAGIC(texture, );
3189
3190     renderer = texture->renderer;
3191     if (texture == renderer->target) {
3192         SDL_SetRenderTarget(renderer, NULL);  /* implies command queue flush */
3193     } else {
3194         FlushRenderCommandsIfTextureNeeded(texture);
3195     }
3196
3197     texture->magic = NULL;
3198
3199     if (texture->next) {
3200         texture->next->prev = texture->prev;
3201     }
3202     if (texture->prev) {
3203         texture->prev->next = texture->next;
3204     } else {
3205         renderer->textures = texture->next;
3206     }
3207
3208     if (texture->native) {
3209         SDL_DestroyTexture(texture->native);
3210     }
3211 #if SDL_HAVE_YUV
3212     if (texture->yuv) {
3213         SDL_SW_DestroyYUVTexture(texture->yuv);
3214     }
3215 #endif
3216     SDL_free(texture->pixels);
3217
3218     renderer->DestroyTexture(renderer, texture);
3219
3220     SDL_FreeSurface(texture->locked_surface);
3221     texture->locked_surface = NULL;
3222
3223     SDL_free(texture);
3224 }
3225
3226 void
3227 SDL_DestroyRenderer(SDL_Renderer * renderer)
3228 {
3229     SDL_RenderCommand *cmd;
3230
3231     CHECK_RENDERER_MAGIC(renderer, );
3232
3233     SDL_DelEventWatch(SDL_RendererEventWatch, renderer);
3234
3235     if (renderer->render_commands_tail != NULL) {
3236         renderer->render_commands_tail->next = renderer->render_commands_pool;
3237         cmd = renderer->render_commands;
3238     } else {
3239         cmd = renderer->render_commands_pool;
3240     }
3241
3242     renderer->render_commands_pool = NULL;
3243     renderer->render_commands_tail = NULL;
3244     renderer->render_commands = NULL;
3245
3246     while (cmd != NULL) {
3247         SDL_RenderCommand *next = cmd->next;
3248         SDL_free(cmd);
3249         cmd = next;
3250     }
3251
3252     SDL_free(renderer->vertex_data);
3253
3254     /* Free existing textures for this renderer */
3255     while (renderer->textures) {
3256         SDL_Texture *tex = renderer->textures; (void) tex;
3257         SDL_DestroyTexture(renderer->textures);
3258         SDL_assert(tex != renderer->textures);  /* satisfy static analysis. */
3259     }
3260
3261     if (renderer->window) {
3262         SDL_SetWindowData(renderer->window, SDL_WINDOWRENDERDATA, NULL);
3263     }
3264
3265     /* It's no longer magical... */
3266     renderer->magic = NULL;
3267
3268     /* Free the target mutex */
3269     SDL_DestroyMutex(renderer->target_mutex);
3270     renderer->target_mutex = NULL;
3271
3272     /* Free the renderer instance */
3273     renderer->DestroyRenderer(renderer);
3274 }
3275
3276 int SDL_GL_BindTexture(SDL_Texture *texture, float *texw, float *texh)
3277 {
3278     SDL_Renderer *renderer;
3279
3280     CHECK_TEXTURE_MAGIC(texture, -1);
3281     renderer = texture->renderer;
3282     if (texture->native) {
3283         return SDL_GL_BindTexture(texture->native, texw, texh);
3284     } else if (renderer && renderer->GL_BindTexture) {
3285         FlushRenderCommandsIfTextureNeeded(texture);  /* in case the app is going to mess with it. */
3286         return renderer->GL_BindTexture(renderer, texture, texw, texh);
3287     } else {
3288         return SDL_Unsupported();
3289     }
3290 }
3291
3292 int SDL_GL_UnbindTexture(SDL_Texture *texture)
3293 {
3294     SDL_Renderer *renderer;
3295
3296     CHECK_TEXTURE_MAGIC(texture, -1);
3297     renderer = texture->renderer;
3298     if (texture->native) {
3299         return SDL_GL_UnbindTexture(texture->native);
3300     } else if (renderer && renderer->GL_UnbindTexture) {
3301         FlushRenderCommandsIfTextureNeeded(texture);  /* in case the app messed with it. */
3302         return renderer->GL_UnbindTexture(renderer, texture);
3303     }
3304
3305     return SDL_Unsupported();
3306 }
3307
3308 void *
3309 SDL_RenderGetMetalLayer(SDL_Renderer * renderer)
3310 {
3311     CHECK_RENDERER_MAGIC(renderer, NULL);
3312
3313     if (renderer->GetMetalLayer) {
3314         FlushRenderCommands(renderer);  /* in case the app is going to mess with it. */
3315         return renderer->GetMetalLayer(renderer);
3316     }
3317     return NULL;
3318 }
3319
3320 void *
3321 SDL_RenderGetMetalCommandEncoder(SDL_Renderer * renderer)
3322 {
3323     CHECK_RENDERER_MAGIC(renderer, NULL);
3324
3325     if (renderer->GetMetalCommandEncoder) {
3326         FlushRenderCommands(renderer);  /* in case the app is going to mess with it. */
3327         return renderer->GetMetalCommandEncoder(renderer);
3328     }
3329     return NULL;
3330 }
3331
3332 static SDL_BlendMode
3333 SDL_GetShortBlendMode(SDL_BlendMode blendMode)
3334 {
3335     if (blendMode == SDL_BLENDMODE_NONE_FULL) {
3336         return SDL_BLENDMODE_NONE;
3337     }
3338     if (blendMode == SDL_BLENDMODE_BLEND_FULL) {
3339         return SDL_BLENDMODE_BLEND;
3340     }
3341     if (blendMode == SDL_BLENDMODE_ADD_FULL) {
3342         return SDL_BLENDMODE_ADD;
3343     }
3344     if (blendMode == SDL_BLENDMODE_MOD_FULL) {
3345         return SDL_BLENDMODE_MOD;
3346     }
3347     if (blendMode == SDL_BLENDMODE_MUL_FULL) {
3348         return SDL_BLENDMODE_MUL;
3349     }
3350     return blendMode;
3351 }
3352
3353 static SDL_BlendMode
3354 SDL_GetLongBlendMode(SDL_BlendMode blendMode)
3355 {
3356     if (blendMode == SDL_BLENDMODE_NONE) {
3357         return SDL_BLENDMODE_NONE_FULL;
3358     }
3359     if (blendMode == SDL_BLENDMODE_BLEND) {
3360         return SDL_BLENDMODE_BLEND_FULL;
3361     }
3362     if (blendMode == SDL_BLENDMODE_ADD) {
3363         return SDL_BLENDMODE_ADD_FULL;
3364     }
3365     if (blendMode == SDL_BLENDMODE_MOD) {
3366         return SDL_BLENDMODE_MOD_FULL;
3367     }
3368     if (blendMode == SDL_BLENDMODE_MUL) {
3369         return SDL_BLENDMODE_MUL_FULL;
3370     }
3371     return blendMode;
3372 }
3373
3374 SDL_BlendMode
3375 SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor,
3376                            SDL_BlendOperation colorOperation,
3377                            SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor,
3378                            SDL_BlendOperation alphaOperation)
3379 {
3380     SDL_BlendMode blendMode = SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation,
3381                                                     srcAlphaFactor, dstAlphaFactor, alphaOperation);
3382     return SDL_GetShortBlendMode(blendMode);
3383 }
3384
3385 SDL_BlendFactor
3386 SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode)
3387 {
3388     blendMode = SDL_GetLongBlendMode(blendMode);
3389     return (SDL_BlendFactor)(((Uint32)blendMode >> 4) & 0xF);
3390 }
3391
3392 SDL_BlendFactor
3393 SDL_GetBlendModeDstColorFactor(SDL_BlendMode blendMode)
3394 {
3395     blendMode = SDL_GetLongBlendMode(blendMode);
3396     return (SDL_BlendFactor)(((Uint32)blendMode >> 8) & 0xF);
3397 }
3398
3399 SDL_BlendOperation
3400 SDL_GetBlendModeColorOperation(SDL_BlendMode blendMode)
3401 {
3402     blendMode = SDL_GetLongBlendMode(blendMode);
3403     return (SDL_BlendOperation)(((Uint32)blendMode >> 0) & 0xF);
3404 }
3405
3406 SDL_BlendFactor
3407 SDL_GetBlendModeSrcAlphaFactor(SDL_BlendMode blendMode)
3408 {
3409     blendMode = SDL_GetLongBlendMode(blendMode);
3410     return (SDL_BlendFactor)(((Uint32)blendMode >> 20) & 0xF);
3411 }
3412
3413 SDL_BlendFactor
3414 SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode)
3415 {
3416     blendMode = SDL_GetLongBlendMode(blendMode);
3417     return (SDL_BlendFactor)(((Uint32)blendMode >> 24) & 0xF);
3418 }
3419
3420 SDL_BlendOperation
3421 SDL_GetBlendModeAlphaOperation(SDL_BlendMode blendMode)
3422 {
3423     blendMode = SDL_GetLongBlendMode(blendMode);
3424     return (SDL_BlendOperation)(((Uint32)blendMode >> 16) & 0xF);
3425 }
3426
3427 /* vi: set ts=4 sw=4 expandtab: */