2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../SDL_internal.h"
23 /* The SDL 2D rendering system */
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"
31 #if defined(__ANDROID__)
32 # include "../core/android/SDL_android.h"
35 #define SDL_WINDOWRENDERDATA "_SDL_WindowRenderData"
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"); \
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"); \
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))
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)
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)
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)
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)
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)
81 #if !SDL_RENDER_DISABLED
82 static const SDL_RenderDriver *render_drivers[] = {
83 #if SDL_VIDEO_RENDER_D3D
86 #if SDL_VIDEO_RENDER_D3D11
89 #if SDL_VIDEO_RENDER_METAL
92 #if SDL_VIDEO_RENDER_OGL
95 #if SDL_VIDEO_RENDER_OGL_ES2
98 #if SDL_VIDEO_RENDER_OGL_ES
101 #if SDL_VIDEO_RENDER_DIRECTFB
102 &DirectFB_RenderDriver,
104 #if SDL_VIDEO_RENDER_PSP
107 #if SDL_VIDEO_RENDER_SW
111 #endif /* !SDL_RENDER_DISABLED */
113 static char renderer_magic;
114 static char texture_magic;
116 static SDL_INLINE void
117 DebugLogRenderCommands(const SDL_RenderCommand *cmd)
121 SDL_Log("Render commands to flush:");
123 switch (cmd->command) {
124 case SDL_RENDERCMD_NO_OP:
125 SDL_Log(" %u. no-op", i++);
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);
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);
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);
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);
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);
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);
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);
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);
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);
208 FlushRenderCommands(SDL_Renderer *renderer)
212 SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
214 if (renderer->render_commands == NULL) { /* nothing to do! */
215 SDL_assert(renderer->vertex_data_used == 0);
219 DebugLogRenderCommands(renderer->render_commands);
221 retval = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used);
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;
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;
239 FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture)
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);
249 static SDL_INLINE int
250 FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer)
252 return renderer->batching ? 0 : FlushRenderCommands(renderer);
256 SDL_RenderFlush(SDL_Renderer * renderer)
258 return FlushRenderCommands(renderer);
262 SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, const size_t alignment, size_t *offset)
264 const size_t needed = renderer->vertex_data_used + numbytes + alignment;
265 size_t current_offset = renderer->vertex_data_used;
267 size_t aligner = (alignment && ((current_offset & (alignment - 1)) != 0)) ? (alignment - (current_offset & (alignment - 1))) : 0;
268 size_t aligned = current_offset + aligner;
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;
274 while (newsize < needed) {
277 ptr = SDL_realloc(renderer->vertex_data, newsize);
282 renderer->vertex_data = ptr;
283 renderer->vertex_data_allocation = newsize;
290 renderer->vertex_data_used += aligner + numbytes;
292 return ((Uint8 *) renderer->vertex_data) + aligned;
295 static SDL_RenderCommand *
296 AllocateRenderCommand(SDL_Renderer *renderer)
298 SDL_RenderCommand *retval = NULL;
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;
306 retval = SDL_calloc(1, sizeof (*retval));
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;
317 renderer->render_commands = retval;
319 renderer->render_commands_tail = retval;
325 QueueCmdSetViewport(SDL_Renderer *renderer)
328 if (!renderer->viewport_queued || (SDL_memcmp(&renderer->viewport, &renderer->last_queued_viewport, sizeof (SDL_Rect)) != 0)) {
329 SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
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);
337 cmd->command = SDL_RENDERCMD_NO_OP;
339 SDL_memcpy(&renderer->last_queued_viewport, &renderer->viewport, sizeof (SDL_Rect));
340 renderer->viewport_queued = SDL_TRUE;
348 QueueCmdSetClipRect(SDL_Renderer *renderer)
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);
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;
370 QueueCmdSetDrawColor(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uint8 b, const Uint8 a)
372 const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
375 if (!renderer->color_queued || (color != renderer->last_queued_color)) {
376 SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
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);
388 cmd->command = SDL_RENDERCMD_NO_OP;
390 renderer->last_queued_color = color;
391 renderer->color_queued = SDL_TRUE;
399 QueueCmdClear(SDL_Renderer *renderer)
401 SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
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;
416 PrepQueueCmdDraw(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uint8 b, const Uint8 a)
418 int retval = QueueCmdSetDrawColor(renderer, r, g, b, a);
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);
425 if (retval == 0 && !renderer->cliprect_queued) {
426 retval = QueueCmdSetClipRect(renderer);
431 static SDL_RenderCommand *
432 PrepQueueCmdDrawSolid(SDL_Renderer *renderer, const SDL_RenderCommandType cmdtype)
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);
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. */
454 QueueCmdDrawPoints(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
456 SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_POINTS);
459 retval = renderer->QueueDrawPoints(renderer, cmd, points, count);
461 cmd->command = SDL_RENDERCMD_NO_OP;
468 QueueCmdDrawLines(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
470 SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_LINES);
473 retval = renderer->QueueDrawLines(renderer, cmd, points, count);
475 cmd->command = SDL_RENDERCMD_NO_OP;
482 QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect * rects, const int count)
484 SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_FILL_RECTS);
487 retval = renderer->QueueFillRects(renderer, cmd, rects, count);
489 cmd->command = SDL_RENDERCMD_NO_OP;
495 static SDL_RenderCommand *
496 PrepQueueCmdDrawTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_RenderCommandType cmdtype)
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);
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;
518 QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_FRect * dstrect)
520 SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY);
523 retval = renderer->QueueCopy(renderer, cmd, texture, srcrect, dstrect);
525 cmd->command = SDL_RENDERCMD_NO_OP;
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)
536 SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY_EX);
538 SDL_assert(renderer->QueueCopyEx != NULL); /* should have caught at higher level. */
540 retval = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip);
542 cmd->command = SDL_RENDERCMD_NO_OP;
549 static int UpdateLogicalSize(SDL_Renderer *renderer);
552 SDL_GetNumRenderDrivers(void)
554 #if !SDL_RENDER_DISABLED
555 return SDL_arraysize(render_drivers);
562 SDL_GetRenderDriverInfo(int index, SDL_RendererInfo * info)
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);
569 *info = render_drivers[index]->info;
572 return SDL_SetError("SDL not built with rendering support");
576 static void GetWindowViewportValues(SDL_Renderer *renderer, int *logical_w, int *logical_h, SDL_Rect *viewport, SDL_FPoint *scale)
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);
587 SDL_RendererEventWatch(void *userdata, SDL_Event *event)
589 SDL_Renderer *renderer = (SDL_Renderer *)userdata;
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);
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);
602 SDL_SetRenderTarget(renderer, NULL);
605 if (renderer->logical_w) {
606 UpdateLogicalSize(renderer);
608 /* Window was resized, reset viewport */
611 if (renderer->GetOutputSize) {
612 renderer->GetOutputSize(renderer, &w, &h);
614 SDL_GetWindowSize(renderer->window, &w, &h);
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;
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);
633 SDL_SetRenderTarget(renderer, saved_target);
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;
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;
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;
656 GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
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;
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;
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;
683 GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
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));
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;
698 GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
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. */
707 if (renderer->GetOutputSize) {
709 renderer->GetOutputSize(renderer, &w, &h);
710 physical_w = (float) w;
711 physical_h = (float) 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;
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;
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. */
729 event->tfinger.x = (event->tfinger.x - normalized_viewport_x) / normalized_viewport_w;
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;
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. */
743 event->tfinger.y = (event->tfinger.y - normalized_viewport_y) / normalized_viewport_h;
752 SDL_CreateWindowAndRenderer(int width, int height, Uint32 window_flags,
753 SDL_Window **window, SDL_Renderer **renderer)
755 *window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED,
756 SDL_WINDOWPOS_UNDEFINED,
757 width, height, window_flags);
763 *renderer = SDL_CreateRenderer(*window, -1, 0);
772 void VerifyDrawQueueFunctions(const SDL_Renderer *renderer)
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);
786 SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags)
788 #if !SDL_RENDER_DISABLED
789 SDL_Renderer *renderer = NULL;
790 int n = SDL_GetNumRenderDrivers();
791 SDL_bool batching = SDL_TRUE;
794 #if defined(__ANDROID__)
795 Android_ActivityMutex_Lock_Running();
799 SDL_SetError("Invalid window");
803 if (SDL_GetRenderer(window)) {
804 SDL_SetError("Renderer already associated with window");
808 if (SDL_GetHint(SDL_HINT_RENDER_VSYNC)) {
809 if (SDL_GetHintBoolean(SDL_HINT_RENDER_VSYNC, SDL_TRUE)) {
810 flags |= SDL_RENDERER_PRESENTVSYNC;
812 flags &= ~SDL_RENDERER_PRESENTVSYNC;
817 hint = SDL_GetHint(SDL_HINT_RENDER_DRIVER);
819 for (index = 0; index < n; ++index) {
820 const SDL_RenderDriver *driver = render_drivers[index];
822 if (SDL_strcasecmp(hint, driver->info.name) == 0) {
823 /* Create a new renderer instance */
824 renderer = driver->CreateRenderer(window, flags);
826 batching = SDL_FALSE;
834 for (index = 0; index < n; ++index) {
835 const SDL_RenderDriver *driver = render_drivers[index];
837 if ((driver->info.flags & flags) == flags) {
838 /* Create a new renderer instance */
839 renderer = driver->CreateRenderer(window, flags);
841 /* Yay, we got one! */
848 SDL_SetError("Couldn't find matching render driver");
852 if (index >= SDL_GetNumRenderDrivers()) {
853 SDL_SetError("index must be -1 or in the range of 0 - %d",
854 SDL_GetNumRenderDrivers() - 1);
857 /* Create a new renderer instance */
858 renderer = render_drivers[index]->CreateRenderer(window, flags);
859 batching = SDL_FALSE;
866 VerifyDrawQueueFunctions(renderer);
868 /* let app/user override batching decisions. */
869 if (renderer->always_batch) {
871 } else if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) {
872 batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE);
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;
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;
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;
897 renderer->relative_scaling = SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_SCALING, SDL_TRUE);
899 if (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN|SDL_WINDOW_MINIMIZED)) {
900 renderer->hidden = SDL_TRUE;
902 renderer->hidden = SDL_FALSE;
905 SDL_SetWindowData(window, SDL_WINDOWRENDERDATA, renderer);
907 SDL_RenderSetViewport(renderer, NULL);
909 SDL_AddEventWatch(SDL_RendererEventWatch, renderer);
911 SDL_LogInfo(SDL_LOG_CATEGORY_RENDER,
912 "Created renderer: %s", renderer->info.name);
914 #if defined(__ANDROID__)
915 Android_ActivityMutex_Unlock();
921 #if defined(__ANDROID__)
922 Android_ActivityMutex_Unlock();
927 SDL_SetError("SDL not built with rendering support");
933 SDL_CreateSoftwareRenderer(SDL_Surface * surface)
935 #if !SDL_RENDER_DISABLED && SDL_VIDEO_RENDER_SW
936 SDL_Renderer *renderer;
938 renderer = SW_CreateRendererForSurface(surface);
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;
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;
950 SDL_RenderSetViewport(renderer, NULL);
954 SDL_SetError("SDL not built with rendering support");
956 #endif /* !SDL_RENDER_DISABLED */
960 SDL_GetRenderer(SDL_Window * window)
962 return (SDL_Renderer *)SDL_GetWindowData(window, SDL_WINDOWRENDERDATA);
966 SDL_GetRendererInfo(SDL_Renderer * renderer, SDL_RendererInfo * info)
968 CHECK_RENDERER_MAGIC(renderer, -1);
970 *info = renderer->info;
975 SDL_GetRendererOutputSize(SDL_Renderer * renderer, int *w, int *h)
977 CHECK_RENDERER_MAGIC(renderer, -1);
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);
987 SDL_assert(0 && "This should never happen");
988 return SDL_SetError("Renderer doesn't support querying output size");
993 IsSupportedBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
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:
1006 return renderer->SupportsBlendMode && renderer->SupportsBlendMode(renderer, blendMode);
1011 IsSupportedFormat(SDL_Renderer * renderer, Uint32 format)
1015 for (i = 0; i < renderer->info.num_texture_formats; ++i) {
1016 if (renderer->info.texture_formats[i] == format) {
1024 GetClosestSupportedFormat(SDL_Renderer * renderer, Uint32 format)
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];
1036 SDL_bool hasAlpha = SDL_ISPIXELFORMAT_ALPHA(format);
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];
1046 return renderer->info.texture_formats[0];
1050 static SDL_ScaleMode SDL_GetScaleMode(void)
1052 const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
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;
1061 return (SDL_ScaleMode)SDL_atoi(hint);
1066 SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h)
1068 SDL_Texture *texture;
1070 CHECK_RENDERER_MAGIC(renderer, NULL);
1073 format = renderer->info.texture_formats[0];
1075 if (SDL_BYTESPERPIXEL(format) == 0) {
1076 SDL_SetError("Invalid texture format");
1079 if (SDL_ISPIXELFORMAT_INDEXED(format)) {
1080 SDL_SetError("Palettized textures are not supported");
1083 if (w <= 0 || h <= 0) {
1084 SDL_SetError("Texture dimensions can't be 0");
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);
1092 texture = (SDL_Texture *) SDL_calloc(1, sizeof(*texture));
1097 texture->magic = &texture_magic;
1098 texture->format = format;
1099 texture->access = access;
1106 texture->scaleMode = SDL_GetScaleMode();
1107 texture->renderer = renderer;
1108 texture->next = renderer->textures;
1109 if (renderer->textures) {
1110 renderer->textures->prev = texture;
1112 renderer->textures = texture;
1114 if (IsSupportedFormat(renderer, format)) {
1115 if (renderer->CreateTexture(renderer, texture) < 0) {
1116 SDL_DestroyTexture(texture);
1120 texture->native = SDL_CreateTexture(renderer,
1121 GetClosestSupportedFormat(renderer, format),
1123 if (!texture->native) {
1124 SDL_DestroyTexture(texture);
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;
1133 texture->prev = texture->native->prev;
1134 if (texture->prev) {
1135 texture->prev->next = texture;
1137 texture->native->prev = texture;
1138 texture->next = texture->native;
1139 renderer->textures = texture;
1141 if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
1143 texture->yuv = SDL_SW_CreateYUVTexture(format, w, h);
1145 SDL_SetError("SDL not built with YUV support");
1147 if (!texture->yuv) {
1148 SDL_DestroyTexture(texture);
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);
1165 SDL_CreateTextureFromSurface(SDL_Renderer * renderer, SDL_Surface * surface)
1167 const SDL_PixelFormat *fmt;
1169 SDL_bool direct_update;
1171 Uint32 format = SDL_PIXELFORMAT_UNKNOWN;
1172 SDL_Texture *texture;
1174 CHECK_RENDERER_MAGIC(renderer, NULL);
1177 SDL_SetError("SDL_CreateTextureFromSurface() passed NULL surface");
1181 /* See what the best texture format is */
1182 fmt = surface->format;
1183 if (fmt->Amask || SDL_HasColorKey(surface)) {
1184 needAlpha = SDL_TRUE;
1186 needAlpha = SDL_FALSE;
1189 /* If Palette contains alpha values, promotes to alpha format */
1191 SDL_bool is_opaque, has_alpha_channel;
1192 SDL_DetectPalette(fmt->palette, &is_opaque, &has_alpha_channel);
1194 needAlpha = SDL_TRUE;
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;
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;
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;
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];
1238 texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STATIC,
1239 surface->w, surface->h);
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;
1250 /* Update Texture directly */
1251 direct_update = SDL_TRUE;
1254 /* Surface and Renderer formats are differents, it needs an intermediate conversion. */
1255 direct_update = SDL_FALSE;
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);
1264 SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
1267 SDL_PixelFormat *dst_fmt;
1268 SDL_Surface *temp = NULL;
1270 /* Set up a destination surface for the texture update */
1271 dst_fmt = SDL_AllocFormat(format);
1273 SDL_DestroyTexture(texture);
1276 temp = SDL_ConvertSurface(surface, dst_fmt, 0);
1277 SDL_FreeFormat(dst_fmt);
1279 SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
1280 SDL_FreeSurface(temp);
1282 SDL_DestroyTexture(texture);
1289 SDL_BlendMode blendMode;
1291 SDL_GetSurfaceColorMod(surface, &r, &g, &b);
1292 SDL_SetTextureColorMod(texture, r, g, b);
1294 SDL_GetSurfaceAlphaMod(surface, &a);
1295 SDL_SetTextureAlphaMod(texture, a);
1297 if (SDL_HasColorKey(surface)) {
1298 /* We converted to a texture with alpha format */
1299 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
1301 SDL_GetSurfaceBlendMode(surface, &blendMode);
1302 SDL_SetTextureBlendMode(texture, blendMode);
1309 SDL_QueryTexture(SDL_Texture * texture, Uint32 * format, int *access,
1312 CHECK_TEXTURE_MAGIC(texture, -1);
1315 *format = texture->format;
1318 *access = texture->access;
1330 SDL_SetTextureColorMod(SDL_Texture * texture, Uint8 r, Uint8 g, Uint8 b)
1332 CHECK_TEXTURE_MAGIC(texture, -1);
1334 if (r < 255 || g < 255 || b < 255) {
1335 texture->modMode |= SDL_TEXTUREMODULATE_COLOR;
1337 texture->modMode &= ~SDL_TEXTUREMODULATE_COLOR;
1342 if (texture->native) {
1343 return SDL_SetTextureColorMod(texture->native, r, g, b);
1349 SDL_GetTextureColorMod(SDL_Texture * texture, Uint8 * r, Uint8 * g,
1352 CHECK_TEXTURE_MAGIC(texture, -1);
1367 SDL_SetTextureAlphaMod(SDL_Texture * texture, Uint8 alpha)
1369 CHECK_TEXTURE_MAGIC(texture, -1);
1372 texture->modMode |= SDL_TEXTUREMODULATE_ALPHA;
1374 texture->modMode &= ~SDL_TEXTUREMODULATE_ALPHA;
1377 if (texture->native) {
1378 return SDL_SetTextureAlphaMod(texture->native, alpha);
1384 SDL_GetTextureAlphaMod(SDL_Texture * texture, Uint8 * alpha)
1386 CHECK_TEXTURE_MAGIC(texture, -1);
1389 *alpha = texture->a;
1395 SDL_SetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode blendMode)
1397 SDL_Renderer *renderer;
1399 CHECK_TEXTURE_MAGIC(texture, -1);
1401 renderer = texture->renderer;
1402 if (!IsSupportedBlendMode(renderer, blendMode)) {
1403 return SDL_Unsupported();
1405 texture->blendMode = blendMode;
1406 if (texture->native) {
1407 return SDL_SetTextureBlendMode(texture->native, blendMode);
1413 SDL_GetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode *blendMode)
1415 CHECK_TEXTURE_MAGIC(texture, -1);
1418 *blendMode = texture->blendMode;
1424 SDL_SetTextureScaleMode(SDL_Texture * texture, SDL_ScaleMode scaleMode)
1426 SDL_Renderer *renderer;
1428 CHECK_TEXTURE_MAGIC(texture, -1);
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);
1440 SDL_GetTextureScaleMode(SDL_Texture * texture, SDL_ScaleMode *scaleMode)
1442 CHECK_TEXTURE_MAGIC(texture, -1);
1445 *scaleMode = texture->scaleMode;
1452 SDL_UpdateTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
1453 const void *pixels, int pitch)
1455 SDL_Texture *native = texture->native;
1458 if (SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch) < 0) {
1464 full_rect.w = texture->w;
1465 full_rect.h = texture->h;
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;
1473 if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
1476 SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
1477 rect->w, rect->h, native_pixels, native_pitch);
1478 SDL_UnlockTexture(native);
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;
1484 void *temp_pixels = SDL_malloc(alloclen);
1486 return SDL_OutOfMemory();
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);
1496 #endif /* SDL_HAVE_YUV */
1499 SDL_UpdateTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
1500 const void *pixels, int pitch)
1502 SDL_Texture *native = texture->native;
1504 if (!rect->w || !rect->h) {
1505 return 0; /* nothing to do. */
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;
1513 if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
1516 SDL_ConvertPixels(rect->w, rect->h,
1517 texture->format, pixels, pitch,
1518 native->format, native_pixels, native_pitch);
1519 SDL_UnlockTexture(native);
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;
1525 void *temp_pixels = SDL_malloc(alloclen);
1527 return SDL_OutOfMemory();
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);
1540 SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect,
1541 const void *pixels, int pitch)
1545 CHECK_TEXTURE_MAGIC(texture, -1);
1548 return SDL_InvalidParamError("pixels");
1551 return SDL_InvalidParamError("pitch");
1557 full_rect.w = texture->w;
1558 full_rect.h = texture->h;
1562 if ((rect->w == 0) || (rect->h == 0)) {
1563 return 0; /* nothing to do. */
1565 } else if (texture->yuv) {
1566 return SDL_UpdateTextureYUV(texture, rect, pixels, pitch);
1568 } else if (texture->native) {
1569 return SDL_UpdateTextureNative(texture, rect, pixels, pitch);
1571 SDL_Renderer *renderer = texture->renderer;
1572 if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
1575 return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
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)
1586 SDL_Texture *native = texture->native;
1589 if (SDL_SW_UpdateYUVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch) < 0) {
1595 full_rect.w = texture->w;
1596 full_rect.h = texture->h;
1599 if (!rect->w || !rect->h) {
1600 return 0; /* nothing to do. */
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;
1608 if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
1611 SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
1612 rect->w, rect->h, native_pixels, native_pitch);
1613 SDL_UnlockTexture(native);
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;
1619 void *temp_pixels = SDL_malloc(alloclen);
1621 return SDL_OutOfMemory();
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);
1631 #endif /* SDL_HAVE_YUV */
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)
1639 SDL_Renderer *renderer;
1642 CHECK_TEXTURE_MAGIC(texture, -1);
1645 return SDL_InvalidParamError("Yplane");
1648 return SDL_InvalidParamError("Ypitch");
1651 return SDL_InvalidParamError("Uplane");
1654 return SDL_InvalidParamError("Upitch");
1657 return SDL_InvalidParamError("Vplane");
1660 return SDL_InvalidParamError("Vpitch");
1663 if (texture->format != SDL_PIXELFORMAT_YV12 &&
1664 texture->format != SDL_PIXELFORMAT_IYUV) {
1665 return SDL_SetError("Texture format must by YV12 or IYUV");
1671 full_rect.w = texture->w;
1672 full_rect.h = texture->h;
1676 if (!rect->w || !rect->h) {
1677 return 0; /* nothing to do. */
1681 return SDL_UpdateTextureYUVPlanar(texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
1683 SDL_assert(!texture->native);
1684 renderer = texture->renderer;
1685 SDL_assert(renderer->UpdateTextureYUV);
1686 if (renderer->UpdateTextureYUV) {
1687 if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
1690 return renderer->UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
1692 return SDL_Unsupported();
1702 SDL_LockTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
1703 void **pixels, int *pitch)
1705 return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch);
1707 #endif /* SDL_HAVE_YUV */
1710 SDL_LockTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
1711 void **pixels, int *pitch)
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;
1722 SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect,
1723 void **pixels, int *pitch)
1727 CHECK_TEXTURE_MAGIC(texture, -1);
1729 if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
1730 return SDL_SetError("SDL_LockTexture(): texture must be streaming");
1736 full_rect.w = texture->w;
1737 full_rect.h = texture->h;
1743 if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
1746 return SDL_LockTextureYUV(texture, rect, pixels, pitch);
1749 if (texture->native) {
1750 /* Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. */
1751 return SDL_LockTextureNative(texture, rect, pixels, pitch);
1753 SDL_Renderer *renderer = texture->renderer;
1754 if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
1757 return renderer->LockTexture(renderer, texture, rect, pixels, pitch);
1762 SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect,
1763 SDL_Surface **surface)
1766 void *pixels = NULL;
1767 int pitch = 0; /* fix static analysis */
1770 if (texture == NULL || surface == NULL) {
1776 real_rect.w = texture->w;
1777 real_rect.h = texture->h;
1780 SDL_IntersectRect(rect, &real_rect, &real_rect);
1783 ret = SDL_LockTexture(texture, &real_rect, &pixels, &pitch);
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);
1794 *surface = texture->locked_surface;
1800 SDL_UnlockTextureYUV(SDL_Texture * texture)
1802 SDL_Texture *native = texture->native;
1803 void *native_pixels = NULL;
1804 int native_pitch = 0;
1809 rect.w = texture->w;
1810 rect.h = texture->h;
1812 if (SDL_LockTexture(native, &rect, &native_pixels, &native_pitch) < 0) {
1815 SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format,
1816 rect.w, rect.h, native_pixels, native_pitch);
1817 SDL_UnlockTexture(native);
1819 #endif /* SDL_HAVE_YUV */
1822 SDL_UnlockTextureNative(SDL_Texture * texture)
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;
1833 if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
1836 SDL_ConvertPixels(rect->w, rect->h,
1837 texture->format, pixels, pitch,
1838 native->format, native_pixels, native_pitch);
1839 SDL_UnlockTexture(native);
1843 SDL_UnlockTexture(SDL_Texture * texture)
1845 CHECK_TEXTURE_MAGIC(texture, );
1847 if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
1852 SDL_UnlockTextureYUV(texture);
1855 if (texture->native) {
1856 SDL_UnlockTextureNative(texture);
1858 SDL_Renderer *renderer = texture->renderer;
1859 renderer->UnlockTexture(renderer, texture);
1862 SDL_FreeSurface(texture->locked_surface);
1863 texture->locked_surface = NULL;
1867 SDL_RenderTargetSupported(SDL_Renderer *renderer)
1869 if (!renderer || !renderer->SetRenderTarget) {
1872 return (renderer->info.flags & SDL_RENDERER_TARGETTEXTURE) != 0;
1876 SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
1878 if (!SDL_RenderTargetSupported(renderer)) {
1879 return SDL_Unsupported();
1882 /* texture == NULL is valid and means reset the target to the window */
1884 CHECK_TEXTURE_MAGIC(texture, -1);
1885 if (renderer != texture->renderer) {
1886 return SDL_SetError("Texture was not created with this renderer");
1888 if (texture->access != SDL_TEXTUREACCESS_TARGET) {
1889 return SDL_SetError("Texture not created with SDL_TEXTUREACCESS_TARGET");
1891 if (texture->native) {
1892 /* Always render to the native texture */
1893 texture = texture->native;
1897 if (texture == renderer->target) {
1898 /* Nothing to do! */
1902 FlushRenderCommands(renderer); /* time to send everything to the GPU! */
1904 SDL_LockMutex(renderer->target_mutex);
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;
1915 renderer->target = texture;
1917 if (renderer->SetRenderTarget(renderer, texture) < 0) {
1918 SDL_UnlockMutex(renderer->target_mutex);
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;
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;
1942 SDL_UnlockMutex(renderer->target_mutex);
1944 if (QueueCmdSetViewport(renderer) < 0) {
1947 if (QueueCmdSetClipRect(renderer) < 0) {
1952 return FlushRenderCommandsIfNotBatching(renderer);
1956 SDL_GetRenderTarget(SDL_Renderer *renderer)
1958 return renderer->target;
1962 UpdateLogicalSize(SDL_Renderer *renderer)
1969 /* 0 is for letterbox, 1 is for overscan */
1970 int scale_policy = 0;
1973 if (!renderer->logical_w || !renderer->logical_h) {
1976 if (SDL_GetRendererOutputSize(renderer, &w, &h) < 0) {
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.
1987 if (SDL_strcasecmp(SDL_GetCurrentVideoDriver(), "direct3d") == 0) {
1988 overscan_supported = SDL_FALSE;
1990 if (overscan_supported) {
1998 want_aspect = (float)renderer->logical_w / renderer->logical_h;
1999 real_aspect = (float)w / h;
2001 /* Clear the scale because we're setting viewport in output coordinates */
2002 SDL_RenderSetScale(renderer, 1.0f, 1.0f);
2004 if (renderer->integer_scale) {
2005 if (want_aspect > real_aspect) {
2006 scale = (float)(w / renderer->logical_w);
2008 scale = (float)(h / renderer->logical_h);
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;
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
2026 scale = (float)h / renderer->logical_h;
2029 viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
2030 viewport.x = (w - viewport.w) / 2;
2031 SDL_RenderSetViewport(renderer, &viewport);
2033 /* We want a wider aspect ratio than is available - letterbox it */
2034 scale = (float)w / renderer->logical_w;
2037 viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
2038 viewport.y = (h - viewport.h) / 2;
2039 SDL_RenderSetViewport(renderer, &viewport);
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
2047 scale = (float)w / renderer->logical_w;
2050 viewport.h = (int)SDL_ceil(renderer->logical_h * scale);
2051 viewport.y = (h - viewport.h) / 2;
2052 SDL_RenderSetViewport(renderer, &viewport);
2054 /* We want a narrower aspect ratio than is available - use side-bars */
2055 scale = (float)h / renderer->logical_h;
2058 viewport.w = (int)SDL_ceil(renderer->logical_w * scale);
2059 viewport.x = (w - viewport.w) / 2;
2060 SDL_RenderSetViewport(renderer, &viewport);
2064 /* Set the new scale */
2065 SDL_RenderSetScale(renderer, scale, scale);
2071 SDL_RenderSetLogicalSize(SDL_Renderer * renderer, int w, int h)
2073 CHECK_RENDERER_MAGIC(renderer, -1);
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);
2084 renderer->logical_w = w;
2085 renderer->logical_h = h;
2087 return UpdateLogicalSize(renderer);
2091 SDL_RenderGetLogicalSize(SDL_Renderer * renderer, int *w, int *h)
2093 CHECK_RENDERER_MAGIC(renderer, );
2096 *w = renderer->logical_w;
2099 *h = renderer->logical_h;
2104 SDL_RenderSetIntegerScale(SDL_Renderer * renderer, SDL_bool enable)
2106 CHECK_RENDERER_MAGIC(renderer, -1);
2108 renderer->integer_scale = enable;
2110 return UpdateLogicalSize(renderer);
2114 SDLCALL SDL_RenderGetIntegerScale(SDL_Renderer * renderer)
2116 CHECK_RENDERER_MAGIC(renderer, SDL_FALSE);
2118 return renderer->integer_scale;
2122 SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect)
2125 CHECK_RENDERER_MAGIC(renderer, -1);
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);
2133 renderer->viewport.x = 0;
2134 renderer->viewport.y = 0;
2135 if (SDL_GetRendererOutputSize(renderer, &renderer->viewport.w, &renderer->viewport.h) < 0) {
2139 retval = QueueCmdSetViewport(renderer);
2140 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2144 SDL_RenderGetViewport(SDL_Renderer * renderer, SDL_Rect * rect)
2146 CHECK_RENDERER_MAGIC(renderer, );
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);
2157 SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect)
2160 CHECK_RENDERER_MAGIC(renderer, -1)
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);
2169 renderer->clipping_enabled = SDL_FALSE;
2170 SDL_zero(renderer->clip_rect);
2173 retval = QueueCmdSetClipRect(renderer);
2174 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2178 SDL_RenderGetClipRect(SDL_Renderer * renderer, SDL_Rect * rect)
2180 CHECK_RENDERER_MAGIC(renderer, )
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);
2191 SDL_RenderIsClipEnabled(SDL_Renderer * renderer)
2193 CHECK_RENDERER_MAGIC(renderer, SDL_FALSE)
2194 return renderer->clipping_enabled;
2198 SDL_RenderSetScale(SDL_Renderer * renderer, float scaleX, float scaleY)
2200 CHECK_RENDERER_MAGIC(renderer, -1);
2202 renderer->scale.x = scaleX;
2203 renderer->scale.y = scaleY;
2208 SDL_RenderGetScale(SDL_Renderer * renderer, float *scaleX, float *scaleY)
2210 CHECK_RENDERER_MAGIC(renderer, );
2213 *scaleX = renderer->scale.x;
2216 *scaleY = renderer->scale.y;
2221 SDL_SetRenderDrawColor(SDL_Renderer * renderer,
2222 Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2224 CHECK_RENDERER_MAGIC(renderer, -1);
2234 SDL_GetRenderDrawColor(SDL_Renderer * renderer,
2235 Uint8 * r, Uint8 * g, Uint8 * b, Uint8 * a)
2237 CHECK_RENDERER_MAGIC(renderer, -1);
2255 SDL_SetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
2257 CHECK_RENDERER_MAGIC(renderer, -1);
2259 if (!IsSupportedBlendMode(renderer, blendMode)) {
2260 return SDL_Unsupported();
2262 renderer->blendMode = blendMode;
2267 SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode *blendMode)
2269 CHECK_RENDERER_MAGIC(renderer, -1);
2271 *blendMode = renderer->blendMode;
2276 SDL_RenderClear(SDL_Renderer * renderer)
2279 CHECK_RENDERER_MAGIC(renderer, -1);
2280 retval = QueueCmdClear(renderer);
2281 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
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. */
2289 SDL_RenderDrawPoint(SDL_Renderer * renderer, int x, int y)
2292 fpoint.x = (float) x;
2293 fpoint.y = (float) y;
2294 return SDL_RenderDrawPointsF(renderer, &fpoint, 1);
2298 SDL_RenderDrawPointF(SDL_Renderer * renderer, float x, float y)
2303 return SDL_RenderDrawPointsF(renderer, &fpoint, 1);
2307 RenderDrawPointsWithRects(SDL_Renderer * renderer,
2308 const SDL_Point * points, const int count)
2312 SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack);
2316 return SDL_OutOfMemory();
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;
2326 retval = QueueCmdFillRects(renderer, frects, count);
2328 SDL_small_free(frects, isstack);
2330 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2334 SDL_RenderDrawPoints(SDL_Renderer * renderer,
2335 const SDL_Point * points, int count)
2337 SDL_FPoint *fpoints;
2342 CHECK_RENDERER_MAGIC(renderer, -1);
2345 return SDL_SetError("SDL_RenderDrawPoints(): Passed NULL points");
2351 /* Don't draw while we're hidden */
2352 if (renderer->hidden) {
2356 if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
2357 return RenderDrawPointsWithRects(renderer, points, count);
2360 fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
2362 return SDL_OutOfMemory();
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;
2369 retval = QueueCmdDrawPoints(renderer, fpoints, count);
2371 SDL_small_free(fpoints, isstack);
2373 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2377 RenderDrawPointsWithRectsF(SDL_Renderer * renderer,
2378 const SDL_FPoint * fpoints, const int count)
2382 SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack);
2386 return SDL_OutOfMemory();
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;
2396 retval = QueueCmdFillRects(renderer, frects, count);
2398 SDL_small_free(frects, isstack);
2400 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2404 SDL_RenderDrawPointsF(SDL_Renderer * renderer,
2405 const SDL_FPoint * points, int count)
2407 SDL_FPoint *fpoints;
2412 CHECK_RENDERER_MAGIC(renderer, -1);
2415 return SDL_SetError("SDL_RenderDrawFPoints(): Passed NULL points");
2421 /* Don't draw while we're hidden */
2422 if (renderer->hidden) {
2426 if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
2427 return RenderDrawPointsWithRectsF(renderer, points, count);
2430 fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
2432 return SDL_OutOfMemory();
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;
2439 retval = QueueCmdDrawPoints(renderer, fpoints, count);
2441 SDL_small_free(fpoints, isstack);
2443 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2447 SDL_RenderDrawLine(SDL_Renderer * renderer, int x1, int y1, int x2, int y2)
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);
2458 SDL_RenderDrawLineF(SDL_Renderer * renderer, float x1, float y1, float x2, float y2)
2460 SDL_FPoint points[2];
2465 return SDL_RenderDrawLinesF(renderer, points, 2);
2469 RenderDrawLinesWithRects(SDL_Renderer * renderer,
2470 const SDL_Point * points, const int count)
2474 SDL_FPoint fpoints[2];
2479 frects = SDL_small_alloc(SDL_FRect, count-1, &isstack);
2481 return SDL_OutOfMemory();
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);
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);
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;
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);
2513 retval += QueueCmdFillRects(renderer, frects, nrects);
2515 SDL_small_free(frects, isstack);
2520 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2524 RenderDrawLinesWithRectsF(SDL_Renderer * renderer,
2525 const SDL_FPoint * points, const int count)
2529 SDL_FPoint fpoints[2];
2534 frects = SDL_small_alloc(SDL_FRect, count-1, &isstack);
2536 return SDL_OutOfMemory();
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);
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);
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;
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);
2568 retval += QueueCmdFillRects(renderer, frects, nrects);
2570 SDL_small_free(frects, isstack);
2575 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2579 SDL_RenderDrawLines(SDL_Renderer * renderer,
2580 const SDL_Point * points, int count)
2582 SDL_FPoint *fpoints;
2587 CHECK_RENDERER_MAGIC(renderer, -1);
2590 return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points");
2596 /* Don't draw while we're hidden */
2597 if (renderer->hidden) {
2601 if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
2602 return RenderDrawLinesWithRects(renderer, points, count);
2605 fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
2607 return SDL_OutOfMemory();
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;
2614 retval = QueueCmdDrawLines(renderer, fpoints, count);
2616 SDL_small_free(fpoints, isstack);
2618 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2622 SDL_RenderDrawLinesF(SDL_Renderer * renderer,
2623 const SDL_FPoint * points, int count)
2625 SDL_FPoint *fpoints;
2630 CHECK_RENDERER_MAGIC(renderer, -1);
2633 return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points");
2639 /* Don't draw while we're hidden */
2640 if (renderer->hidden) {
2644 if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) {
2645 return RenderDrawLinesWithRectsF(renderer, points, count);
2648 fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack);
2650 return SDL_OutOfMemory();
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;
2657 retval = QueueCmdDrawLines(renderer, fpoints, count);
2659 SDL_small_free(fpoints, isstack);
2661 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2665 SDL_RenderDrawRect(SDL_Renderer * renderer, const SDL_Rect * rect)
2668 SDL_FRect *prect = NULL;
2671 frect.x = (float) rect->x;
2672 frect.y = (float) rect->y;
2673 frect.w = (float) rect->w;
2674 frect.h = (float) rect->h;
2678 return SDL_RenderDrawRectF(renderer, prect);
2682 SDL_RenderDrawRectF(SDL_Renderer * renderer, const SDL_FRect * rect)
2685 SDL_FPoint points[5];
2687 CHECK_RENDERER_MAGIC(renderer, -1);
2689 /* If 'rect' == NULL, then outline the whole surface */
2692 SDL_RenderGetViewport(renderer, &r);
2695 frect.w = (float) r.w;
2696 frect.h = (float) r.h;
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);
2714 SDL_RenderDrawRects(SDL_Renderer * renderer,
2715 const SDL_Rect * rects, int count)
2719 CHECK_RENDERER_MAGIC(renderer, -1);
2722 return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects");
2728 /* Don't draw while we're hidden */
2729 if (renderer->hidden) {
2733 for (i = 0; i < count; ++i) {
2734 if (SDL_RenderDrawRect(renderer, &rects[i]) < 0) {
2742 SDL_RenderDrawRectsF(SDL_Renderer * renderer,
2743 const SDL_FRect * rects, int count)
2747 CHECK_RENDERER_MAGIC(renderer, -1);
2750 return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects");
2756 /* Don't draw while we're hidden */
2757 if (renderer->hidden) {
2761 for (i = 0; i < count; ++i) {
2762 if (SDL_RenderDrawRectF(renderer, &rects[i]) < 0) {
2770 SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect)
2774 CHECK_RENDERER_MAGIC(renderer, -1);
2776 /* If 'rect' == NULL, then outline the whole surface */
2778 frect.x = (float) rect->x;
2779 frect.y = (float) rect->y;
2780 frect.w = (float) rect->w;
2781 frect.h = (float) rect->h;
2785 SDL_RenderGetViewport(renderer, &r);
2788 frect.w = (float) r.w;
2789 frect.h = (float) r.h;
2791 return SDL_RenderFillRectsF(renderer, &frect, 1);
2795 SDL_RenderFillRectF(SDL_Renderer * renderer, const SDL_FRect * rect)
2799 CHECK_RENDERER_MAGIC(renderer, -1);
2801 /* If 'rect' == NULL, then outline the whole surface */
2805 SDL_RenderGetViewport(renderer, &r);
2808 frect.w = (float) r.w;
2809 frect.h = (float) r.h;
2812 return SDL_RenderFillRectsF(renderer, rect, 1);
2816 SDL_RenderFillRects(SDL_Renderer * renderer,
2817 const SDL_Rect * rects, int count)
2824 CHECK_RENDERER_MAGIC(renderer, -1);
2827 return SDL_SetError("SDL_RenderFillRects(): Passed NULL rects");
2833 /* Don't draw while we're hidden */
2834 if (renderer->hidden) {
2838 frects = SDL_small_alloc(SDL_FRect, count, &isstack);
2840 return SDL_OutOfMemory();
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;
2849 retval = QueueCmdFillRects(renderer, frects, count);
2851 SDL_small_free(frects, isstack);
2853 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
2857 SDL_RenderFillRectsF(SDL_Renderer * renderer,
2858 const SDL_FRect * rects, int count)
2865 CHECK_RENDERER_MAGIC(renderer, -1);
2868 return SDL_SetError("SDL_RenderFillFRects(): Passed NULL rects");
2874 /* Don't draw while we're hidden */
2875 if (renderer->hidden) {
2879 frects = SDL_small_alloc(SDL_FRect, count, &isstack);
2881 return SDL_OutOfMemory();
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;
2890 retval = QueueCmdFillRects(renderer, frects, count);
2892 SDL_small_free(frects, isstack);
2894 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
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)
2900 return ((!r) || (r->w <= 0.0f) || (r->h <= 0.0f)) ? SDL_TRUE : SDL_FALSE;
2903 /* !!! FIXME: move this to a public API if we want to do float versions of all of these later */
2905 SDL_HasIntersectionF(const SDL_FRect * A, const SDL_FRect * B)
2907 float Amin, Amax, Bmin, Bmax;
2910 SDL_InvalidParamError("A");
2915 SDL_InvalidParamError("B");
2919 /* Special cases for empty rects */
2920 if (SDL_FRectEmpty(A) || SDL_FRectEmpty(B)) {
2924 /* Horizontal intersection */
2936 /* Vertical intersection */
2952 SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
2953 const SDL_Rect * srcrect, const SDL_Rect * dstrect)
2956 SDL_FRect *pdstfrect = NULL;
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;
2964 return SDL_RenderCopyF(renderer, texture, srcrect, pdstfrect);
2968 SDL_RenderCopyF(SDL_Renderer * renderer, SDL_Texture * texture,
2969 const SDL_Rect * srcrect, const SDL_FRect * dstrect)
2971 SDL_Rect real_srcrect;
2972 SDL_FRect real_dstrect;
2976 CHECK_RENDERER_MAGIC(renderer, -1);
2977 CHECK_TEXTURE_MAGIC(texture, -1);
2979 if (renderer != texture->renderer) {
2980 return SDL_SetError("Texture was not created with this renderer");
2983 /* Don't draw while we're hidden */
2984 if (renderer->hidden) {
2990 real_srcrect.w = texture->w;
2991 real_srcrect.h = texture->h;
2993 if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
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;
3005 if (!SDL_HasIntersectionF(dstrect, &real_dstrect)) {
3008 real_dstrect = *dstrect;
3011 if (texture->native) {
3012 texture = texture->native;
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;
3020 texture->last_command_generation = renderer->render_command_generation;
3022 retval = QueueCmdCopy(renderer, texture, &real_srcrect, &real_dstrect);
3023 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
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)
3032 SDL_FRect *pdstfrect = NULL;
3034 SDL_FPoint *pfcenter = NULL;
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;
3045 fcenter.x = (float) center->x;
3046 fcenter.y = (float) center->y;
3047 pfcenter = &fcenter;
3050 return SDL_RenderCopyExF(renderer, texture, srcrect, pdstfrect, angle, pfcenter, flip);
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)
3058 SDL_Rect real_srcrect;
3059 SDL_FRect real_dstrect;
3060 SDL_FPoint real_center;
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);
3067 CHECK_RENDERER_MAGIC(renderer, -1);
3068 CHECK_TEXTURE_MAGIC(texture, -1);
3070 if (renderer != texture->renderer) {
3071 return SDL_SetError("Texture was not created with this renderer");
3073 if (!renderer->QueueCopyEx) {
3074 return SDL_SetError("Renderer does not support RenderCopyEx");
3077 /* Don't draw while we're hidden */
3078 if (renderer->hidden) {
3084 real_srcrect.w = texture->w;
3085 real_srcrect.h = texture->h;
3087 if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
3092 /* We don't intersect the dstrect with the viewport as RenderCopy does because of potential rotation clipping issues... TODO: should we? */
3094 real_dstrect = *dstrect;
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;
3105 if (texture->native) {
3106 texture = texture->native;
3110 real_center = *center;
3112 real_center.x = real_dstrect.w / 2.0f;
3113 real_center.y = real_dstrect.h / 2.0f;
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;
3121 real_center.x *= renderer->scale.x;
3122 real_center.y *= renderer->scale.y;
3124 texture->last_command_generation = renderer->render_command_generation;
3126 retval = QueueCmdCopyEx(renderer, texture, &real_srcrect, &real_dstrect, angle, &real_center, flip);
3127 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
3131 SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
3132 Uint32 format, void * pixels, int pitch)
3136 CHECK_RENDERER_MAGIC(renderer, -1);
3138 if (!renderer->RenderReadPixels) {
3139 return SDL_Unsupported();
3142 FlushRenderCommands(renderer); /* we need to render before we read the results. */
3145 format = SDL_GetWindowPixelFormat(renderer->window);
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;
3153 if (!SDL_IntersectRect(rect, &real_rect, &real_rect)) {
3156 if (real_rect.y > rect->y) {
3157 pixels = (Uint8 *)pixels + pitch * (real_rect.y - rect->y);
3159 if (real_rect.x > rect->x) {
3160 int bpp = SDL_BYTESPERPIXEL(format);
3161 pixels = (Uint8 *)pixels + bpp * (real_rect.x - rect->x);
3165 return renderer->RenderReadPixels(renderer, &real_rect,
3166 format, pixels, pitch);
3170 SDL_RenderPresent(SDL_Renderer * renderer)
3172 CHECK_RENDERER_MAGIC(renderer, );
3174 FlushRenderCommands(renderer); /* time to send everything to the GPU! */
3176 /* Don't present while we're hidden */
3177 if (renderer->hidden) {
3180 renderer->RenderPresent(renderer);
3184 SDL_DestroyTexture(SDL_Texture * texture)
3186 SDL_Renderer *renderer;
3188 CHECK_TEXTURE_MAGIC(texture, );
3190 renderer = texture->renderer;
3191 if (texture == renderer->target) {
3192 SDL_SetRenderTarget(renderer, NULL); /* implies command queue flush */
3194 FlushRenderCommandsIfTextureNeeded(texture);
3197 texture->magic = NULL;
3199 if (texture->next) {
3200 texture->next->prev = texture->prev;
3202 if (texture->prev) {
3203 texture->prev->next = texture->next;
3205 renderer->textures = texture->next;
3208 if (texture->native) {
3209 SDL_DestroyTexture(texture->native);
3213 SDL_SW_DestroyYUVTexture(texture->yuv);
3216 SDL_free(texture->pixels);
3218 renderer->DestroyTexture(renderer, texture);
3220 SDL_FreeSurface(texture->locked_surface);
3221 texture->locked_surface = NULL;
3227 SDL_DestroyRenderer(SDL_Renderer * renderer)
3229 SDL_RenderCommand *cmd;
3231 CHECK_RENDERER_MAGIC(renderer, );
3233 SDL_DelEventWatch(SDL_RendererEventWatch, renderer);
3235 if (renderer->render_commands_tail != NULL) {
3236 renderer->render_commands_tail->next = renderer->render_commands_pool;
3237 cmd = renderer->render_commands;
3239 cmd = renderer->render_commands_pool;
3242 renderer->render_commands_pool = NULL;
3243 renderer->render_commands_tail = NULL;
3244 renderer->render_commands = NULL;
3246 while (cmd != NULL) {
3247 SDL_RenderCommand *next = cmd->next;
3252 SDL_free(renderer->vertex_data);
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. */
3261 if (renderer->window) {
3262 SDL_SetWindowData(renderer->window, SDL_WINDOWRENDERDATA, NULL);
3265 /* It's no longer magical... */
3266 renderer->magic = NULL;
3268 /* Free the target mutex */
3269 SDL_DestroyMutex(renderer->target_mutex);
3270 renderer->target_mutex = NULL;
3272 /* Free the renderer instance */
3273 renderer->DestroyRenderer(renderer);
3276 int SDL_GL_BindTexture(SDL_Texture *texture, float *texw, float *texh)
3278 SDL_Renderer *renderer;
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);
3288 return SDL_Unsupported();
3292 int SDL_GL_UnbindTexture(SDL_Texture *texture)
3294 SDL_Renderer *renderer;
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);
3305 return SDL_Unsupported();
3309 SDL_RenderGetMetalLayer(SDL_Renderer * renderer)
3311 CHECK_RENDERER_MAGIC(renderer, NULL);
3313 if (renderer->GetMetalLayer) {
3314 FlushRenderCommands(renderer); /* in case the app is going to mess with it. */
3315 return renderer->GetMetalLayer(renderer);
3321 SDL_RenderGetMetalCommandEncoder(SDL_Renderer * renderer)
3323 CHECK_RENDERER_MAGIC(renderer, NULL);
3325 if (renderer->GetMetalCommandEncoder) {
3326 FlushRenderCommands(renderer); /* in case the app is going to mess with it. */
3327 return renderer->GetMetalCommandEncoder(renderer);
3332 static SDL_BlendMode
3333 SDL_GetShortBlendMode(SDL_BlendMode blendMode)
3335 if (blendMode == SDL_BLENDMODE_NONE_FULL) {
3336 return SDL_BLENDMODE_NONE;
3338 if (blendMode == SDL_BLENDMODE_BLEND_FULL) {
3339 return SDL_BLENDMODE_BLEND;
3341 if (blendMode == SDL_BLENDMODE_ADD_FULL) {
3342 return SDL_BLENDMODE_ADD;
3344 if (blendMode == SDL_BLENDMODE_MOD_FULL) {
3345 return SDL_BLENDMODE_MOD;
3347 if (blendMode == SDL_BLENDMODE_MUL_FULL) {
3348 return SDL_BLENDMODE_MUL;
3353 static SDL_BlendMode
3354 SDL_GetLongBlendMode(SDL_BlendMode blendMode)
3356 if (blendMode == SDL_BLENDMODE_NONE) {
3357 return SDL_BLENDMODE_NONE_FULL;
3359 if (blendMode == SDL_BLENDMODE_BLEND) {
3360 return SDL_BLENDMODE_BLEND_FULL;
3362 if (blendMode == SDL_BLENDMODE_ADD) {
3363 return SDL_BLENDMODE_ADD_FULL;
3365 if (blendMode == SDL_BLENDMODE_MOD) {
3366 return SDL_BLENDMODE_MOD_FULL;
3368 if (blendMode == SDL_BLENDMODE_MUL) {
3369 return SDL_BLENDMODE_MUL_FULL;
3375 SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor,
3376 SDL_BlendOperation colorOperation,
3377 SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor,
3378 SDL_BlendOperation alphaOperation)
3380 SDL_BlendMode blendMode = SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation,
3381 srcAlphaFactor, dstAlphaFactor, alphaOperation);
3382 return SDL_GetShortBlendMode(blendMode);
3386 SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode)
3388 blendMode = SDL_GetLongBlendMode(blendMode);
3389 return (SDL_BlendFactor)(((Uint32)blendMode >> 4) & 0xF);
3393 SDL_GetBlendModeDstColorFactor(SDL_BlendMode blendMode)
3395 blendMode = SDL_GetLongBlendMode(blendMode);
3396 return (SDL_BlendFactor)(((Uint32)blendMode >> 8) & 0xF);
3400 SDL_GetBlendModeColorOperation(SDL_BlendMode blendMode)
3402 blendMode = SDL_GetLongBlendMode(blendMode);
3403 return (SDL_BlendOperation)(((Uint32)blendMode >> 0) & 0xF);
3407 SDL_GetBlendModeSrcAlphaFactor(SDL_BlendMode blendMode)
3409 blendMode = SDL_GetLongBlendMode(blendMode);
3410 return (SDL_BlendFactor)(((Uint32)blendMode >> 20) & 0xF);
3414 SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode)
3416 blendMode = SDL_GetLongBlendMode(blendMode);
3417 return (SDL_BlendFactor)(((Uint32)blendMode >> 24) & 0xF);
3421 SDL_GetBlendModeAlphaOperation(SDL_BlendMode blendMode)
3423 blendMode = SDL_GetLongBlendMode(blendMode);
3424 return (SDL_BlendOperation)(((Uint32)blendMode >> 16) & 0xF);
3427 /* vi: set ts=4 sw=4 expandtab: */