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