1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2025 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_sysrender.h" |
26 | #include "SDL_render_debug_font.h" |
27 | #include "software/SDL_render_sw_c.h" |
28 | #include "../events/SDL_windowevents_c.h" |
29 | #include "../video/SDL_pixels_c.h" |
30 | #include "../video/SDL_video_c.h" |
31 | |
32 | #ifdef SDL_PLATFORM_ANDROID |
33 | #include "../core/android/SDL_android.h" |
34 | #include "../video/android/SDL_androidevents.h" |
35 | #endif |
36 | |
37 | /* as a courtesy to iOS apps, we don't try to draw when in the background, as |
38 | that will crash the app. However, these apps _should_ have used |
39 | SDL_AddEventWatch to catch SDL_EVENT_WILL_ENTER_BACKGROUND events and stopped |
40 | drawing themselves. Other platforms still draw, as the compositor can use it, |
41 | and more importantly: drawing to render targets isn't lost. But I still think |
42 | this should probably be removed at some point in the future. --ryan. */ |
43 | #if defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS) || defined(SDL_PLATFORM_ANDROID) |
44 | #define DONT_DRAW_WHILE_HIDDEN 1 |
45 | #else |
46 | #define DONT_DRAW_WHILE_HIDDEN 0 |
47 | #endif |
48 | |
49 | #define SDL_PROP_WINDOW_RENDERER_POINTER "SDL.internal.window.renderer" |
50 | #define SDL_PROP_TEXTURE_PARENT_POINTER "SDL.internal.texture.parent" |
51 | |
52 | #define CHECK_RENDERER_MAGIC_BUT_NOT_DESTROYED_FLAG(renderer, result) \ |
53 | if (!SDL_ObjectValid(renderer, SDL_OBJECT_TYPE_RENDERER)) { \ |
54 | SDL_InvalidParamError("renderer"); \ |
55 | return result; \ |
56 | } |
57 | |
58 | #define CHECK_RENDERER_MAGIC(renderer, result) \ |
59 | CHECK_RENDERER_MAGIC_BUT_NOT_DESTROYED_FLAG(renderer, result); \ |
60 | if ((renderer)->destroyed) { \ |
61 | SDL_SetError("Renderer's window has been destroyed, can't use further"); \ |
62 | return result; \ |
63 | } |
64 | |
65 | #define CHECK_TEXTURE_MAGIC(texture, result) \ |
66 | if (!SDL_ObjectValid(texture, SDL_OBJECT_TYPE_TEXTURE)) { \ |
67 | SDL_InvalidParamError("texture"); \ |
68 | return result; \ |
69 | } |
70 | |
71 | // Predefined blend modes |
72 | #define SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation, \ |
73 | srcAlphaFactor, dstAlphaFactor, alphaOperation) \ |
74 | (SDL_BlendMode)(((Uint32)(colorOperation) << 0) | \ |
75 | ((Uint32)(srcColorFactor) << 4) | \ |
76 | ((Uint32)(dstColorFactor) << 8) | \ |
77 | ((Uint32)(alphaOperation) << 16) | \ |
78 | ((Uint32)(srcAlphaFactor) << 20) | \ |
79 | ((Uint32)(dstAlphaFactor) << 24)) |
80 | |
81 | #define SDL_BLENDMODE_NONE_FULL \ |
82 | SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD, \ |
83 | SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD) |
84 | |
85 | #define SDL_BLENDMODE_BLEND_FULL \ |
86 | SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \ |
87 | SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD) |
88 | |
89 | #define SDL_BLENDMODE_BLEND_PREMULTIPLIED_FULL \ |
90 | SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \ |
91 | SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD) |
92 | |
93 | #define SDL_BLENDMODE_ADD_FULL \ |
94 | SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, \ |
95 | SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD) |
96 | |
97 | #define SDL_BLENDMODE_ADD_PREMULTIPLIED_FULL \ |
98 | SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, \ |
99 | SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD) |
100 | |
101 | #define SDL_BLENDMODE_MOD_FULL \ |
102 | SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_COLOR, SDL_BLENDOPERATION_ADD, \ |
103 | SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD) |
104 | |
105 | #define SDL_BLENDMODE_MUL_FULL \ |
106 | SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_DST_COLOR, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \ |
107 | SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD) |
108 | |
109 | #ifndef SDL_RENDER_DISABLED |
110 | static const SDL_RenderDriver *render_drivers[] = { |
111 | #ifdef SDL_VIDEO_RENDER_D3D11 |
112 | &D3D11_RenderDriver, |
113 | #endif |
114 | #ifdef SDL_VIDEO_RENDER_D3D12 |
115 | &D3D12_RenderDriver, |
116 | #endif |
117 | #ifdef SDL_VIDEO_RENDER_D3D |
118 | &D3D_RenderDriver, |
119 | #endif |
120 | #ifdef SDL_VIDEO_RENDER_METAL |
121 | &METAL_RenderDriver, |
122 | #endif |
123 | #ifdef SDL_VIDEO_RENDER_OGL |
124 | &GL_RenderDriver, |
125 | #endif |
126 | #ifdef SDL_VIDEO_RENDER_OGL_ES2 |
127 | &GLES2_RenderDriver, |
128 | #endif |
129 | #ifdef SDL_VIDEO_RENDER_PS2 |
130 | &PS2_RenderDriver, |
131 | #endif |
132 | #ifdef SDL_VIDEO_RENDER_PSP |
133 | &PSP_RenderDriver, |
134 | #endif |
135 | #ifdef SDL_VIDEO_RENDER_VITA_GXM |
136 | &VITA_GXM_RenderDriver, |
137 | #endif |
138 | #ifdef SDL_VIDEO_RENDER_VULKAN |
139 | &VULKAN_RenderDriver, |
140 | #endif |
141 | #ifdef SDL_VIDEO_RENDER_GPU |
142 | &GPU_RenderDriver, |
143 | #endif |
144 | #ifdef SDL_VIDEO_RENDER_SW |
145 | &SW_RenderDriver, |
146 | #endif |
147 | NULL |
148 | }; |
149 | #endif // !SDL_RENDER_DISABLED |
150 | |
151 | static SDL_Renderer *SDL_renderers; |
152 | |
153 | static const int rect_index_order[] = { 0, 1, 2, 0, 2, 3 }; |
154 | |
155 | void SDL_QuitRender(void) |
156 | { |
157 | while (SDL_renderers) { |
158 | SDL_DestroyRenderer(SDL_renderers); |
159 | } |
160 | } |
161 | |
162 | bool SDL_AddSupportedTextureFormat(SDL_Renderer *renderer, SDL_PixelFormat format) |
163 | { |
164 | SDL_PixelFormat *texture_formats = (SDL_PixelFormat *)SDL_realloc((void *)renderer->texture_formats, (renderer->num_texture_formats + 2) * sizeof(SDL_PixelFormat)); |
165 | if (!texture_formats) { |
166 | return false; |
167 | } |
168 | texture_formats[renderer->num_texture_formats++] = format; |
169 | texture_formats[renderer->num_texture_formats] = SDL_PIXELFORMAT_UNKNOWN; |
170 | renderer->texture_formats = texture_formats; |
171 | SDL_SetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, texture_formats); |
172 | return true; |
173 | } |
174 | |
175 | void SDL_SetupRendererColorspace(SDL_Renderer *renderer, SDL_PropertiesID props) |
176 | { |
177 | renderer->output_colorspace = (SDL_Colorspace)SDL_GetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB); |
178 | } |
179 | |
180 | bool SDL_RenderingLinearSpace(SDL_Renderer *renderer) |
181 | { |
182 | SDL_Colorspace colorspace; |
183 | |
184 | if (renderer->target) { |
185 | colorspace = renderer->target->colorspace; |
186 | } else { |
187 | colorspace = renderer->output_colorspace; |
188 | } |
189 | if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) { |
190 | return true; |
191 | } |
192 | return false; |
193 | } |
194 | |
195 | void SDL_ConvertToLinear(SDL_FColor *color) |
196 | { |
197 | color->r = SDL_sRGBtoLinear(color->r); |
198 | color->g = SDL_sRGBtoLinear(color->g); |
199 | color->b = SDL_sRGBtoLinear(color->b); |
200 | } |
201 | |
202 | void SDL_ConvertFromLinear(SDL_FColor *color) |
203 | { |
204 | color->r = SDL_sRGBfromLinear(color->r); |
205 | color->g = SDL_sRGBfromLinear(color->g); |
206 | color->b = SDL_sRGBfromLinear(color->b); |
207 | } |
208 | |
209 | static SDL_INLINE void DebugLogRenderCommands(const SDL_RenderCommand *cmd) |
210 | { |
211 | #if 0 |
212 | unsigned int i = 1; |
213 | SDL_Log("Render commands to flush:" ); |
214 | while (cmd) { |
215 | switch (cmd->command) { |
216 | case SDL_RENDERCMD_NO_OP: |
217 | SDL_Log(" %u. no-op" , i++); |
218 | break; |
219 | |
220 | case SDL_RENDERCMD_SETVIEWPORT: |
221 | SDL_Log(" %u. set viewport (first=%u, rect={(%d, %d), %dx%d})" , i++, |
222 | (unsigned int) cmd->data.viewport.first, |
223 | cmd->data.viewport.rect.x, cmd->data.viewport.rect.y, |
224 | cmd->data.viewport.rect.w, cmd->data.viewport.rect.h); |
225 | break; |
226 | |
227 | case SDL_RENDERCMD_SETCLIPRECT: |
228 | SDL_Log(" %u. set cliprect (enabled=%s, rect={(%d, %d), %dx%d})" , i++, |
229 | cmd->data.cliprect.enabled ? "true" : "false" , |
230 | cmd->data.cliprect.rect.x, cmd->data.cliprect.rect.y, |
231 | cmd->data.cliprect.rect.w, cmd->data.cliprect.rect.h); |
232 | break; |
233 | |
234 | case SDL_RENDERCMD_SETDRAWCOLOR: |
235 | SDL_Log(" %u. set draw color (first=%u, r=%d, g=%d, b=%d, a=%d, color_scale=%g)" , i++, |
236 | (unsigned int) cmd->data.color.first, |
237 | (int) cmd->data.color.color.r, (int) cmd->data.color.color.g, |
238 | (int) cmd->data.color.color.b, (int) cmd->data.color.color.a, cmd->data.color.color_scale); |
239 | break; |
240 | |
241 | case SDL_RENDERCMD_CLEAR: |
242 | SDL_Log(" %u. clear (first=%u, r=%d, g=%d, b=%d, a=%d, color_scale=%g)" , i++, |
243 | (unsigned int) cmd->data.color.first, |
244 | (int) cmd->data.color.color.r, (int) cmd->data.color.color.g, |
245 | (int) cmd->data.color.color.b, (int) cmd->data.color.color.a, cmd->data.color.color_scale); |
246 | break; |
247 | |
248 | case SDL_RENDERCMD_DRAW_POINTS: |
249 | SDL_Log(" %u. draw points (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, color_scale=%g)" , i++, |
250 | (unsigned int) cmd->data.draw.first, |
251 | (unsigned int) cmd->data.draw.count, |
252 | (int) cmd->data.draw.color.r, (int) cmd->data.draw.color.g, |
253 | (int) cmd->data.draw.color.b, (int) cmd->data.draw.color.a, |
254 | (int) cmd->data.draw.blend, cmd->data.draw.color_scale); |
255 | break; |
256 | |
257 | case SDL_RENDERCMD_DRAW_LINES: |
258 | SDL_Log(" %u. draw lines (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, color_scale=%g)" , i++, |
259 | (unsigned int) cmd->data.draw.first, |
260 | (unsigned int) cmd->data.draw.count, |
261 | (int) cmd->data.draw.color.r, (int) cmd->data.draw.color.g, |
262 | (int) cmd->data.draw.color.b, (int) cmd->data.draw.color.a, |
263 | (int) cmd->data.draw.blend, cmd->data.draw.color_scale); |
264 | break; |
265 | |
266 | case SDL_RENDERCMD_FILL_RECTS: |
267 | SDL_Log(" %u. fill rects (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, color_scale=%g)" , i++, |
268 | (unsigned int) cmd->data.draw.first, |
269 | (unsigned int) cmd->data.draw.count, |
270 | (int) cmd->data.draw.color.r, (int) cmd->data.draw.color.g, |
271 | (int) cmd->data.draw.color.b, (int) cmd->data.draw.color.a, |
272 | (int) cmd->data.draw.blend, cmd->data.draw.color_scale); |
273 | break; |
274 | |
275 | case SDL_RENDERCMD_COPY: |
276 | SDL_Log(" %u. copy (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, color_scale=%g, tex=%p)" , i++, |
277 | (unsigned int) cmd->data.draw.first, |
278 | (unsigned int) cmd->data.draw.count, |
279 | (int) cmd->data.draw.color.r, (int) cmd->data.draw.color.g, |
280 | (int) cmd->data.draw.color.b, (int) cmd->data.draw.color.a, |
281 | (int) cmd->data.draw.blend, cmd->data.draw.color_scale, cmd->data.draw.texture); |
282 | break; |
283 | |
284 | |
285 | case SDL_RENDERCMD_COPY_EX: |
286 | SDL_Log(" %u. copyex (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, color_scale=%g, tex=%p)" , i++, |
287 | (unsigned int) cmd->data.draw.first, |
288 | (unsigned int) cmd->data.draw.count, |
289 | (int) cmd->data.draw.color.r, (int) cmd->data.draw.color.g, |
290 | (int) cmd->data.draw.color.b, (int) cmd->data.draw.color.a, |
291 | (int) cmd->data.draw.blend, cmd->data.draw.color_scale, cmd->data.draw.texture); |
292 | break; |
293 | |
294 | case SDL_RENDERCMD_GEOMETRY: |
295 | SDL_Log(" %u. geometry (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, color_scale=%g, tex=%p)" , i++, |
296 | (unsigned int) cmd->data.draw.first, |
297 | (unsigned int) cmd->data.draw.count, |
298 | (int) cmd->data.draw.color.r, (int) cmd->data.draw.color.g, |
299 | (int) cmd->data.draw.color.b, (int) cmd->data.draw.color.a, |
300 | (int) cmd->data.draw.blend, cmd->data.draw.color_scale, cmd->data.draw.texture); |
301 | break; |
302 | |
303 | } |
304 | cmd = cmd->next; |
305 | } |
306 | #endif |
307 | } |
308 | |
309 | static bool FlushRenderCommands(SDL_Renderer *renderer) |
310 | { |
311 | bool result; |
312 | |
313 | SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL)); |
314 | |
315 | if (!renderer->render_commands) { // nothing to do! |
316 | SDL_assert(renderer->vertex_data_used == 0); |
317 | return true; |
318 | } |
319 | |
320 | DebugLogRenderCommands(renderer->render_commands); |
321 | |
322 | result = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used); |
323 | |
324 | // Move the whole render command queue to the unused pool so we can reuse them next time. |
325 | if (renderer->render_commands_tail) { |
326 | renderer->render_commands_tail->next = renderer->render_commands_pool; |
327 | renderer->render_commands_pool = renderer->render_commands; |
328 | renderer->render_commands_tail = NULL; |
329 | renderer->render_commands = NULL; |
330 | } |
331 | renderer->vertex_data_used = 0; |
332 | renderer->render_command_generation++; |
333 | renderer->color_queued = false; |
334 | renderer->viewport_queued = false; |
335 | renderer->cliprect_queued = false; |
336 | return result; |
337 | } |
338 | |
339 | static bool FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture) |
340 | { |
341 | SDL_Renderer *renderer = texture->renderer; |
342 | if (texture->last_command_generation == renderer->render_command_generation) { |
343 | // the current command queue depends on this texture, flush the queue now before it changes |
344 | return FlushRenderCommands(renderer); |
345 | } |
346 | return true; |
347 | } |
348 | |
349 | static bool FlushRenderCommandsIfGPURenderStateNeeded(SDL_GPURenderState *state) |
350 | { |
351 | SDL_Renderer *renderer = state->renderer; |
352 | if (state->last_command_generation == renderer->render_command_generation) { |
353 | // the current command queue depends on this state, flush the queue now before it changes |
354 | return FlushRenderCommands(renderer); |
355 | } |
356 | return true; |
357 | } |
358 | |
359 | bool SDL_FlushRenderer(SDL_Renderer *renderer) |
360 | { |
361 | if (!FlushRenderCommands(renderer)) { |
362 | return false; |
363 | } |
364 | renderer->InvalidateCachedState(renderer); |
365 | return true; |
366 | } |
367 | |
368 | void *SDL_AllocateRenderVertices(SDL_Renderer *renderer, size_t numbytes, size_t alignment, size_t *offset) |
369 | { |
370 | const size_t needed = renderer->vertex_data_used + numbytes + alignment; |
371 | const size_t current_offset = renderer->vertex_data_used; |
372 | |
373 | const size_t aligner = (alignment && ((current_offset & (alignment - 1)) != 0)) ? (alignment - (current_offset & (alignment - 1))) : 0; |
374 | const size_t aligned = current_offset + aligner; |
375 | |
376 | if (renderer->vertex_data_allocation < needed) { |
377 | const size_t current_allocation = renderer->vertex_data ? renderer->vertex_data_allocation : 1024; |
378 | size_t newsize = current_allocation * 2; |
379 | void *ptr; |
380 | while (newsize < needed) { |
381 | newsize *= 2; |
382 | } |
383 | |
384 | ptr = SDL_realloc(renderer->vertex_data, newsize); |
385 | |
386 | if (!ptr) { |
387 | return NULL; |
388 | } |
389 | renderer->vertex_data = ptr; |
390 | renderer->vertex_data_allocation = newsize; |
391 | } |
392 | |
393 | if (offset) { |
394 | *offset = aligned; |
395 | } |
396 | |
397 | renderer->vertex_data_used += aligner + numbytes; |
398 | |
399 | return ((Uint8 *)renderer->vertex_data) + aligned; |
400 | } |
401 | |
402 | static SDL_RenderCommand *AllocateRenderCommand(SDL_Renderer *renderer) |
403 | { |
404 | SDL_RenderCommand *result = NULL; |
405 | |
406 | result = renderer->render_commands_pool; |
407 | if (result) { |
408 | renderer->render_commands_pool = result->next; |
409 | result->next = NULL; |
410 | } else { |
411 | result = (SDL_RenderCommand *)SDL_calloc(1, sizeof(*result)); |
412 | if (!result) { |
413 | return NULL; |
414 | } |
415 | } |
416 | |
417 | SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL)); |
418 | if (renderer->render_commands_tail) { |
419 | renderer->render_commands_tail->next = result; |
420 | } else { |
421 | renderer->render_commands = result; |
422 | } |
423 | renderer->render_commands_tail = result; |
424 | |
425 | return result; |
426 | } |
427 | |
428 | static void UpdatePixelViewport(SDL_Renderer *renderer, SDL_RenderViewState *view) |
429 | { |
430 | view->pixel_viewport.x = (int)SDL_floorf((view->viewport.x * view->current_scale.x) + view->logical_offset.x); |
431 | view->pixel_viewport.y = (int)SDL_floorf((view->viewport.y * view->current_scale.y) + view->logical_offset.y); |
432 | if (view->viewport.w >= 0) { |
433 | view->pixel_viewport.w = (int)SDL_ceilf(view->viewport.w * view->current_scale.x); |
434 | } else { |
435 | view->pixel_viewport.w = view->pixel_w; |
436 | } |
437 | if (view->viewport.h >= 0) { |
438 | view->pixel_viewport.h = (int)SDL_ceilf(view->viewport.h * view->current_scale.y); |
439 | } else { |
440 | view->pixel_viewport.h = view->pixel_h; |
441 | } |
442 | } |
443 | |
444 | static bool QueueCmdSetViewport(SDL_Renderer *renderer) |
445 | { |
446 | bool result = true; |
447 | |
448 | SDL_Rect viewport = renderer->view->pixel_viewport; |
449 | |
450 | if (!renderer->viewport_queued || |
451 | SDL_memcmp(&viewport, &renderer->last_queued_viewport, sizeof(viewport)) != 0) { |
452 | SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); |
453 | if (cmd) { |
454 | cmd->command = SDL_RENDERCMD_SETVIEWPORT; |
455 | cmd->data.viewport.first = 0; // render backend will fill this in. |
456 | SDL_copyp(&cmd->data.viewport.rect, &viewport); |
457 | result = renderer->QueueSetViewport(renderer, cmd); |
458 | if (!result) { |
459 | cmd->command = SDL_RENDERCMD_NO_OP; |
460 | } else { |
461 | SDL_copyp(&renderer->last_queued_viewport, &viewport); |
462 | renderer->viewport_queued = true; |
463 | } |
464 | } else { |
465 | result = false; |
466 | } |
467 | } |
468 | return result; |
469 | } |
470 | |
471 | static void UpdatePixelClipRect(SDL_Renderer *renderer, SDL_RenderViewState *view) |
472 | { |
473 | const float scale_x = view->current_scale.x; |
474 | const float scale_y = view->current_scale.y; |
475 | view->pixel_clip_rect.x = (int)SDL_floorf(view->clip_rect.x * scale_x); |
476 | view->pixel_clip_rect.y = (int)SDL_floorf(view->clip_rect.y * scale_y); |
477 | view->pixel_clip_rect.w = (int)SDL_ceilf(view->clip_rect.w * scale_x); |
478 | view->pixel_clip_rect.h = (int)SDL_ceilf(view->clip_rect.h * scale_y); |
479 | } |
480 | |
481 | static bool QueueCmdSetClipRect(SDL_Renderer *renderer) |
482 | { |
483 | bool result = true; |
484 | |
485 | const SDL_RenderViewState *view = renderer->view; |
486 | SDL_Rect clip_rect = view->pixel_clip_rect; |
487 | if (!renderer->cliprect_queued || |
488 | view->clipping_enabled != renderer->last_queued_cliprect_enabled || |
489 | SDL_memcmp(&clip_rect, &renderer->last_queued_cliprect, sizeof(clip_rect)) != 0) { |
490 | SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); |
491 | if (cmd) { |
492 | cmd->command = SDL_RENDERCMD_SETCLIPRECT; |
493 | cmd->data.cliprect.enabled = view->clipping_enabled; |
494 | SDL_copyp(&cmd->data.cliprect.rect, &clip_rect); |
495 | SDL_copyp(&renderer->last_queued_cliprect, &clip_rect); |
496 | renderer->last_queued_cliprect_enabled = view->clipping_enabled; |
497 | renderer->cliprect_queued = true; |
498 | } else { |
499 | result = false; |
500 | } |
501 | } |
502 | return result; |
503 | } |
504 | |
505 | static bool QueueCmdSetDrawColor(SDL_Renderer *renderer, SDL_FColor *color) |
506 | { |
507 | bool result = true; |
508 | |
509 | if (!renderer->color_queued || |
510 | color->r != renderer->last_queued_color.r || |
511 | color->g != renderer->last_queued_color.g || |
512 | color->b != renderer->last_queued_color.b || |
513 | color->a != renderer->last_queued_color.a) { |
514 | SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); |
515 | result = false; |
516 | |
517 | if (cmd) { |
518 | cmd->command = SDL_RENDERCMD_SETDRAWCOLOR; |
519 | cmd->data.color.first = 0; // render backend will fill this in. |
520 | cmd->data.color.color_scale = renderer->color_scale; |
521 | cmd->data.color.color = *color; |
522 | result = renderer->QueueSetDrawColor(renderer, cmd); |
523 | if (!result) { |
524 | cmd->command = SDL_RENDERCMD_NO_OP; |
525 | } else { |
526 | renderer->last_queued_color = *color; |
527 | renderer->color_queued = true; |
528 | } |
529 | } |
530 | } |
531 | return result; |
532 | } |
533 | |
534 | static bool QueueCmdClear(SDL_Renderer *renderer) |
535 | { |
536 | SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); |
537 | if (!cmd) { |
538 | return false; |
539 | } |
540 | |
541 | cmd->command = SDL_RENDERCMD_CLEAR; |
542 | cmd->data.color.first = 0; |
543 | cmd->data.color.color_scale = renderer->color_scale; |
544 | cmd->data.color.color = renderer->color; |
545 | return true; |
546 | } |
547 | |
548 | static SDL_RenderCommand *PrepQueueCmdDraw(SDL_Renderer *renderer, const SDL_RenderCommandType cmdtype, SDL_Texture *texture) |
549 | { |
550 | SDL_RenderCommand *cmd = NULL; |
551 | bool result = true; |
552 | SDL_FColor *color; |
553 | SDL_BlendMode blendMode; |
554 | |
555 | if (texture) { |
556 | color = &texture->color; |
557 | blendMode = texture->blendMode; |
558 | } else { |
559 | color = &renderer->color; |
560 | blendMode = renderer->blendMode; |
561 | } |
562 | |
563 | if (cmdtype != SDL_RENDERCMD_GEOMETRY) { |
564 | result = QueueCmdSetDrawColor(renderer, color); |
565 | } |
566 | |
567 | /* Set the viewport and clip rect directly before draws, so the backends |
568 | * don't have to worry about that state not being valid at draw time. */ |
569 | if (result && !renderer->viewport_queued) { |
570 | result = QueueCmdSetViewport(renderer); |
571 | } |
572 | if (result && !renderer->cliprect_queued) { |
573 | result = QueueCmdSetClipRect(renderer); |
574 | } |
575 | |
576 | if (result) { |
577 | cmd = AllocateRenderCommand(renderer); |
578 | if (cmd) { |
579 | cmd->command = cmdtype; |
580 | cmd->data.draw.first = 0; // render backend will fill this in. |
581 | cmd->data.draw.count = 0; // render backend will fill this in. |
582 | cmd->data.draw.color_scale = renderer->color_scale; |
583 | cmd->data.draw.color = *color; |
584 | cmd->data.draw.blend = blendMode; |
585 | cmd->data.draw.texture = texture; |
586 | if (texture) { |
587 | cmd->data.draw.texture_scale_mode = texture->scaleMode; |
588 | } |
589 | cmd->data.draw.texture_address_mode = SDL_TEXTURE_ADDRESS_CLAMP; |
590 | cmd->data.draw.gpu_render_state = renderer->gpu_render_state; |
591 | if (renderer->gpu_render_state) { |
592 | renderer->gpu_render_state->last_command_generation = renderer->render_command_generation; |
593 | } |
594 | } |
595 | } |
596 | return cmd; |
597 | } |
598 | |
599 | static bool QueueCmdDrawPoints(SDL_Renderer *renderer, const SDL_FPoint *points, const int count) |
600 | { |
601 | SDL_RenderCommand *cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_DRAW_POINTS, NULL); |
602 | bool result = false; |
603 | if (cmd) { |
604 | result = renderer->QueueDrawPoints(renderer, cmd, points, count); |
605 | if (!result) { |
606 | cmd->command = SDL_RENDERCMD_NO_OP; |
607 | } |
608 | } |
609 | return result; |
610 | } |
611 | |
612 | static bool QueueCmdDrawLines(SDL_Renderer *renderer, const SDL_FPoint *points, const int count) |
613 | { |
614 | SDL_RenderCommand *cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_DRAW_LINES, NULL); |
615 | bool result = false; |
616 | if (cmd) { |
617 | result = renderer->QueueDrawLines(renderer, cmd, points, count); |
618 | if (!result) { |
619 | cmd->command = SDL_RENDERCMD_NO_OP; |
620 | } |
621 | } |
622 | return result; |
623 | } |
624 | |
625 | static bool QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, const int count) |
626 | { |
627 | SDL_RenderCommand *cmd; |
628 | bool result = false; |
629 | const int use_rendergeometry = (!renderer->QueueFillRects); |
630 | |
631 | cmd = PrepQueueCmdDraw(renderer, (use_rendergeometry ? SDL_RENDERCMD_GEOMETRY : SDL_RENDERCMD_FILL_RECTS), NULL); |
632 | |
633 | if (cmd) { |
634 | if (use_rendergeometry) { |
635 | bool isstack1; |
636 | bool isstack2; |
637 | float *xy = SDL_small_alloc(float, 4 * 2 * count, &isstack1); |
638 | int *indices = SDL_small_alloc(int, 6 * count, &isstack2); |
639 | |
640 | if (xy && indices) { |
641 | int i; |
642 | float *ptr_xy = xy; |
643 | int *ptr_indices = indices; |
644 | const int xy_stride = 2 * sizeof(float); |
645 | const int num_vertices = 4 * count; |
646 | const int num_indices = 6 * count; |
647 | const int size_indices = 4; |
648 | int cur_index = 0; |
649 | |
650 | for (i = 0; i < count; ++i) { |
651 | float minx, miny, maxx, maxy; |
652 | |
653 | minx = rects[i].x; |
654 | miny = rects[i].y; |
655 | maxx = rects[i].x + rects[i].w; |
656 | maxy = rects[i].y + rects[i].h; |
657 | |
658 | *ptr_xy++ = minx; |
659 | *ptr_xy++ = miny; |
660 | *ptr_xy++ = maxx; |
661 | *ptr_xy++ = miny; |
662 | *ptr_xy++ = maxx; |
663 | *ptr_xy++ = maxy; |
664 | *ptr_xy++ = minx; |
665 | *ptr_xy++ = maxy; |
666 | |
667 | *ptr_indices++ = cur_index + rect_index_order[0]; |
668 | *ptr_indices++ = cur_index + rect_index_order[1]; |
669 | *ptr_indices++ = cur_index + rect_index_order[2]; |
670 | *ptr_indices++ = cur_index + rect_index_order[3]; |
671 | *ptr_indices++ = cur_index + rect_index_order[4]; |
672 | *ptr_indices++ = cur_index + rect_index_order[5]; |
673 | cur_index += 4; |
674 | } |
675 | |
676 | result = renderer->QueueGeometry(renderer, cmd, NULL, |
677 | xy, xy_stride, &renderer->color, 0 /* color_stride */, NULL, 0, |
678 | num_vertices, indices, num_indices, size_indices, |
679 | 1.0f, 1.0f); |
680 | |
681 | if (!result) { |
682 | cmd->command = SDL_RENDERCMD_NO_OP; |
683 | } |
684 | } |
685 | SDL_small_free(xy, isstack1); |
686 | SDL_small_free(indices, isstack2); |
687 | |
688 | } else { |
689 | result = renderer->QueueFillRects(renderer, cmd, rects, count); |
690 | if (!result) { |
691 | cmd->command = SDL_RENDERCMD_NO_OP; |
692 | } |
693 | } |
694 | } |
695 | return result; |
696 | } |
697 | |
698 | static bool QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect) |
699 | { |
700 | SDL_RenderCommand *cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_COPY, texture); |
701 | bool result = false; |
702 | if (cmd) { |
703 | result = renderer->QueueCopy(renderer, cmd, texture, srcrect, dstrect); |
704 | if (!result) { |
705 | cmd->command = SDL_RENDERCMD_NO_OP; |
706 | } |
707 | } |
708 | return result; |
709 | } |
710 | |
711 | static bool QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, |
712 | const SDL_FRect *srcquad, const SDL_FRect *dstrect, |
713 | const double angle, const SDL_FPoint *center, const SDL_FlipMode flip, float scale_x, float scale_y) |
714 | { |
715 | SDL_RenderCommand *cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_COPY_EX, texture); |
716 | bool result = false; |
717 | if (cmd) { |
718 | result = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip, scale_x, scale_y); |
719 | if (!result) { |
720 | cmd->command = SDL_RENDERCMD_NO_OP; |
721 | } |
722 | } |
723 | return result; |
724 | } |
725 | |
726 | static bool QueueCmdGeometry(SDL_Renderer *renderer, SDL_Texture *texture, |
727 | const float *xy, int xy_stride, |
728 | const SDL_FColor *color, int color_stride, |
729 | const float *uv, int uv_stride, |
730 | int num_vertices, |
731 | const void *indices, int num_indices, int size_indices, |
732 | float scale_x, float scale_y, SDL_TextureAddressMode texture_address_mode) |
733 | { |
734 | SDL_RenderCommand *cmd; |
735 | bool result = false; |
736 | cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_GEOMETRY, texture); |
737 | if (cmd) { |
738 | cmd->data.draw.texture_address_mode = texture_address_mode; |
739 | result = renderer->QueueGeometry(renderer, cmd, texture, |
740 | xy, xy_stride, |
741 | color, color_stride, uv, uv_stride, |
742 | num_vertices, indices, num_indices, size_indices, |
743 | scale_x, scale_y); |
744 | if (!result) { |
745 | cmd->command = SDL_RENDERCMD_NO_OP; |
746 | } |
747 | } |
748 | return result; |
749 | } |
750 | |
751 | static void UpdateMainViewDimensions(SDL_Renderer *renderer) |
752 | { |
753 | int window_w = 0, window_h = 0; |
754 | |
755 | if (renderer->window) { |
756 | SDL_GetWindowSize(renderer->window, &window_w, &window_h); |
757 | } |
758 | |
759 | SDL_GetRenderOutputSize(renderer, &renderer->main_view.pixel_w, &renderer->main_view.pixel_h); |
760 | |
761 | if (window_w > 0 && window_h > 0) { |
762 | renderer->dpi_scale.x = (float)renderer->main_view.pixel_w / window_w; |
763 | renderer->dpi_scale.y = (float)renderer->main_view.pixel_h / window_h; |
764 | } else { |
765 | renderer->dpi_scale.x = 1.0f; |
766 | renderer->dpi_scale.y = 1.0f; |
767 | } |
768 | UpdatePixelViewport(renderer, &renderer->main_view); |
769 | } |
770 | |
771 | static void UpdateColorScale(SDL_Renderer *renderer) |
772 | { |
773 | float SDR_white_point; |
774 | if (renderer->target) { |
775 | SDR_white_point = renderer->target->SDR_white_point; |
776 | } else { |
777 | SDR_white_point = renderer->SDR_white_point; |
778 | } |
779 | renderer->color_scale = renderer->desired_color_scale * SDR_white_point; |
780 | } |
781 | |
782 | static void UpdateHDRProperties(SDL_Renderer *renderer) |
783 | { |
784 | SDL_PropertiesID window_props; |
785 | SDL_PropertiesID renderer_props; |
786 | |
787 | window_props = SDL_GetWindowProperties(renderer->window); |
788 | if (!window_props) { |
789 | return; |
790 | } |
791 | |
792 | renderer_props = SDL_GetRendererProperties(renderer); |
793 | if (!renderer_props) { |
794 | return; |
795 | } |
796 | |
797 | if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { |
798 | renderer->SDR_white_point = SDL_GetFloatProperty(window_props, SDL_PROP_WINDOW_SDR_WHITE_LEVEL_FLOAT, 1.0f); |
799 | renderer->HDR_headroom = SDL_GetFloatProperty(window_props, SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT, 1.0f); |
800 | } else { |
801 | renderer->SDR_white_point = 1.0f; |
802 | renderer->HDR_headroom = 1.0f; |
803 | } |
804 | |
805 | if (renderer->HDR_headroom > 1.0f) { |
806 | SDL_SetBooleanProperty(renderer_props, SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN, true); |
807 | } else { |
808 | SDL_SetBooleanProperty(renderer_props, SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN, false); |
809 | } |
810 | SDL_SetFloatProperty(renderer_props, SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point); |
811 | SDL_SetFloatProperty(renderer_props, SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT, renderer->HDR_headroom); |
812 | |
813 | UpdateColorScale(renderer); |
814 | } |
815 | |
816 | static void UpdateLogicalPresentation(SDL_Renderer *renderer); |
817 | |
818 | |
819 | int SDL_GetNumRenderDrivers(void) |
820 | { |
821 | #ifndef SDL_RENDER_DISABLED |
822 | return SDL_arraysize(render_drivers) - 1; |
823 | #else |
824 | return 0; |
825 | #endif |
826 | } |
827 | |
828 | const char *SDL_GetRenderDriver(int index) |
829 | { |
830 | #ifndef SDL_RENDER_DISABLED |
831 | if (index < 0 || index >= SDL_GetNumRenderDrivers()) { |
832 | SDL_InvalidParamError("index" ); |
833 | return NULL; |
834 | } |
835 | return render_drivers[index]->name; |
836 | #else |
837 | SDL_SetError("SDL not built with rendering support" ); |
838 | return NULL; |
839 | #endif |
840 | } |
841 | |
842 | static bool SDL_RendererEventWatch(void *userdata, SDL_Event *event) |
843 | { |
844 | SDL_Renderer *renderer = (SDL_Renderer *)userdata; |
845 | SDL_Window *window = renderer->window; |
846 | |
847 | if (renderer->WindowEvent) { |
848 | renderer->WindowEvent(renderer, &event->window); |
849 | } |
850 | |
851 | if (event->type == SDL_EVENT_WINDOW_RESIZED || |
852 | event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED || |
853 | event->type == SDL_EVENT_WINDOW_METAL_VIEW_RESIZED) { |
854 | SDL_RenderViewState *view = renderer->view; |
855 | renderer->view = &renderer->main_view; // only update the main_view (the window framebuffer) for window changes. |
856 | UpdateLogicalPresentation(renderer); |
857 | renderer->view = view; // put us back on whatever the current render target's actual view is. |
858 | } else if (event->type == SDL_EVENT_WINDOW_HIDDEN) { |
859 | renderer->hidden = true; |
860 | } else if (event->type == SDL_EVENT_WINDOW_SHOWN) { |
861 | if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)) { |
862 | renderer->hidden = false; |
863 | } |
864 | } else if (event->type == SDL_EVENT_WINDOW_MINIMIZED) { |
865 | renderer->hidden = true; |
866 | } else if (event->type == SDL_EVENT_WINDOW_RESTORED || |
867 | event->type == SDL_EVENT_WINDOW_MAXIMIZED) { |
868 | if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN)) { |
869 | renderer->hidden = false; |
870 | } |
871 | } else if (event->type == SDL_EVENT_WINDOW_DISPLAY_CHANGED || |
872 | event->type == SDL_EVENT_WINDOW_HDR_STATE_CHANGED) { |
873 | UpdateHDRProperties(renderer); |
874 | } |
875 | return true; |
876 | } |
877 | |
878 | bool SDL_CreateWindowAndRenderer(const char *title, int width, int height, SDL_WindowFlags window_flags, SDL_Window **window, SDL_Renderer **renderer) |
879 | { |
880 | bool hidden = (window_flags & SDL_WINDOW_HIDDEN) != 0; |
881 | |
882 | if (!window) { |
883 | return SDL_InvalidParamError("window" ); |
884 | } |
885 | |
886 | if (!renderer) { |
887 | return SDL_InvalidParamError("renderer" ); |
888 | } |
889 | |
890 | // Hide the window so if the renderer recreates it, we don't get a visual flash on screen |
891 | window_flags |= SDL_WINDOW_HIDDEN; |
892 | *window = SDL_CreateWindow(title, width, height, window_flags); |
893 | if (!*window) { |
894 | *renderer = NULL; |
895 | return false; |
896 | } |
897 | |
898 | *renderer = SDL_CreateRenderer(*window, NULL); |
899 | if (!*renderer) { |
900 | SDL_DestroyWindow(*window); |
901 | *window = NULL; |
902 | return false; |
903 | } |
904 | |
905 | if (!hidden) { |
906 | SDL_ShowWindow(*window); |
907 | } |
908 | |
909 | return true; |
910 | } |
911 | |
912 | #ifndef SDL_RENDER_DISABLED |
913 | static SDL_INLINE void VerifyDrawQueueFunctions(const SDL_Renderer *renderer) |
914 | { |
915 | /* all of these functions are required to be implemented, even as no-ops, so we don't |
916 | have to check that they aren't NULL over and over. */ |
917 | SDL_assert(renderer->QueueSetViewport != NULL); |
918 | SDL_assert(renderer->QueueSetDrawColor != NULL); |
919 | SDL_assert(renderer->QueueDrawPoints != NULL); |
920 | SDL_assert(renderer->QueueDrawLines != NULL || renderer->QueueGeometry != NULL); |
921 | SDL_assert(renderer->QueueFillRects != NULL || renderer->QueueGeometry != NULL); |
922 | SDL_assert(renderer->QueueCopy != NULL || renderer->QueueGeometry != NULL); |
923 | SDL_assert(renderer->RunCommandQueue != NULL); |
924 | } |
925 | |
926 | static SDL_RenderLineMethod SDL_GetRenderLineMethod(void) |
927 | { |
928 | const char *hint = SDL_GetHint(SDL_HINT_RENDER_LINE_METHOD); |
929 | |
930 | int method = 0; |
931 | if (hint) { |
932 | method = SDL_atoi(hint); |
933 | } |
934 | switch (method) { |
935 | case 1: |
936 | return SDL_RENDERLINEMETHOD_POINTS; |
937 | case 2: |
938 | return SDL_RENDERLINEMETHOD_LINES; |
939 | case 3: |
940 | return SDL_RENDERLINEMETHOD_GEOMETRY; |
941 | default: |
942 | return SDL_RENDERLINEMETHOD_POINTS; |
943 | } |
944 | } |
945 | |
946 | static void SDL_CalculateSimulatedVSyncInterval(SDL_Renderer *renderer, SDL_Window *window) |
947 | { |
948 | SDL_DisplayID displayID = SDL_GetDisplayForWindow(window); |
949 | const SDL_DisplayMode *mode; |
950 | int refresh_num, refresh_den; |
951 | |
952 | if (displayID == 0) { |
953 | displayID = SDL_GetPrimaryDisplay(); |
954 | } |
955 | mode = SDL_GetDesktopDisplayMode(displayID); |
956 | if (mode && mode->refresh_rate_numerator > 0 && mode->refresh_rate_denominator > 0) { |
957 | refresh_num = mode->refresh_rate_numerator; |
958 | refresh_den = mode->refresh_rate_denominator; |
959 | } else { |
960 | // Pick a good default refresh rate |
961 | refresh_num = 60; |
962 | refresh_den = 1; |
963 | } |
964 | // Flip numerator and denominator to change from framerate to interval |
965 | renderer->simulate_vsync_interval_ns = (SDL_NS_PER_SECOND * refresh_den) / refresh_num; |
966 | } |
967 | |
968 | #endif // !SDL_RENDER_DISABLED |
969 | |
970 | |
971 | SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props) |
972 | { |
973 | #ifndef SDL_RENDER_DISABLED |
974 | SDL_Window *window = (SDL_Window *)SDL_GetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, NULL); |
975 | SDL_Surface *surface = (SDL_Surface *)SDL_GetPointerProperty(props, SDL_PROP_RENDERER_CREATE_SURFACE_POINTER, NULL); |
976 | const char *driver_name = SDL_GetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, NULL); |
977 | const char *hint; |
978 | SDL_PropertiesID new_props; |
979 | |
980 | #ifdef SDL_PLATFORM_ANDROID |
981 | if (!Android_WaitActiveAndLockActivity()) { |
982 | return NULL; |
983 | } |
984 | #endif |
985 | |
986 | SDL_Renderer *renderer = (SDL_Renderer *)SDL_calloc(1, sizeof(*renderer)); |
987 | if (!renderer) { |
988 | goto error; |
989 | } |
990 | |
991 | SDL_SetObjectValid(renderer, SDL_OBJECT_TYPE_RENDERER, true); |
992 | |
993 | if ((!window && !surface) || (window && surface)) { |
994 | SDL_InvalidParamError("window" ); |
995 | goto error; |
996 | } |
997 | |
998 | if (window && SDL_WindowHasSurface(window)) { |
999 | SDL_SetError("Surface already associated with window" ); |
1000 | goto error; |
1001 | } |
1002 | |
1003 | if (window && SDL_GetRenderer(window)) { |
1004 | SDL_SetError("Renderer already associated with window" ); |
1005 | goto error; |
1006 | } |
1007 | |
1008 | hint = SDL_GetHint(SDL_HINT_RENDER_VSYNC); |
1009 | if (hint && *hint) { |
1010 | SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, SDL_GetHintBoolean(SDL_HINT_RENDER_VSYNC, true)); |
1011 | } |
1012 | |
1013 | if (surface) { |
1014 | #ifdef SDL_VIDEO_RENDER_SW |
1015 | const bool rc = SW_CreateRendererForSurface(renderer, surface, props); |
1016 | #else |
1017 | const bool rc = SDL_SetError("SDL not built with software renderer" ); |
1018 | #endif |
1019 | if (!rc) { |
1020 | goto error; |
1021 | } |
1022 | } else { |
1023 | bool rc = false; |
1024 | if (!driver_name) { |
1025 | driver_name = SDL_GetHint(SDL_HINT_RENDER_DRIVER); |
1026 | } |
1027 | |
1028 | if (driver_name && *driver_name != 0) { |
1029 | const char *driver_attempt = driver_name; |
1030 | while (driver_attempt && *driver_attempt != 0 && !rc) { |
1031 | const char *driver_attempt_end = SDL_strchr(driver_attempt, ','); |
1032 | const size_t driver_attempt_len = (driver_attempt_end) ? (driver_attempt_end - driver_attempt) : SDL_strlen(driver_attempt); |
1033 | |
1034 | for (int i = 0; render_drivers[i]; i++) { |
1035 | const SDL_RenderDriver *driver = render_drivers[i]; |
1036 | if ((driver_attempt_len == SDL_strlen(driver->name)) && (SDL_strncasecmp(driver->name, driver_attempt, driver_attempt_len) == 0)) { |
1037 | rc = driver->CreateRenderer(renderer, window, props); |
1038 | if (rc) { |
1039 | break; |
1040 | } |
1041 | } |
1042 | } |
1043 | |
1044 | driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL; |
1045 | } |
1046 | } else { |
1047 | for (int i = 0; render_drivers[i]; i++) { |
1048 | const SDL_RenderDriver *driver = render_drivers[i]; |
1049 | rc = driver->CreateRenderer(renderer, window, props); |
1050 | if (rc) { |
1051 | break; |
1052 | } |
1053 | SDL_DestroyRendererWithoutFreeing(renderer); |
1054 | SDL_zerop(renderer); // make sure we don't leave function pointers from a previous CreateRenderer() in this struct. |
1055 | } |
1056 | } |
1057 | |
1058 | if (!rc) { |
1059 | if (driver_name) { |
1060 | SDL_SetError("%s not available" , driver_name); |
1061 | } else { |
1062 | SDL_SetError("Couldn't find matching render driver" ); |
1063 | } |
1064 | goto error; |
1065 | } |
1066 | } |
1067 | |
1068 | VerifyDrawQueueFunctions(renderer); |
1069 | |
1070 | renderer->window = window; |
1071 | renderer->target_mutex = SDL_CreateMutex(); |
1072 | if (surface) { |
1073 | renderer->main_view.pixel_w = surface->w; |
1074 | renderer->main_view.pixel_h = surface->h; |
1075 | } |
1076 | renderer->main_view.viewport.w = -1; |
1077 | renderer->main_view.viewport.h = -1; |
1078 | renderer->main_view.scale.x = 1.0f; |
1079 | renderer->main_view.scale.y = 1.0f; |
1080 | renderer->main_view.logical_scale.x = 1.0f; |
1081 | renderer->main_view.logical_scale.y = 1.0f; |
1082 | renderer->main_view.current_scale.x = 1.0f; |
1083 | renderer->main_view.current_scale.y = 1.0f; |
1084 | renderer->view = &renderer->main_view; |
1085 | renderer->dpi_scale.x = 1.0f; |
1086 | renderer->dpi_scale.y = 1.0f; |
1087 | UpdatePixelViewport(renderer, &renderer->main_view); |
1088 | UpdatePixelClipRect(renderer, &renderer->main_view); |
1089 | UpdateMainViewDimensions(renderer); |
1090 | |
1091 | // new textures start at zero, so we start at 1 so first render doesn't flush by accident. |
1092 | renderer->render_command_generation = 1; |
1093 | |
1094 | if (renderer->software) { |
1095 | // Software renderer always uses line method, for speed |
1096 | renderer->line_method = SDL_RENDERLINEMETHOD_LINES; |
1097 | } else { |
1098 | renderer->line_method = SDL_GetRenderLineMethod(); |
1099 | } |
1100 | |
1101 | renderer->scale_mode = SDL_SCALEMODE_LINEAR; |
1102 | |
1103 | renderer->SDR_white_point = 1.0f; |
1104 | renderer->HDR_headroom = 1.0f; |
1105 | renderer->desired_color_scale = 1.0f; |
1106 | renderer->color_scale = 1.0f; |
1107 | |
1108 | if (window) { |
1109 | if (SDL_GetWindowFlags(window) & SDL_WINDOW_TRANSPARENT) { |
1110 | renderer->transparent_window = true; |
1111 | } |
1112 | |
1113 | if (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED)) { |
1114 | renderer->hidden = true; |
1115 | } |
1116 | } |
1117 | |
1118 | new_props = SDL_GetRendererProperties(renderer); |
1119 | SDL_SetStringProperty(new_props, SDL_PROP_RENDERER_NAME_STRING, renderer->name); |
1120 | if (window) { |
1121 | SDL_SetPointerProperty(new_props, SDL_PROP_RENDERER_WINDOW_POINTER, window); |
1122 | } |
1123 | if (surface) { |
1124 | SDL_SetPointerProperty(new_props, SDL_PROP_RENDERER_SURFACE_POINTER, surface); |
1125 | } |
1126 | SDL_SetNumberProperty(new_props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, renderer->output_colorspace); |
1127 | UpdateHDRProperties(renderer); |
1128 | |
1129 | if (window) { |
1130 | SDL_SetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_RENDERER_POINTER, renderer); |
1131 | SDL_AddWindowRenderer(window, renderer); |
1132 | } |
1133 | |
1134 | SDL_SetRenderViewport(renderer, NULL); |
1135 | |
1136 | if (window) { |
1137 | SDL_AddWindowEventWatch(SDL_WINDOW_EVENT_WATCH_NORMAL, SDL_RendererEventWatch, renderer); |
1138 | } |
1139 | |
1140 | int vsync = (int)SDL_GetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 0); |
1141 | SDL_SetRenderVSync(renderer, vsync); |
1142 | SDL_CalculateSimulatedVSyncInterval(renderer, window); |
1143 | |
1144 | SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, |
1145 | "Created renderer: %s" , renderer->name); |
1146 | |
1147 | renderer->next = SDL_renderers; |
1148 | SDL_renderers = renderer; |
1149 | |
1150 | #ifdef SDL_PLATFORM_ANDROID |
1151 | Android_UnlockActivityMutex(); |
1152 | #endif |
1153 | |
1154 | SDL_ClearError(); |
1155 | |
1156 | return renderer; |
1157 | |
1158 | error: |
1159 | #ifdef SDL_PLATFORM_ANDROID |
1160 | Android_UnlockActivityMutex(); |
1161 | #endif |
1162 | |
1163 | if (renderer) { |
1164 | SDL_DestroyRenderer(renderer); |
1165 | } |
1166 | return NULL; |
1167 | |
1168 | #else |
1169 | SDL_SetError("SDL not built with rendering support" ); |
1170 | return NULL; |
1171 | #endif |
1172 | } |
1173 | |
1174 | SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, const char *name) |
1175 | { |
1176 | SDL_Renderer *renderer; |
1177 | SDL_PropertiesID props = SDL_CreateProperties(); |
1178 | SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window); |
1179 | SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, name); |
1180 | renderer = SDL_CreateRendererWithProperties(props); |
1181 | SDL_DestroyProperties(props); |
1182 | return renderer; |
1183 | } |
1184 | |
1185 | SDL_Renderer *SDL_CreateSoftwareRenderer(SDL_Surface *surface) |
1186 | { |
1187 | #ifdef SDL_VIDEO_RENDER_SW |
1188 | SDL_Renderer *renderer; |
1189 | |
1190 | if (!surface) { |
1191 | SDL_InvalidParamError("surface" ); |
1192 | return NULL; |
1193 | } |
1194 | |
1195 | SDL_PropertiesID props = SDL_CreateProperties(); |
1196 | SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_SURFACE_POINTER, surface); |
1197 | renderer = SDL_CreateRendererWithProperties(props); |
1198 | SDL_DestroyProperties(props); |
1199 | return renderer; |
1200 | #else |
1201 | SDL_SetError("SDL not built with rendering support" ); |
1202 | return NULL; |
1203 | #endif // !SDL_RENDER_DISABLED |
1204 | } |
1205 | |
1206 | SDL_Renderer *SDL_GetRenderer(SDL_Window *window) |
1207 | { |
1208 | return (SDL_Renderer *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_RENDERER_POINTER, NULL); |
1209 | } |
1210 | |
1211 | SDL_Window *SDL_GetRenderWindow(SDL_Renderer *renderer) |
1212 | { |
1213 | CHECK_RENDERER_MAGIC(renderer, NULL); |
1214 | return renderer->window; |
1215 | } |
1216 | |
1217 | const char *SDL_GetRendererName(SDL_Renderer *renderer) |
1218 | { |
1219 | CHECK_RENDERER_MAGIC(renderer, NULL); |
1220 | |
1221 | return SDL_GetPersistentString(renderer->name); |
1222 | } |
1223 | |
1224 | SDL_PropertiesID SDL_GetRendererProperties(SDL_Renderer *renderer) |
1225 | { |
1226 | CHECK_RENDERER_MAGIC(renderer, 0); |
1227 | |
1228 | if (renderer->props == 0) { |
1229 | renderer->props = SDL_CreateProperties(); |
1230 | } |
1231 | return renderer->props; |
1232 | } |
1233 | |
1234 | bool SDL_GetRenderOutputSize(SDL_Renderer *renderer, int *w, int *h) |
1235 | { |
1236 | if (w) { |
1237 | *w = 0; |
1238 | } |
1239 | if (h) { |
1240 | *h = 0; |
1241 | } |
1242 | |
1243 | CHECK_RENDERER_MAGIC(renderer, false); |
1244 | |
1245 | if (renderer->GetOutputSize) { |
1246 | return renderer->GetOutputSize(renderer, w, h); |
1247 | } else if (renderer->window) { |
1248 | return SDL_GetWindowSizeInPixels(renderer->window, w, h); |
1249 | } else { |
1250 | SDL_assert(!"This should never happen" ); |
1251 | return SDL_SetError("Renderer doesn't support querying output size" ); |
1252 | } |
1253 | } |
1254 | |
1255 | bool SDL_GetCurrentRenderOutputSize(SDL_Renderer *renderer, int *w, int *h) |
1256 | { |
1257 | if (w) { |
1258 | *w = 0; |
1259 | } |
1260 | if (h) { |
1261 | *h = 0; |
1262 | } |
1263 | |
1264 | CHECK_RENDERER_MAGIC(renderer, false); |
1265 | |
1266 | const SDL_RenderViewState *view = renderer->view; |
1267 | if (w) { |
1268 | *w = view->pixel_w; |
1269 | } |
1270 | if (h) { |
1271 | *h = view->pixel_h; |
1272 | } |
1273 | return true; |
1274 | } |
1275 | |
1276 | static bool IsSupportedBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode) |
1277 | { |
1278 | switch (blendMode) { |
1279 | // These are required to be supported by all renderers |
1280 | case SDL_BLENDMODE_NONE: |
1281 | case SDL_BLENDMODE_BLEND: |
1282 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
1283 | case SDL_BLENDMODE_ADD: |
1284 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
1285 | case SDL_BLENDMODE_MOD: |
1286 | case SDL_BLENDMODE_MUL: |
1287 | return true; |
1288 | |
1289 | default: |
1290 | return renderer->SupportsBlendMode && renderer->SupportsBlendMode(renderer, blendMode); |
1291 | } |
1292 | } |
1293 | |
1294 | static bool IsSupportedFormat(SDL_Renderer *renderer, SDL_PixelFormat format) |
1295 | { |
1296 | int i; |
1297 | |
1298 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1299 | if (renderer->texture_formats[i] == format) { |
1300 | return true; |
1301 | } |
1302 | } |
1303 | return false; |
1304 | } |
1305 | |
1306 | static SDL_PixelFormat GetClosestSupportedFormat(SDL_Renderer *renderer, SDL_PixelFormat format) |
1307 | { |
1308 | int i; |
1309 | |
1310 | if (format == SDL_PIXELFORMAT_MJPG) { |
1311 | // We'll decode to SDL_PIXELFORMAT_NV12 or SDL_PIXELFORMAT_RGBA32 |
1312 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1313 | if (renderer->texture_formats[i] == SDL_PIXELFORMAT_NV12) { |
1314 | return renderer->texture_formats[i]; |
1315 | } |
1316 | } |
1317 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1318 | if (renderer->texture_formats[i] == SDL_PIXELFORMAT_RGBA32) { |
1319 | return renderer->texture_formats[i]; |
1320 | } |
1321 | } |
1322 | } else if (SDL_ISPIXELFORMAT_FOURCC(format)) { |
1323 | // Look for an exact match |
1324 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1325 | if (renderer->texture_formats[i] == format) { |
1326 | return renderer->texture_formats[i]; |
1327 | } |
1328 | } |
1329 | } else if (SDL_ISPIXELFORMAT_10BIT(format) || SDL_ISPIXELFORMAT_FLOAT(format)) { |
1330 | if (SDL_ISPIXELFORMAT_10BIT(format)) { |
1331 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1332 | if (SDL_ISPIXELFORMAT_10BIT(renderer->texture_formats[i])) { |
1333 | return renderer->texture_formats[i]; |
1334 | } |
1335 | } |
1336 | } |
1337 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1338 | if (SDL_ISPIXELFORMAT_FLOAT(renderer->texture_formats[i])) { |
1339 | return renderer->texture_formats[i]; |
1340 | } |
1341 | } |
1342 | } else { |
1343 | bool hasAlpha = SDL_ISPIXELFORMAT_ALPHA(format); |
1344 | |
1345 | // We just want to match the first format that has the same channels |
1346 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1347 | if (!SDL_ISPIXELFORMAT_FOURCC(renderer->texture_formats[i]) && |
1348 | SDL_ISPIXELFORMAT_ALPHA(renderer->texture_formats[i]) == hasAlpha) { |
1349 | return renderer->texture_formats[i]; |
1350 | } |
1351 | } |
1352 | } |
1353 | return renderer->texture_formats[0]; |
1354 | } |
1355 | |
1356 | SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_PropertiesID props) |
1357 | { |
1358 | SDL_Texture *texture; |
1359 | SDL_PixelFormat format = (SDL_PixelFormat)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, SDL_PIXELFORMAT_UNKNOWN); |
1360 | SDL_TextureAccess access = (SDL_TextureAccess)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); |
1361 | int w = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, 0); |
1362 | int h = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, 0); |
1363 | SDL_Colorspace default_colorspace; |
1364 | bool texture_is_fourcc_and_target; |
1365 | |
1366 | CHECK_RENDERER_MAGIC(renderer, NULL); |
1367 | |
1368 | if (!format) { |
1369 | format = renderer->texture_formats[0]; |
1370 | } |
1371 | if (SDL_BYTESPERPIXEL(format) == 0) { |
1372 | SDL_SetError("Invalid texture format" ); |
1373 | return NULL; |
1374 | } |
1375 | if (SDL_ISPIXELFORMAT_INDEXED(format)) { |
1376 | if (!IsSupportedFormat(renderer, format)) { |
1377 | SDL_SetError("Palettized textures are not supported" ); |
1378 | return NULL; |
1379 | } |
1380 | } |
1381 | if (w <= 0 || h <= 0) { |
1382 | SDL_SetError("Texture dimensions can't be 0" ); |
1383 | return NULL; |
1384 | } |
1385 | int max_texture_size = (int)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 0); |
1386 | if (max_texture_size && (w > max_texture_size || h > max_texture_size)) { |
1387 | SDL_SetError("Texture dimensions are limited to %dx%d" , max_texture_size, max_texture_size); |
1388 | return NULL; |
1389 | } |
1390 | |
1391 | default_colorspace = SDL_GetDefaultColorspaceForFormat(format); |
1392 | |
1393 | texture = (SDL_Texture *)SDL_calloc(1, sizeof(*texture)); |
1394 | if (!texture) { |
1395 | return NULL; |
1396 | } |
1397 | texture->refcount = 1; |
1398 | SDL_SetObjectValid(texture, SDL_OBJECT_TYPE_TEXTURE, true); |
1399 | texture->colorspace = (SDL_Colorspace)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, default_colorspace); |
1400 | texture->format = format; |
1401 | texture->access = access; |
1402 | texture->w = w; |
1403 | texture->h = h; |
1404 | texture->color.r = 1.0f; |
1405 | texture->color.g = 1.0f; |
1406 | texture->color.b = 1.0f; |
1407 | texture->color.a = 1.0f; |
1408 | texture->blendMode = SDL_ISPIXELFORMAT_ALPHA(format) ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE; |
1409 | texture->scaleMode = renderer->scale_mode; |
1410 | texture->view.pixel_w = w; |
1411 | texture->view.pixel_h = h; |
1412 | texture->view.viewport.w = -1; |
1413 | texture->view.viewport.h = -1; |
1414 | texture->view.scale.x = 1.0f; |
1415 | texture->view.scale.y = 1.0f; |
1416 | texture->view.logical_scale.x = 1.0f; |
1417 | texture->view.logical_scale.y = 1.0f; |
1418 | texture->view.current_scale.x = 1.0f; |
1419 | texture->view.current_scale.y = 1.0f; |
1420 | texture->renderer = renderer; |
1421 | texture->next = renderer->textures; |
1422 | if (renderer->textures) { |
1423 | renderer->textures->prev = texture; |
1424 | } |
1425 | renderer->textures = texture; |
1426 | |
1427 | UpdatePixelViewport(renderer, &texture->view); |
1428 | UpdatePixelClipRect(renderer, &texture->view); |
1429 | |
1430 | texture->SDR_white_point = SDL_GetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, SDL_GetDefaultSDRWhitePoint(texture->colorspace)); |
1431 | texture->HDR_headroom = SDL_GetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, SDL_GetDefaultHDRHeadroom(texture->colorspace)); |
1432 | |
1433 | // FOURCC format cannot be used directly by renderer back-ends for target texture |
1434 | texture_is_fourcc_and_target = (access == SDL_TEXTUREACCESS_TARGET && SDL_ISPIXELFORMAT_FOURCC(format)); |
1435 | |
1436 | if (!texture_is_fourcc_and_target && IsSupportedFormat(renderer, format)) { |
1437 | if (!renderer->CreateTexture(renderer, texture, props)) { |
1438 | SDL_DestroyTexture(texture); |
1439 | return NULL; |
1440 | } |
1441 | } else { |
1442 | SDL_PixelFormat closest_format; |
1443 | SDL_PropertiesID native_props = SDL_CreateProperties(); |
1444 | |
1445 | if (!texture_is_fourcc_and_target) { |
1446 | closest_format = GetClosestSupportedFormat(renderer, format); |
1447 | } else { |
1448 | closest_format = renderer->texture_formats[0]; |
1449 | } |
1450 | |
1451 | if (format == SDL_PIXELFORMAT_MJPG && closest_format == SDL_PIXELFORMAT_NV12) { |
1452 | SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, SDL_COLORSPACE_JPEG); |
1453 | } else { |
1454 | default_colorspace = SDL_GetDefaultColorspaceForFormat(closest_format); |
1455 | if (SDL_COLORSPACETYPE(texture->colorspace) == SDL_COLORSPACETYPE(default_colorspace)) { |
1456 | SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, texture->colorspace); |
1457 | } else { |
1458 | SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, default_colorspace); |
1459 | } |
1460 | } |
1461 | SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, closest_format); |
1462 | SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, texture->access); |
1463 | SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, texture->w); |
1464 | SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, texture->h); |
1465 | |
1466 | texture->native = SDL_CreateTextureWithProperties(renderer, native_props); |
1467 | SDL_DestroyProperties(native_props); |
1468 | if (!texture->native) { |
1469 | SDL_DestroyTexture(texture); |
1470 | return NULL; |
1471 | } |
1472 | |
1473 | SDL_SetPointerProperty(SDL_GetTextureProperties(texture->native), SDL_PROP_TEXTURE_PARENT_POINTER, texture); |
1474 | |
1475 | // Swap textures to have texture before texture->native in the list |
1476 | texture->native->next = texture->next; |
1477 | if (texture->native->next) { |
1478 | texture->native->next->prev = texture->native; |
1479 | } |
1480 | texture->prev = texture->native->prev; |
1481 | if (texture->prev) { |
1482 | texture->prev->next = texture; |
1483 | } |
1484 | texture->native->prev = texture; |
1485 | texture->next = texture->native; |
1486 | renderer->textures = texture; |
1487 | |
1488 | if (texture->format == SDL_PIXELFORMAT_MJPG) { |
1489 | // We have a custom decode + upload path for this |
1490 | } else if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) { |
1491 | #ifdef SDL_HAVE_YUV |
1492 | texture->yuv = SDL_SW_CreateYUVTexture(texture->format, texture->colorspace, w, h); |
1493 | #else |
1494 | SDL_SetError("SDL not built with YUV support" ); |
1495 | #endif |
1496 | if (!texture->yuv) { |
1497 | SDL_DestroyTexture(texture); |
1498 | return NULL; |
1499 | } |
1500 | } else if (access == SDL_TEXTUREACCESS_STREAMING) { |
1501 | // The pitch is 4 byte aligned |
1502 | texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3); |
1503 | texture->pixels = SDL_calloc(1, (size_t)texture->pitch * h); |
1504 | if (!texture->pixels) { |
1505 | SDL_DestroyTexture(texture); |
1506 | return NULL; |
1507 | } |
1508 | } |
1509 | } |
1510 | |
1511 | // Now set the properties for the new texture |
1512 | props = SDL_GetTextureProperties(texture); |
1513 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_COLORSPACE_NUMBER, texture->colorspace); |
1514 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_FORMAT_NUMBER, texture->format); |
1515 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_ACCESS_NUMBER, texture->access); |
1516 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_WIDTH_NUMBER, texture->w); |
1517 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_HEIGHT_NUMBER, texture->h); |
1518 | SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_SDR_WHITE_POINT_FLOAT, texture->SDR_white_point); |
1519 | if (texture->HDR_headroom > 0.0f) { |
1520 | SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_HDR_HEADROOM_FLOAT, texture->HDR_headroom); |
1521 | } |
1522 | return texture; |
1523 | } |
1524 | |
1525 | SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, SDL_PixelFormat format, SDL_TextureAccess access, int w, int h) |
1526 | { |
1527 | SDL_Texture *texture; |
1528 | SDL_PropertiesID props = SDL_CreateProperties(); |
1529 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); |
1530 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, access); |
1531 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, w); |
1532 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, h); |
1533 | texture = SDL_CreateTextureWithProperties(renderer, props); |
1534 | SDL_DestroyProperties(props); |
1535 | return texture; |
1536 | } |
1537 | |
1538 | static bool SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, SDL_Surface *surface) |
1539 | { |
1540 | SDL_TextureAccess access; |
1541 | bool direct_update; |
1542 | SDL_PixelFormat tex_format; |
1543 | SDL_PropertiesID surface_props; |
1544 | SDL_PropertiesID tex_props; |
1545 | SDL_Colorspace surface_colorspace = SDL_COLORSPACE_UNKNOWN; |
1546 | SDL_Colorspace texture_colorspace = SDL_COLORSPACE_UNKNOWN; |
1547 | |
1548 | if (texture == NULL || surface == NULL) { |
1549 | return false; |
1550 | } |
1551 | |
1552 | tex_props = SDL_GetTextureProperties(texture); |
1553 | if (!tex_props) { |
1554 | return false; |
1555 | } |
1556 | |
1557 | surface_props = SDL_GetSurfaceProperties(surface); |
1558 | if (!surface_props) { |
1559 | return false; |
1560 | } |
1561 | |
1562 | tex_format = (SDL_PixelFormat)SDL_GetNumberProperty(tex_props, SDL_PROP_TEXTURE_FORMAT_NUMBER, 0); |
1563 | access = (SDL_TextureAccess)SDL_GetNumberProperty(tex_props, SDL_PROP_TEXTURE_ACCESS_NUMBER, 0); |
1564 | |
1565 | if (access != SDL_TEXTUREACCESS_STATIC && access != SDL_TEXTUREACCESS_STREAMING) { |
1566 | return false; |
1567 | } |
1568 | |
1569 | surface_colorspace = SDL_GetSurfaceColorspace(surface); |
1570 | texture_colorspace = surface_colorspace; |
1571 | |
1572 | if (surface_colorspace == SDL_COLORSPACE_SRGB_LINEAR || |
1573 | SDL_COLORSPACETRANSFER(surface_colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) { |
1574 | if (SDL_ISPIXELFORMAT_FLOAT(tex_format)) { |
1575 | texture_colorspace = SDL_COLORSPACE_SRGB_LINEAR; |
1576 | } else if (SDL_ISPIXELFORMAT_10BIT(tex_format)) { |
1577 | texture_colorspace = SDL_COLORSPACE_HDR10; |
1578 | } else { |
1579 | texture_colorspace = SDL_COLORSPACE_SRGB; |
1580 | } |
1581 | } |
1582 | |
1583 | if (tex_format == surface->format && texture_colorspace == surface_colorspace) { |
1584 | if (SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) { |
1585 | /* Surface and Renderer formats are identical. |
1586 | * Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */ |
1587 | direct_update = false; |
1588 | } else { |
1589 | // Update Texture directly |
1590 | direct_update = true; |
1591 | } |
1592 | } else { |
1593 | // Surface and Renderer formats are different, it needs an intermediate conversion. |
1594 | direct_update = false; |
1595 | } |
1596 | |
1597 | if (direct_update) { |
1598 | if (SDL_MUSTLOCK(surface)) { |
1599 | SDL_LockSurface(surface); |
1600 | SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch); |
1601 | SDL_UnlockSurface(surface); |
1602 | } else { |
1603 | SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch); |
1604 | } |
1605 | } else { |
1606 | SDL_Surface *temp = NULL; |
1607 | |
1608 | // Set up a destination surface for the texture update |
1609 | temp = SDL_ConvertSurfaceAndColorspace(surface, tex_format, NULL, texture_colorspace, surface_props); |
1610 | if (temp) { |
1611 | SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch); |
1612 | SDL_DestroySurface(temp); |
1613 | } else { |
1614 | return false; |
1615 | } |
1616 | } |
1617 | |
1618 | { |
1619 | Uint8 r, g, b, a; |
1620 | SDL_BlendMode blendMode; |
1621 | |
1622 | SDL_GetSurfaceColorMod(surface, &r, &g, &b); |
1623 | SDL_SetTextureColorMod(texture, r, g, b); |
1624 | |
1625 | SDL_GetSurfaceAlphaMod(surface, &a); |
1626 | SDL_SetTextureAlphaMod(texture, a); |
1627 | |
1628 | if (SDL_SurfaceHasColorKey(surface)) { |
1629 | // We converted to a texture with alpha format |
1630 | SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); |
1631 | } else { |
1632 | SDL_GetSurfaceBlendMode(surface, &blendMode); |
1633 | SDL_SetTextureBlendMode(texture, blendMode); |
1634 | } |
1635 | } |
1636 | |
1637 | return true; |
1638 | } |
1639 | |
1640 | SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *surface) |
1641 | { |
1642 | bool needAlpha; |
1643 | int i; |
1644 | SDL_PixelFormat format = SDL_PIXELFORMAT_UNKNOWN; |
1645 | SDL_Palette *palette; |
1646 | SDL_Texture *texture; |
1647 | SDL_PropertiesID props; |
1648 | SDL_Colorspace surface_colorspace = SDL_COLORSPACE_UNKNOWN; |
1649 | SDL_Colorspace texture_colorspace = SDL_COLORSPACE_UNKNOWN; |
1650 | |
1651 | CHECK_RENDERER_MAGIC(renderer, NULL); |
1652 | |
1653 | if (!SDL_SurfaceValid(surface)) { |
1654 | SDL_InvalidParamError("SDL_CreateTextureFromSurface(): surface" ); |
1655 | return NULL; |
1656 | } |
1657 | |
1658 | // See what the best texture format is |
1659 | if (SDL_ISPIXELFORMAT_ALPHA(surface->format) || SDL_SurfaceHasColorKey(surface)) { |
1660 | needAlpha = true; |
1661 | } else { |
1662 | needAlpha = false; |
1663 | } |
1664 | |
1665 | // If Palette contains alpha values, promotes to alpha format |
1666 | palette = SDL_GetSurfacePalette(surface); |
1667 | if (palette) { |
1668 | bool is_opaque, has_alpha_channel; |
1669 | SDL_DetectPalette(palette, &is_opaque, &has_alpha_channel); |
1670 | if (!is_opaque) { |
1671 | needAlpha = true; |
1672 | } |
1673 | } |
1674 | |
1675 | surface_colorspace = SDL_GetSurfaceColorspace(surface); |
1676 | |
1677 | // Try to have the best pixel format for the texture |
1678 | // No alpha, but a colorkey => promote to alpha |
1679 | if (!SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) { |
1680 | if (surface->format == SDL_PIXELFORMAT_XRGB8888) { |
1681 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1682 | if (renderer->texture_formats[i] == SDL_PIXELFORMAT_ARGB8888) { |
1683 | format = SDL_PIXELFORMAT_ARGB8888; |
1684 | break; |
1685 | } |
1686 | } |
1687 | } else if (surface->format == SDL_PIXELFORMAT_XBGR8888) { |
1688 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1689 | if (renderer->texture_formats[i] == SDL_PIXELFORMAT_ABGR8888) { |
1690 | format = SDL_PIXELFORMAT_ABGR8888; |
1691 | break; |
1692 | } |
1693 | } |
1694 | } |
1695 | } else { |
1696 | // Exact match would be fine |
1697 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1698 | if (renderer->texture_formats[i] == surface->format) { |
1699 | format = surface->format; |
1700 | break; |
1701 | } |
1702 | } |
1703 | } |
1704 | |
1705 | // Look for 10-bit pixel formats if needed |
1706 | if (format == SDL_PIXELFORMAT_UNKNOWN && SDL_ISPIXELFORMAT_10BIT(surface->format)) { |
1707 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1708 | if (SDL_ISPIXELFORMAT_10BIT(renderer->texture_formats[i])) { |
1709 | format = renderer->texture_formats[i]; |
1710 | break; |
1711 | } |
1712 | } |
1713 | } |
1714 | |
1715 | // Look for floating point pixel formats if needed |
1716 | if (format == SDL_PIXELFORMAT_UNKNOWN && |
1717 | (SDL_ISPIXELFORMAT_10BIT(surface->format) || SDL_ISPIXELFORMAT_FLOAT(surface->format))) { |
1718 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1719 | if (SDL_ISPIXELFORMAT_FLOAT(renderer->texture_formats[i])) { |
1720 | format = renderer->texture_formats[i]; |
1721 | break; |
1722 | } |
1723 | } |
1724 | } |
1725 | |
1726 | // Fallback, choose a valid pixel format |
1727 | if (format == SDL_PIXELFORMAT_UNKNOWN) { |
1728 | format = renderer->texture_formats[0]; |
1729 | for (i = 0; i < renderer->num_texture_formats; ++i) { |
1730 | if (!SDL_ISPIXELFORMAT_FOURCC(renderer->texture_formats[i]) && |
1731 | SDL_ISPIXELFORMAT_ALPHA(renderer->texture_formats[i]) == needAlpha) { |
1732 | format = renderer->texture_formats[i]; |
1733 | break; |
1734 | } |
1735 | } |
1736 | } |
1737 | |
1738 | if (surface_colorspace == SDL_COLORSPACE_SRGB_LINEAR || |
1739 | SDL_COLORSPACETRANSFER(surface_colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) { |
1740 | if (SDL_ISPIXELFORMAT_FLOAT(format)) { |
1741 | texture_colorspace = SDL_COLORSPACE_SRGB_LINEAR; |
1742 | } else if (SDL_ISPIXELFORMAT_10BIT(format)) { |
1743 | texture_colorspace = SDL_COLORSPACE_HDR10; |
1744 | } else { |
1745 | texture_colorspace = SDL_COLORSPACE_SRGB; |
1746 | } |
1747 | } |
1748 | |
1749 | props = SDL_CreateProperties(); |
1750 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, texture_colorspace); |
1751 | if (surface_colorspace == texture_colorspace) { |
1752 | SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, |
1753 | SDL_GetSurfaceSDRWhitePoint(surface, surface_colorspace)); |
1754 | } |
1755 | SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, |
1756 | SDL_GetSurfaceHDRHeadroom(surface, surface_colorspace)); |
1757 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); |
1758 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); |
1759 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, surface->w); |
1760 | SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, surface->h); |
1761 | texture = SDL_CreateTextureWithProperties(renderer, props); |
1762 | SDL_DestroyProperties(props); |
1763 | if (!texture) { |
1764 | return NULL; |
1765 | } |
1766 | |
1767 | if (!SDL_UpdateTextureFromSurface(texture, NULL, surface)) { |
1768 | SDL_DestroyTexture(texture); |
1769 | return NULL; |
1770 | } |
1771 | |
1772 | return texture; |
1773 | } |
1774 | |
1775 | SDL_Renderer *SDL_GetRendererFromTexture(SDL_Texture *texture) |
1776 | { |
1777 | CHECK_TEXTURE_MAGIC(texture, NULL); |
1778 | |
1779 | return texture->renderer; |
1780 | } |
1781 | |
1782 | SDL_PropertiesID SDL_GetTextureProperties(SDL_Texture *texture) |
1783 | { |
1784 | CHECK_TEXTURE_MAGIC(texture, 0); |
1785 | |
1786 | if (texture->props == 0) { |
1787 | texture->props = SDL_CreateProperties(); |
1788 | } |
1789 | return texture->props; |
1790 | } |
1791 | |
1792 | bool SDL_GetTextureSize(SDL_Texture *texture, float *w, float *h) |
1793 | { |
1794 | if (w) { |
1795 | *w = 0; |
1796 | } |
1797 | if (h) { |
1798 | *h = 0; |
1799 | } |
1800 | |
1801 | CHECK_TEXTURE_MAGIC(texture, false); |
1802 | |
1803 | if (w) { |
1804 | *w = (float)texture->w; |
1805 | } |
1806 | if (h) { |
1807 | *h = (float)texture->h; |
1808 | } |
1809 | return true; |
1810 | } |
1811 | |
1812 | bool SDL_SetTextureColorMod(SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b) |
1813 | { |
1814 | const float fR = (float)r / 255.0f; |
1815 | const float fG = (float)g / 255.0f; |
1816 | const float fB = (float)b / 255.0f; |
1817 | |
1818 | return SDL_SetTextureColorModFloat(texture, fR, fG, fB); |
1819 | } |
1820 | |
1821 | bool SDL_SetTextureColorModFloat(SDL_Texture *texture, float r, float g, float b) |
1822 | { |
1823 | CHECK_TEXTURE_MAGIC(texture, false); |
1824 | |
1825 | texture->color.r = r; |
1826 | texture->color.g = g; |
1827 | texture->color.b = b; |
1828 | if (texture->native) { |
1829 | return SDL_SetTextureColorModFloat(texture->native, r, g, b); |
1830 | } |
1831 | return true; |
1832 | } |
1833 | |
1834 | bool SDL_GetTextureColorMod(SDL_Texture *texture, Uint8 *r, Uint8 *g, Uint8 *b) |
1835 | { |
1836 | float fR = 1.0f, fG = 1.0f, fB = 1.0f; |
1837 | |
1838 | if (!SDL_GetTextureColorModFloat(texture, &fR, &fG, &fB)) { |
1839 | if (r) { |
1840 | *r = 255; |
1841 | } |
1842 | if (g) { |
1843 | *g = 255; |
1844 | } |
1845 | if (b) { |
1846 | *b = 255; |
1847 | } |
1848 | return false; |
1849 | } |
1850 | |
1851 | if (r) { |
1852 | *r = (Uint8)SDL_roundf(SDL_clamp(fR, 0.0f, 1.0f) * 255.0f); |
1853 | } |
1854 | if (g) { |
1855 | *g = (Uint8)SDL_roundf(SDL_clamp(fG, 0.0f, 1.0f) * 255.0f); |
1856 | } |
1857 | if (b) { |
1858 | *b = (Uint8)SDL_roundf(SDL_clamp(fB, 0.0f, 1.0f) * 255.0f); |
1859 | } |
1860 | return true; |
1861 | } |
1862 | |
1863 | bool SDL_GetTextureColorModFloat(SDL_Texture *texture, float *r, float *g, float *b) |
1864 | { |
1865 | SDL_FColor color; |
1866 | |
1867 | if (r) { |
1868 | *r = 1.0f; |
1869 | } |
1870 | if (g) { |
1871 | *g = 1.0f; |
1872 | } |
1873 | if (b) { |
1874 | *b = 1.0f; |
1875 | } |
1876 | |
1877 | CHECK_TEXTURE_MAGIC(texture, false); |
1878 | |
1879 | color = texture->color; |
1880 | |
1881 | if (r) { |
1882 | *r = color.r; |
1883 | } |
1884 | if (g) { |
1885 | *g = color.g; |
1886 | } |
1887 | if (b) { |
1888 | *b = color.b; |
1889 | } |
1890 | return true; |
1891 | } |
1892 | |
1893 | bool SDL_SetTextureAlphaMod(SDL_Texture *texture, Uint8 alpha) |
1894 | { |
1895 | const float fA = (float)alpha / 255.0f; |
1896 | |
1897 | return SDL_SetTextureAlphaModFloat(texture, fA); |
1898 | } |
1899 | |
1900 | bool SDL_SetTextureAlphaModFloat(SDL_Texture *texture, float alpha) |
1901 | { |
1902 | CHECK_TEXTURE_MAGIC(texture, false); |
1903 | |
1904 | texture->color.a = alpha; |
1905 | if (texture->native) { |
1906 | return SDL_SetTextureAlphaModFloat(texture->native, alpha); |
1907 | } |
1908 | return true; |
1909 | } |
1910 | |
1911 | bool SDL_GetTextureAlphaMod(SDL_Texture *texture, Uint8 *alpha) |
1912 | { |
1913 | float fA = 1.0f; |
1914 | |
1915 | if (!SDL_GetTextureAlphaModFloat(texture, &fA)) { |
1916 | if (alpha) { |
1917 | *alpha = 255; |
1918 | } |
1919 | return false; |
1920 | } |
1921 | |
1922 | if (alpha) { |
1923 | *alpha = (Uint8)SDL_roundf(SDL_clamp(fA, 0.0f, 1.0f) * 255.0f); |
1924 | } |
1925 | return true; |
1926 | } |
1927 | |
1928 | bool SDL_GetTextureAlphaModFloat(SDL_Texture *texture, float *alpha) |
1929 | { |
1930 | if (alpha) { |
1931 | *alpha = 1.0f; |
1932 | } |
1933 | |
1934 | CHECK_TEXTURE_MAGIC(texture, false); |
1935 | |
1936 | if (alpha) { |
1937 | *alpha = texture->color.a; |
1938 | } |
1939 | return true; |
1940 | } |
1941 | |
1942 | bool SDL_SetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode blendMode) |
1943 | { |
1944 | SDL_Renderer *renderer; |
1945 | |
1946 | CHECK_TEXTURE_MAGIC(texture, false); |
1947 | |
1948 | if (blendMode == SDL_BLENDMODE_INVALID) { |
1949 | return SDL_InvalidParamError("blendMode" ); |
1950 | } |
1951 | |
1952 | renderer = texture->renderer; |
1953 | if (!IsSupportedBlendMode(renderer, blendMode)) { |
1954 | return SDL_Unsupported(); |
1955 | } |
1956 | texture->blendMode = blendMode; |
1957 | if (texture->native) { |
1958 | return SDL_SetTextureBlendMode(texture->native, blendMode); |
1959 | } |
1960 | return true; |
1961 | } |
1962 | |
1963 | bool SDL_GetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode *blendMode) |
1964 | { |
1965 | if (blendMode) { |
1966 | *blendMode = SDL_BLENDMODE_INVALID; |
1967 | } |
1968 | |
1969 | CHECK_TEXTURE_MAGIC(texture, false); |
1970 | |
1971 | if (blendMode) { |
1972 | *blendMode = texture->blendMode; |
1973 | } |
1974 | return true; |
1975 | } |
1976 | |
1977 | bool SDL_SetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode scaleMode) |
1978 | { |
1979 | CHECK_TEXTURE_MAGIC(texture, false); |
1980 | |
1981 | switch (scaleMode) { |
1982 | case SDL_SCALEMODE_NEAREST: |
1983 | case SDL_SCALEMODE_LINEAR: |
1984 | case SDL_SCALEMODE_PIXELART: |
1985 | break; |
1986 | default: |
1987 | return SDL_InvalidParamError("scaleMode" ); |
1988 | } |
1989 | |
1990 | texture->scaleMode = scaleMode; |
1991 | |
1992 | if (texture->native) { |
1993 | return SDL_SetTextureScaleMode(texture->native, scaleMode); |
1994 | } |
1995 | return true; |
1996 | } |
1997 | |
1998 | bool SDL_GetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode *scaleMode) |
1999 | { |
2000 | if (scaleMode) { |
2001 | *scaleMode = SDL_SCALEMODE_LINEAR; |
2002 | } |
2003 | |
2004 | CHECK_TEXTURE_MAGIC(texture, false); |
2005 | |
2006 | if (scaleMode) { |
2007 | *scaleMode = texture->scaleMode; |
2008 | } |
2009 | return true; |
2010 | } |
2011 | |
2012 | #ifdef SDL_HAVE_YUV |
2013 | static bool SDL_UpdateTextureYUV(SDL_Texture *texture, const SDL_Rect *rect, |
2014 | const void *pixels, int pitch) |
2015 | { |
2016 | SDL_Texture *native = texture->native; |
2017 | SDL_Rect full_rect; |
2018 | |
2019 | if (!SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch)) { |
2020 | return false; |
2021 | } |
2022 | |
2023 | full_rect.x = 0; |
2024 | full_rect.y = 0; |
2025 | full_rect.w = texture->w; |
2026 | full_rect.h = texture->h; |
2027 | rect = &full_rect; |
2028 | |
2029 | if (texture->access == SDL_TEXTUREACCESS_STREAMING) { |
2030 | // We can lock the texture and copy to it |
2031 | void *native_pixels = NULL; |
2032 | int native_pitch = 0; |
2033 | |
2034 | if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) { |
2035 | return false; |
2036 | } |
2037 | SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, |
2038 | rect->w, rect->h, native_pixels, native_pitch); |
2039 | SDL_UnlockTexture(native); |
2040 | } else { |
2041 | // Use a temporary buffer for updating |
2042 | const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3); |
2043 | const size_t alloclen = (size_t)rect->h * temp_pitch; |
2044 | if (alloclen > 0) { |
2045 | void *temp_pixels = SDL_malloc(alloclen); |
2046 | if (!temp_pixels) { |
2047 | return false; |
2048 | } |
2049 | SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, |
2050 | rect->w, rect->h, temp_pixels, temp_pitch); |
2051 | SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch); |
2052 | SDL_free(temp_pixels); |
2053 | } |
2054 | } |
2055 | return true; |
2056 | } |
2057 | #endif // SDL_HAVE_YUV |
2058 | |
2059 | static bool SDL_UpdateTextureNative(SDL_Texture *texture, const SDL_Rect *rect, |
2060 | const void *pixels, int pitch) |
2061 | { |
2062 | SDL_Texture *native = texture->native; |
2063 | |
2064 | if (!rect->w || !rect->h) { |
2065 | return true; // nothing to do. |
2066 | } |
2067 | |
2068 | if (texture->access == SDL_TEXTUREACCESS_STREAMING) { |
2069 | // We can lock the texture and copy to it |
2070 | void *native_pixels = NULL; |
2071 | int native_pitch = 0; |
2072 | |
2073 | if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) { |
2074 | return false; |
2075 | } |
2076 | SDL_ConvertPixelsAndColorspace(rect->w, rect->h, |
2077 | texture->format, texture->colorspace, 0, pixels, pitch, |
2078 | native->format, native->colorspace, 0, native_pixels, native_pitch); |
2079 | SDL_UnlockTexture(native); |
2080 | } else { |
2081 | // Use a temporary buffer for updating |
2082 | const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3); |
2083 | const size_t alloclen = (size_t)rect->h * temp_pitch; |
2084 | if (alloclen > 0) { |
2085 | void *temp_pixels = SDL_malloc(alloclen); |
2086 | if (!temp_pixels) { |
2087 | return false; |
2088 | } |
2089 | SDL_ConvertPixelsAndColorspace(rect->w, rect->h, |
2090 | texture->format, texture->colorspace, 0, pixels, pitch, |
2091 | native->format, native->colorspace, 0, temp_pixels, temp_pitch); |
2092 | SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch); |
2093 | SDL_free(temp_pixels); |
2094 | } |
2095 | } |
2096 | return true; |
2097 | } |
2098 | |
2099 | bool SDL_UpdateTexture(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) |
2100 | { |
2101 | SDL_Rect real_rect; |
2102 | |
2103 | CHECK_TEXTURE_MAGIC(texture, false); |
2104 | |
2105 | if (!pixels) { |
2106 | return SDL_InvalidParamError("pixels" ); |
2107 | } |
2108 | if (!pitch) { |
2109 | return SDL_InvalidParamError("pitch" ); |
2110 | } |
2111 | |
2112 | real_rect.x = 0; |
2113 | real_rect.y = 0; |
2114 | real_rect.w = texture->w; |
2115 | real_rect.h = texture->h; |
2116 | if (rect) { |
2117 | if (!SDL_GetRectIntersection(rect, &real_rect, &real_rect)) { |
2118 | return true; |
2119 | } |
2120 | } |
2121 | |
2122 | if (real_rect.w == 0 || real_rect.h == 0) { |
2123 | return true; // nothing to do. |
2124 | #ifdef SDL_HAVE_YUV |
2125 | } else if (texture->yuv) { |
2126 | return SDL_UpdateTextureYUV(texture, &real_rect, pixels, pitch); |
2127 | #endif |
2128 | } else if (texture->native) { |
2129 | return SDL_UpdateTextureNative(texture, &real_rect, pixels, pitch); |
2130 | } else { |
2131 | SDL_Renderer *renderer = texture->renderer; |
2132 | if (!FlushRenderCommandsIfTextureNeeded(texture)) { |
2133 | return false; |
2134 | } |
2135 | return renderer->UpdateTexture(renderer, texture, &real_rect, pixels, pitch); |
2136 | } |
2137 | } |
2138 | |
2139 | #ifdef SDL_HAVE_YUV |
2140 | static bool SDL_UpdateTextureYUVPlanar(SDL_Texture *texture, const SDL_Rect *rect, |
2141 | const Uint8 *Yplane, int Ypitch, |
2142 | const Uint8 *Uplane, int Upitch, |
2143 | const Uint8 *Vplane, int Vpitch) |
2144 | { |
2145 | SDL_Texture *native = texture->native; |
2146 | SDL_Rect full_rect; |
2147 | |
2148 | if (!SDL_SW_UpdateYUVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch)) { |
2149 | return false; |
2150 | } |
2151 | |
2152 | full_rect.x = 0; |
2153 | full_rect.y = 0; |
2154 | full_rect.w = texture->w; |
2155 | full_rect.h = texture->h; |
2156 | rect = &full_rect; |
2157 | |
2158 | if (!rect->w || !rect->h) { |
2159 | return true; // nothing to do. |
2160 | } |
2161 | |
2162 | if (texture->access == SDL_TEXTUREACCESS_STREAMING) { |
2163 | // We can lock the texture and copy to it |
2164 | void *native_pixels = NULL; |
2165 | int native_pitch = 0; |
2166 | |
2167 | if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) { |
2168 | return false; |
2169 | } |
2170 | SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, |
2171 | rect->w, rect->h, native_pixels, native_pitch); |
2172 | SDL_UnlockTexture(native); |
2173 | } else { |
2174 | // Use a temporary buffer for updating |
2175 | const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3); |
2176 | const size_t alloclen = (size_t)rect->h * temp_pitch; |
2177 | if (alloclen > 0) { |
2178 | void *temp_pixels = SDL_malloc(alloclen); |
2179 | if (!temp_pixels) { |
2180 | return false; |
2181 | } |
2182 | SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, |
2183 | rect->w, rect->h, temp_pixels, temp_pitch); |
2184 | SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch); |
2185 | SDL_free(temp_pixels); |
2186 | } |
2187 | } |
2188 | return true; |
2189 | } |
2190 | |
2191 | static bool SDL_UpdateTextureNVPlanar(SDL_Texture *texture, const SDL_Rect *rect, |
2192 | const Uint8 *Yplane, int Ypitch, |
2193 | const Uint8 *UVplane, int UVpitch) |
2194 | { |
2195 | SDL_Texture *native = texture->native; |
2196 | SDL_Rect full_rect; |
2197 | |
2198 | if (!SDL_SW_UpdateNVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, UVplane, UVpitch)) { |
2199 | return false; |
2200 | } |
2201 | |
2202 | full_rect.x = 0; |
2203 | full_rect.y = 0; |
2204 | full_rect.w = texture->w; |
2205 | full_rect.h = texture->h; |
2206 | rect = &full_rect; |
2207 | |
2208 | if (!rect->w || !rect->h) { |
2209 | return true; // nothing to do. |
2210 | } |
2211 | |
2212 | if (texture->access == SDL_TEXTUREACCESS_STREAMING) { |
2213 | // We can lock the texture and copy to it |
2214 | void *native_pixels = NULL; |
2215 | int native_pitch = 0; |
2216 | |
2217 | if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) { |
2218 | return false; |
2219 | } |
2220 | SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, |
2221 | rect->w, rect->h, native_pixels, native_pitch); |
2222 | SDL_UnlockTexture(native); |
2223 | } else { |
2224 | // Use a temporary buffer for updating |
2225 | const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3); |
2226 | const size_t alloclen = (size_t)rect->h * temp_pitch; |
2227 | if (alloclen > 0) { |
2228 | void *temp_pixels = SDL_malloc(alloclen); |
2229 | if (!temp_pixels) { |
2230 | return false; |
2231 | } |
2232 | SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, |
2233 | rect->w, rect->h, temp_pixels, temp_pitch); |
2234 | SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch); |
2235 | SDL_free(temp_pixels); |
2236 | } |
2237 | } |
2238 | return true; |
2239 | } |
2240 | |
2241 | #endif // SDL_HAVE_YUV |
2242 | |
2243 | bool SDL_UpdateYUVTexture(SDL_Texture *texture, const SDL_Rect *rect, |
2244 | const Uint8 *Yplane, int Ypitch, |
2245 | const Uint8 *Uplane, int Upitch, |
2246 | const Uint8 *Vplane, int Vpitch) |
2247 | { |
2248 | #ifdef SDL_HAVE_YUV |
2249 | SDL_Renderer *renderer; |
2250 | SDL_Rect real_rect; |
2251 | |
2252 | CHECK_TEXTURE_MAGIC(texture, false); |
2253 | |
2254 | if (!Yplane) { |
2255 | return SDL_InvalidParamError("Yplane" ); |
2256 | } |
2257 | if (!Ypitch) { |
2258 | return SDL_InvalidParamError("Ypitch" ); |
2259 | } |
2260 | if (!Uplane) { |
2261 | return SDL_InvalidParamError("Uplane" ); |
2262 | } |
2263 | if (!Upitch) { |
2264 | return SDL_InvalidParamError("Upitch" ); |
2265 | } |
2266 | if (!Vplane) { |
2267 | return SDL_InvalidParamError("Vplane" ); |
2268 | } |
2269 | if (!Vpitch) { |
2270 | return SDL_InvalidParamError("Vpitch" ); |
2271 | } |
2272 | |
2273 | if (texture->format != SDL_PIXELFORMAT_YV12 && |
2274 | texture->format != SDL_PIXELFORMAT_IYUV) { |
2275 | return SDL_SetError("Texture format must by YV12 or IYUV" ); |
2276 | } |
2277 | |
2278 | real_rect.x = 0; |
2279 | real_rect.y = 0; |
2280 | real_rect.w = texture->w; |
2281 | real_rect.h = texture->h; |
2282 | if (rect) { |
2283 | SDL_GetRectIntersection(rect, &real_rect, &real_rect); |
2284 | } |
2285 | |
2286 | if (real_rect.w == 0 || real_rect.h == 0) { |
2287 | return true; // nothing to do. |
2288 | } |
2289 | |
2290 | if (texture->yuv) { |
2291 | return SDL_UpdateTextureYUVPlanar(texture, &real_rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch); |
2292 | } else { |
2293 | SDL_assert(!texture->native); |
2294 | renderer = texture->renderer; |
2295 | SDL_assert(renderer->UpdateTextureYUV); |
2296 | if (renderer->UpdateTextureYUV) { |
2297 | if (!FlushRenderCommandsIfTextureNeeded(texture)) { |
2298 | return false; |
2299 | } |
2300 | return renderer->UpdateTextureYUV(renderer, texture, &real_rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch); |
2301 | } else { |
2302 | return SDL_Unsupported(); |
2303 | } |
2304 | } |
2305 | #else |
2306 | return false; |
2307 | #endif |
2308 | } |
2309 | |
2310 | bool SDL_UpdateNVTexture(SDL_Texture *texture, const SDL_Rect *rect, |
2311 | const Uint8 *Yplane, int Ypitch, |
2312 | const Uint8 *UVplane, int UVpitch) |
2313 | { |
2314 | #ifdef SDL_HAVE_YUV |
2315 | SDL_Renderer *renderer; |
2316 | SDL_Rect real_rect; |
2317 | |
2318 | CHECK_TEXTURE_MAGIC(texture, false); |
2319 | |
2320 | if (!Yplane) { |
2321 | return SDL_InvalidParamError("Yplane" ); |
2322 | } |
2323 | if (!Ypitch) { |
2324 | return SDL_InvalidParamError("Ypitch" ); |
2325 | } |
2326 | if (!UVplane) { |
2327 | return SDL_InvalidParamError("UVplane" ); |
2328 | } |
2329 | if (!UVpitch) { |
2330 | return SDL_InvalidParamError("UVpitch" ); |
2331 | } |
2332 | |
2333 | if (texture->format != SDL_PIXELFORMAT_NV12 && |
2334 | texture->format != SDL_PIXELFORMAT_NV21) { |
2335 | return SDL_SetError("Texture format must by NV12 or NV21" ); |
2336 | } |
2337 | |
2338 | real_rect.x = 0; |
2339 | real_rect.y = 0; |
2340 | real_rect.w = texture->w; |
2341 | real_rect.h = texture->h; |
2342 | if (rect) { |
2343 | SDL_GetRectIntersection(rect, &real_rect, &real_rect); |
2344 | } |
2345 | |
2346 | if (real_rect.w == 0 || real_rect.h == 0) { |
2347 | return true; // nothing to do. |
2348 | } |
2349 | |
2350 | if (texture->yuv) { |
2351 | return SDL_UpdateTextureNVPlanar(texture, &real_rect, Yplane, Ypitch, UVplane, UVpitch); |
2352 | } else { |
2353 | SDL_assert(!texture->native); |
2354 | renderer = texture->renderer; |
2355 | SDL_assert(renderer->UpdateTextureNV); |
2356 | if (renderer->UpdateTextureNV) { |
2357 | if (!FlushRenderCommandsIfTextureNeeded(texture)) { |
2358 | return false; |
2359 | } |
2360 | return renderer->UpdateTextureNV(renderer, texture, &real_rect, Yplane, Ypitch, UVplane, UVpitch); |
2361 | } else { |
2362 | return SDL_Unsupported(); |
2363 | } |
2364 | } |
2365 | #else |
2366 | return false; |
2367 | #endif |
2368 | } |
2369 | |
2370 | #ifdef SDL_HAVE_YUV |
2371 | static bool SDL_LockTextureYUV(SDL_Texture *texture, const SDL_Rect *rect, |
2372 | void **pixels, int *pitch) |
2373 | { |
2374 | return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch); |
2375 | } |
2376 | #endif // SDL_HAVE_YUV |
2377 | |
2378 | static bool SDL_LockTextureNative(SDL_Texture *texture, const SDL_Rect *rect, |
2379 | void **pixels, int *pitch) |
2380 | { |
2381 | texture->locked_rect = *rect; |
2382 | *pixels = (void *)((Uint8 *)texture->pixels + |
2383 | rect->y * texture->pitch + |
2384 | rect->x * SDL_BYTESPERPIXEL(texture->format)); |
2385 | *pitch = texture->pitch; |
2386 | return true; |
2387 | } |
2388 | |
2389 | bool SDL_LockTexture(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch) |
2390 | { |
2391 | SDL_Rect full_rect; |
2392 | |
2393 | CHECK_TEXTURE_MAGIC(texture, false); |
2394 | |
2395 | if (texture->access != SDL_TEXTUREACCESS_STREAMING) { |
2396 | return SDL_SetError("SDL_LockTexture(): texture must be streaming" ); |
2397 | } |
2398 | |
2399 | if (!rect) { |
2400 | full_rect.x = 0; |
2401 | full_rect.y = 0; |
2402 | full_rect.w = texture->w; |
2403 | full_rect.h = texture->h; |
2404 | rect = &full_rect; |
2405 | } |
2406 | |
2407 | #ifdef SDL_HAVE_YUV |
2408 | if (texture->yuv) { |
2409 | if (!FlushRenderCommandsIfTextureNeeded(texture)) { |
2410 | return false; |
2411 | } |
2412 | return SDL_LockTextureYUV(texture, rect, pixels, pitch); |
2413 | } else |
2414 | #endif |
2415 | if (texture->native) { |
2416 | // Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. |
2417 | return SDL_LockTextureNative(texture, rect, pixels, pitch); |
2418 | } else { |
2419 | SDL_Renderer *renderer = texture->renderer; |
2420 | if (!FlushRenderCommandsIfTextureNeeded(texture)) { |
2421 | return false; |
2422 | } |
2423 | return renderer->LockTexture(renderer, texture, rect, pixels, pitch); |
2424 | } |
2425 | } |
2426 | |
2427 | bool SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect, SDL_Surface **surface) |
2428 | { |
2429 | SDL_Rect real_rect; |
2430 | void *pixels = NULL; |
2431 | int pitch = 0; // fix static analysis |
2432 | |
2433 | if (!texture || !surface) { |
2434 | return false; |
2435 | } |
2436 | |
2437 | real_rect.x = 0; |
2438 | real_rect.y = 0; |
2439 | real_rect.w = texture->w; |
2440 | real_rect.h = texture->h; |
2441 | if (rect) { |
2442 | SDL_GetRectIntersection(rect, &real_rect, &real_rect); |
2443 | } |
2444 | |
2445 | if (!SDL_LockTexture(texture, &real_rect, &pixels, &pitch)) { |
2446 | return false; |
2447 | } |
2448 | |
2449 | texture->locked_surface = SDL_CreateSurfaceFrom(real_rect.w, real_rect.h, texture->format, pixels, pitch); |
2450 | if (!texture->locked_surface) { |
2451 | SDL_UnlockTexture(texture); |
2452 | return false; |
2453 | } |
2454 | |
2455 | *surface = texture->locked_surface; |
2456 | return true; |
2457 | } |
2458 | |
2459 | #ifdef SDL_HAVE_YUV |
2460 | static void SDL_UnlockTextureYUV(SDL_Texture *texture) |
2461 | { |
2462 | SDL_Texture *native = texture->native; |
2463 | void *native_pixels = NULL; |
2464 | int native_pitch = 0; |
2465 | SDL_Rect rect; |
2466 | |
2467 | rect.x = 0; |
2468 | rect.y = 0; |
2469 | rect.w = texture->w; |
2470 | rect.h = texture->h; |
2471 | |
2472 | if (!SDL_LockTexture(native, &rect, &native_pixels, &native_pitch)) { |
2473 | return; |
2474 | } |
2475 | SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format, |
2476 | rect.w, rect.h, native_pixels, native_pitch); |
2477 | SDL_UnlockTexture(native); |
2478 | } |
2479 | #endif // SDL_HAVE_YUV |
2480 | |
2481 | static void SDL_UnlockTextureNative(SDL_Texture *texture) |
2482 | { |
2483 | SDL_Texture *native = texture->native; |
2484 | void *native_pixels = NULL; |
2485 | int native_pitch = 0; |
2486 | const SDL_Rect *rect = &texture->locked_rect; |
2487 | const void *pixels = (void *)((Uint8 *)texture->pixels + |
2488 | rect->y * texture->pitch + |
2489 | rect->x * SDL_BYTESPERPIXEL(texture->format)); |
2490 | int pitch = texture->pitch; |
2491 | |
2492 | if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) { |
2493 | return; |
2494 | } |
2495 | SDL_ConvertPixels(rect->w, rect->h, |
2496 | texture->format, pixels, pitch, |
2497 | native->format, native_pixels, native_pitch); |
2498 | SDL_UnlockTexture(native); |
2499 | } |
2500 | |
2501 | void SDL_UnlockTexture(SDL_Texture *texture) |
2502 | { |
2503 | CHECK_TEXTURE_MAGIC(texture,); |
2504 | |
2505 | if (texture->access != SDL_TEXTUREACCESS_STREAMING) { |
2506 | return; |
2507 | } |
2508 | #ifdef SDL_HAVE_YUV |
2509 | if (texture->yuv) { |
2510 | SDL_UnlockTextureYUV(texture); |
2511 | } else |
2512 | #endif |
2513 | if (texture->native) { |
2514 | SDL_UnlockTextureNative(texture); |
2515 | } else { |
2516 | SDL_Renderer *renderer = texture->renderer; |
2517 | renderer->UnlockTexture(renderer, texture); |
2518 | } |
2519 | |
2520 | SDL_DestroySurface(texture->locked_surface); |
2521 | texture->locked_surface = NULL; |
2522 | } |
2523 | |
2524 | bool SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) |
2525 | { |
2526 | // texture == NULL is valid and means reset the target to the window |
2527 | if (texture) { |
2528 | CHECK_TEXTURE_MAGIC(texture, false); |
2529 | if (renderer != texture->renderer) { |
2530 | return SDL_SetError("Texture was not created with this renderer" ); |
2531 | } |
2532 | if (texture->access != SDL_TEXTUREACCESS_TARGET) { |
2533 | return SDL_SetError("Texture not created with SDL_TEXTUREACCESS_TARGET" ); |
2534 | } |
2535 | if (texture->native) { |
2536 | // Always render to the native texture |
2537 | texture = texture->native; |
2538 | } |
2539 | } |
2540 | |
2541 | if (texture == renderer->target) { |
2542 | // Nothing to do! |
2543 | return true; |
2544 | } |
2545 | |
2546 | FlushRenderCommands(renderer); // time to send everything to the GPU! |
2547 | |
2548 | SDL_LockMutex(renderer->target_mutex); |
2549 | |
2550 | renderer->target = texture; |
2551 | if (texture) { |
2552 | renderer->view = &texture->view; |
2553 | } else { |
2554 | renderer->view = &renderer->main_view; |
2555 | } |
2556 | UpdateColorScale(renderer); |
2557 | |
2558 | if (!renderer->SetRenderTarget(renderer, texture)) { |
2559 | SDL_UnlockMutex(renderer->target_mutex); |
2560 | return false; |
2561 | } |
2562 | |
2563 | SDL_UnlockMutex(renderer->target_mutex); |
2564 | |
2565 | if (!QueueCmdSetViewport(renderer)) { |
2566 | return false; |
2567 | } |
2568 | if (!QueueCmdSetClipRect(renderer)) { |
2569 | return false; |
2570 | } |
2571 | |
2572 | // All set! |
2573 | return true; |
2574 | } |
2575 | |
2576 | SDL_Texture *SDL_GetRenderTarget(SDL_Renderer *renderer) |
2577 | { |
2578 | CHECK_RENDERER_MAGIC(renderer, NULL); |
2579 | if (!renderer->target) { |
2580 | return NULL; |
2581 | } |
2582 | return (SDL_Texture *) SDL_GetPointerProperty(SDL_GetTextureProperties(renderer->target), SDL_PROP_TEXTURE_PARENT_POINTER, renderer->target); |
2583 | } |
2584 | |
2585 | static void UpdateLogicalPresentation(SDL_Renderer *renderer) |
2586 | { |
2587 | SDL_RenderViewState *view = renderer->view; |
2588 | const bool is_main_view = (view == &renderer->main_view); |
2589 | const float logical_w = view->logical_w; |
2590 | const float logical_h = view->logical_h; |
2591 | int iwidth, iheight; |
2592 | |
2593 | if (renderer->target) { |
2594 | iwidth = (int)renderer->target->w; |
2595 | iheight = (int)renderer->target->h; |
2596 | } else { |
2597 | SDL_GetRenderOutputSize(renderer, &iwidth, &iheight); |
2598 | } |
2599 | |
2600 | view->logical_src_rect.x = 0.0f; |
2601 | view->logical_src_rect.y = 0.0f; |
2602 | view->logical_src_rect.w = logical_w; |
2603 | view->logical_src_rect.h = logical_h; |
2604 | |
2605 | if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_DISABLED) { |
2606 | view->logical_dst_rect.x = 0.0f; |
2607 | view->logical_dst_rect.y = 0.0f; |
2608 | view->logical_dst_rect.w = iwidth; |
2609 | view->logical_dst_rect.h = iheight; |
2610 | view->logical_offset.x = view->logical_offset.y = 0.0f; |
2611 | view->logical_scale.x = view->logical_scale.y = 1.0f; |
2612 | view->current_scale.x = view->scale.x; // skip the multiplications against 1.0f. |
2613 | view->current_scale.y = view->scale.y; |
2614 | } else { |
2615 | const float output_w = (float)iwidth; |
2616 | const float output_h = (float)iheight; |
2617 | const float want_aspect = logical_w / logical_h; |
2618 | const float real_aspect = output_w / output_h; |
2619 | |
2620 | if ((logical_w <= 0.0f) || (logical_h <= 0.0f)) { |
2621 | view->logical_dst_rect.x = 0.0f; |
2622 | view->logical_dst_rect.y = 0.0f; |
2623 | view->logical_dst_rect.w = output_w; |
2624 | view->logical_dst_rect.h = output_h; |
2625 | } else if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE) { |
2626 | float scale; |
2627 | if (want_aspect > real_aspect) { |
2628 | scale = (float)((int)output_w / (int)logical_w); // This an integer division! |
2629 | } else { |
2630 | scale = (float)((int)output_h / (int)logical_h); // This an integer division! |
2631 | } |
2632 | |
2633 | if (scale < 1.0f) { |
2634 | scale = 1.0f; |
2635 | } |
2636 | |
2637 | view->logical_dst_rect.w = SDL_floorf(logical_w * scale); |
2638 | view->logical_dst_rect.x = (output_w - view->logical_dst_rect.w) / 2.0f; |
2639 | view->logical_dst_rect.h = SDL_floorf(logical_h * scale); |
2640 | view->logical_dst_rect.y = (output_h - view->logical_dst_rect.h) / 2.0f; |
2641 | |
2642 | } else if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_STRETCH || SDL_fabsf(want_aspect - real_aspect) < 0.0001f) { |
2643 | view->logical_dst_rect.x = 0.0f; |
2644 | view->logical_dst_rect.y = 0.0f; |
2645 | view->logical_dst_rect.w = output_w; |
2646 | view->logical_dst_rect.h = output_h; |
2647 | |
2648 | } else if (want_aspect > real_aspect) { |
2649 | if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) { |
2650 | // We want a wider aspect ratio than is available - letterbox it |
2651 | const float scale = output_w / logical_w; |
2652 | view->logical_dst_rect.x = 0.0f; |
2653 | view->logical_dst_rect.w = output_w; |
2654 | view->logical_dst_rect.h = SDL_floorf(logical_h * scale); |
2655 | view->logical_dst_rect.y = (output_h - view->logical_dst_rect.h) / 2.0f; |
2656 | } else { // view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_OVERSCAN |
2657 | /* We want a wider aspect ratio than is available - |
2658 | zoom so logical height matches the real height |
2659 | and the width will grow off the screen |
2660 | */ |
2661 | const float scale = output_h / logical_h; |
2662 | view->logical_dst_rect.y = 0.0f; |
2663 | view->logical_dst_rect.h = output_h; |
2664 | view->logical_dst_rect.w = SDL_floorf(logical_w * scale); |
2665 | view->logical_dst_rect.x = (output_w - view->logical_dst_rect.w) / 2.0f; |
2666 | } |
2667 | } else { |
2668 | if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) { |
2669 | // We want a narrower aspect ratio than is available - use side-bars |
2670 | const float scale = output_h / logical_h; |
2671 | view->logical_dst_rect.y = 0.0f; |
2672 | view->logical_dst_rect.h = output_h; |
2673 | view->logical_dst_rect.w = SDL_floorf(logical_w * scale); |
2674 | view->logical_dst_rect.x = (output_w - view->logical_dst_rect.w) / 2.0f; |
2675 | } else { // view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_OVERSCAN |
2676 | /* We want a narrower aspect ratio than is available - |
2677 | zoom so logical width matches the real width |
2678 | and the height will grow off the screen |
2679 | */ |
2680 | const float scale = output_w / logical_w; |
2681 | view->logical_dst_rect.x = 0.0f; |
2682 | view->logical_dst_rect.w = output_w; |
2683 | view->logical_dst_rect.h = SDL_floorf(logical_h * scale); |
2684 | view->logical_dst_rect.y = (output_h - view->logical_dst_rect.h) / 2.0f; |
2685 | } |
2686 | } |
2687 | |
2688 | view->logical_scale.x = (logical_w > 0.0f) ? view->logical_dst_rect.w / logical_w : 0.0f; |
2689 | view->logical_scale.y = (logical_h > 0.0f) ? view->logical_dst_rect.h / logical_h : 0.0f; |
2690 | view->current_scale.x = view->scale.x * view->logical_scale.x; |
2691 | view->current_scale.y = view->scale.y * view->logical_scale.y; |
2692 | view->logical_offset.x = view->logical_dst_rect.x; |
2693 | view->logical_offset.y = view->logical_dst_rect.y; |
2694 | } |
2695 | |
2696 | if (is_main_view) { |
2697 | // This makes sure the dpi_scale is right. It also sets pixel_w and pixel_h, but we're going to change them directly below here. |
2698 | UpdateMainViewDimensions(renderer); |
2699 | } |
2700 | |
2701 | view->pixel_w = (int) view->logical_dst_rect.w; |
2702 | view->pixel_h = (int) view->logical_dst_rect.h; |
2703 | UpdatePixelViewport(renderer, view); |
2704 | UpdatePixelClipRect(renderer, view); |
2705 | } |
2706 | |
2707 | bool SDL_SetRenderLogicalPresentation(SDL_Renderer *renderer, int w, int h, SDL_RendererLogicalPresentation mode) |
2708 | { |
2709 | CHECK_RENDERER_MAGIC(renderer, false); |
2710 | |
2711 | SDL_RenderViewState *view = renderer->view; |
2712 | view->logical_presentation_mode = mode; |
2713 | view->logical_w = w; |
2714 | view->logical_h = h; |
2715 | |
2716 | UpdateLogicalPresentation(renderer); |
2717 | |
2718 | return true; |
2719 | } |
2720 | |
2721 | bool SDL_GetRenderLogicalPresentation(SDL_Renderer *renderer, int *w, int *h, SDL_RendererLogicalPresentation *mode) |
2722 | { |
2723 | #define SETVAL(ptr, val) if (ptr) { *ptr = val; } |
2724 | |
2725 | SETVAL(w, 0); |
2726 | SETVAL(h, 0); |
2727 | SETVAL(mode, SDL_LOGICAL_PRESENTATION_DISABLED); |
2728 | |
2729 | CHECK_RENDERER_MAGIC(renderer, false); |
2730 | |
2731 | const SDL_RenderViewState *view = renderer->view; |
2732 | SETVAL(w, view->logical_w); |
2733 | SETVAL(h, view->logical_h); |
2734 | SETVAL(mode, view->logical_presentation_mode); |
2735 | |
2736 | #undef SETVAL |
2737 | |
2738 | return true; |
2739 | } |
2740 | |
2741 | bool SDL_GetRenderLogicalPresentationRect(SDL_Renderer *renderer, SDL_FRect *rect) |
2742 | { |
2743 | if (rect) { |
2744 | SDL_zerop(rect); |
2745 | } |
2746 | |
2747 | CHECK_RENDERER_MAGIC(renderer, false); |
2748 | |
2749 | if (rect) { |
2750 | SDL_copyp(rect, &renderer->view->logical_dst_rect); |
2751 | } |
2752 | return true; |
2753 | } |
2754 | |
2755 | static void SDL_RenderLogicalBorders(SDL_Renderer *renderer, const SDL_FRect *dst) |
2756 | { |
2757 | const SDL_RenderViewState *view = renderer->view; |
2758 | |
2759 | if (dst->x > 0.0f || dst->y > 0.0f) { |
2760 | SDL_BlendMode saved_blend_mode = renderer->blendMode; |
2761 | SDL_FColor saved_color = renderer->color; |
2762 | |
2763 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); |
2764 | SDL_SetRenderDrawColorFloat(renderer, 0.0f, 0.0f, 0.0f, 1.0f); |
2765 | |
2766 | if (dst->x > 0.0f) { |
2767 | SDL_FRect rect; |
2768 | |
2769 | rect.x = 0.0f; |
2770 | rect.y = 0.0f; |
2771 | rect.w = dst->x; |
2772 | rect.h = (float)view->pixel_h; |
2773 | SDL_RenderFillRect(renderer, &rect); |
2774 | |
2775 | rect.x = dst->x + dst->w; |
2776 | rect.w = (float)view->pixel_w - rect.x; |
2777 | SDL_RenderFillRect(renderer, &rect); |
2778 | } |
2779 | |
2780 | if (dst->y > 0.0f) { |
2781 | SDL_FRect rect; |
2782 | |
2783 | rect.x = 0.0f; |
2784 | rect.y = 0.0f; |
2785 | rect.w = (float)view->pixel_w; |
2786 | rect.h = dst->y; |
2787 | SDL_RenderFillRect(renderer, &rect); |
2788 | |
2789 | rect.y = dst->y + dst->h; |
2790 | rect.h = (float)view->pixel_h - rect.y; |
2791 | SDL_RenderFillRect(renderer, &rect); |
2792 | } |
2793 | |
2794 | SDL_SetRenderDrawBlendMode(renderer, saved_blend_mode); |
2795 | SDL_SetRenderDrawColorFloat(renderer, saved_color.r, saved_color.g, saved_color.b, saved_color.a); |
2796 | } |
2797 | } |
2798 | |
2799 | static void SDL_RenderLogicalPresentation(SDL_Renderer *renderer) |
2800 | { |
2801 | SDL_assert(renderer->view == &renderer->main_view); |
2802 | |
2803 | SDL_RenderViewState *view = &renderer->main_view; |
2804 | const SDL_RendererLogicalPresentation mode = view->logical_presentation_mode; |
2805 | if (mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) { |
2806 | // save off some state we're going to trample. |
2807 | const int logical_w = view->logical_w; |
2808 | const int logical_h = view->logical_h; |
2809 | const float scale_x = view->scale.x; |
2810 | const float scale_y = view->scale.y; |
2811 | const bool clipping_enabled = view->clipping_enabled; |
2812 | SDL_Rect orig_viewport, orig_cliprect; |
2813 | const SDL_FRect logical_dst_rect = view->logical_dst_rect; |
2814 | |
2815 | SDL_copyp(&orig_viewport, &view->viewport); |
2816 | if (clipping_enabled) { |
2817 | SDL_copyp(&orig_cliprect, &view->clip_rect); |
2818 | } |
2819 | |
2820 | // trample some state. |
2821 | SDL_SetRenderLogicalPresentation(renderer, logical_w, logical_h, SDL_LOGICAL_PRESENTATION_DISABLED); |
2822 | SDL_SetRenderViewport(renderer, NULL); |
2823 | if (clipping_enabled) { |
2824 | SDL_SetRenderClipRect(renderer, NULL); |
2825 | } |
2826 | SDL_SetRenderScale(renderer, 1.0f, 1.0f); |
2827 | |
2828 | // draw the borders. |
2829 | SDL_RenderLogicalBorders(renderer, &logical_dst_rect); |
2830 | |
2831 | // now set everything back. |
2832 | view->logical_presentation_mode = mode; |
2833 | SDL_SetRenderViewport(renderer, &orig_viewport); |
2834 | if (clipping_enabled) { |
2835 | SDL_SetRenderClipRect(renderer, &orig_cliprect); |
2836 | } |
2837 | SDL_SetRenderScale(renderer, scale_x, scale_y); |
2838 | |
2839 | SDL_SetRenderLogicalPresentation(renderer, logical_w, logical_h, mode); |
2840 | } |
2841 | } |
2842 | |
2843 | static bool SDL_RenderVectorFromWindow(SDL_Renderer *renderer, float window_dx, float window_dy, float *dx, float *dy) |
2844 | { |
2845 | // Convert from window coordinates to pixels within the window |
2846 | window_dx *= renderer->dpi_scale.x; |
2847 | window_dy *= renderer->dpi_scale.y; |
2848 | |
2849 | // Convert from pixels within the window to pixels within the view |
2850 | const SDL_RenderViewState *view = &renderer->main_view; |
2851 | if (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) { |
2852 | const SDL_FRect *src = &view->logical_src_rect; |
2853 | const SDL_FRect *dst = &view->logical_dst_rect; |
2854 | window_dx = (window_dx * src->w) / dst->w; |
2855 | window_dy = (window_dy * src->h) / dst->h; |
2856 | } |
2857 | |
2858 | window_dx /= view->scale.x; |
2859 | window_dy /= view->scale.y; |
2860 | |
2861 | *dx = window_dx; |
2862 | *dy = window_dy; |
2863 | return true; |
2864 | } |
2865 | |
2866 | bool SDL_RenderCoordinatesFromWindow(SDL_Renderer *renderer, float window_x, float window_y, float *x, float *y) |
2867 | { |
2868 | float render_x, render_y; |
2869 | |
2870 | CHECK_RENDERER_MAGIC(renderer, false); |
2871 | |
2872 | // Convert from window coordinates to pixels within the window |
2873 | render_x = window_x * renderer->dpi_scale.x; |
2874 | render_y = window_y * renderer->dpi_scale.y; |
2875 | |
2876 | // Convert from pixels within the window to pixels within the view |
2877 | const SDL_RenderViewState *view = &renderer->main_view; |
2878 | if (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) { |
2879 | const SDL_FRect *src = &view->logical_src_rect; |
2880 | const SDL_FRect *dst = &view->logical_dst_rect; |
2881 | render_x = ((render_x - dst->x) * src->w) / dst->w; |
2882 | render_y = ((render_y - dst->y) * src->h) / dst->h; |
2883 | } |
2884 | |
2885 | render_x = (render_x / view->scale.x) - view->viewport.x; |
2886 | render_y = (render_y / view->scale.y) - view->viewport.y; |
2887 | |
2888 | if (x) { |
2889 | *x = render_x; |
2890 | } |
2891 | if (y) { |
2892 | *y = render_y; |
2893 | } |
2894 | return true; |
2895 | } |
2896 | |
2897 | bool SDL_RenderCoordinatesToWindow(SDL_Renderer *renderer, float x, float y, float *window_x, float *window_y) |
2898 | { |
2899 | CHECK_RENDERER_MAGIC(renderer, false); |
2900 | |
2901 | const SDL_RenderViewState *view = &renderer->main_view; |
2902 | x = (view->viewport.x + x) * view->scale.x; |
2903 | y = (view->viewport.y + y) * view->scale.y; |
2904 | |
2905 | // Convert from render coordinates to pixels within the window |
2906 | if (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) { |
2907 | const SDL_FRect *src = &view->logical_src_rect; |
2908 | const SDL_FRect *dst = &view->logical_dst_rect; |
2909 | x = dst->x + ((x * dst->w) / src->w); |
2910 | y = dst->y + ((y * dst->h) / src->h); |
2911 | } |
2912 | |
2913 | // Convert from pixels within the window to window coordinates |
2914 | x /= renderer->dpi_scale.x; |
2915 | y /= renderer->dpi_scale.y; |
2916 | |
2917 | if (window_x) { |
2918 | *window_x = x; |
2919 | } |
2920 | if (window_y) { |
2921 | *window_y = y; |
2922 | } |
2923 | return true; |
2924 | } |
2925 | |
2926 | bool SDL_ConvertEventToRenderCoordinates(SDL_Renderer *renderer, SDL_Event *event) |
2927 | { |
2928 | CHECK_RENDERER_MAGIC(renderer, false); |
2929 | |
2930 | if (event->type == SDL_EVENT_MOUSE_MOTION) { |
2931 | SDL_Window *window = SDL_GetWindowFromID(event->motion.windowID); |
2932 | if (window == renderer->window) { |
2933 | SDL_RenderCoordinatesFromWindow(renderer, event->motion.x, event->motion.y, &event->motion.x, &event->motion.y); |
2934 | SDL_RenderVectorFromWindow(renderer, event->motion.xrel, event->motion.yrel, &event->motion.xrel, &event->motion.yrel); |
2935 | } |
2936 | } else if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN || |
2937 | event->type == SDL_EVENT_MOUSE_BUTTON_UP) { |
2938 | SDL_Window *window = SDL_GetWindowFromID(event->button.windowID); |
2939 | if (window == renderer->window) { |
2940 | SDL_RenderCoordinatesFromWindow(renderer, event->button.x, event->button.y, &event->button.x, &event->button.y); |
2941 | } |
2942 | } else if (event->type == SDL_EVENT_MOUSE_WHEEL) { |
2943 | SDL_Window *window = SDL_GetWindowFromID(event->wheel.windowID); |
2944 | if (window == renderer->window) { |
2945 | SDL_RenderCoordinatesFromWindow(renderer, event->wheel.mouse_x, |
2946 | event->wheel.mouse_y, |
2947 | &event->wheel.mouse_x, |
2948 | &event->wheel.mouse_y); |
2949 | } |
2950 | } else if (event->type == SDL_EVENT_FINGER_DOWN || |
2951 | event->type == SDL_EVENT_FINGER_UP || |
2952 | event->type == SDL_EVENT_FINGER_CANCELED || |
2953 | event->type == SDL_EVENT_FINGER_MOTION) { |
2954 | // FIXME: Are these events guaranteed to be window relative? |
2955 | if (renderer->window) { |
2956 | int w, h; |
2957 | if (!SDL_GetWindowSize(renderer->window, &w, &h)) { |
2958 | return false; |
2959 | } |
2960 | SDL_RenderCoordinatesFromWindow(renderer, event->tfinger.x * w, event->tfinger.y * h, &event->tfinger.x, &event->tfinger.y); |
2961 | SDL_RenderVectorFromWindow(renderer, event->tfinger.dx * w, event->tfinger.dy * h, &event->tfinger.dx, &event->tfinger.dy); |
2962 | } |
2963 | } else if (event->type == SDL_EVENT_PEN_MOTION) { |
2964 | SDL_Window *window = SDL_GetWindowFromID(event->pmotion.windowID); |
2965 | if (window == renderer->window) { |
2966 | SDL_RenderCoordinatesFromWindow(renderer, event->pmotion.x, event->pmotion.y, &event->pmotion.x, &event->pmotion.y); |
2967 | } |
2968 | } else if ((event->type == SDL_EVENT_PEN_DOWN) || (event->type == SDL_EVENT_PEN_UP)) { |
2969 | SDL_Window *window = SDL_GetWindowFromID(event->ptouch.windowID); |
2970 | if (window == renderer->window) { |
2971 | SDL_RenderCoordinatesFromWindow(renderer, event->ptouch.x, event->ptouch.y, &event->ptouch.x, &event->ptouch.y); |
2972 | } |
2973 | } else if ((event->type == SDL_EVENT_PEN_BUTTON_DOWN) || (event->type == SDL_EVENT_PEN_BUTTON_UP)) { |
2974 | SDL_Window *window = SDL_GetWindowFromID(event->pbutton.windowID); |
2975 | if (window == renderer->window) { |
2976 | SDL_RenderCoordinatesFromWindow(renderer, event->pbutton.x, event->pbutton.y, &event->pbutton.x, &event->pbutton.y); |
2977 | } |
2978 | } else if (event->type == SDL_EVENT_PEN_AXIS) { |
2979 | SDL_Window *window = SDL_GetWindowFromID(event->paxis.windowID); |
2980 | if (window == renderer->window) { |
2981 | SDL_RenderCoordinatesFromWindow(renderer, event->paxis.x, event->paxis.y, &event->paxis.x, &event->paxis.y); |
2982 | } |
2983 | } else if (event->type == SDL_EVENT_DROP_POSITION || |
2984 | event->type == SDL_EVENT_DROP_FILE || |
2985 | event->type == SDL_EVENT_DROP_TEXT || |
2986 | event->type == SDL_EVENT_DROP_COMPLETE) { |
2987 | SDL_Window *window = SDL_GetWindowFromID(event->drop.windowID); |
2988 | if (window == renderer->window) { |
2989 | SDL_RenderCoordinatesFromWindow(renderer, event->drop.x, event->drop.y, &event->drop.x, &event->drop.y); |
2990 | } |
2991 | } |
2992 | return true; |
2993 | } |
2994 | |
2995 | bool SDL_SetRenderViewport(SDL_Renderer *renderer, const SDL_Rect *rect) |
2996 | { |
2997 | CHECK_RENDERER_MAGIC(renderer, false); |
2998 | |
2999 | SDL_RenderViewState *view = renderer->view; |
3000 | if (rect) { |
3001 | if ((rect->w < 0) || (rect->h < 0)) { |
3002 | return SDL_SetError("rect has a negative size" ); |
3003 | } |
3004 | SDL_copyp(&view->viewport, rect); |
3005 | } else { |
3006 | view->viewport.x = view->viewport.y = 0; |
3007 | view->viewport.w = view->viewport.h = -1; |
3008 | } |
3009 | UpdatePixelViewport(renderer, view); |
3010 | |
3011 | return QueueCmdSetViewport(renderer); |
3012 | } |
3013 | |
3014 | bool SDL_GetRenderViewport(SDL_Renderer *renderer, SDL_Rect *rect) |
3015 | { |
3016 | if (rect) { |
3017 | SDL_zerop(rect); |
3018 | } |
3019 | |
3020 | CHECK_RENDERER_MAGIC(renderer, false); |
3021 | |
3022 | if (rect) { |
3023 | const SDL_RenderViewState *view = renderer->view; |
3024 | rect->x = view->viewport.x; |
3025 | rect->y = view->viewport.y; |
3026 | if (view->viewport.w >= 0) { |
3027 | rect->w = view->viewport.w; |
3028 | } else { |
3029 | rect->w = (int)SDL_ceilf(view->pixel_w / view->current_scale.x); |
3030 | } |
3031 | if (view->viewport.h >= 0) { |
3032 | rect->h = view->viewport.h; |
3033 | } else { |
3034 | rect->h = (int)SDL_ceilf(view->pixel_h / view->current_scale.y); |
3035 | } |
3036 | } |
3037 | return true; |
3038 | } |
3039 | |
3040 | bool SDL_RenderViewportSet(SDL_Renderer *renderer) |
3041 | { |
3042 | CHECK_RENDERER_MAGIC(renderer, false); |
3043 | |
3044 | const SDL_RenderViewState *view = renderer->view; |
3045 | return (view->viewport.w >= 0 && view->viewport.h >= 0); |
3046 | } |
3047 | |
3048 | static void GetRenderViewportSize(SDL_Renderer *renderer, SDL_FRect *rect) |
3049 | { |
3050 | const SDL_RenderViewState *view = renderer->view; |
3051 | const float scale_x = view->current_scale.x; |
3052 | const float scale_y = view->current_scale.y; |
3053 | |
3054 | rect->x = 0.0f; |
3055 | rect->y = 0.0f; |
3056 | |
3057 | if (view->viewport.w >= 0) { |
3058 | rect->w = (float)view->viewport.w; |
3059 | } else { |
3060 | rect->w = view->pixel_w / scale_x; |
3061 | } |
3062 | |
3063 | if (view->viewport.h >= 0) { |
3064 | rect->h = (float)view->viewport.h; |
3065 | } else { |
3066 | rect->h = view->pixel_h / scale_y; |
3067 | } |
3068 | } |
3069 | |
3070 | bool SDL_GetRenderSafeArea(SDL_Renderer *renderer, SDL_Rect *rect) |
3071 | { |
3072 | if (rect) { |
3073 | SDL_zerop(rect); |
3074 | } |
3075 | |
3076 | CHECK_RENDERER_MAGIC(renderer, false); |
3077 | |
3078 | if (renderer->target || !renderer->window) { |
3079 | // The entire viewport is safe for rendering |
3080 | return SDL_GetRenderViewport(renderer, rect); |
3081 | } |
3082 | |
3083 | if (rect) { |
3084 | // Get the window safe rect |
3085 | SDL_Rect safe; |
3086 | if (!SDL_GetWindowSafeArea(renderer->window, &safe)) { |
3087 | return false; |
3088 | } |
3089 | |
3090 | // Convert the coordinates into the render space |
3091 | float minx = (float)safe.x; |
3092 | float miny = (float)safe.y; |
3093 | float maxx = (float)safe.x + safe.w; |
3094 | float maxy = (float)safe.y + safe.h; |
3095 | if (!SDL_RenderCoordinatesFromWindow(renderer, minx, miny, &minx, &miny) || |
3096 | !SDL_RenderCoordinatesFromWindow(renderer, maxx, maxy, &maxx, &maxy)) { |
3097 | return false; |
3098 | } |
3099 | |
3100 | rect->x = (int)SDL_ceilf(minx); |
3101 | rect->y = (int)SDL_ceilf(miny); |
3102 | rect->w = (int)SDL_ceilf(maxx - minx); |
3103 | rect->h = (int)SDL_ceilf(maxy - miny); |
3104 | |
3105 | // Clip with the viewport |
3106 | SDL_Rect viewport; |
3107 | if (!SDL_GetRenderViewport(renderer, &viewport)) { |
3108 | return false; |
3109 | } |
3110 | if (!SDL_GetRectIntersection(rect, &viewport, rect)) { |
3111 | return SDL_SetError("No safe area within viewport" ); |
3112 | } |
3113 | } |
3114 | return true; |
3115 | } |
3116 | |
3117 | bool SDL_SetRenderClipRect(SDL_Renderer *renderer, const SDL_Rect *rect) |
3118 | { |
3119 | CHECK_RENDERER_MAGIC(renderer, false) |
3120 | |
3121 | SDL_RenderViewState *view = renderer->view; |
3122 | if (rect && rect->w >= 0 && rect->h >= 0) { |
3123 | view->clipping_enabled = true; |
3124 | SDL_copyp(&view->clip_rect, rect); |
3125 | } else { |
3126 | view->clipping_enabled = false; |
3127 | SDL_zero(view->clip_rect); |
3128 | } |
3129 | UpdatePixelClipRect(renderer, view); |
3130 | |
3131 | return QueueCmdSetClipRect(renderer); |
3132 | } |
3133 | |
3134 | bool SDL_GetRenderClipRect(SDL_Renderer *renderer, SDL_Rect *rect) |
3135 | { |
3136 | if (rect) { |
3137 | SDL_zerop(rect); |
3138 | } |
3139 | |
3140 | CHECK_RENDERER_MAGIC(renderer, false) |
3141 | |
3142 | if (rect) { |
3143 | SDL_copyp(rect, &renderer->view->clip_rect); |
3144 | } |
3145 | return true; |
3146 | } |
3147 | |
3148 | bool SDL_RenderClipEnabled(SDL_Renderer *renderer) |
3149 | { |
3150 | CHECK_RENDERER_MAGIC(renderer, false) |
3151 | return renderer->view->clipping_enabled; |
3152 | } |
3153 | |
3154 | bool SDL_SetRenderScale(SDL_Renderer *renderer, float scaleX, float scaleY) |
3155 | { |
3156 | bool result = true; |
3157 | |
3158 | CHECK_RENDERER_MAGIC(renderer, false); |
3159 | |
3160 | SDL_RenderViewState *view = renderer->view; |
3161 | |
3162 | if ((view->scale.x == scaleX) && (view->scale.y == scaleY)) { |
3163 | return true; |
3164 | } |
3165 | |
3166 | view->scale.x = scaleX; |
3167 | view->scale.y = scaleY; |
3168 | view->current_scale.x = scaleX * view->logical_scale.x; |
3169 | view->current_scale.y = scaleY * view->logical_scale.y; |
3170 | UpdatePixelViewport(renderer, view); |
3171 | UpdatePixelClipRect(renderer, view); |
3172 | |
3173 | // The scale affects the existing viewport and clip rectangle |
3174 | result &= QueueCmdSetViewport(renderer); |
3175 | result &= QueueCmdSetClipRect(renderer); |
3176 | return result; |
3177 | } |
3178 | |
3179 | bool SDL_GetRenderScale(SDL_Renderer *renderer, float *scaleX, float *scaleY) |
3180 | { |
3181 | if (scaleX) { |
3182 | *scaleX = 1.0f; |
3183 | } |
3184 | if (scaleY) { |
3185 | *scaleY = 1.0f; |
3186 | } |
3187 | |
3188 | CHECK_RENDERER_MAGIC(renderer, false); |
3189 | |
3190 | const SDL_RenderViewState *view = renderer->view; |
3191 | |
3192 | if (scaleX) { |
3193 | *scaleX = view->scale.x; |
3194 | } |
3195 | if (scaleY) { |
3196 | *scaleY = view->scale.y; |
3197 | } |
3198 | return true; |
3199 | } |
3200 | |
3201 | bool SDL_SetRenderDrawColor(SDL_Renderer *renderer, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
3202 | { |
3203 | const float fR = (float)r / 255.0f; |
3204 | const float fG = (float)g / 255.0f; |
3205 | const float fB = (float)b / 255.0f; |
3206 | const float fA = (float)a / 255.0f; |
3207 | |
3208 | return SDL_SetRenderDrawColorFloat(renderer, fR, fG, fB, fA); |
3209 | } |
3210 | |
3211 | bool SDL_SetRenderDrawColorFloat(SDL_Renderer *renderer, float r, float g, float b, float a) |
3212 | { |
3213 | CHECK_RENDERER_MAGIC(renderer, false); |
3214 | |
3215 | renderer->color.r = r; |
3216 | renderer->color.g = g; |
3217 | renderer->color.b = b; |
3218 | renderer->color.a = a; |
3219 | return true; |
3220 | } |
3221 | |
3222 | bool SDL_GetRenderDrawColor(SDL_Renderer *renderer, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a) |
3223 | { |
3224 | float fR, fG, fB, fA; |
3225 | |
3226 | if (!SDL_GetRenderDrawColorFloat(renderer, &fR, &fG, &fB, &fA)) { |
3227 | if (r) { |
3228 | *r = 0; |
3229 | } |
3230 | if (g) { |
3231 | *g = 0; |
3232 | } |
3233 | if (b) { |
3234 | *b = 0; |
3235 | } |
3236 | if (a) { |
3237 | *a = 0; |
3238 | } |
3239 | return false; |
3240 | } |
3241 | |
3242 | if (r) { |
3243 | *r = (Uint8)(fR * 255.0f); |
3244 | } |
3245 | if (g) { |
3246 | *g = (Uint8)(fG * 255.0f); |
3247 | } |
3248 | if (b) { |
3249 | *b = (Uint8)(fB * 255.0f); |
3250 | } |
3251 | if (a) { |
3252 | *a = (Uint8)(fA * 255.0f); |
3253 | } |
3254 | return true; |
3255 | } |
3256 | |
3257 | bool SDL_GetRenderDrawColorFloat(SDL_Renderer *renderer, float *r, float *g, float *b, float *a) |
3258 | { |
3259 | SDL_FColor color; |
3260 | |
3261 | if (r) { |
3262 | *r = 0.0f; |
3263 | } |
3264 | if (g) { |
3265 | *g = 0.0f; |
3266 | } |
3267 | if (b) { |
3268 | *b = 0.0f; |
3269 | } |
3270 | if (a) { |
3271 | *a = 0.0f; |
3272 | } |
3273 | |
3274 | CHECK_RENDERER_MAGIC(renderer, false); |
3275 | |
3276 | color = renderer->color; |
3277 | |
3278 | if (r) { |
3279 | *r = color.r; |
3280 | } |
3281 | if (g) { |
3282 | *g = color.g; |
3283 | } |
3284 | if (b) { |
3285 | *b = color.b; |
3286 | } |
3287 | if (a) { |
3288 | *a = color.a; |
3289 | } |
3290 | return true; |
3291 | } |
3292 | |
3293 | bool SDL_SetRenderColorScale(SDL_Renderer *renderer, float scale) |
3294 | { |
3295 | CHECK_RENDERER_MAGIC(renderer, false); |
3296 | |
3297 | renderer->desired_color_scale = scale; |
3298 | UpdateColorScale(renderer); |
3299 | return true; |
3300 | } |
3301 | |
3302 | bool SDL_GetRenderColorScale(SDL_Renderer *renderer, float *scale) |
3303 | { |
3304 | if (scale) { |
3305 | *scale = 1.0f; |
3306 | } |
3307 | |
3308 | CHECK_RENDERER_MAGIC(renderer, false); |
3309 | |
3310 | if (scale) { |
3311 | *scale = renderer->desired_color_scale; |
3312 | } |
3313 | return true; |
3314 | } |
3315 | |
3316 | bool SDL_SetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode) |
3317 | { |
3318 | CHECK_RENDERER_MAGIC(renderer, false); |
3319 | |
3320 | if (blendMode == SDL_BLENDMODE_INVALID) { |
3321 | return SDL_InvalidParamError("blendMode" ); |
3322 | } |
3323 | |
3324 | if (blendMode == SDL_BLENDMODE_INVALID) { |
3325 | return SDL_InvalidParamError("blendMode" ); |
3326 | } |
3327 | |
3328 | if (!IsSupportedBlendMode(renderer, blendMode)) { |
3329 | return SDL_Unsupported(); |
3330 | } |
3331 | |
3332 | renderer->blendMode = blendMode; |
3333 | return true; |
3334 | } |
3335 | |
3336 | bool SDL_GetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode *blendMode) |
3337 | { |
3338 | if (blendMode) { |
3339 | *blendMode = SDL_BLENDMODE_INVALID; |
3340 | } |
3341 | |
3342 | CHECK_RENDERER_MAGIC(renderer, false); |
3343 | |
3344 | if (blendMode) { |
3345 | *blendMode = renderer->blendMode; |
3346 | } |
3347 | return true; |
3348 | } |
3349 | |
3350 | bool SDL_RenderClear(SDL_Renderer *renderer) |
3351 | { |
3352 | CHECK_RENDERER_MAGIC(renderer, false); |
3353 | |
3354 | return QueueCmdClear(renderer); |
3355 | } |
3356 | |
3357 | bool SDL_RenderPoint(SDL_Renderer *renderer, float x, float y) |
3358 | { |
3359 | SDL_FPoint fpoint; |
3360 | fpoint.x = x; |
3361 | fpoint.y = y; |
3362 | return SDL_RenderPoints(renderer, &fpoint, 1); |
3363 | } |
3364 | |
3365 | static bool RenderPointsWithRects(SDL_Renderer *renderer, const SDL_FPoint *fpoints, const int count) |
3366 | { |
3367 | bool result; |
3368 | bool isstack; |
3369 | SDL_FRect *frects; |
3370 | int i; |
3371 | |
3372 | if (count < 1) { |
3373 | return true; |
3374 | } |
3375 | |
3376 | frects = SDL_small_alloc(SDL_FRect, count, &isstack); |
3377 | if (!frects) { |
3378 | return false; |
3379 | } |
3380 | |
3381 | const SDL_RenderViewState *view = renderer->view; |
3382 | const float scale_x = view->current_scale.x; |
3383 | const float scale_y = view->current_scale.y; |
3384 | for (i = 0; i < count; ++i) { |
3385 | frects[i].x = fpoints[i].x * scale_x; |
3386 | frects[i].y = fpoints[i].y * scale_y; |
3387 | frects[i].w = scale_x; |
3388 | frects[i].h = scale_y; |
3389 | } |
3390 | |
3391 | result = QueueCmdFillRects(renderer, frects, count); |
3392 | |
3393 | SDL_small_free(frects, isstack); |
3394 | |
3395 | return result; |
3396 | } |
3397 | |
3398 | bool SDL_RenderPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int count) |
3399 | { |
3400 | bool result; |
3401 | |
3402 | CHECK_RENDERER_MAGIC(renderer, false); |
3403 | |
3404 | if (!points) { |
3405 | return SDL_InvalidParamError("SDL_RenderPoints(): points" ); |
3406 | } |
3407 | if (count < 1) { |
3408 | return true; |
3409 | } |
3410 | |
3411 | #if DONT_DRAW_WHILE_HIDDEN |
3412 | // Don't draw while we're hidden |
3413 | if (renderer->hidden) { |
3414 | return true; |
3415 | } |
3416 | #endif |
3417 | |
3418 | const SDL_RenderViewState *view = renderer->view; |
3419 | if ((view->current_scale.x != 1.0f) || (view->current_scale.y != 1.0f)) { |
3420 | result = RenderPointsWithRects(renderer, points, count); |
3421 | } else { |
3422 | result = QueueCmdDrawPoints(renderer, points, count); |
3423 | } |
3424 | return result; |
3425 | } |
3426 | |
3427 | bool SDL_RenderLine(SDL_Renderer *renderer, float x1, float y1, float x2, float y2) |
3428 | { |
3429 | SDL_FPoint points[2]; |
3430 | points[0].x = x1; |
3431 | points[0].y = y1; |
3432 | points[1].x = x2; |
3433 | points[1].y = y2; |
3434 | return SDL_RenderLines(renderer, points, 2); |
3435 | } |
3436 | |
3437 | static bool RenderLineBresenham(SDL_Renderer *renderer, int x1, int y1, int x2, int y2, bool draw_last) |
3438 | { |
3439 | const SDL_RenderViewState *view = renderer->view; |
3440 | const int MAX_PIXELS = SDL_max(view->pixel_w, view->pixel_h) * 4; |
3441 | int i, deltax, deltay, numpixels; |
3442 | int d, dinc1, dinc2; |
3443 | int x, xinc1, xinc2; |
3444 | int y, yinc1, yinc2; |
3445 | bool result; |
3446 | bool isstack; |
3447 | SDL_FPoint *points; |
3448 | SDL_Rect viewport; |
3449 | |
3450 | /* the backend might clip this further to the clipping rect, but we |
3451 | just want a basic safety against generating millions of points for |
3452 | massive lines. */ |
3453 | viewport = view->pixel_viewport; |
3454 | viewport.x = 0; |
3455 | viewport.y = 0; |
3456 | if (!SDL_GetRectAndLineIntersection(&viewport, &x1, &y1, &x2, &y2)) { |
3457 | return true; |
3458 | } |
3459 | |
3460 | deltax = SDL_abs(x2 - x1); |
3461 | deltay = SDL_abs(y2 - y1); |
3462 | |
3463 | if (deltax >= deltay) { |
3464 | numpixels = deltax + 1; |
3465 | d = (2 * deltay) - deltax; |
3466 | dinc1 = deltay * 2; |
3467 | dinc2 = (deltay - deltax) * 2; |
3468 | xinc1 = 1; |
3469 | xinc2 = 1; |
3470 | yinc1 = 0; |
3471 | yinc2 = 1; |
3472 | } else { |
3473 | numpixels = deltay + 1; |
3474 | d = (2 * deltax) - deltay; |
3475 | dinc1 = deltax * 2; |
3476 | dinc2 = (deltax - deltay) * 2; |
3477 | xinc1 = 0; |
3478 | xinc2 = 1; |
3479 | yinc1 = 1; |
3480 | yinc2 = 1; |
3481 | } |
3482 | |
3483 | if (x1 > x2) { |
3484 | xinc1 = -xinc1; |
3485 | xinc2 = -xinc2; |
3486 | } |
3487 | if (y1 > y2) { |
3488 | yinc1 = -yinc1; |
3489 | yinc2 = -yinc2; |
3490 | } |
3491 | |
3492 | x = x1; |
3493 | y = y1; |
3494 | |
3495 | if (!draw_last) { |
3496 | --numpixels; |
3497 | } |
3498 | |
3499 | if (numpixels > MAX_PIXELS) { |
3500 | return SDL_SetError("Line too long (tried to draw %d pixels, max %d)" , numpixels, MAX_PIXELS); |
3501 | } |
3502 | |
3503 | points = SDL_small_alloc(SDL_FPoint, numpixels, &isstack); |
3504 | if (!points) { |
3505 | return false; |
3506 | } |
3507 | for (i = 0; i < numpixels; ++i) { |
3508 | points[i].x = (float)x; |
3509 | points[i].y = (float)y; |
3510 | |
3511 | if (d < 0) { |
3512 | d += dinc1; |
3513 | x += xinc1; |
3514 | y += yinc1; |
3515 | } else { |
3516 | d += dinc2; |
3517 | x += xinc2; |
3518 | y += yinc2; |
3519 | } |
3520 | } |
3521 | |
3522 | if ((view->current_scale.x != 1.0f) || (view->current_scale.y != 1.0f)) { |
3523 | result = RenderPointsWithRects(renderer, points, numpixels); |
3524 | } else { |
3525 | result = QueueCmdDrawPoints(renderer, points, numpixels); |
3526 | } |
3527 | |
3528 | SDL_small_free(points, isstack); |
3529 | |
3530 | return result; |
3531 | } |
3532 | |
3533 | static bool RenderLinesWithRectsF(SDL_Renderer *renderer, const SDL_FPoint *points, const int count) |
3534 | { |
3535 | const SDL_RenderViewState *view = renderer->view; |
3536 | const float scale_x = view->current_scale.x; |
3537 | const float scale_y = view->current_scale.y; |
3538 | SDL_FRect *frect; |
3539 | SDL_FRect *frects; |
3540 | int i, nrects = 0; |
3541 | bool result = true; |
3542 | bool isstack; |
3543 | bool drew_line = false; |
3544 | bool draw_last = false; |
3545 | |
3546 | frects = SDL_small_alloc(SDL_FRect, count - 1, &isstack); |
3547 | if (!frects) { |
3548 | return false; |
3549 | } |
3550 | |
3551 | for (i = 0; i < count - 1; ++i) { |
3552 | bool same_x = (points[i].x == points[i + 1].x); |
3553 | bool same_y = (points[i].y == points[i + 1].y); |
3554 | |
3555 | if (i == (count - 2)) { |
3556 | if (!drew_line || points[i + 1].x != points[0].x || points[i + 1].y != points[0].y) { |
3557 | draw_last = true; |
3558 | } |
3559 | } else { |
3560 | if (same_x && same_y) { |
3561 | continue; |
3562 | } |
3563 | } |
3564 | if (same_x) { |
3565 | const float minY = SDL_min(points[i].y, points[i + 1].y); |
3566 | const float maxY = SDL_max(points[i].y, points[i + 1].y); |
3567 | |
3568 | frect = &frects[nrects++]; |
3569 | frect->x = points[i].x * scale_x; |
3570 | frect->y = minY * scale_y; |
3571 | frect->w = scale_x; |
3572 | frect->h = (maxY - minY + draw_last) * scale_y; |
3573 | if (!draw_last && points[i + 1].y < points[i].y) { |
3574 | frect->y += scale_y; |
3575 | } |
3576 | } else if (same_y) { |
3577 | const float minX = SDL_min(points[i].x, points[i + 1].x); |
3578 | const float maxX = SDL_max(points[i].x, points[i + 1].x); |
3579 | |
3580 | frect = &frects[nrects++]; |
3581 | frect->x = minX * scale_x; |
3582 | frect->y = points[i].y * scale_y; |
3583 | frect->w = (maxX - minX + draw_last) * scale_x; |
3584 | frect->h = scale_y; |
3585 | if (!draw_last && points[i + 1].x < points[i].x) { |
3586 | frect->x += scale_x; |
3587 | } |
3588 | } else { |
3589 | result &= RenderLineBresenham(renderer, (int)SDL_roundf(points[i].x), (int)SDL_roundf(points[i].y), |
3590 | (int)SDL_roundf(points[i + 1].x), (int)SDL_roundf(points[i + 1].y), draw_last); |
3591 | } |
3592 | drew_line = true; |
3593 | } |
3594 | |
3595 | if (nrects) { |
3596 | result &= QueueCmdFillRects(renderer, frects, nrects); |
3597 | } |
3598 | |
3599 | SDL_small_free(frects, isstack); |
3600 | |
3601 | return result; |
3602 | } |
3603 | |
3604 | bool SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count) |
3605 | { |
3606 | bool result = true; |
3607 | |
3608 | CHECK_RENDERER_MAGIC(renderer, false); |
3609 | |
3610 | if (!points) { |
3611 | return SDL_InvalidParamError("SDL_RenderLines(): points" ); |
3612 | } |
3613 | if (count < 2) { |
3614 | return true; |
3615 | } |
3616 | |
3617 | #if DONT_DRAW_WHILE_HIDDEN |
3618 | // Don't draw while we're hidden |
3619 | if (renderer->hidden) { |
3620 | return true; |
3621 | } |
3622 | #endif |
3623 | |
3624 | SDL_RenderViewState *view = renderer->view; |
3625 | const bool islogical = ((view == &renderer->main_view) && (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED)); |
3626 | |
3627 | if (islogical || (renderer->line_method == SDL_RENDERLINEMETHOD_GEOMETRY)) { |
3628 | const float scale_x = view->current_scale.x; |
3629 | const float scale_y = view->current_scale.y; |
3630 | bool isstack1; |
3631 | bool isstack2; |
3632 | float *xy = SDL_small_alloc(float, 4 * 2 * count, &isstack1); |
3633 | int *indices = SDL_small_alloc(int, (4) * 3 * (count - 1) + (2) * 3 * (count), &isstack2); |
3634 | |
3635 | if (xy && indices) { |
3636 | int i; |
3637 | float *ptr_xy = xy; |
3638 | int *ptr_indices = indices; |
3639 | const int xy_stride = 2 * sizeof(float); |
3640 | int num_vertices = 4 * count; |
3641 | int num_indices = 0; |
3642 | const int size_indices = 4; |
3643 | int cur_index = -4; |
3644 | const int is_looping = (points[0].x == points[count - 1].x && points[0].y == points[count - 1].y); |
3645 | SDL_FPoint p; // previous point |
3646 | p.x = p.y = 0.0f; |
3647 | /* p q |
3648 | |
3649 | 0----1------ 4----5 |
3650 | | \ |``\ | \ | |
3651 | | \ | ` `\| \ | |
3652 | 3----2-------7----6 |
3653 | */ |
3654 | for (i = 0; i < count; ++i) { |
3655 | SDL_FPoint q = points[i]; // current point |
3656 | |
3657 | q.x *= scale_x; |
3658 | q.y *= scale_y; |
3659 | |
3660 | *ptr_xy++ = q.x; |
3661 | *ptr_xy++ = q.y; |
3662 | *ptr_xy++ = q.x + scale_x; |
3663 | *ptr_xy++ = q.y; |
3664 | *ptr_xy++ = q.x + scale_x; |
3665 | *ptr_xy++ = q.y + scale_y; |
3666 | *ptr_xy++ = q.x; |
3667 | *ptr_xy++ = q.y + scale_y; |
3668 | |
3669 | #define ADD_TRIANGLE(i1, i2, i3) \ |
3670 | *ptr_indices++ = cur_index + (i1); \ |
3671 | *ptr_indices++ = cur_index + (i2); \ |
3672 | *ptr_indices++ = cur_index + (i3); \ |
3673 | num_indices += 3; |
3674 | |
3675 | // closed polyline, don´t draw twice the point |
3676 | if (i || is_looping == 0) { |
3677 | ADD_TRIANGLE(4, 5, 6) |
3678 | ADD_TRIANGLE(4, 6, 7) |
3679 | } |
3680 | |
3681 | // first point only, no segment |
3682 | if (i == 0) { |
3683 | p = q; |
3684 | cur_index += 4; |
3685 | continue; |
3686 | } |
3687 | |
3688 | // draw segment |
3689 | if (p.y == q.y) { |
3690 | if (p.x < q.x) { |
3691 | ADD_TRIANGLE(1, 4, 7) |
3692 | ADD_TRIANGLE(1, 7, 2) |
3693 | } else { |
3694 | ADD_TRIANGLE(5, 0, 3) |
3695 | ADD_TRIANGLE(5, 3, 6) |
3696 | } |
3697 | } else if (p.x == q.x) { |
3698 | if (p.y < q.y) { |
3699 | ADD_TRIANGLE(2, 5, 4) |
3700 | ADD_TRIANGLE(2, 4, 3) |
3701 | } else { |
3702 | ADD_TRIANGLE(6, 1, 0) |
3703 | ADD_TRIANGLE(6, 0, 7) |
3704 | } |
3705 | } else { |
3706 | if (p.y < q.y) { |
3707 | if (p.x < q.x) { |
3708 | ADD_TRIANGLE(1, 5, 4) |
3709 | ADD_TRIANGLE(1, 4, 2) |
3710 | ADD_TRIANGLE(2, 4, 7) |
3711 | ADD_TRIANGLE(2, 7, 3) |
3712 | } else { |
3713 | ADD_TRIANGLE(4, 0, 5) |
3714 | ADD_TRIANGLE(5, 0, 3) |
3715 | ADD_TRIANGLE(5, 3, 6) |
3716 | ADD_TRIANGLE(6, 3, 2) |
3717 | } |
3718 | } else { |
3719 | if (p.x < q.x) { |
3720 | ADD_TRIANGLE(0, 4, 7) |
3721 | ADD_TRIANGLE(0, 7, 1) |
3722 | ADD_TRIANGLE(1, 7, 6) |
3723 | ADD_TRIANGLE(1, 6, 2) |
3724 | } else { |
3725 | ADD_TRIANGLE(6, 5, 1) |
3726 | ADD_TRIANGLE(6, 1, 0) |
3727 | ADD_TRIANGLE(7, 6, 0) |
3728 | ADD_TRIANGLE(7, 0, 3) |
3729 | } |
3730 | } |
3731 | } |
3732 | |
3733 | p = q; |
3734 | cur_index += 4; |
3735 | } |
3736 | |
3737 | result = QueueCmdGeometry(renderer, NULL, |
3738 | xy, xy_stride, &renderer->color, 0 /* color_stride */, NULL, 0, |
3739 | num_vertices, indices, num_indices, size_indices, |
3740 | 1.0f, 1.0f, SDL_TEXTURE_ADDRESS_CLAMP); |
3741 | } |
3742 | |
3743 | SDL_small_free(xy, isstack1); |
3744 | SDL_small_free(indices, isstack2); |
3745 | |
3746 | } else if (renderer->line_method == SDL_RENDERLINEMETHOD_POINTS) { |
3747 | result = RenderLinesWithRectsF(renderer, points, count); |
3748 | } else if (view->scale.x != 1.0f || view->scale.y != 1.0f) { /* we checked for logical scale elsewhere. */ |
3749 | result = RenderLinesWithRectsF(renderer, points, count); |
3750 | } else { |
3751 | result = QueueCmdDrawLines(renderer, points, count); |
3752 | } |
3753 | |
3754 | return result; |
3755 | } |
3756 | |
3757 | bool SDL_RenderRect(SDL_Renderer *renderer, const SDL_FRect *rect) |
3758 | { |
3759 | SDL_FRect frect; |
3760 | SDL_FPoint points[5]; |
3761 | |
3762 | CHECK_RENDERER_MAGIC(renderer, false); |
3763 | |
3764 | // If 'rect' == NULL, then outline the whole surface |
3765 | if (!rect) { |
3766 | GetRenderViewportSize(renderer, &frect); |
3767 | rect = &frect; |
3768 | } |
3769 | |
3770 | points[0].x = rect->x; |
3771 | points[0].y = rect->y; |
3772 | points[1].x = rect->x + rect->w - 1; |
3773 | points[1].y = rect->y; |
3774 | points[2].x = rect->x + rect->w - 1; |
3775 | points[2].y = rect->y + rect->h - 1; |
3776 | points[3].x = rect->x; |
3777 | points[3].y = rect->y + rect->h - 1; |
3778 | points[4].x = rect->x; |
3779 | points[4].y = rect->y; |
3780 | return SDL_RenderLines(renderer, points, 5); |
3781 | } |
3782 | |
3783 | bool SDL_RenderRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count) |
3784 | { |
3785 | int i; |
3786 | |
3787 | CHECK_RENDERER_MAGIC(renderer, false); |
3788 | |
3789 | if (!rects) { |
3790 | return SDL_InvalidParamError("SDL_RenderRects(): rects" ); |
3791 | } |
3792 | if (count < 1) { |
3793 | return true; |
3794 | } |
3795 | |
3796 | #if DONT_DRAW_WHILE_HIDDEN |
3797 | // Don't draw while we're hidden |
3798 | if (renderer->hidden) { |
3799 | return true; |
3800 | } |
3801 | #endif |
3802 | |
3803 | for (i = 0; i < count; ++i) { |
3804 | if (!SDL_RenderRect(renderer, &rects[i])) { |
3805 | return false; |
3806 | } |
3807 | } |
3808 | return true; |
3809 | } |
3810 | |
3811 | bool SDL_RenderFillRect(SDL_Renderer *renderer, const SDL_FRect *rect) |
3812 | { |
3813 | SDL_FRect frect; |
3814 | |
3815 | CHECK_RENDERER_MAGIC(renderer, false); |
3816 | |
3817 | // If 'rect' == NULL, then fill the whole surface |
3818 | if (!rect) { |
3819 | GetRenderViewportSize(renderer, &frect); |
3820 | rect = &frect; |
3821 | } |
3822 | return SDL_RenderFillRects(renderer, rect, 1); |
3823 | } |
3824 | |
3825 | bool SDL_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count) |
3826 | { |
3827 | SDL_FRect *frects; |
3828 | int i; |
3829 | bool result; |
3830 | bool isstack; |
3831 | |
3832 | CHECK_RENDERER_MAGIC(renderer, false); |
3833 | |
3834 | if (!rects) { |
3835 | return SDL_InvalidParamError("SDL_RenderFillRects(): rects" ); |
3836 | } |
3837 | if (count < 1) { |
3838 | return true; |
3839 | } |
3840 | |
3841 | #if DONT_DRAW_WHILE_HIDDEN |
3842 | // Don't draw while we're hidden |
3843 | if (renderer->hidden) { |
3844 | return true; |
3845 | } |
3846 | #endif |
3847 | |
3848 | frects = SDL_small_alloc(SDL_FRect, count, &isstack); |
3849 | if (!frects) { |
3850 | return false; |
3851 | } |
3852 | |
3853 | const SDL_RenderViewState *view = renderer->view; |
3854 | const float scale_x = view->current_scale.x; |
3855 | const float scale_y = view->current_scale.y; |
3856 | for (i = 0; i < count; ++i) { |
3857 | frects[i].x = rects[i].x * scale_x; |
3858 | frects[i].y = rects[i].y * scale_y; |
3859 | frects[i].w = rects[i].w * scale_x; |
3860 | frects[i].h = rects[i].h * scale_y; |
3861 | } |
3862 | |
3863 | result = QueueCmdFillRects(renderer, frects, count); |
3864 | |
3865 | SDL_small_free(frects, isstack); |
3866 | |
3867 | return result; |
3868 | } |
3869 | |
3870 | static bool SDL_RenderTextureInternal(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect) |
3871 | { |
3872 | const SDL_RenderViewState *view = renderer->view; |
3873 | const float scale_x = view->current_scale.x; |
3874 | const float scale_y = view->current_scale.y; |
3875 | const bool use_rendergeometry = (!renderer->QueueCopy); |
3876 | bool result; |
3877 | |
3878 | if (use_rendergeometry) { |
3879 | float xy[8]; |
3880 | const int xy_stride = 2 * sizeof(float); |
3881 | float uv[8]; |
3882 | const int uv_stride = 2 * sizeof(float); |
3883 | const int num_vertices = 4; |
3884 | const int *indices = rect_index_order; |
3885 | const int num_indices = 6; |
3886 | const int size_indices = 4; |
3887 | float minu, minv, maxu, maxv; |
3888 | float minx, miny, maxx, maxy; |
3889 | |
3890 | minu = srcrect->x / texture->w; |
3891 | minv = srcrect->y / texture->h; |
3892 | maxu = (srcrect->x + srcrect->w) / texture->w; |
3893 | maxv = (srcrect->y + srcrect->h) / texture->h; |
3894 | |
3895 | minx = dstrect->x; |
3896 | miny = dstrect->y; |
3897 | maxx = dstrect->x + dstrect->w; |
3898 | maxy = dstrect->y + dstrect->h; |
3899 | |
3900 | uv[0] = minu; |
3901 | uv[1] = minv; |
3902 | uv[2] = maxu; |
3903 | uv[3] = minv; |
3904 | uv[4] = maxu; |
3905 | uv[5] = maxv; |
3906 | uv[6] = minu; |
3907 | uv[7] = maxv; |
3908 | |
3909 | xy[0] = minx; |
3910 | xy[1] = miny; |
3911 | xy[2] = maxx; |
3912 | xy[3] = miny; |
3913 | xy[4] = maxx; |
3914 | xy[5] = maxy; |
3915 | xy[6] = minx; |
3916 | xy[7] = maxy; |
3917 | |
3918 | result = QueueCmdGeometry(renderer, texture, |
3919 | xy, xy_stride, &texture->color, 0 /* color_stride */, uv, uv_stride, |
3920 | num_vertices, indices, num_indices, size_indices, |
3921 | scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP); |
3922 | } else { |
3923 | const SDL_FRect rect = { dstrect->x * scale_x, dstrect->y * scale_y, dstrect->w * scale_x, dstrect->h * scale_y }; |
3924 | result = QueueCmdCopy(renderer, texture, srcrect, &rect); |
3925 | } |
3926 | return result; |
3927 | } |
3928 | |
3929 | bool SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect) |
3930 | { |
3931 | CHECK_RENDERER_MAGIC(renderer, false); |
3932 | CHECK_TEXTURE_MAGIC(texture, false); |
3933 | |
3934 | if (renderer != texture->renderer) { |
3935 | return SDL_SetError("Texture was not created with this renderer" ); |
3936 | } |
3937 | |
3938 | #if DONT_DRAW_WHILE_HIDDEN |
3939 | // Don't draw while we're hidden |
3940 | if (renderer->hidden) { |
3941 | return true; |
3942 | } |
3943 | #endif |
3944 | |
3945 | SDL_FRect real_srcrect; |
3946 | real_srcrect.x = 0.0f; |
3947 | real_srcrect.y = 0.0f; |
3948 | real_srcrect.w = (float)texture->w; |
3949 | real_srcrect.h = (float)texture->h; |
3950 | if (srcrect) { |
3951 | if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect) || |
3952 | real_srcrect.w == 0.0f || real_srcrect.h == 0.0f) { |
3953 | return true; |
3954 | } |
3955 | } |
3956 | |
3957 | SDL_FRect full_dstrect; |
3958 | if (!dstrect) { |
3959 | GetRenderViewportSize(renderer, &full_dstrect); |
3960 | dstrect = &full_dstrect; |
3961 | } |
3962 | |
3963 | if (texture->native) { |
3964 | texture = texture->native; |
3965 | } |
3966 | |
3967 | texture->last_command_generation = renderer->render_command_generation; |
3968 | |
3969 | return SDL_RenderTextureInternal(renderer, texture, &real_srcrect, dstrect); |
3970 | } |
3971 | |
3972 | bool SDL_RenderTextureAffine(SDL_Renderer *renderer, SDL_Texture *texture, |
3973 | const SDL_FRect *srcrect, const SDL_FPoint *origin, const SDL_FPoint *right, const SDL_FPoint *down) |
3974 | { |
3975 | SDL_FRect real_srcrect; |
3976 | SDL_FRect real_dstrect; |
3977 | bool result; |
3978 | |
3979 | CHECK_RENDERER_MAGIC(renderer, false); |
3980 | CHECK_TEXTURE_MAGIC(texture, false); |
3981 | |
3982 | if (renderer != texture->renderer) { |
3983 | return SDL_SetError("Texture was not created with this renderer" ); |
3984 | } |
3985 | if (!renderer->QueueCopyEx && !renderer->QueueGeometry) { |
3986 | return SDL_SetError("Renderer does not support RenderCopyEx" ); |
3987 | } |
3988 | |
3989 | #if DONT_DRAW_WHILE_HIDDEN |
3990 | // Don't draw while we're hidden |
3991 | if (renderer->hidden) { |
3992 | return true; |
3993 | } |
3994 | #endif |
3995 | |
3996 | real_srcrect.x = 0.0f; |
3997 | real_srcrect.y = 0.0f; |
3998 | real_srcrect.w = (float)texture->w; |
3999 | real_srcrect.h = (float)texture->h; |
4000 | if (srcrect) { |
4001 | if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) { |
4002 | return true; |
4003 | } |
4004 | } |
4005 | |
4006 | GetRenderViewportSize(renderer, &real_dstrect); |
4007 | |
4008 | if (texture->native) { |
4009 | texture = texture->native; |
4010 | } |
4011 | |
4012 | texture->last_command_generation = renderer->render_command_generation; |
4013 | |
4014 | const SDL_RenderViewState *view = renderer->view; |
4015 | const float scale_x = view->current_scale.x; |
4016 | const float scale_y = view->current_scale.y; |
4017 | |
4018 | { |
4019 | float xy[8]; |
4020 | const int xy_stride = 2 * sizeof(float); |
4021 | float uv[8]; |
4022 | const int uv_stride = 2 * sizeof(float); |
4023 | const int num_vertices = 4; |
4024 | const int *indices = rect_index_order; |
4025 | const int num_indices = 6; |
4026 | const int size_indices = 4; |
4027 | |
4028 | float minu = real_srcrect.x / texture->w; |
4029 | float minv = real_srcrect.y / texture->h; |
4030 | float maxu = (real_srcrect.x + real_srcrect.w) / texture->w; |
4031 | float maxv = (real_srcrect.y + real_srcrect.h) / texture->h; |
4032 | |
4033 | uv[0] = minu; |
4034 | uv[1] = minv; |
4035 | uv[2] = maxu; |
4036 | uv[3] = minv; |
4037 | uv[4] = maxu; |
4038 | uv[5] = maxv; |
4039 | uv[6] = minu; |
4040 | uv[7] = maxv; |
4041 | |
4042 | // (minx, miny) |
4043 | if (origin) { |
4044 | xy[0] = origin->x; |
4045 | xy[1] = origin->y; |
4046 | } else { |
4047 | xy[0] = real_dstrect.x; |
4048 | xy[1] = real_dstrect.y; |
4049 | } |
4050 | |
4051 | // (maxx, miny) |
4052 | if (right) { |
4053 | xy[2] = right->x; |
4054 | xy[3] = right->y; |
4055 | } else { |
4056 | xy[2] = real_dstrect.x + real_dstrect.w; |
4057 | xy[3] = real_dstrect.y; |
4058 | } |
4059 | |
4060 | // (minx, maxy) |
4061 | if (down) { |
4062 | xy[6] = down->x; |
4063 | xy[7] = down->y; |
4064 | } else { |
4065 | xy[6] = real_dstrect.x; |
4066 | xy[7] = real_dstrect.y + real_dstrect.h; |
4067 | } |
4068 | |
4069 | // (maxx, maxy) |
4070 | if (origin || right || down) { |
4071 | xy[4] = xy[2] + xy[6] - xy[0]; |
4072 | xy[5] = xy[3] + xy[7] - xy[1]; |
4073 | } else { |
4074 | xy[4] = real_dstrect.x + real_dstrect.w; |
4075 | xy[5] = real_dstrect.y + real_dstrect.h; |
4076 | } |
4077 | |
4078 | result = QueueCmdGeometry( |
4079 | renderer, texture, |
4080 | xy, xy_stride, |
4081 | &texture->color, 0 /* color_stride */, |
4082 | uv, uv_stride, |
4083 | num_vertices, indices, num_indices, size_indices, |
4084 | scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP |
4085 | ); |
4086 | } |
4087 | return result; |
4088 | } |
4089 | |
4090 | bool SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture, |
4091 | const SDL_FRect *srcrect, const SDL_FRect *dstrect, |
4092 | const double angle, const SDL_FPoint *center, const SDL_FlipMode flip) |
4093 | { |
4094 | SDL_FRect real_srcrect; |
4095 | SDL_FPoint real_center; |
4096 | bool result; |
4097 | |
4098 | if (flip == SDL_FLIP_NONE && (int)(angle / 360) == angle / 360) { // fast path when we don't need rotation or flipping |
4099 | return SDL_RenderTexture(renderer, texture, srcrect, dstrect); |
4100 | } |
4101 | |
4102 | CHECK_RENDERER_MAGIC(renderer, false); |
4103 | CHECK_TEXTURE_MAGIC(texture, false); |
4104 | |
4105 | if (renderer != texture->renderer) { |
4106 | return SDL_SetError("Texture was not created with this renderer" ); |
4107 | } |
4108 | if (!renderer->QueueCopyEx && !renderer->QueueGeometry) { |
4109 | return SDL_SetError("Renderer does not support RenderCopyEx" ); |
4110 | } |
4111 | |
4112 | #if DONT_DRAW_WHILE_HIDDEN |
4113 | // Don't draw while we're hidden |
4114 | if (renderer->hidden) { |
4115 | return true; |
4116 | } |
4117 | #endif |
4118 | |
4119 | real_srcrect.x = 0.0f; |
4120 | real_srcrect.y = 0.0f; |
4121 | real_srcrect.w = (float)texture->w; |
4122 | real_srcrect.h = (float)texture->h; |
4123 | if (srcrect) { |
4124 | if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) { |
4125 | return true; |
4126 | } |
4127 | } |
4128 | |
4129 | // We don't intersect the dstrect with the viewport as RenderCopy does because of potential rotation clipping issues... TODO: should we? |
4130 | SDL_FRect full_dstrect; |
4131 | if (!dstrect) { |
4132 | GetRenderViewportSize(renderer, &full_dstrect); |
4133 | dstrect = &full_dstrect; |
4134 | } |
4135 | |
4136 | if (texture->native) { |
4137 | texture = texture->native; |
4138 | } |
4139 | |
4140 | if (center) { |
4141 | real_center = *center; |
4142 | } else { |
4143 | real_center.x = dstrect->w / 2.0f; |
4144 | real_center.y = dstrect->h / 2.0f; |
4145 | } |
4146 | |
4147 | texture->last_command_generation = renderer->render_command_generation; |
4148 | |
4149 | const SDL_RenderViewState *view = renderer->view; |
4150 | const float scale_x = view->current_scale.x; |
4151 | const float scale_y = view->current_scale.y; |
4152 | |
4153 | const bool use_rendergeometry = (!renderer->QueueCopyEx); |
4154 | if (use_rendergeometry) { |
4155 | float xy[8]; |
4156 | const int xy_stride = 2 * sizeof(float); |
4157 | float uv[8]; |
4158 | const int uv_stride = 2 * sizeof(float); |
4159 | const int num_vertices = 4; |
4160 | const int *indices = rect_index_order; |
4161 | const int num_indices = 6; |
4162 | const int size_indices = 4; |
4163 | float minu, minv, maxu, maxv; |
4164 | float minx, miny, maxx, maxy; |
4165 | float centerx, centery; |
4166 | |
4167 | float s_minx, s_miny, s_maxx, s_maxy; |
4168 | float c_minx, c_miny, c_maxx, c_maxy; |
4169 | |
4170 | const float radian_angle = (float)((SDL_PI_D * angle) / 180.0); |
4171 | const float s = SDL_sinf(radian_angle); |
4172 | const float c = SDL_cosf(radian_angle); |
4173 | |
4174 | minu = real_srcrect.x / texture->w; |
4175 | minv = real_srcrect.y / texture->h; |
4176 | maxu = (real_srcrect.x + real_srcrect.w) / texture->w; |
4177 | maxv = (real_srcrect.y + real_srcrect.h) / texture->h; |
4178 | |
4179 | centerx = real_center.x + dstrect->x; |
4180 | centery = real_center.y + dstrect->y; |
4181 | |
4182 | if (flip & SDL_FLIP_HORIZONTAL) { |
4183 | minx = dstrect->x + dstrect->w; |
4184 | maxx = dstrect->x; |
4185 | } else { |
4186 | minx = dstrect->x; |
4187 | maxx = dstrect->x + dstrect->w; |
4188 | } |
4189 | |
4190 | if (flip & SDL_FLIP_VERTICAL) { |
4191 | miny = dstrect->y + dstrect->h; |
4192 | maxy = dstrect->y; |
4193 | } else { |
4194 | miny = dstrect->y; |
4195 | maxy = dstrect->y + dstrect->h; |
4196 | } |
4197 | |
4198 | uv[0] = minu; |
4199 | uv[1] = minv; |
4200 | uv[2] = maxu; |
4201 | uv[3] = minv; |
4202 | uv[4] = maxu; |
4203 | uv[5] = maxv; |
4204 | uv[6] = minu; |
4205 | uv[7] = maxv; |
4206 | |
4207 | /* apply rotation with 2x2 matrix ( c -s ) |
4208 | * ( s c ) */ |
4209 | s_minx = s * (minx - centerx); |
4210 | s_miny = s * (miny - centery); |
4211 | s_maxx = s * (maxx - centerx); |
4212 | s_maxy = s * (maxy - centery); |
4213 | c_minx = c * (minx - centerx); |
4214 | c_miny = c * (miny - centery); |
4215 | c_maxx = c * (maxx - centerx); |
4216 | c_maxy = c * (maxy - centery); |
4217 | |
4218 | // (minx, miny) |
4219 | xy[0] = (c_minx - s_miny) + centerx; |
4220 | xy[1] = (s_minx + c_miny) + centery; |
4221 | // (maxx, miny) |
4222 | xy[2] = (c_maxx - s_miny) + centerx; |
4223 | xy[3] = (s_maxx + c_miny) + centery; |
4224 | // (maxx, maxy) |
4225 | xy[4] = (c_maxx - s_maxy) + centerx; |
4226 | xy[5] = (s_maxx + c_maxy) + centery; |
4227 | // (minx, maxy) |
4228 | xy[6] = (c_minx - s_maxy) + centerx; |
4229 | xy[7] = (s_minx + c_maxy) + centery; |
4230 | |
4231 | result = QueueCmdGeometry(renderer, texture, |
4232 | xy, xy_stride, &texture->color, 0 /* color_stride */, uv, uv_stride, |
4233 | num_vertices, indices, num_indices, size_indices, |
4234 | scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP); |
4235 | } else { |
4236 | result = QueueCmdCopyEx(renderer, texture, &real_srcrect, dstrect, angle, &real_center, flip, scale_x, scale_y); |
4237 | } |
4238 | return result; |
4239 | } |
4240 | |
4241 | static bool SDL_RenderTextureTiled_Wrap(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float scale, const SDL_FRect *dstrect) |
4242 | { |
4243 | float xy[8]; |
4244 | const int xy_stride = 2 * sizeof(float); |
4245 | float uv[8]; |
4246 | const int uv_stride = 2 * sizeof(float); |
4247 | const int num_vertices = 4; |
4248 | const int *indices = rect_index_order; |
4249 | const int num_indices = 6; |
4250 | const int size_indices = 4; |
4251 | float minu, minv, maxu, maxv; |
4252 | float minx, miny, maxx, maxy; |
4253 | |
4254 | minu = 0.0f; |
4255 | minv = 0.0f; |
4256 | maxu = dstrect->w / (srcrect->w * scale); |
4257 | maxv = dstrect->h / (srcrect->h * scale); |
4258 | |
4259 | minx = dstrect->x; |
4260 | miny = dstrect->y; |
4261 | maxx = dstrect->x + dstrect->w; |
4262 | maxy = dstrect->y + dstrect->h; |
4263 | |
4264 | uv[0] = minu; |
4265 | uv[1] = minv; |
4266 | uv[2] = maxu; |
4267 | uv[3] = minv; |
4268 | uv[4] = maxu; |
4269 | uv[5] = maxv; |
4270 | uv[6] = minu; |
4271 | uv[7] = maxv; |
4272 | |
4273 | xy[0] = minx; |
4274 | xy[1] = miny; |
4275 | xy[2] = maxx; |
4276 | xy[3] = miny; |
4277 | xy[4] = maxx; |
4278 | xy[5] = maxy; |
4279 | xy[6] = minx; |
4280 | xy[7] = maxy; |
4281 | |
4282 | const SDL_RenderViewState *view = renderer->view; |
4283 | return QueueCmdGeometry(renderer, texture, |
4284 | xy, xy_stride, &texture->color, 0 /* color_stride */, uv, uv_stride, |
4285 | num_vertices, indices, num_indices, size_indices, |
4286 | view->current_scale.x, view->current_scale.y, SDL_TEXTURE_ADDRESS_WRAP); |
4287 | } |
4288 | |
4289 | static bool SDL_RenderTextureTiled_Iterate(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float scale, const SDL_FRect *dstrect) |
4290 | { |
4291 | float tile_width = srcrect->w * scale; |
4292 | float tile_height = srcrect->h * scale; |
4293 | float float_rows, float_cols; |
4294 | float remaining_w = SDL_modff(dstrect->w / tile_width, &float_cols); |
4295 | float remaining_h = SDL_modff(dstrect->h / tile_height, &float_rows); |
4296 | float remaining_src_w = remaining_w * srcrect->w; |
4297 | float remaining_src_h = remaining_h * srcrect->h; |
4298 | float remaining_dst_w = remaining_w * tile_width; |
4299 | float remaining_dst_h = remaining_h * tile_height; |
4300 | int rows = (int)float_rows; |
4301 | int cols = (int)float_cols; |
4302 | SDL_FRect curr_src, curr_dst; |
4303 | |
4304 | SDL_copyp(&curr_src, srcrect); |
4305 | curr_dst.y = dstrect->y; |
4306 | curr_dst.w = tile_width; |
4307 | curr_dst.h = tile_height; |
4308 | for (int y = 0; y < rows; ++y) { |
4309 | curr_dst.x = dstrect->x; |
4310 | for (int x = 0; x < cols; ++x) { |
4311 | if (!SDL_RenderTextureInternal(renderer, texture, &curr_src, &curr_dst)) { |
4312 | return false; |
4313 | } |
4314 | curr_dst.x += curr_dst.w; |
4315 | } |
4316 | if (remaining_dst_w > 0.0f) { |
4317 | curr_src.w = remaining_src_w; |
4318 | curr_dst.w = remaining_dst_w; |
4319 | if (!SDL_RenderTextureInternal(renderer, texture, &curr_src, &curr_dst)) { |
4320 | return false; |
4321 | } |
4322 | curr_src.w = srcrect->w; |
4323 | curr_dst.w = tile_width; |
4324 | } |
4325 | curr_dst.y += curr_dst.h; |
4326 | } |
4327 | if (remaining_dst_h > 0.0f) { |
4328 | curr_src.h = remaining_src_h; |
4329 | curr_dst.h = remaining_dst_h; |
4330 | curr_dst.x = dstrect->x; |
4331 | for (int x = 0; x < cols; ++x) { |
4332 | if (!SDL_RenderTextureInternal(renderer, texture, &curr_src, &curr_dst)) { |
4333 | return false; |
4334 | } |
4335 | curr_dst.x += curr_dst.w; |
4336 | } |
4337 | if (remaining_dst_w > 0.0f) { |
4338 | curr_src.w = remaining_src_w; |
4339 | curr_dst.w = remaining_dst_w; |
4340 | if (!SDL_RenderTextureInternal(renderer, texture, &curr_src, &curr_dst)) { |
4341 | return false; |
4342 | } |
4343 | } |
4344 | } |
4345 | return true; |
4346 | } |
4347 | |
4348 | bool SDL_RenderTextureTiled(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float scale, const SDL_FRect *dstrect) |
4349 | { |
4350 | SDL_FRect real_srcrect; |
4351 | |
4352 | CHECK_RENDERER_MAGIC(renderer, false); |
4353 | CHECK_TEXTURE_MAGIC(texture, false); |
4354 | |
4355 | if (renderer != texture->renderer) { |
4356 | return SDL_SetError("Texture was not created with this renderer" ); |
4357 | } |
4358 | |
4359 | if (scale <= 0.0f) { |
4360 | return SDL_InvalidParamError("scale" ); |
4361 | } |
4362 | |
4363 | #if DONT_DRAW_WHILE_HIDDEN |
4364 | // Don't draw while we're hidden |
4365 | if (renderer->hidden) { |
4366 | return true; |
4367 | } |
4368 | #endif |
4369 | |
4370 | real_srcrect.x = 0.0f; |
4371 | real_srcrect.y = 0.0f; |
4372 | real_srcrect.w = (float)texture->w; |
4373 | real_srcrect.h = (float)texture->h; |
4374 | if (srcrect) { |
4375 | if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) { |
4376 | return true; |
4377 | } |
4378 | } |
4379 | |
4380 | SDL_FRect full_dstrect; |
4381 | if (!dstrect) { |
4382 | GetRenderViewportSize(renderer, &full_dstrect); |
4383 | dstrect = &full_dstrect; |
4384 | } |
4385 | |
4386 | if (texture->native) { |
4387 | texture = texture->native; |
4388 | } |
4389 | |
4390 | texture->last_command_generation = renderer->render_command_generation; |
4391 | |
4392 | // See if we can use geometry with repeating texture coordinates |
4393 | if (!renderer->software && |
4394 | (!srcrect || |
4395 | (real_srcrect.x == 0.0f && real_srcrect.y == 0.0f && |
4396 | real_srcrect.w == (float)texture->w && real_srcrect.h == (float)texture->h))) { |
4397 | return SDL_RenderTextureTiled_Wrap(renderer, texture, &real_srcrect, scale, dstrect); |
4398 | } else { |
4399 | return SDL_RenderTextureTiled_Iterate(renderer, texture, &real_srcrect, scale, dstrect); |
4400 | } |
4401 | } |
4402 | |
4403 | bool SDL_RenderTexture9Grid(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float left_width, float right_width, float top_height, float bottom_height, float scale, const SDL_FRect *dstrect) |
4404 | { |
4405 | SDL_FRect full_src, full_dst; |
4406 | SDL_FRect curr_src, curr_dst; |
4407 | float dst_left_width; |
4408 | float dst_right_width; |
4409 | float dst_top_height; |
4410 | float dst_bottom_height; |
4411 | |
4412 | CHECK_RENDERER_MAGIC(renderer, false); |
4413 | CHECK_TEXTURE_MAGIC(texture, false); |
4414 | |
4415 | if (renderer != texture->renderer) { |
4416 | return SDL_SetError("Texture was not created with this renderer" ); |
4417 | } |
4418 | |
4419 | if (!srcrect) { |
4420 | full_src.x = 0; |
4421 | full_src.y = 0; |
4422 | full_src.w = (float)texture->w; |
4423 | full_src.h = (float)texture->h; |
4424 | srcrect = &full_src; |
4425 | } |
4426 | |
4427 | if (!dstrect) { |
4428 | GetRenderViewportSize(renderer, &full_dst); |
4429 | dstrect = &full_dst; |
4430 | } |
4431 | |
4432 | if (scale <= 0.0f || scale == 1.0f) { |
4433 | dst_left_width = SDL_ceilf(left_width); |
4434 | dst_right_width = SDL_ceilf(right_width); |
4435 | dst_top_height = SDL_ceilf(top_height); |
4436 | dst_bottom_height = SDL_ceilf(bottom_height); |
4437 | } else { |
4438 | dst_left_width = SDL_ceilf(left_width * scale); |
4439 | dst_right_width = SDL_ceilf(right_width * scale); |
4440 | dst_top_height = SDL_ceilf(top_height * scale); |
4441 | dst_bottom_height = SDL_ceilf(bottom_height * scale); |
4442 | } |
4443 | |
4444 | // Center |
4445 | curr_src.x = srcrect->x + left_width; |
4446 | curr_src.y = srcrect->y + top_height; |
4447 | curr_src.w = srcrect->w - left_width - right_width; |
4448 | curr_src.h = srcrect->h - top_height - bottom_height; |
4449 | curr_dst.x = dstrect->x + dst_left_width; |
4450 | curr_dst.y = dstrect->y + dst_top_height; |
4451 | curr_dst.w = dstrect->w - dst_left_width - dst_right_width; |
4452 | curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; |
4453 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4454 | return false; |
4455 | } |
4456 | |
4457 | // Upper-left corner |
4458 | curr_src.x = srcrect->x; |
4459 | curr_src.y = srcrect->y; |
4460 | curr_src.w = left_width; |
4461 | curr_src.h = top_height; |
4462 | curr_dst.x = dstrect->x; |
4463 | curr_dst.y = dstrect->y; |
4464 | curr_dst.w = dst_left_width; |
4465 | curr_dst.h = dst_top_height; |
4466 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4467 | return false; |
4468 | } |
4469 | |
4470 | // Upper-right corner |
4471 | curr_src.x = srcrect->x + srcrect->w - right_width; |
4472 | curr_src.w = right_width; |
4473 | curr_dst.x = dstrect->x + dstrect->w - dst_right_width; |
4474 | curr_dst.w = dst_right_width; |
4475 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4476 | return false; |
4477 | } |
4478 | |
4479 | // Lower-right corner |
4480 | curr_src.y = srcrect->y + srcrect->h - bottom_height; |
4481 | curr_src.h = bottom_height; |
4482 | curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height; |
4483 | curr_dst.h = dst_bottom_height; |
4484 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4485 | return false; |
4486 | } |
4487 | |
4488 | // Lower-left corner |
4489 | curr_src.x = srcrect->x; |
4490 | curr_src.w = left_width; |
4491 | curr_dst.x = dstrect->x; |
4492 | curr_dst.w = dst_left_width; |
4493 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4494 | return false; |
4495 | } |
4496 | |
4497 | // Left |
4498 | curr_src.y = srcrect->y + top_height; |
4499 | curr_src.h = srcrect->h - top_height - bottom_height; |
4500 | curr_dst.y = dstrect->y + dst_top_height; |
4501 | curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; |
4502 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4503 | return false; |
4504 | } |
4505 | |
4506 | // Right |
4507 | curr_src.x = srcrect->x + srcrect->w - right_width; |
4508 | curr_src.w = right_width; |
4509 | curr_dst.x = dstrect->x + dstrect->w - dst_right_width; |
4510 | curr_dst.w = dst_right_width; |
4511 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4512 | return false; |
4513 | } |
4514 | |
4515 | // Top |
4516 | curr_src.x = srcrect->x + left_width; |
4517 | curr_src.y = srcrect->y; |
4518 | curr_src.w = srcrect->w - left_width - right_width; |
4519 | curr_src.h = top_height; |
4520 | curr_dst.x = dstrect->x + dst_left_width; |
4521 | curr_dst.y = dstrect->y; |
4522 | curr_dst.w = dstrect->w - dst_left_width - dst_right_width; |
4523 | curr_dst.h = dst_top_height; |
4524 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4525 | return false; |
4526 | } |
4527 | |
4528 | // Bottom |
4529 | curr_src.y = srcrect->y + srcrect->h - bottom_height; |
4530 | curr_src.h = bottom_height; |
4531 | curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height; |
4532 | curr_dst.h = dst_bottom_height; |
4533 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4534 | return false; |
4535 | } |
4536 | |
4537 | return true; |
4538 | } |
4539 | |
4540 | bool SDL_RenderTexture9GridTiled(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float left_width, float right_width, float top_height, float bottom_height, float scale, const SDL_FRect *dstrect, float tileScale) |
4541 | { |
4542 | SDL_FRect full_src, full_dst; |
4543 | SDL_FRect curr_src, curr_dst; |
4544 | float dst_left_width; |
4545 | float dst_right_width; |
4546 | float dst_top_height; |
4547 | float dst_bottom_height; |
4548 | |
4549 | CHECK_RENDERER_MAGIC(renderer, false); |
4550 | CHECK_TEXTURE_MAGIC(texture, false); |
4551 | |
4552 | if (renderer != texture->renderer) { |
4553 | return SDL_SetError("Texture was not created with this renderer" ); |
4554 | } |
4555 | |
4556 | if (!srcrect) { |
4557 | full_src.x = 0; |
4558 | full_src.y = 0; |
4559 | full_src.w = (float)texture->w; |
4560 | full_src.h = (float)texture->h; |
4561 | srcrect = &full_src; |
4562 | } |
4563 | |
4564 | if (!dstrect) { |
4565 | GetRenderViewportSize(renderer, &full_dst); |
4566 | dstrect = &full_dst; |
4567 | } |
4568 | |
4569 | if (scale <= 0.0f || scale == 1.0f) { |
4570 | dst_left_width = SDL_ceilf(left_width); |
4571 | dst_right_width = SDL_ceilf(right_width); |
4572 | dst_top_height = SDL_ceilf(top_height); |
4573 | dst_bottom_height = SDL_ceilf(bottom_height); |
4574 | } else { |
4575 | dst_left_width = SDL_ceilf(left_width * scale); |
4576 | dst_right_width = SDL_ceilf(right_width * scale); |
4577 | dst_top_height = SDL_ceilf(top_height * scale); |
4578 | dst_bottom_height = SDL_ceilf(bottom_height * scale); |
4579 | } |
4580 | |
4581 | // Center |
4582 | curr_src.x = srcrect->x + left_width; |
4583 | curr_src.y = srcrect->y + top_height; |
4584 | curr_src.w = srcrect->w - left_width - right_width; |
4585 | curr_src.h = srcrect->h - top_height - bottom_height; |
4586 | curr_dst.x = dstrect->x + dst_left_width; |
4587 | curr_dst.y = dstrect->y + dst_top_height; |
4588 | curr_dst.w = dstrect->w - dst_left_width - dst_right_width; |
4589 | curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; |
4590 | if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) { |
4591 | return false; |
4592 | } |
4593 | |
4594 | // Upper-left corner |
4595 | curr_src.x = srcrect->x; |
4596 | curr_src.y = srcrect->y; |
4597 | curr_src.w = left_width; |
4598 | curr_src.h = top_height; |
4599 | curr_dst.x = dstrect->x; |
4600 | curr_dst.y = dstrect->y; |
4601 | curr_dst.w = dst_left_width; |
4602 | curr_dst.h = dst_top_height; |
4603 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4604 | return false; |
4605 | } |
4606 | |
4607 | // Upper-right corner |
4608 | curr_src.x = srcrect->x + srcrect->w - right_width; |
4609 | curr_src.w = right_width; |
4610 | curr_dst.x = dstrect->x + dstrect->w - dst_right_width; |
4611 | curr_dst.w = dst_right_width; |
4612 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4613 | return false; |
4614 | } |
4615 | |
4616 | // Lower-right corner |
4617 | curr_src.y = srcrect->y + srcrect->h - bottom_height; |
4618 | curr_src.h = bottom_height; |
4619 | curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height; |
4620 | curr_dst.h = dst_bottom_height; |
4621 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4622 | return false; |
4623 | } |
4624 | |
4625 | // Lower-left corner |
4626 | curr_src.x = srcrect->x; |
4627 | curr_src.w = left_width; |
4628 | curr_dst.x = dstrect->x; |
4629 | curr_dst.w = dst_left_width; |
4630 | if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { |
4631 | return false; |
4632 | } |
4633 | |
4634 | // Left |
4635 | curr_src.y = srcrect->y + top_height; |
4636 | curr_src.h = srcrect->h - top_height - bottom_height; |
4637 | curr_dst.y = dstrect->y + dst_top_height; |
4638 | curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; |
4639 | if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) { |
4640 | return false; |
4641 | } |
4642 | |
4643 | // Right |
4644 | curr_src.x = srcrect->x + srcrect->w - right_width; |
4645 | curr_src.w = right_width; |
4646 | curr_dst.x = dstrect->x + dstrect->w - dst_right_width; |
4647 | curr_dst.w = dst_right_width; |
4648 | if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) { |
4649 | return false; |
4650 | } |
4651 | |
4652 | // Top |
4653 | curr_src.x = srcrect->x + left_width; |
4654 | curr_src.y = srcrect->y; |
4655 | curr_src.w = srcrect->w - left_width - right_width; |
4656 | curr_src.h = top_height; |
4657 | curr_dst.x = dstrect->x + dst_left_width; |
4658 | curr_dst.y = dstrect->y; |
4659 | curr_dst.w = dstrect->w - dst_left_width - dst_right_width; |
4660 | curr_dst.h = dst_top_height; |
4661 | if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) { |
4662 | return false; |
4663 | } |
4664 | |
4665 | // Bottom |
4666 | curr_src.y = srcrect->y + srcrect->h - bottom_height; |
4667 | curr_src.h = bottom_height; |
4668 | curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height; |
4669 | curr_dst.h = dst_bottom_height; |
4670 | if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) { |
4671 | return false; |
4672 | } |
4673 | |
4674 | return true; |
4675 | } |
4676 | |
4677 | bool SDL_RenderGeometry(SDL_Renderer *renderer, |
4678 | SDL_Texture *texture, |
4679 | const SDL_Vertex *vertices, int num_vertices, |
4680 | const int *indices, int num_indices) |
4681 | { |
4682 | if (vertices) { |
4683 | const float *xy = &vertices->position.x; |
4684 | int xy_stride = sizeof(SDL_Vertex); |
4685 | const SDL_FColor *color = &vertices->color; |
4686 | int color_stride = sizeof(SDL_Vertex); |
4687 | const float *uv = &vertices->tex_coord.x; |
4688 | int uv_stride = sizeof(SDL_Vertex); |
4689 | int size_indices = 4; |
4690 | return SDL_RenderGeometryRaw(renderer, texture, xy, xy_stride, color, color_stride, uv, uv_stride, num_vertices, indices, num_indices, size_indices); |
4691 | } else { |
4692 | return SDL_InvalidParamError("vertices" ); |
4693 | } |
4694 | } |
4695 | |
4696 | #ifdef SDL_VIDEO_RENDER_SW |
4697 | static int remap_one_indice( |
4698 | int prev, |
4699 | int k, |
4700 | SDL_Texture *texture, |
4701 | const float *xy, int xy_stride, |
4702 | const SDL_FColor *color, int color_stride, |
4703 | const float *uv, int uv_stride) |
4704 | { |
4705 | const float *xy0_, *xy1_, *uv0_, *uv1_; |
4706 | const SDL_FColor *col0_, *col1_; |
4707 | xy0_ = (const float *)((const char *)xy + prev * xy_stride); |
4708 | xy1_ = (const float *)((const char *)xy + k * xy_stride); |
4709 | if (xy0_[0] != xy1_[0]) { |
4710 | return k; |
4711 | } |
4712 | if (xy0_[1] != xy1_[1]) { |
4713 | return k; |
4714 | } |
4715 | if (texture) { |
4716 | uv0_ = (const float *)((const char *)uv + prev * uv_stride); |
4717 | uv1_ = (const float *)((const char *)uv + k * uv_stride); |
4718 | if (uv0_[0] != uv1_[0]) { |
4719 | return k; |
4720 | } |
4721 | if (uv0_[1] != uv1_[1]) { |
4722 | return k; |
4723 | } |
4724 | } |
4725 | col0_ = (const SDL_FColor *)((const char *)color + prev * color_stride); |
4726 | col1_ = (const SDL_FColor *)((const char *)color + k * color_stride); |
4727 | |
4728 | if (SDL_memcmp(col0_, col1_, sizeof(*col0_)) != 0) { |
4729 | return k; |
4730 | } |
4731 | |
4732 | return prev; |
4733 | } |
4734 | |
4735 | static int remap_indices( |
4736 | int prev[3], |
4737 | int k, |
4738 | SDL_Texture *texture, |
4739 | const float *xy, int xy_stride, |
4740 | const SDL_FColor *color, int color_stride, |
4741 | const float *uv, int uv_stride) |
4742 | { |
4743 | int i; |
4744 | if (prev[0] == -1) { |
4745 | return k; |
4746 | } |
4747 | |
4748 | for (i = 0; i < 3; i++) { |
4749 | int new_k = remap_one_indice(prev[i], k, texture, xy, xy_stride, color, color_stride, uv, uv_stride); |
4750 | if (new_k != k) { |
4751 | return new_k; |
4752 | } |
4753 | } |
4754 | return k; |
4755 | } |
4756 | |
4757 | #define DEBUG_SW_RENDER_GEOMETRY 0 |
4758 | // For the software renderer, try to reinterpret triangles as SDL_Rect |
4759 | static bool SDLCALL SDL_SW_RenderGeometryRaw(SDL_Renderer *renderer, |
4760 | SDL_Texture *texture, |
4761 | const float *xy, int xy_stride, |
4762 | const SDL_FColor *color, int color_stride, |
4763 | const float *uv, int uv_stride, |
4764 | int num_vertices, |
4765 | const void *indices, int num_indices, int size_indices) |
4766 | { |
4767 | int i; |
4768 | bool result = true; |
4769 | int count = indices ? num_indices : num_vertices; |
4770 | int prev[3]; // Previous triangle vertex indices |
4771 | float texw = 0.0f, texh = 0.0f; |
4772 | SDL_BlendMode blendMode = SDL_BLENDMODE_NONE; |
4773 | float r = 0, g = 0, b = 0, a = 0; |
4774 | const SDL_RenderViewState *view = renderer->view; |
4775 | const float scale_x = view->current_scale.x; |
4776 | const float scale_y = view->current_scale.y; |
4777 | |
4778 | // Save |
4779 | SDL_GetRenderDrawBlendMode(renderer, &blendMode); |
4780 | SDL_GetRenderDrawColorFloat(renderer, &r, &g, &b, &a); |
4781 | |
4782 | if (texture) { |
4783 | SDL_GetTextureSize(texture, &texw, &texh); |
4784 | } |
4785 | |
4786 | prev[0] = -1; |
4787 | prev[1] = -1; |
4788 | prev[2] = -1; |
4789 | size_indices = indices ? size_indices : 0; |
4790 | |
4791 | for (i = 0; i < count; i += 3) { |
4792 | int k0, k1, k2; // Current triangle indices |
4793 | int is_quad = 1; |
4794 | #if DEBUG_SW_RENDER_GEOMETRY |
4795 | int is_uniform = 1; |
4796 | int is_rectangle = 1; |
4797 | #endif |
4798 | int A = -1; // Top left vertex |
4799 | int B = -1; // Bottom right vertex |
4800 | int C = -1; // Third vertex of current triangle |
4801 | int C2 = -1; // Last, vertex of previous triangle |
4802 | |
4803 | if (size_indices == 4) { |
4804 | k0 = ((const Uint32 *)indices)[i]; |
4805 | k1 = ((const Uint32 *)indices)[i + 1]; |
4806 | k2 = ((const Uint32 *)indices)[i + 2]; |
4807 | } else if (size_indices == 2) { |
4808 | k0 = ((const Uint16 *)indices)[i]; |
4809 | k1 = ((const Uint16 *)indices)[i + 1]; |
4810 | k2 = ((const Uint16 *)indices)[i + 2]; |
4811 | } else if (size_indices == 1) { |
4812 | k0 = ((const Uint8 *)indices)[i]; |
4813 | k1 = ((const Uint8 *)indices)[i + 1]; |
4814 | k2 = ((const Uint8 *)indices)[i + 2]; |
4815 | } else { |
4816 | /* Vertices were not provided by indices. Maybe some are duplicated. |
4817 | * We try to indentificate the duplicates by comparing with the previous three vertices */ |
4818 | k0 = remap_indices(prev, i, texture, xy, xy_stride, color, color_stride, uv, uv_stride); |
4819 | k1 = remap_indices(prev, i + 1, texture, xy, xy_stride, color, color_stride, uv, uv_stride); |
4820 | k2 = remap_indices(prev, i + 2, texture, xy, xy_stride, color, color_stride, uv, uv_stride); |
4821 | } |
4822 | |
4823 | if (prev[0] == -1) { |
4824 | prev[0] = k0; |
4825 | prev[1] = k1; |
4826 | prev[2] = k2; |
4827 | continue; |
4828 | } |
4829 | |
4830 | /* Two triangles forming a quadialateral, |
4831 | * prev and current triangles must have exactly 2 common vertices */ |
4832 | { |
4833 | int cnt = 0, j = 3; |
4834 | while (j--) { |
4835 | int p = prev[j]; |
4836 | if (p == k0 || p == k1 || p == k2) { |
4837 | cnt++; |
4838 | } |
4839 | } |
4840 | is_quad = (cnt == 2); |
4841 | } |
4842 | |
4843 | // Identify vertices |
4844 | if (is_quad) { |
4845 | const float *xy0_, *xy1_, *xy2_; |
4846 | float x0, x1, x2; |
4847 | float y0, y1, y2; |
4848 | xy0_ = (const float *)((const char *)xy + k0 * xy_stride); |
4849 | xy1_ = (const float *)((const char *)xy + k1 * xy_stride); |
4850 | xy2_ = (const float *)((const char *)xy + k2 * xy_stride); |
4851 | x0 = xy0_[0]; |
4852 | y0 = xy0_[1]; |
4853 | x1 = xy1_[0]; |
4854 | y1 = xy1_[1]; |
4855 | x2 = xy2_[0]; |
4856 | y2 = xy2_[1]; |
4857 | |
4858 | // Find top-left |
4859 | if (x0 <= x1 && y0 <= y1) { |
4860 | if (x0 <= x2 && y0 <= y2) { |
4861 | A = k0; |
4862 | } else { |
4863 | A = k2; |
4864 | } |
4865 | } else { |
4866 | if (x1 <= x2 && y1 <= y2) { |
4867 | A = k1; |
4868 | } else { |
4869 | A = k2; |
4870 | } |
4871 | } |
4872 | |
4873 | // Find bottom-right |
4874 | if (x0 >= x1 && y0 >= y1) { |
4875 | if (x0 >= x2 && y0 >= y2) { |
4876 | B = k0; |
4877 | } else { |
4878 | B = k2; |
4879 | } |
4880 | } else { |
4881 | if (x1 >= x2 && y1 >= y2) { |
4882 | B = k1; |
4883 | } else { |
4884 | B = k2; |
4885 | } |
4886 | } |
4887 | |
4888 | // Find C |
4889 | if (k0 != A && k0 != B) { |
4890 | C = k0; |
4891 | } else if (k1 != A && k1 != B) { |
4892 | C = k1; |
4893 | } else { |
4894 | C = k2; |
4895 | } |
4896 | |
4897 | // Find C2 |
4898 | if (prev[0] != A && prev[0] != B) { |
4899 | C2 = prev[0]; |
4900 | } else if (prev[1] != A && prev[1] != B) { |
4901 | C2 = prev[1]; |
4902 | } else { |
4903 | C2 = prev[2]; |
4904 | } |
4905 | |
4906 | xy0_ = (const float *)((const char *)xy + A * xy_stride); |
4907 | xy1_ = (const float *)((const char *)xy + B * xy_stride); |
4908 | xy2_ = (const float *)((const char *)xy + C * xy_stride); |
4909 | x0 = xy0_[0]; |
4910 | y0 = xy0_[1]; |
4911 | x1 = xy1_[0]; |
4912 | y1 = xy1_[1]; |
4913 | x2 = xy2_[0]; |
4914 | y2 = xy2_[1]; |
4915 | |
4916 | // Check if triangle A B C is rectangle |
4917 | if ((x0 == x2 && y1 == y2) || (y0 == y2 && x1 == x2)) { |
4918 | // ok |
4919 | } else { |
4920 | is_quad = 0; |
4921 | #if DEBUG_SW_RENDER_GEOMETRY |
4922 | is_rectangle = 0; |
4923 | #endif |
4924 | } |
4925 | |
4926 | xy2_ = (const float *)((const char *)xy + C2 * xy_stride); |
4927 | x2 = xy2_[0]; |
4928 | y2 = xy2_[1]; |
4929 | |
4930 | // Check if triangle A B C2 is rectangle |
4931 | if ((x0 == x2 && y1 == y2) || (y0 == y2 && x1 == x2)) { |
4932 | // ok |
4933 | } else { |
4934 | is_quad = 0; |
4935 | #if DEBUG_SW_RENDER_GEOMETRY |
4936 | is_rectangle = 0; |
4937 | #endif |
4938 | } |
4939 | } |
4940 | |
4941 | // Check if uniformly colored |
4942 | if (is_quad) { |
4943 | const SDL_FColor *col0_ = (const SDL_FColor *)((const char *)color + A * color_stride); |
4944 | const SDL_FColor *col1_ = (const SDL_FColor *)((const char *)color + B * color_stride); |
4945 | const SDL_FColor *col2_ = (const SDL_FColor *)((const char *)color + C * color_stride); |
4946 | const SDL_FColor *col3_ = (const SDL_FColor *)((const char *)color + C2 * color_stride); |
4947 | if (SDL_memcmp(col0_, col1_, sizeof(*col0_)) == 0 && |
4948 | SDL_memcmp(col0_, col2_, sizeof(*col0_)) == 0 && |
4949 | SDL_memcmp(col0_, col3_, sizeof(*col0_)) == 0) { |
4950 | // ok |
4951 | } else { |
4952 | is_quad = 0; |
4953 | #if DEBUG_SW_RENDER_GEOMETRY |
4954 | is_uniform = 0; |
4955 | #endif |
4956 | } |
4957 | } |
4958 | |
4959 | // Start rendering rect |
4960 | if (is_quad) { |
4961 | SDL_FRect s; |
4962 | SDL_FRect d; |
4963 | const float *xy0_, *xy1_, *uv0_, *uv1_; |
4964 | const SDL_FColor *col0_ = (const SDL_FColor *)((const char *)color + k0 * color_stride); |
4965 | |
4966 | xy0_ = (const float *)((const char *)xy + A * xy_stride); |
4967 | xy1_ = (const float *)((const char *)xy + B * xy_stride); |
4968 | |
4969 | if (texture) { |
4970 | uv0_ = (const float *)((const char *)uv + A * uv_stride); |
4971 | uv1_ = (const float *)((const char *)uv + B * uv_stride); |
4972 | s.x = uv0_[0] * texw; |
4973 | s.y = uv0_[1] * texh; |
4974 | s.w = uv1_[0] * texw - s.x; |
4975 | s.h = uv1_[1] * texh - s.y; |
4976 | } else { |
4977 | s.x = s.y = s.w = s.h = 0; |
4978 | } |
4979 | |
4980 | d.x = xy0_[0]; |
4981 | d.y = xy0_[1]; |
4982 | d.w = xy1_[0] - d.x; |
4983 | d.h = xy1_[1] - d.y; |
4984 | |
4985 | // Rect + texture |
4986 | if (texture && s.w != 0 && s.h != 0) { |
4987 | SDL_SetTextureAlphaModFloat(texture, col0_->a); |
4988 | SDL_SetTextureColorModFloat(texture, col0_->r, col0_->g, col0_->b); |
4989 | if (s.w > 0 && s.h > 0) { |
4990 | SDL_RenderTexture(renderer, texture, &s, &d); |
4991 | } else { |
4992 | int flags = 0; |
4993 | if (s.w < 0) { |
4994 | flags |= SDL_FLIP_HORIZONTAL; |
4995 | s.w *= -1; |
4996 | s.x -= s.w; |
4997 | } |
4998 | if (s.h < 0) { |
4999 | flags |= SDL_FLIP_VERTICAL; |
5000 | s.h *= -1; |
5001 | s.y -= s.h; |
5002 | } |
5003 | SDL_RenderTextureRotated(renderer, texture, &s, &d, 0, NULL, (SDL_FlipMode)flags); |
5004 | } |
5005 | |
5006 | #if DEBUG_SW_RENDER_GEOMETRY |
5007 | SDL_Log("Rect-COPY: RGB %f %f %f - Alpha:%f - texture=%p: src=(%d,%d, %d x %d) dst (%f, %f, %f x %f)" , col0_->r, col0_->g, col0_->b, col0_->a, |
5008 | (void *)texture, s.x, s.y, s.w, s.h, d.x, d.y, d.w, d.h); |
5009 | #endif |
5010 | } else if (d.w != 0.0f && d.h != 0.0f) { // Rect, no texture |
5011 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); |
5012 | SDL_SetRenderDrawColorFloat(renderer, col0_->r, col0_->g, col0_->b, col0_->a); |
5013 | SDL_RenderFillRect(renderer, &d); |
5014 | #if DEBUG_SW_RENDER_GEOMETRY |
5015 | SDL_Log("Rect-FILL: RGB %f %f %f - Alpha:%f - texture=%p: dst (%f, %f, %f x %f)" , col0_->r, col0_->g, col0_->b, col0_->a, |
5016 | (void *)texture, d.x, d.y, d.w, d.h); |
5017 | } else { |
5018 | SDL_Log("Rect-DISMISS: RGB %f %f %f - Alpha:%f - texture=%p: src=(%d,%d, %d x %d) dst (%f, %f, %f x %f)" , col0_->r, col0_->g, col0_->b, col0_->a, |
5019 | (void *)texture, s.x, s.y, s.w, s.h, d.x, d.y, d.w, d.h); |
5020 | #endif |
5021 | } |
5022 | |
5023 | prev[0] = -1; |
5024 | } else { |
5025 | // Render triangles |
5026 | if (prev[0] != -1) { |
5027 | #if DEBUG_SW_RENDER_GEOMETRY |
5028 | SDL_Log("Triangle %d %d %d - is_uniform:%d is_rectangle:%d" , prev[0], prev[1], prev[2], is_uniform, is_rectangle); |
5029 | #endif |
5030 | result = QueueCmdGeometry(renderer, texture, |
5031 | xy, xy_stride, color, color_stride, uv, uv_stride, |
5032 | num_vertices, prev, 3, 4, |
5033 | scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP); |
5034 | if (!result) { |
5035 | goto end; |
5036 | } |
5037 | } |
5038 | |
5039 | prev[0] = k0; |
5040 | prev[1] = k1; |
5041 | prev[2] = k2; |
5042 | } |
5043 | } // End for (), next triangle |
5044 | |
5045 | if (prev[0] != -1) { |
5046 | // flush the last triangle |
5047 | #if DEBUG_SW_RENDER_GEOMETRY |
5048 | SDL_Log("Last triangle %d %d %d" , prev[0], prev[1], prev[2]); |
5049 | #endif |
5050 | result = QueueCmdGeometry(renderer, texture, |
5051 | xy, xy_stride, color, color_stride, uv, uv_stride, |
5052 | num_vertices, prev, 3, 4, |
5053 | scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP); |
5054 | if (!result) { |
5055 | goto end; |
5056 | } |
5057 | } |
5058 | |
5059 | end: |
5060 | // Restore |
5061 | SDL_SetRenderDrawBlendMode(renderer, blendMode); |
5062 | SDL_SetRenderDrawColorFloat(renderer, r, g, b, a); |
5063 | |
5064 | return result; |
5065 | } |
5066 | #endif // SDL_VIDEO_RENDER_SW |
5067 | |
5068 | bool SDL_RenderGeometryRaw(SDL_Renderer *renderer, |
5069 | SDL_Texture *texture, |
5070 | const float *xy, int xy_stride, |
5071 | const SDL_FColor *color, int color_stride, |
5072 | const float *uv, int uv_stride, |
5073 | int num_vertices, |
5074 | const void *indices, int num_indices, int size_indices) |
5075 | { |
5076 | int i; |
5077 | int count = indices ? num_indices : num_vertices; |
5078 | SDL_TextureAddressMode texture_address_mode; |
5079 | |
5080 | CHECK_RENDERER_MAGIC(renderer, false); |
5081 | |
5082 | if (!renderer->QueueGeometry) { |
5083 | return SDL_Unsupported(); |
5084 | } |
5085 | |
5086 | if (texture) { |
5087 | CHECK_TEXTURE_MAGIC(texture, false); |
5088 | |
5089 | if (renderer != texture->renderer) { |
5090 | return SDL_SetError("Texture was not created with this renderer" ); |
5091 | } |
5092 | } |
5093 | |
5094 | if (!xy) { |
5095 | return SDL_InvalidParamError("xy" ); |
5096 | } |
5097 | |
5098 | if (!color) { |
5099 | return SDL_InvalidParamError("color" ); |
5100 | } |
5101 | |
5102 | if (texture && !uv) { |
5103 | return SDL_InvalidParamError("uv" ); |
5104 | } |
5105 | |
5106 | if (count % 3 != 0) { |
5107 | return SDL_InvalidParamError(indices ? "num_indices" : "num_vertices" ); |
5108 | } |
5109 | |
5110 | if (indices) { |
5111 | if (size_indices != 1 && size_indices != 2 && size_indices != 4) { |
5112 | return SDL_InvalidParamError("size_indices" ); |
5113 | } |
5114 | } else { |
5115 | size_indices = 0; |
5116 | } |
5117 | |
5118 | #if DONT_DRAW_WHILE_HIDDEN |
5119 | // Don't draw while we're hidden |
5120 | if (renderer->hidden) { |
5121 | return true; |
5122 | } |
5123 | #endif |
5124 | |
5125 | if (num_vertices < 3) { |
5126 | return true; |
5127 | } |
5128 | |
5129 | if (texture && texture->native) { |
5130 | texture = texture->native; |
5131 | } |
5132 | |
5133 | texture_address_mode = renderer->texture_address_mode; |
5134 | if (texture_address_mode == SDL_TEXTURE_ADDRESS_AUTO && texture) { |
5135 | texture_address_mode = SDL_TEXTURE_ADDRESS_CLAMP; |
5136 | for (i = 0; i < num_vertices; ++i) { |
5137 | const float *uv_ = (const float *)((const char *)uv + i * uv_stride); |
5138 | float u = uv_[0]; |
5139 | float v = uv_[1]; |
5140 | if (u < 0.0f || v < 0.0f || u > 1.0f || v > 1.0f) { |
5141 | texture_address_mode = SDL_TEXTURE_ADDRESS_WRAP; |
5142 | break; |
5143 | } |
5144 | } |
5145 | } |
5146 | |
5147 | if (indices) { |
5148 | for (i = 0; i < num_indices; ++i) { |
5149 | int j; |
5150 | if (size_indices == 4) { |
5151 | j = ((const Uint32 *)indices)[i]; |
5152 | } else if (size_indices == 2) { |
5153 | j = ((const Uint16 *)indices)[i]; |
5154 | } else { |
5155 | j = ((const Uint8 *)indices)[i]; |
5156 | } |
5157 | if (j < 0 || j >= num_vertices) { |
5158 | return SDL_SetError("Values of 'indices' out of bounds" ); |
5159 | } |
5160 | } |
5161 | } |
5162 | |
5163 | if (texture) { |
5164 | texture->last_command_generation = renderer->render_command_generation; |
5165 | } |
5166 | |
5167 | // For the software renderer, try to reinterpret triangles as SDL_Rect |
5168 | #ifdef SDL_VIDEO_RENDER_SW |
5169 | if (renderer->software && texture_address_mode == SDL_TEXTURE_ADDRESS_CLAMP) { |
5170 | return SDL_SW_RenderGeometryRaw(renderer, texture, |
5171 | xy, xy_stride, color, color_stride, uv, uv_stride, num_vertices, |
5172 | indices, num_indices, size_indices); |
5173 | } |
5174 | #endif |
5175 | |
5176 | const SDL_RenderViewState *view = renderer->view; |
5177 | return QueueCmdGeometry(renderer, texture, |
5178 | xy, xy_stride, color, color_stride, uv, uv_stride, |
5179 | num_vertices, indices, num_indices, size_indices, |
5180 | view->current_scale.x, view->current_scale.y, |
5181 | texture_address_mode); |
5182 | } |
5183 | |
5184 | SDL_Surface *SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect) |
5185 | { |
5186 | CHECK_RENDERER_MAGIC(renderer, NULL); |
5187 | |
5188 | if (!renderer->RenderReadPixels) { |
5189 | SDL_Unsupported(); |
5190 | return NULL; |
5191 | } |
5192 | |
5193 | FlushRenderCommands(renderer); // we need to render before we read the results. |
5194 | |
5195 | SDL_Rect real_rect = renderer->view->pixel_viewport; |
5196 | |
5197 | if (rect) { |
5198 | if (!SDL_GetRectIntersection(rect, &real_rect, &real_rect)) { |
5199 | SDL_SetError("Can't read outside the current viewport" ); |
5200 | return NULL; |
5201 | } |
5202 | } |
5203 | |
5204 | SDL_Surface *surface = renderer->RenderReadPixels(renderer, &real_rect); |
5205 | if (surface) { |
5206 | SDL_PropertiesID props = SDL_GetSurfaceProperties(surface); |
5207 | |
5208 | if (renderer->target) { |
5209 | SDL_Texture *target = renderer->target; |
5210 | SDL_Texture *parent = SDL_GetPointerProperty(SDL_GetTextureProperties(target), SDL_PROP_TEXTURE_PARENT_POINTER, NULL); |
5211 | SDL_PixelFormat expected_format = (parent ? parent->format : target->format); |
5212 | |
5213 | SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, target->SDR_white_point); |
5214 | SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, target->HDR_headroom); |
5215 | |
5216 | // Set the expected surface format |
5217 | if ((surface->format == SDL_PIXELFORMAT_ARGB8888 && expected_format == SDL_PIXELFORMAT_XRGB8888) || |
5218 | (surface->format == SDL_PIXELFORMAT_RGBA8888 && expected_format == SDL_PIXELFORMAT_RGBX8888) || |
5219 | (surface->format == SDL_PIXELFORMAT_ABGR8888 && expected_format == SDL_PIXELFORMAT_XBGR8888) || |
5220 | (surface->format == SDL_PIXELFORMAT_BGRA8888 && expected_format == SDL_PIXELFORMAT_BGRX8888)) { |
5221 | surface->format = expected_format; |
5222 | surface->fmt = SDL_GetPixelFormatDetails(expected_format); |
5223 | } |
5224 | } else { |
5225 | SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point); |
5226 | SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, renderer->HDR_headroom); |
5227 | } |
5228 | } |
5229 | return surface; |
5230 | } |
5231 | |
5232 | static void SDL_RenderApplyWindowShape(SDL_Renderer *renderer) |
5233 | { |
5234 | SDL_Surface *shape = (SDL_Surface *)SDL_GetPointerProperty(SDL_GetWindowProperties(renderer->window), SDL_PROP_WINDOW_SHAPE_POINTER, NULL); |
5235 | if (shape != renderer->shape_surface) { |
5236 | if (renderer->shape_texture) { |
5237 | SDL_DestroyTexture(renderer->shape_texture); |
5238 | renderer->shape_texture = NULL; |
5239 | } |
5240 | |
5241 | if (shape) { |
5242 | // There's nothing we can do if this fails, so just keep on going |
5243 | renderer->shape_texture = SDL_CreateTextureFromSurface(renderer, shape); |
5244 | |
5245 | SDL_SetTextureBlendMode(renderer->shape_texture, |
5246 | SDL_ComposeCustomBlendMode( |
5247 | SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD, |
5248 | SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD)); |
5249 | } |
5250 | renderer->shape_surface = shape; |
5251 | } |
5252 | |
5253 | if (renderer->shape_texture) { |
5254 | SDL_RenderTexture(renderer, renderer->shape_texture, NULL, NULL); |
5255 | } |
5256 | } |
5257 | |
5258 | static void SDL_SimulateRenderVSync(SDL_Renderer *renderer) |
5259 | { |
5260 | Uint64 now, elapsed; |
5261 | const Uint64 interval = renderer->simulate_vsync_interval_ns; |
5262 | |
5263 | if (!interval) { |
5264 | // We can't do sub-ns delay, so just return here |
5265 | return; |
5266 | } |
5267 | |
5268 | now = SDL_GetTicksNS(); |
5269 | elapsed = (now - renderer->last_present); |
5270 | if (elapsed < interval) { |
5271 | Uint64 duration = (interval - elapsed); |
5272 | SDL_DelayPrecise(duration); |
5273 | now = SDL_GetTicksNS(); |
5274 | } |
5275 | |
5276 | elapsed = (now - renderer->last_present); |
5277 | if (!renderer->last_present || elapsed > SDL_MS_TO_NS(1000)) { |
5278 | // It's been too long, reset the presentation timeline |
5279 | renderer->last_present = now; |
5280 | } else { |
5281 | renderer->last_present += (elapsed / interval) * interval; |
5282 | } |
5283 | } |
5284 | |
5285 | bool SDL_RenderPresent(SDL_Renderer *renderer) |
5286 | { |
5287 | bool presented = true; |
5288 | |
5289 | CHECK_RENDERER_MAGIC(renderer, false); |
5290 | |
5291 | if (renderer->target) { |
5292 | return SDL_SetError("You can't present on a render target" ); |
5293 | } |
5294 | |
5295 | SDL_RenderLogicalPresentation(renderer); |
5296 | |
5297 | if (renderer->transparent_window) { |
5298 | SDL_RenderApplyWindowShape(renderer); |
5299 | } |
5300 | |
5301 | FlushRenderCommands(renderer); // time to send everything to the GPU! |
5302 | |
5303 | #if DONT_DRAW_WHILE_HIDDEN |
5304 | // Don't present while we're hidden |
5305 | if (renderer->hidden) { |
5306 | presented = false; |
5307 | } else |
5308 | #endif |
5309 | if (!renderer->RenderPresent(renderer)) { |
5310 | presented = false; |
5311 | } |
5312 | |
5313 | if (renderer->simulate_vsync || |
5314 | (!presented && renderer->wanted_vsync)) { |
5315 | SDL_SimulateRenderVSync(renderer); |
5316 | } |
5317 | return true; |
5318 | } |
5319 | |
5320 | static void SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying) |
5321 | { |
5322 | SDL_Renderer *renderer; |
5323 | |
5324 | SDL_DestroyProperties(texture->props); |
5325 | |
5326 | renderer = texture->renderer; |
5327 | if (is_destroying) { |
5328 | // Renderer get destroyed, avoid to queue more commands |
5329 | } else { |
5330 | if (texture == renderer->target) { |
5331 | SDL_SetRenderTarget(renderer, NULL); // implies command queue flush |
5332 | } else { |
5333 | FlushRenderCommandsIfTextureNeeded(texture); |
5334 | } |
5335 | } |
5336 | |
5337 | SDL_SetObjectValid(texture, SDL_OBJECT_TYPE_TEXTURE, false); |
5338 | |
5339 | if (texture->next) { |
5340 | texture->next->prev = texture->prev; |
5341 | } |
5342 | if (texture->prev) { |
5343 | texture->prev->next = texture->next; |
5344 | } else { |
5345 | renderer->textures = texture->next; |
5346 | } |
5347 | |
5348 | if (texture->native) { |
5349 | SDL_DestroyTextureInternal(texture->native, is_destroying); |
5350 | } |
5351 | #ifdef SDL_HAVE_YUV |
5352 | if (texture->yuv) { |
5353 | SDL_SW_DestroyYUVTexture(texture->yuv); |
5354 | } |
5355 | #endif |
5356 | SDL_free(texture->pixels); |
5357 | |
5358 | renderer->DestroyTexture(renderer, texture); |
5359 | |
5360 | SDL_DestroySurface(texture->locked_surface); |
5361 | texture->locked_surface = NULL; |
5362 | |
5363 | SDL_free(texture); |
5364 | } |
5365 | |
5366 | void SDL_DestroyTexture(SDL_Texture *texture) |
5367 | { |
5368 | CHECK_TEXTURE_MAGIC(texture, ); |
5369 | |
5370 | if (--texture->refcount > 0) { |
5371 | return; |
5372 | } |
5373 | |
5374 | SDL_DestroyTextureInternal(texture, false /* is_destroying */); |
5375 | } |
5376 | |
5377 | static void SDL_DiscardAllCommands(SDL_Renderer *renderer) |
5378 | { |
5379 | SDL_RenderCommand *cmd; |
5380 | |
5381 | if (renderer->render_commands_tail) { |
5382 | renderer->render_commands_tail->next = renderer->render_commands_pool; |
5383 | cmd = renderer->render_commands; |
5384 | } else { |
5385 | cmd = renderer->render_commands_pool; |
5386 | } |
5387 | |
5388 | renderer->render_commands_pool = NULL; |
5389 | renderer->render_commands_tail = NULL; |
5390 | renderer->render_commands = NULL; |
5391 | renderer->vertex_data_used = 0; |
5392 | |
5393 | while (cmd) { |
5394 | SDL_RenderCommand *next = cmd->next; |
5395 | SDL_free(cmd); |
5396 | cmd = next; |
5397 | } |
5398 | } |
5399 | |
5400 | void SDL_DestroyRendererWithoutFreeing(SDL_Renderer *renderer) |
5401 | { |
5402 | SDL_assert(renderer != NULL); |
5403 | SDL_assert(!renderer->destroyed); |
5404 | |
5405 | renderer->destroyed = true; |
5406 | |
5407 | SDL_RemoveWindowEventWatch(SDL_WINDOW_EVENT_WATCH_NORMAL, SDL_RendererEventWatch, renderer); |
5408 | |
5409 | if (renderer->window) { |
5410 | SDL_PropertiesID props = SDL_GetWindowProperties(renderer->window); |
5411 | if (SDL_GetPointerProperty(props, SDL_PROP_WINDOW_RENDERER_POINTER, NULL) == renderer) { |
5412 | SDL_ClearProperty(props, SDL_PROP_WINDOW_RENDERER_POINTER); |
5413 | } |
5414 | SDL_RemoveWindowRenderer(renderer->window, renderer); |
5415 | } |
5416 | |
5417 | if (renderer->software) { |
5418 | // Make sure all drawing to a surface is complete |
5419 | FlushRenderCommands(renderer); |
5420 | } |
5421 | SDL_DiscardAllCommands(renderer); |
5422 | |
5423 | if (renderer->debug_char_texture_atlas) { |
5424 | SDL_DestroyTexture(renderer->debug_char_texture_atlas); |
5425 | renderer->debug_char_texture_atlas = NULL; |
5426 | } |
5427 | |
5428 | // Free existing textures for this renderer |
5429 | while (renderer->textures) { |
5430 | SDL_Texture *tex = renderer->textures; |
5431 | SDL_DestroyTextureInternal(renderer->textures, true /* is_destroying */); |
5432 | SDL_assert(tex != renderer->textures); // satisfy static analysis. |
5433 | } |
5434 | |
5435 | // Clean up renderer-specific resources |
5436 | if (renderer->DestroyRenderer) { |
5437 | renderer->DestroyRenderer(renderer); |
5438 | } |
5439 | |
5440 | if (renderer->target_mutex) { |
5441 | SDL_DestroyMutex(renderer->target_mutex); |
5442 | renderer->target_mutex = NULL; |
5443 | } |
5444 | if (renderer->vertex_data) { |
5445 | SDL_free(renderer->vertex_data); |
5446 | renderer->vertex_data = NULL; |
5447 | } |
5448 | if (renderer->texture_formats) { |
5449 | SDL_free(renderer->texture_formats); |
5450 | renderer->texture_formats = NULL; |
5451 | } |
5452 | if (renderer->props) { |
5453 | SDL_DestroyProperties(renderer->props); |
5454 | renderer->props = 0; |
5455 | } |
5456 | } |
5457 | |
5458 | void SDL_DestroyRenderer(SDL_Renderer *renderer) |
5459 | { |
5460 | CHECK_RENDERER_MAGIC_BUT_NOT_DESTROYED_FLAG(renderer,); |
5461 | |
5462 | // if we've already destroyed the renderer through SDL_DestroyWindow, we just need |
5463 | // to free the renderer pointer. This lets apps destroy the window and renderer |
5464 | // in either order. |
5465 | if (!renderer->destroyed) { |
5466 | SDL_DestroyRendererWithoutFreeing(renderer); |
5467 | } |
5468 | |
5469 | SDL_Renderer *curr = SDL_renderers; |
5470 | SDL_Renderer *prev = NULL; |
5471 | while (curr) { |
5472 | if (curr == renderer) { |
5473 | if (prev) { |
5474 | prev->next = renderer->next; |
5475 | } else { |
5476 | SDL_renderers = renderer->next; |
5477 | } |
5478 | break; |
5479 | } |
5480 | prev = curr; |
5481 | curr = curr->next; |
5482 | } |
5483 | |
5484 | SDL_SetObjectValid(renderer, SDL_OBJECT_TYPE_RENDERER, false); // It's no longer magical... |
5485 | |
5486 | SDL_free(renderer); |
5487 | } |
5488 | |
5489 | void *SDL_GetRenderMetalLayer(SDL_Renderer *renderer) |
5490 | { |
5491 | CHECK_RENDERER_MAGIC(renderer, NULL); |
5492 | |
5493 | if (renderer->GetMetalLayer) { |
5494 | FlushRenderCommands(renderer); // in case the app is going to mess with it. |
5495 | return renderer->GetMetalLayer(renderer); |
5496 | } |
5497 | return NULL; |
5498 | } |
5499 | |
5500 | void *SDL_GetRenderMetalCommandEncoder(SDL_Renderer *renderer) |
5501 | { |
5502 | CHECK_RENDERER_MAGIC(renderer, NULL); |
5503 | |
5504 | if (renderer->GetMetalCommandEncoder) { |
5505 | FlushRenderCommands(renderer); // in case the app is going to mess with it. |
5506 | return renderer->GetMetalCommandEncoder(renderer); |
5507 | } |
5508 | return NULL; |
5509 | } |
5510 | |
5511 | bool SDL_AddVulkanRenderSemaphores(SDL_Renderer *renderer, Uint32 wait_stage_mask, Sint64 wait_semaphore, Sint64 signal_semaphore) |
5512 | { |
5513 | CHECK_RENDERER_MAGIC(renderer, false); |
5514 | |
5515 | if (!renderer->AddVulkanRenderSemaphores) { |
5516 | return SDL_Unsupported(); |
5517 | } |
5518 | return renderer->AddVulkanRenderSemaphores(renderer, wait_stage_mask, wait_semaphore, signal_semaphore); |
5519 | } |
5520 | |
5521 | static SDL_BlendMode SDL_GetShortBlendMode(SDL_BlendMode blendMode) |
5522 | { |
5523 | if (blendMode == SDL_BLENDMODE_NONE_FULL) { |
5524 | return SDL_BLENDMODE_NONE; |
5525 | } |
5526 | if (blendMode == SDL_BLENDMODE_BLEND_FULL) { |
5527 | return SDL_BLENDMODE_BLEND; |
5528 | } |
5529 | if (blendMode == SDL_BLENDMODE_BLEND_PREMULTIPLIED_FULL) { |
5530 | return SDL_BLENDMODE_BLEND_PREMULTIPLIED; |
5531 | } |
5532 | if (blendMode == SDL_BLENDMODE_ADD_FULL) { |
5533 | return SDL_BLENDMODE_ADD; |
5534 | } |
5535 | if (blendMode == SDL_BLENDMODE_ADD_PREMULTIPLIED_FULL) { |
5536 | return SDL_BLENDMODE_ADD_PREMULTIPLIED; |
5537 | } |
5538 | if (blendMode == SDL_BLENDMODE_MOD_FULL) { |
5539 | return SDL_BLENDMODE_MOD; |
5540 | } |
5541 | if (blendMode == SDL_BLENDMODE_MUL_FULL) { |
5542 | return SDL_BLENDMODE_MUL; |
5543 | } |
5544 | return blendMode; |
5545 | } |
5546 | |
5547 | static SDL_BlendMode SDL_GetLongBlendMode(SDL_BlendMode blendMode) |
5548 | { |
5549 | if (blendMode == SDL_BLENDMODE_NONE) { |
5550 | return SDL_BLENDMODE_NONE_FULL; |
5551 | } |
5552 | if (blendMode == SDL_BLENDMODE_BLEND) { |
5553 | return SDL_BLENDMODE_BLEND_FULL; |
5554 | } |
5555 | if (blendMode == SDL_BLENDMODE_BLEND_PREMULTIPLIED) { |
5556 | return SDL_BLENDMODE_BLEND_PREMULTIPLIED_FULL; |
5557 | } |
5558 | if (blendMode == SDL_BLENDMODE_ADD) { |
5559 | return SDL_BLENDMODE_ADD_FULL; |
5560 | } |
5561 | if (blendMode == SDL_BLENDMODE_ADD_PREMULTIPLIED) { |
5562 | return SDL_BLENDMODE_ADD_PREMULTIPLIED_FULL; |
5563 | } |
5564 | if (blendMode == SDL_BLENDMODE_MOD) { |
5565 | return SDL_BLENDMODE_MOD_FULL; |
5566 | } |
5567 | if (blendMode == SDL_BLENDMODE_MUL) { |
5568 | return SDL_BLENDMODE_MUL_FULL; |
5569 | } |
5570 | return blendMode; |
5571 | } |
5572 | |
5573 | SDL_BlendMode SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor, |
5574 | SDL_BlendOperation colorOperation, |
5575 | SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor, |
5576 | SDL_BlendOperation alphaOperation) |
5577 | { |
5578 | SDL_BlendMode blendMode = SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation, |
5579 | srcAlphaFactor, dstAlphaFactor, alphaOperation); |
5580 | return SDL_GetShortBlendMode(blendMode); |
5581 | } |
5582 | |
5583 | SDL_BlendFactor SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode) |
5584 | { |
5585 | blendMode = SDL_GetLongBlendMode(blendMode); |
5586 | return (SDL_BlendFactor)(((Uint32)blendMode >> 4) & 0xF); |
5587 | } |
5588 | |
5589 | SDL_BlendFactor SDL_GetBlendModeDstColorFactor(SDL_BlendMode blendMode) |
5590 | { |
5591 | blendMode = SDL_GetLongBlendMode(blendMode); |
5592 | return (SDL_BlendFactor)(((Uint32)blendMode >> 8) & 0xF); |
5593 | } |
5594 | |
5595 | SDL_BlendOperation SDL_GetBlendModeColorOperation(SDL_BlendMode blendMode) |
5596 | { |
5597 | blendMode = SDL_GetLongBlendMode(blendMode); |
5598 | return (SDL_BlendOperation)(((Uint32)blendMode >> 0) & 0xF); |
5599 | } |
5600 | |
5601 | SDL_BlendFactor SDL_GetBlendModeSrcAlphaFactor(SDL_BlendMode blendMode) |
5602 | { |
5603 | blendMode = SDL_GetLongBlendMode(blendMode); |
5604 | return (SDL_BlendFactor)(((Uint32)blendMode >> 20) & 0xF); |
5605 | } |
5606 | |
5607 | SDL_BlendFactor SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode) |
5608 | { |
5609 | blendMode = SDL_GetLongBlendMode(blendMode); |
5610 | return (SDL_BlendFactor)(((Uint32)blendMode >> 24) & 0xF); |
5611 | } |
5612 | |
5613 | SDL_BlendOperation SDL_GetBlendModeAlphaOperation(SDL_BlendMode blendMode) |
5614 | { |
5615 | blendMode = SDL_GetLongBlendMode(blendMode); |
5616 | return (SDL_BlendOperation)(((Uint32)blendMode >> 16) & 0xF); |
5617 | } |
5618 | |
5619 | bool SDL_SetRenderVSync(SDL_Renderer *renderer, int vsync) |
5620 | { |
5621 | CHECK_RENDERER_MAGIC(renderer, false); |
5622 | |
5623 | renderer->wanted_vsync = vsync ? true : false; |
5624 | |
5625 | // for the software renderer, forward the call to the WindowTexture renderer |
5626 | #ifdef SDL_VIDEO_RENDER_SW |
5627 | if (renderer->software) { |
5628 | if (!renderer->window) { |
5629 | if (!vsync) { |
5630 | return true; |
5631 | } else { |
5632 | return SDL_Unsupported(); |
5633 | } |
5634 | } |
5635 | if (SDL_SetWindowTextureVSync(NULL, renderer->window, vsync)) { |
5636 | renderer->simulate_vsync = false; |
5637 | return true; |
5638 | } |
5639 | } |
5640 | #endif |
5641 | |
5642 | if (!renderer->SetVSync || |
5643 | !renderer->SetVSync(renderer, vsync)) { |
5644 | switch (vsync) { |
5645 | case 0: |
5646 | renderer->simulate_vsync = false; |
5647 | break; |
5648 | case 1: |
5649 | renderer->simulate_vsync = true; |
5650 | break; |
5651 | default: |
5652 | return SDL_Unsupported(); |
5653 | } |
5654 | } |
5655 | SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_VSYNC_NUMBER, vsync); |
5656 | return true; |
5657 | } |
5658 | |
5659 | bool SDL_GetRenderVSync(SDL_Renderer *renderer, int *vsync) |
5660 | { |
5661 | if (vsync) { |
5662 | *vsync = 0; |
5663 | } |
5664 | |
5665 | CHECK_RENDERER_MAGIC(renderer, false); |
5666 | |
5667 | if (vsync) { |
5668 | *vsync = (int)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_VSYNC_NUMBER, 0); |
5669 | } |
5670 | return true; |
5671 | } |
5672 | |
5673 | |
5674 | #define SDL_DEBUG_FONT_GLYPHS_PER_ROW 14 |
5675 | |
5676 | static bool CreateDebugTextAtlas(SDL_Renderer *renderer) |
5677 | { |
5678 | SDL_assert(renderer->debug_char_texture_atlas == NULL); // don't double-create it! |
5679 | |
5680 | const int charWidth = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; |
5681 | const int charHeight = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; |
5682 | |
5683 | // actually make each glyph two pixels taller/wider, to prevent scaling artifacts. |
5684 | const int rows = (SDL_DEBUG_FONT_NUM_GLYPHS / SDL_DEBUG_FONT_GLYPHS_PER_ROW) + 1; |
5685 | SDL_Surface *atlas = SDL_CreateSurface((charWidth + 2) * SDL_DEBUG_FONT_GLYPHS_PER_ROW, rows * (charHeight + 2), SDL_PIXELFORMAT_RGBA8888); |
5686 | if (!atlas) { |
5687 | return false; |
5688 | } |
5689 | |
5690 | const int pitch = atlas->pitch; |
5691 | SDL_memset(atlas->pixels, '\0', atlas->h * atlas->pitch); |
5692 | |
5693 | int column = 0; |
5694 | int row = 0; |
5695 | for (int glyph = 0; glyph < SDL_DEBUG_FONT_NUM_GLYPHS; glyph++) { |
5696 | // find top-left of this glyph in destination surface. The +2's account for glyph padding. |
5697 | Uint8 *linepos = (((Uint8 *)atlas->pixels) + ((row * (charHeight + 2) + 1) * pitch)) + ((column * (charWidth + 2) + 1) * sizeof (Uint32)); |
5698 | const Uint8 *charpos = SDL_RenderDebugTextFontData + (glyph * 8); |
5699 | |
5700 | // Draw the glyph to the surface... |
5701 | for (int iy = 0; iy < charHeight; iy++) { |
5702 | Uint32 *curpos = (Uint32 *)linepos; |
5703 | for (int ix = 0; ix < charWidth; ix++) { |
5704 | if ((*charpos) & (1 << ix)) { |
5705 | *curpos = 0xffffffff; |
5706 | } else { |
5707 | *curpos = 0; |
5708 | } |
5709 | ++curpos; |
5710 | } |
5711 | linepos += pitch; |
5712 | ++charpos; |
5713 | } |
5714 | |
5715 | // move to next position (and if too far, start the next row). |
5716 | column++; |
5717 | if (column >= SDL_DEBUG_FONT_GLYPHS_PER_ROW) { |
5718 | row++; |
5719 | column = 0; |
5720 | } |
5721 | } |
5722 | |
5723 | SDL_assert((row < rows) || ((row == rows) && (column == 0))); // make sure we didn't overflow the surface. |
5724 | |
5725 | // Convert temp surface into texture |
5726 | SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, atlas); |
5727 | if (texture) { |
5728 | SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_PIXELART); |
5729 | renderer->debug_char_texture_atlas = texture; |
5730 | } |
5731 | SDL_DestroySurface(atlas); |
5732 | |
5733 | return texture != NULL; |
5734 | } |
5735 | |
5736 | static bool DrawDebugCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c) |
5737 | { |
5738 | SDL_assert(renderer->debug_char_texture_atlas != NULL); // should have been created by now! |
5739 | |
5740 | const int charWidth = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; |
5741 | const int charHeight = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; |
5742 | |
5743 | // Character index in cache |
5744 | Uint32 ci = c; |
5745 | if ((ci <= 32) || ((ci >= 127) && (ci <= 160))) { |
5746 | return true; // these are just completely blank chars, don't bother doing anything. |
5747 | } else if (ci >= SDL_DEBUG_FONT_NUM_GLYPHS) { |
5748 | ci = SDL_DEBUG_FONT_NUM_GLYPHS - 1; // use our "not a valid/supported character" glyph. |
5749 | } else if (ci < 127) { |
5750 | ci -= 33; // adjust for the 33 blank glyphs at the start |
5751 | } else { |
5752 | ci -= 67; // adjust for the 33 blank glyphs at the start AND the 34 gap in the middle. |
5753 | } |
5754 | |
5755 | const float src_x = (float) (((ci % SDL_DEBUG_FONT_GLYPHS_PER_ROW) * (charWidth + 2)) + 1); |
5756 | const float src_y = (float) (((ci / SDL_DEBUG_FONT_GLYPHS_PER_ROW) * (charHeight + 2)) + 1); |
5757 | |
5758 | // Draw texture onto destination |
5759 | const SDL_FRect srect = { src_x, src_y, (float) charWidth, (float) charHeight }; |
5760 | const SDL_FRect drect = { x, y, (float) charWidth, (float) charHeight }; |
5761 | return SDL_RenderTexture(renderer, renderer->debug_char_texture_atlas, &srect, &drect); |
5762 | } |
5763 | |
5764 | bool SDL_RenderDebugText(SDL_Renderer *renderer, float x, float y, const char *s) |
5765 | { |
5766 | CHECK_RENDERER_MAGIC(renderer, false); |
5767 | |
5768 | // Allocate a texture atlas for this renderer if needed. |
5769 | if (!renderer->debug_char_texture_atlas) { |
5770 | if (!CreateDebugTextAtlas(renderer)) { |
5771 | return false; |
5772 | } |
5773 | } |
5774 | |
5775 | bool result = true; |
5776 | |
5777 | Uint8 r, g, b, a; |
5778 | result &= SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); |
5779 | result &= SDL_SetTextureColorMod(renderer->debug_char_texture_atlas, r, g, b); |
5780 | result &= SDL_SetTextureAlphaMod(renderer->debug_char_texture_atlas, a); |
5781 | |
5782 | float curx = x; |
5783 | Uint32 ch; |
5784 | |
5785 | while (result && ((ch = SDL_StepUTF8(&s, NULL)) != 0)) { |
5786 | result &= DrawDebugCharacter(renderer, curx, y, ch); |
5787 | curx += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; |
5788 | } |
5789 | |
5790 | return result; |
5791 | } |
5792 | |
5793 | bool SDL_RenderDebugTextFormat(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) |
5794 | { |
5795 | va_list ap; |
5796 | va_start(ap, fmt); |
5797 | |
5798 | // fast path to avoid unnecessary allocation and copy. If you're going through the dynapi, there's a good chance |
5799 | // you _always_ hit this path, since it probably had to process varargs before calling into the jumptable. |
5800 | if (SDL_strcmp(fmt, "%s" ) == 0) { |
5801 | const char *str = va_arg(ap, const char *); |
5802 | va_end(ap); |
5803 | return SDL_RenderDebugText(renderer, x, y, str); |
5804 | } |
5805 | |
5806 | char *str = NULL; |
5807 | const int rc = SDL_vasprintf(&str, fmt, ap); |
5808 | va_end(ap); |
5809 | |
5810 | if (rc == -1) { |
5811 | return false; |
5812 | } |
5813 | |
5814 | const bool retval = SDL_RenderDebugText(renderer, x, y, str); |
5815 | SDL_free(str); |
5816 | return retval; |
5817 | } |
5818 | |
5819 | bool SDL_SetDefaultTextureScaleMode(SDL_Renderer *renderer, SDL_ScaleMode scale_mode) |
5820 | { |
5821 | CHECK_RENDERER_MAGIC(renderer, false); |
5822 | |
5823 | renderer->scale_mode = scale_mode; |
5824 | |
5825 | return true; |
5826 | } |
5827 | |
5828 | bool SDL_GetDefaultTextureScaleMode(SDL_Renderer *renderer, SDL_ScaleMode *scale_mode) |
5829 | { |
5830 | if (scale_mode) { |
5831 | *scale_mode = SDL_SCALEMODE_LINEAR; |
5832 | } |
5833 | |
5834 | CHECK_RENDERER_MAGIC(renderer, false); |
5835 | |
5836 | if (scale_mode) { |
5837 | *scale_mode = renderer->scale_mode; |
5838 | } |
5839 | return true; |
5840 | } |
5841 | |
5842 | SDL_GPURenderState *SDL_CreateGPURenderState(SDL_Renderer *renderer, SDL_GPURenderStateDesc *desc) |
5843 | { |
5844 | CHECK_RENDERER_MAGIC(renderer, NULL); |
5845 | |
5846 | if (!desc) { |
5847 | SDL_InvalidParamError("desc" ); |
5848 | return NULL; |
5849 | } |
5850 | |
5851 | if (desc->version < sizeof(*desc)) { |
5852 | // Update this to handle older versions of this interface |
5853 | SDL_SetError("Invalid desc, should be initialized with SDL_INIT_INTERFACE()" ); |
5854 | return NULL; |
5855 | } |
5856 | |
5857 | if (!desc->fragment_shader) { |
5858 | SDL_SetError("desc->fragment_shader is required" ); |
5859 | return NULL; |
5860 | } |
5861 | |
5862 | SDL_GPUDevice *device = (SDL_GPUDevice *)SDL_GetPointerProperty(renderer->props, SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL); |
5863 | if (!device) { |
5864 | SDL_SetError("Renderer isn't associated with a GPU device" ); |
5865 | return NULL; |
5866 | } |
5867 | |
5868 | SDL_GPURenderState *state = (SDL_GPURenderState *)SDL_calloc(1, sizeof(*state)); |
5869 | if (!state) { |
5870 | return NULL; |
5871 | } |
5872 | |
5873 | state->renderer = renderer; |
5874 | state->fragment_shader = desc->fragment_shader; |
5875 | |
5876 | if (desc->num_sampler_bindings > 0) { |
5877 | state->sampler_bindings = (SDL_GPUTextureSamplerBinding *)SDL_calloc(desc->num_sampler_bindings, sizeof(*state->sampler_bindings)); |
5878 | if (!state->sampler_bindings) { |
5879 | SDL_DestroyGPURenderState(state); |
5880 | return NULL; |
5881 | } |
5882 | SDL_memcpy(state->sampler_bindings, desc->sampler_bindings, desc->num_sampler_bindings * sizeof(*state->sampler_bindings)); |
5883 | state->num_sampler_bindings = desc->num_sampler_bindings; |
5884 | } |
5885 | |
5886 | if (desc->num_storage_textures > 0) { |
5887 | state->storage_textures = (SDL_GPUTexture **)SDL_calloc(desc->num_storage_textures, sizeof(*state->storage_textures)); |
5888 | if (!state->storage_textures) { |
5889 | SDL_DestroyGPURenderState(state); |
5890 | return NULL; |
5891 | } |
5892 | SDL_memcpy(state->storage_textures, desc->storage_textures, desc->num_storage_textures * sizeof(*state->storage_textures)); |
5893 | state->num_storage_textures = desc->num_storage_textures; |
5894 | } |
5895 | |
5896 | if (desc->num_storage_buffers > 0) { |
5897 | state->storage_buffers = (SDL_GPUBuffer **)SDL_calloc(desc->num_storage_buffers, sizeof(*state->storage_buffers)); |
5898 | if (!state->storage_buffers) { |
5899 | SDL_DestroyGPURenderState(state); |
5900 | return NULL; |
5901 | } |
5902 | SDL_memcpy(state->storage_buffers, desc->storage_buffers, desc->num_storage_buffers * sizeof(*state->storage_buffers)); |
5903 | state->num_storage_buffers = desc->num_storage_buffers; |
5904 | } |
5905 | |
5906 | return state; |
5907 | } |
5908 | |
5909 | bool SDL_SetGPURenderStateFragmentUniforms(SDL_GPURenderState *state, Uint32 slot_index, const void *data, Uint32 length) |
5910 | { |
5911 | if (!state) { |
5912 | return SDL_InvalidParamError("state" ); |
5913 | } |
5914 | |
5915 | if (!FlushRenderCommandsIfGPURenderStateNeeded(state)) { |
5916 | return false; |
5917 | } |
5918 | |
5919 | for (int i = 0; i < state->num_uniform_buffers; i++) { |
5920 | SDL_GPURenderStateUniformBuffer *buffer = &state->uniform_buffers[i]; |
5921 | if (buffer->slot_index == slot_index) { |
5922 | void *new_data = SDL_realloc(buffer->data, length); |
5923 | if (!new_data) { |
5924 | return false; |
5925 | } |
5926 | SDL_memcpy(new_data, data, length); |
5927 | buffer->data = new_data; |
5928 | buffer->length = length; |
5929 | return true; |
5930 | } |
5931 | } |
5932 | |
5933 | SDL_GPURenderStateUniformBuffer *buffers = (SDL_GPURenderStateUniformBuffer *)SDL_realloc(state->uniform_buffers, (state->num_uniform_buffers + 1) * sizeof(*state->uniform_buffers)); |
5934 | if (!buffers) { |
5935 | return false; |
5936 | } |
5937 | |
5938 | SDL_GPURenderStateUniformBuffer *buffer = &buffers[state->num_uniform_buffers]; |
5939 | buffer->slot_index = slot_index; |
5940 | buffer->length = length; |
5941 | buffer->data = SDL_malloc(length); |
5942 | if (!buffer->data) { |
5943 | SDL_free(buffers); |
5944 | return false; |
5945 | } |
5946 | SDL_memcpy(buffer->data, data, length); |
5947 | |
5948 | state->uniform_buffers = buffers; |
5949 | ++state->num_uniform_buffers; |
5950 | return true; |
5951 | } |
5952 | |
5953 | bool SDL_SetRenderGPUState(SDL_Renderer *renderer, SDL_GPURenderState *state) |
5954 | { |
5955 | CHECK_RENDERER_MAGIC(renderer, false); |
5956 | |
5957 | renderer->gpu_render_state = state; |
5958 | return true; |
5959 | } |
5960 | |
5961 | void SDL_DestroyGPURenderState(SDL_GPURenderState *state) |
5962 | { |
5963 | if (!state) { |
5964 | return; |
5965 | } |
5966 | |
5967 | FlushRenderCommandsIfGPURenderStateNeeded(state); |
5968 | |
5969 | if (state->num_uniform_buffers > 0) { |
5970 | for (int i = 0; i < state->num_uniform_buffers; i++) { |
5971 | SDL_free(state->uniform_buffers[i].data); |
5972 | } |
5973 | SDL_free(state->uniform_buffers); |
5974 | } |
5975 | SDL_free(state->sampler_bindings); |
5976 | SDL_free(state->storage_textures); |
5977 | SDL_free(state->storage_buffers); |
5978 | SDL_free(state); |
5979 | } |
5980 | |