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 |
36 | that will crash the app. However, these apps _should_ have used |
37 | SDL_AddEventWatch to catch SDL_APP_WILLENTERBACKGROUND events and stopped |
38 | drawing themselves. Other platforms still draw, as the compositor can use it, |
39 | and more importantly: drawing to render targets isn't lost. But I still think |
40 | this 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 |
94 | static 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 | |
131 | static char renderer_magic; |
132 | static char texture_magic; |
133 | |
134 | static SDL_INLINE void |
135 | DebugLogRenderCommands(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 | |
225 | static int |
226 | FlushRenderCommands(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 | |
256 | static int |
257 | FlushRenderCommandsIfTextureNeeded(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 | |
267 | static SDL_INLINE int |
268 | FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer) |
269 | { |
270 | return renderer->batching ? 0 : FlushRenderCommands(renderer); |
271 | } |
272 | |
273 | int |
274 | SDL_RenderFlush(SDL_Renderer * renderer) |
275 | { |
276 | return FlushRenderCommands(renderer); |
277 | } |
278 | |
279 | void * |
280 | SDL_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 | |
315 | static SDL_RenderCommand * |
316 | AllocateRenderCommand(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 | |
344 | static int |
345 | QueueCmdSetViewport(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 | |
367 | static int |
368 | QueueCmdSetClipRect(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 | |
389 | static int |
390 | QueueCmdSetDrawColor(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 | |
418 | static int |
419 | QueueCmdClear(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 | |
435 | static int |
436 | PrepQueueCmdDraw(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 | |
451 | static SDL_RenderCommand * |
452 | PrepQueueCmdDrawSolid(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 | |
473 | static int |
474 | QueueCmdDrawPoints(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 | |
487 | static int |
488 | QueueCmdDrawLines(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 | |
501 | static int |
502 | QueueCmdFillRects(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 | |
515 | static SDL_RenderCommand * |
516 | PrepQueueCmdDrawTexture(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 | |
537 | static int |
538 | QueueCmdCopy(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 | |
551 | static int |
552 | QueueCmdCopyEx(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 | |
569 | static int UpdateLogicalSize(SDL_Renderer *renderer); |
570 | |
571 | int |
572 | SDL_GetNumRenderDrivers(void) |
573 | { |
574 | #if !SDL_RENDER_DISABLED |
575 | return SDL_arraysize(render_drivers); |
576 | #else |
577 | return 0; |
578 | #endif |
579 | } |
580 | |
581 | int |
582 | SDL_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 | |
596 | static 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 | |
606 | static int SDLCALL |
607 | SDL_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 | |
771 | int |
772 | SDL_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 | |
791 | static SDL_INLINE |
792 | void 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 | |
805 | SDL_Renderer * |
806 | SDL_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 | |
939 | error: |
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 | |
952 | SDL_Renderer * |
953 | SDL_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 | |
979 | SDL_Renderer * |
980 | SDL_GetRenderer(SDL_Window * window) |
981 | { |
982 | return (SDL_Renderer *)SDL_GetWindowData(window, SDL_WINDOWRENDERDATA); |
983 | } |
984 | |
985 | int |
986 | SDL_GetRendererInfo(SDL_Renderer * renderer, SDL_RendererInfo * info) |
987 | { |
988 | CHECK_RENDERER_MAGIC(renderer, -1); |
989 | |
990 | *info = renderer->info; |
991 | return 0; |
992 | } |
993 | |
994 | int |
995 | SDL_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 | |
1012 | static SDL_bool |
1013 | IsSupportedBlendMode(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 | |
1030 | static SDL_bool |
1031 | IsSupportedFormat(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 | |
1043 | static Uint32 |
1044 | GetClosestSupportedFormat(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 | |
1070 | static 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 | |
1085 | SDL_Texture * |
1086 | SDL_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 | |
1194 | SDL_Texture * |
1195 | SDL_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 | |
1338 | int |
1339 | SDL_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 | |
1359 | int |
1360 | SDL_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 | |
1378 | int |
1379 | SDL_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 | |
1396 | int |
1397 | SDL_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 | |
1413 | int |
1414 | SDL_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 | |
1424 | int |
1425 | SDL_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 | |
1442 | int |
1443 | SDL_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 | |
1453 | int |
1454 | SDL_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 | |
1469 | int |
1470 | SDL_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 |
1481 | static int |
1482 | SDL_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 | |
1528 | static int |
1529 | SDL_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 | |
1569 | int |
1570 | SDL_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 |
1612 | static int |
1613 | SDL_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 | |
1664 | static int |
1665 | SDL_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 | |
1718 | int 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 | |
1785 | int 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 |
1848 | static int |
1849 | SDL_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 | |
1856 | static int |
1857 | SDL_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 | |
1868 | int |
1869 | SDL_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 | |
1908 | int |
1909 | SDL_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 |
1945 | static void |
1946 | SDL_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 | |
1967 | static void |
1968 | SDL_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 | |
1988 | void |
1989 | SDL_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 | |
2012 | SDL_bool |
2013 | SDL_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 | |
2021 | int |
2022 | SDL_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 | |
2101 | SDL_Texture * |
2102 | SDL_GetRenderTarget(SDL_Renderer *renderer) |
2103 | { |
2104 | return renderer->target; |
2105 | } |
2106 | |
2107 | static int |
2108 | UpdateLogicalSize(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 | |
2216 | int |
2217 | SDL_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 | |
2236 | void |
2237 | SDL_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 | |
2249 | int |
2250 | SDL_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 | |
2259 | SDL_bool |
2260 | SDLCALL SDL_RenderGetIntegerScale(SDL_Renderer * renderer) |
2261 | { |
2262 | CHECK_RENDERER_MAGIC(renderer, SDL_FALSE); |
2263 | |
2264 | return renderer->integer_scale; |
2265 | } |
2266 | |
2267 | int |
2268 | SDL_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 | |
2289 | void |
2290 | SDL_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 | |
2302 | static void |
2303 | RenderGetViewportSize(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 | |
2311 | int |
2312 | SDL_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 | |
2332 | void |
2333 | SDL_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 | |
2345 | SDL_bool |
2346 | SDL_RenderIsClipEnabled(SDL_Renderer * renderer) |
2347 | { |
2348 | CHECK_RENDERER_MAGIC(renderer, SDL_FALSE) |
2349 | return renderer->clipping_enabled; |
2350 | } |
2351 | |
2352 | int |
2353 | SDL_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 | |
2362 | void |
2363 | SDL_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 | |
2375 | int |
2376 | SDL_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 | |
2388 | int |
2389 | SDL_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 | |
2409 | int |
2410 | SDL_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 | |
2421 | int |
2422 | SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode *blendMode) |
2423 | { |
2424 | CHECK_RENDERER_MAGIC(renderer, -1); |
2425 | |
2426 | *blendMode = renderer->blendMode; |
2427 | return 0; |
2428 | } |
2429 | |
2430 | int |
2431 | SDL_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 | |
2443 | int |
2444 | SDL_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 | |
2452 | int |
2453 | SDL_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 | |
2461 | static int |
2462 | RenderDrawPointsWithRects(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 | |
2488 | int |
2489 | SDL_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 | |
2533 | static int |
2534 | RenderDrawPointsWithRectsF(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 | |
2560 | int |
2561 | SDL_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 | |
2605 | int |
2606 | SDL_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 | |
2616 | int |
2617 | SDL_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 | |
2627 | static int |
2628 | RenderDrawLinesWithRects(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 | |
2682 | static int |
2683 | RenderDrawLinesWithRectsF(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 | |
2737 | int |
2738 | SDL_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 | |
2782 | int |
2783 | SDL_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 | |
2827 | int |
2828 | SDL_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 | |
2844 | int |
2845 | SDL_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 | |
2871 | int |
2872 | SDL_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 | |
2901 | int |
2902 | SDL_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 | |
2931 | int |
2932 | SDL_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 | |
2950 | int |
2951 | SDL_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 | |
2965 | int |
2966 | SDL_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 | |
3008 | int |
3009 | SDL_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 */ |
3052 | SDL_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 */ |
3058 | static SDL_bool |
3059 | SDL_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 | |
3105 | int |
3106 | SDL_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 | |
3121 | int |
3122 | SDL_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 | |
3176 | int |
3177 | SDL_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 | |
3203 | int |
3204 | SDL_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 | |
3276 | int |
3277 | SDL_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 | |
3315 | void |
3316 | SDL_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 | |
3332 | void |
3333 | SDL_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 | |
3375 | void |
3376 | SDL_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 | |
3425 | int 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 | |
3441 | int 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 | |
3457 | void * |
3458 | SDL_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 | |
3469 | void * |
3470 | SDL_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 | |
3481 | static SDL_BlendMode |
3482 | SDL_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 | |
3502 | static SDL_BlendMode |
3503 | SDL_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 | |
3523 | SDL_BlendMode |
3524 | SDL_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 | |
3534 | SDL_BlendFactor |
3535 | SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode) |
3536 | { |
3537 | blendMode = SDL_GetLongBlendMode(blendMode); |
3538 | return (SDL_BlendFactor)(((Uint32)blendMode >> 4) & 0xF); |
3539 | } |
3540 | |
3541 | SDL_BlendFactor |
3542 | SDL_GetBlendModeDstColorFactor(SDL_BlendMode blendMode) |
3543 | { |
3544 | blendMode = SDL_GetLongBlendMode(blendMode); |
3545 | return (SDL_BlendFactor)(((Uint32)blendMode >> 8) & 0xF); |
3546 | } |
3547 | |
3548 | SDL_BlendOperation |
3549 | SDL_GetBlendModeColorOperation(SDL_BlendMode blendMode) |
3550 | { |
3551 | blendMode = SDL_GetLongBlendMode(blendMode); |
3552 | return (SDL_BlendOperation)(((Uint32)blendMode >> 0) & 0xF); |
3553 | } |
3554 | |
3555 | SDL_BlendFactor |
3556 | SDL_GetBlendModeSrcAlphaFactor(SDL_BlendMode blendMode) |
3557 | { |
3558 | blendMode = SDL_GetLongBlendMode(blendMode); |
3559 | return (SDL_BlendFactor)(((Uint32)blendMode >> 20) & 0xF); |
3560 | } |
3561 | |
3562 | SDL_BlendFactor |
3563 | SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode) |
3564 | { |
3565 | blendMode = SDL_GetLongBlendMode(blendMode); |
3566 | return (SDL_BlendFactor)(((Uint32)blendMode >> 24) & 0xF); |
3567 | } |
3568 | |
3569 | SDL_BlendOperation |
3570 | SDL_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 | |