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