Atlas - SDL_render.c
Home / ext / SDL / src / render Lines: 2 | Size: 209987 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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_RENDER_DISABLED 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 1473 // We just want to match the first format that has the same channels 1474 for (i = 0; i < renderer->num_texture_formats; ++i) { 1475 if (!SDL_ISPIXELFORMAT_FOURCC(renderer->texture_formats[i]) && 1476 SDL_ISPIXELFORMAT_ALPHA(renderer->texture_formats[i]) == hasAlpha && 1477 SDL_ISPIXELFORMAT_INDEXED(renderer->texture_formats[i]) == isIndexed) { 1478 return renderer->texture_formats[i]; 1479 } 1480 } 1481 } 1482 return renderer->texture_formats[0]; 1483} 1484 1485SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_PropertiesID props) 1486{ 1487 SDL_Texture *texture; 1488 SDL_PixelFormat format = (SDL_PixelFormat)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, SDL_PIXELFORMAT_UNKNOWN); 1489 SDL_TextureAccess access = (SDL_TextureAccess)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); 1490 int w = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, 0); 1491 int h = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, 0); 1492 SDL_Palette *palette = (SDL_Palette *)SDL_GetPointerProperty(props, SDL_PROP_TEXTURE_CREATE_PALETTE_POINTER, NULL); 1493 SDL_Colorspace default_colorspace; 1494 bool texture_is_fourcc_and_target; 1495 1496 CHECK_RENDERER_MAGIC(renderer, NULL); 1497 1498 if (!format) { 1499 format = renderer->texture_formats[0]; 1500 } 1501 1502 CHECK_PARAM(SDL_BYTESPERPIXEL(format) == 0) { 1503 SDL_SetError("Invalid texture format"); 1504 return NULL; 1505 } 1506 CHECK_PARAM(SDL_ISPIXELFORMAT_INDEXED(format) && access == SDL_TEXTUREACCESS_TARGET) { 1507 SDL_SetError("Palettized textures can't be render targets"); 1508 return NULL; 1509 } 1510 CHECK_PARAM(w <= 0 || h <= 0) { 1511 SDL_SetError("Texture dimensions can't be 0"); 1512 return NULL; 1513 } 1514 int max_texture_size = (int)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 0); 1515 CHECK_PARAM(max_texture_size && (w > max_texture_size || h > max_texture_size)) { 1516 SDL_SetError("Texture dimensions are limited to %dx%d", max_texture_size, max_texture_size); 1517 return NULL; 1518 } 1519 1520 default_colorspace = SDL_GetDefaultColorspaceForFormat(format); 1521 1522 texture = (SDL_Texture *)SDL_calloc(1, sizeof(*texture)); 1523 if (!texture) { 1524 return NULL; 1525 } 1526 texture->refcount = 1; 1527 SDL_SetObjectValid(texture, SDL_OBJECT_TYPE_TEXTURE, true); 1528 texture->colorspace = (SDL_Colorspace)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, default_colorspace); 1529 texture->format = format; 1530 texture->access = access; 1531 texture->w = w; 1532 texture->h = h; 1533 texture->color.r = 1.0f; 1534 texture->color.g = 1.0f; 1535 texture->color.b = 1.0f; 1536 texture->color.a = 1.0f; 1537 texture->blendMode = SDL_ISPIXELFORMAT_ALPHA(format) ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE; 1538 texture->scaleMode = renderer->scale_mode; 1539 texture->view.pixel_w = w; 1540 texture->view.pixel_h = h; 1541 texture->view.viewport.w = -1; 1542 texture->view.viewport.h = -1; 1543 texture->view.scale.x = 1.0f; 1544 texture->view.scale.y = 1.0f; 1545 texture->view.logical_scale.x = 1.0f; 1546 texture->view.logical_scale.y = 1.0f; 1547 texture->view.current_scale.x = 1.0f; 1548 texture->view.current_scale.y = 1.0f; 1549 texture->renderer = renderer; 1550 texture->next = renderer->textures; 1551 if (renderer->textures) { 1552 renderer->textures->prev = texture; 1553 } 1554 renderer->textures = texture; 1555 1556 UpdatePixelViewport(renderer, &texture->view); 1557 UpdatePixelClipRect(renderer, &texture->view); 1558 1559 texture->SDR_white_point = SDL_GetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, SDL_GetDefaultSDRWhitePoint(texture->colorspace)); 1560 texture->HDR_headroom = SDL_GetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, SDL_GetDefaultHDRHeadroom(texture->colorspace)); 1561 1562 // FOURCC format cannot be used directly by renderer back-ends for target texture 1563 texture_is_fourcc_and_target = (access == SDL_TEXTUREACCESS_TARGET && SDL_ISPIXELFORMAT_FOURCC(format)); 1564 1565 if (!texture_is_fourcc_and_target && IsSupportedFormat(renderer, format)) { 1566 if (!renderer->CreateTexture(renderer, texture, props)) { 1567 SDL_DestroyTexture(texture); 1568 return NULL; 1569 } 1570 } else { 1571 SDL_PixelFormat closest_format; 1572 SDL_PropertiesID native_props = SDL_CreateProperties(); 1573 1574 if (!texture_is_fourcc_and_target) { 1575 closest_format = GetClosestSupportedFormat(renderer, format); 1576 } else { 1577 closest_format = renderer->texture_formats[0]; 1578 } 1579 1580 if (format == SDL_PIXELFORMAT_MJPG && closest_format == SDL_PIXELFORMAT_NV12) { 1581 SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, SDL_COLORSPACE_JPEG); 1582 } else { 1583 default_colorspace = SDL_GetDefaultColorspaceForFormat(closest_format); 1584 if (SDL_COLORSPACETYPE(texture->colorspace) == SDL_COLORSPACETYPE(default_colorspace) && 1585 SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_COLORSPACETRANSFER(default_colorspace)) { 1586 SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, texture->colorspace); 1587 } else { 1588 SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, default_colorspace); 1589 } 1590 } 1591 SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, closest_format); 1592 if (SDL_ISPIXELFORMAT_INDEXED(texture->format)) { 1593 // We're going to be uploading pixels frequently as the palette changes 1594 SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING); 1595 } else { 1596 SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, texture->access); 1597 } 1598 SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, texture->w); 1599 SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, texture->h); 1600 1601 texture->native = SDL_CreateTextureWithProperties(renderer, native_props); 1602 SDL_DestroyProperties(native_props); 1603 if (!texture->native) { 1604 SDL_DestroyTexture(texture); 1605 return NULL; 1606 } 1607 1608 SDL_SetPointerProperty(SDL_GetTextureProperties(texture->native), SDL_PROP_TEXTURE_PARENT_POINTER, texture); 1609 1610 // Swap textures to have texture before texture->native in the list 1611 texture->native->next = texture->next; 1612 if (texture->native->next) { 1613 texture->native->next->prev = texture->native; 1614 } 1615 texture->prev = texture->native->prev; 1616 if (texture->prev) { 1617 texture->prev->next = texture; 1618 } 1619 texture->native->prev = texture; 1620 texture->next = texture->native; 1621 renderer->textures = texture; 1622 1623 SDL_SetTextureScaleMode(texture->native, texture->scaleMode); 1624 1625 if (texture->format == SDL_PIXELFORMAT_MJPG) { 1626 // We have a custom decode + upload path for this 1627 } else if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) { 1628#ifdef SDL_HAVE_YUV 1629 texture->yuv = SDL_SW_CreateYUVTexture(texture->format, texture->colorspace, w, h); 1630#else 1631 SDL_SetError("SDL not built with YUV support"); 1632#endif 1633 if (!texture->yuv) { 1634 SDL_DestroyTexture(texture); 1635 return NULL; 1636 } 1637 } else if (SDL_ISPIXELFORMAT_INDEXED(texture->format)) { 1638 texture->palette_surface = SDL_CreateSurface(w, h, texture->format); 1639 if (!texture->palette_surface) { 1640 SDL_DestroyTexture(texture); 1641 return NULL; 1642 } 1643 } else if (access == SDL_TEXTUREACCESS_STREAMING) { 1644 // The pitch is 4 byte aligned 1645 texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3); 1646 texture->pixels = SDL_calloc(1, (size_t)texture->pitch * h); 1647 if (!texture->pixels) { 1648 SDL_DestroyTexture(texture); 1649 return NULL; 1650 } 1651 } 1652 } 1653 1654 if (SDL_ISPIXELFORMAT_INDEXED(texture->format) && palette) { 1655 SDL_SetTexturePalette(texture, palette); 1656 } 1657 1658 // Now set the properties for the new texture 1659 props = SDL_GetTextureProperties(texture); 1660 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_COLORSPACE_NUMBER, texture->colorspace); 1661 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_FORMAT_NUMBER, texture->format); 1662 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_ACCESS_NUMBER, texture->access); 1663 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_WIDTH_NUMBER, texture->w); 1664 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_HEIGHT_NUMBER, texture->h); 1665 SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_SDR_WHITE_POINT_FLOAT, texture->SDR_white_point); 1666 if (texture->HDR_headroom > 0.0f) { 1667 SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_HDR_HEADROOM_FLOAT, texture->HDR_headroom); 1668 } 1669 return texture; 1670} 1671 1672SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, SDL_PixelFormat format, SDL_TextureAccess access, int w, int h) 1673{ 1674 SDL_Texture *texture; 1675 SDL_PropertiesID props = SDL_CreateProperties(); 1676 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); 1677 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, access); 1678 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, w); 1679 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, h); 1680 texture = SDL_CreateTextureWithProperties(renderer, props); 1681 SDL_DestroyProperties(props); 1682 return texture; 1683} 1684 1685static bool SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, SDL_Surface *surface) 1686{ 1687 bool direct_update; 1688 1689 if (surface->format == texture->format && 1690 SDL_GetSurfaceColorspace(surface) == texture->colorspace) { 1691 if (SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) { 1692 /* Surface and Renderer formats are identical. 1693 * Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */ 1694 direct_update = false; 1695 } else { 1696 // Update Texture directly 1697 direct_update = true; 1698 } 1699 } else { 1700 // Surface and Renderer formats are different, it needs an intermediate conversion. 1701 direct_update = false; 1702 } 1703 1704 if (direct_update) { 1705 if (SDL_MUSTLOCK(surface)) { 1706 if (SDL_LockSurface(surface)) { 1707 SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch); 1708 SDL_UnlockSurface(surface); 1709 } 1710 } else { 1711 SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch); 1712 } 1713 } else { 1714 // Set up a destination surface for the texture update 1715 SDL_Surface *temp = SDL_ConvertSurfaceAndColorspace(surface, texture->format, texture->public_palette, texture->colorspace, SDL_GetSurfaceProperties(surface)); 1716 if (temp) { 1717 SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch); 1718 SDL_DestroySurface(temp); 1719 } else { 1720 return false; 1721 } 1722 } 1723 1724 if (texture->format == surface->format && surface->palette) { 1725 // Copy the palette to the new texture 1726 SDL_Palette *existing = surface->palette; 1727 SDL_Palette *palette = SDL_CreatePalette(existing->ncolors); 1728 if (palette && 1729 SDL_SetPaletteColors(palette, existing->colors, 0, existing->ncolors) && 1730 SDL_SetTexturePalette(texture, palette)) { 1731 // The texture has a reference to the palette now 1732 SDL_DestroyPalette(palette); 1733 } else { 1734 SDL_DestroyPalette(palette); 1735 return false; 1736 } 1737 } 1738 1739 { 1740 Uint8 r, g, b, a; 1741 SDL_BlendMode blendMode; 1742 1743 SDL_GetSurfaceColorMod(surface, &r, &g, &b); 1744 SDL_SetTextureColorMod(texture, r, g, b); 1745 1746 SDL_GetSurfaceAlphaMod(surface, &a); 1747 SDL_SetTextureAlphaMod(texture, a); 1748 1749 if (SDL_SurfaceHasColorKey(surface)) { 1750 // We converted to a texture with alpha format 1751 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); 1752 } else { 1753 SDL_GetSurfaceBlendMode(surface, &blendMode); 1754 SDL_SetTextureBlendMode(texture, blendMode); 1755 } 1756 } 1757 1758 return true; 1759} 1760 1761SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *surface) 1762{ 1763 int i; 1764 SDL_PixelFormat format = SDL_PIXELFORMAT_UNKNOWN; 1765 SDL_Texture *texture; 1766 SDL_PropertiesID props; 1767 SDL_Colorspace surface_colorspace = SDL_COLORSPACE_UNKNOWN; 1768 SDL_Colorspace texture_colorspace = SDL_COLORSPACE_UNKNOWN; 1769 1770 CHECK_RENDERER_MAGIC(renderer, NULL); 1771 1772 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 1773 SDL_InvalidParamError("SDL_CreateTextureFromSurface(): surface"); 1774 return NULL; 1775 } 1776 1777 // Try to have the best pixel format for the texture 1778 // No alpha, but a colorkey => promote to alpha 1779 if (!SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) { 1780 if (surface->format == SDL_PIXELFORMAT_XRGB8888) { 1781 for (i = 0; i < renderer->num_texture_formats; ++i) { 1782 if (renderer->texture_formats[i] == SDL_PIXELFORMAT_ARGB8888) { 1783 format = SDL_PIXELFORMAT_ARGB8888; 1784 break; 1785 } 1786 } 1787 } else if (surface->format == SDL_PIXELFORMAT_XBGR8888) { 1788 for (i = 0; i < renderer->num_texture_formats; ++i) { 1789 if (renderer->texture_formats[i] == SDL_PIXELFORMAT_ABGR8888) { 1790 format = SDL_PIXELFORMAT_ABGR8888; 1791 break; 1792 } 1793 } 1794 } 1795 } else { 1796 // Exact match would be fine 1797 for (i = 0; i < renderer->num_texture_formats; ++i) { 1798 if (renderer->texture_formats[i] == surface->format) { 1799 format = surface->format; 1800 break; 1801 } 1802 } 1803 } 1804 1805 // Look for 10-bit pixel formats if needed 1806 if (format == SDL_PIXELFORMAT_UNKNOWN && SDL_ISPIXELFORMAT_10BIT(surface->format)) { 1807 for (i = 0; i < renderer->num_texture_formats; ++i) { 1808 if (SDL_ISPIXELFORMAT_10BIT(renderer->texture_formats[i])) { 1809 format = renderer->texture_formats[i]; 1810 break; 1811 } 1812 } 1813 } 1814 1815 // Look for floating point pixel formats if needed 1816 if (format == SDL_PIXELFORMAT_UNKNOWN && 1817 (SDL_ISPIXELFORMAT_10BIT(surface->format) || SDL_ISPIXELFORMAT_FLOAT(surface->format))) { 1818 for (i = 0; i < renderer->num_texture_formats; ++i) { 1819 if (SDL_ISPIXELFORMAT_FLOAT(renderer->texture_formats[i])) { 1820 format = renderer->texture_formats[i]; 1821 break; 1822 } 1823 } 1824 } 1825 1826 // Fallback, choose a valid pixel format 1827 if (format == SDL_PIXELFORMAT_UNKNOWN) { 1828 format = renderer->texture_formats[0]; 1829 1830 // See what the best texture format is 1831 bool needAlpha; 1832 if (SDL_ISPIXELFORMAT_ALPHA(surface->format) || SDL_SurfaceHasColorKey(surface)) { 1833 needAlpha = true; 1834 } else { 1835 needAlpha = false; 1836 } 1837 1838 // If palette contains alpha values, promotes to alpha format 1839 if (surface->palette) { 1840 bool is_opaque, has_alpha_channel; 1841 SDL_DetectPalette(surface->palette, &is_opaque, &has_alpha_channel); 1842 if (!is_opaque) { 1843 needAlpha = true; 1844 } 1845 } 1846 1847 // Indexed formats don't support the transparency needed for color-keyed surfaces 1848 bool preferIndexed = SDL_ISPIXELFORMAT_INDEXED(surface->format) && !needAlpha; 1849 1850 for (i = 0; i < renderer->num_texture_formats; ++i) { 1851 if (!SDL_ISPIXELFORMAT_FOURCC(renderer->texture_formats[i]) && 1852 SDL_ISPIXELFORMAT_ALPHA(renderer->texture_formats[i]) == needAlpha && 1853 SDL_ISPIXELFORMAT_INDEXED(renderer->texture_formats[i]) == preferIndexed) { 1854 format = renderer->texture_formats[i]; 1855 break; 1856 } 1857 } 1858 } 1859 1860 surface_colorspace = SDL_GetSurfaceColorspace(surface); 1861 texture_colorspace = surface_colorspace; 1862 1863 if (surface_colorspace == SDL_COLORSPACE_SRGB_LINEAR || 1864 SDL_COLORSPACETRANSFER(surface_colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) { 1865 if (SDL_ISPIXELFORMAT_FLOAT(format)) { 1866 texture_colorspace = SDL_COLORSPACE_SRGB_LINEAR; 1867 } else if (SDL_ISPIXELFORMAT_10BIT(format)) { 1868 texture_colorspace = SDL_COLORSPACE_HDR10; 1869 } else { 1870 texture_colorspace = SDL_COLORSPACE_SRGB; 1871 } 1872 } 1873 1874 props = SDL_CreateProperties(); 1875 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, texture_colorspace); 1876 if (surface_colorspace == texture_colorspace) { 1877 SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, 1878 SDL_GetSurfaceSDRWhitePoint(surface, surface_colorspace)); 1879 } 1880 SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, 1881 SDL_GetSurfaceHDRHeadroom(surface, surface_colorspace)); 1882 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); 1883 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); 1884 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, surface->w); 1885 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, surface->h); 1886 texture = SDL_CreateTextureWithProperties(renderer, props); 1887 SDL_DestroyProperties(props); 1888 if (!texture) { 1889 return NULL; 1890 } 1891 1892 if (!SDL_UpdateTextureFromSurface(texture, NULL, surface)) { 1893 SDL_DestroyTexture(texture); 1894 return NULL; 1895 } 1896 1897 return texture; 1898} 1899 1900SDL_Renderer *SDL_GetRendererFromTexture(SDL_Texture *texture) 1901{ 1902 CHECK_TEXTURE_MAGIC(texture, NULL); 1903 1904 return texture->renderer; 1905} 1906 1907SDL_PropertiesID SDL_GetTextureProperties(SDL_Texture *texture) 1908{ 1909 CHECK_TEXTURE_MAGIC(texture, 0); 1910 1911 if (texture->props == 0) { 1912 texture->props = SDL_CreateProperties(); 1913 } 1914 return texture->props; 1915} 1916 1917bool SDL_GetTextureSize(SDL_Texture *texture, float *w, float *h) 1918{ 1919 if (w) { 1920 *w = 0; 1921 } 1922 if (h) { 1923 *h = 0; 1924 } 1925 1926 CHECK_TEXTURE_MAGIC(texture, false); 1927 1928 if (w) { 1929 *w = (float)texture->w; 1930 } 1931 if (h) { 1932 *h = (float)texture->h; 1933 } 1934 return true; 1935} 1936 1937bool SDL_SetTexturePalette(SDL_Texture *texture, SDL_Palette *palette) 1938{ 1939 CHECK_TEXTURE_MAGIC(texture, false); 1940 1941 CHECK_PARAM(!SDL_ISPIXELFORMAT_INDEXED(texture->format)) { 1942 return SDL_SetError("Texture isn't palettized format"); 1943 } 1944 1945 CHECK_PARAM(palette && palette->ncolors > (1 << SDL_BITSPERPIXEL(texture->format))) { 1946 return SDL_SetError("Palette doesn't match surface format"); 1947 } 1948 1949 if (palette != texture->public_palette) { 1950 SDL_Renderer *renderer = texture->renderer; 1951 1952 if (texture->public_palette) { 1953 SDL_DestroyPalette(texture->public_palette); 1954 1955 if (!texture->native) { 1956 // Clean up the texture palette 1957 --texture->palette->refcount; 1958 if (texture->palette->refcount == 0) { 1959 FlushRenderCommandsIfPaletteNeeded(renderer, texture->palette); 1960 renderer->DestroyPalette(renderer, texture->palette); 1961 SDL_RemoveFromHashTable(renderer->palettes, texture->public_palette); 1962 } 1963 texture->palette = NULL; 1964 } 1965 } 1966 1967 texture->public_palette = palette; 1968 texture->palette_version = 0; 1969 1970 if (texture->public_palette) { 1971 ++texture->public_palette->refcount; 1972 1973 if (!texture->native) { 1974 if (SDL_FindInHashTable(renderer->palettes, palette, (const void **)&texture->palette)) { 1975 ++texture->palette->refcount; 1976 } else { 1977 SDL_TexturePalette *texture_palette = (SDL_TexturePalette *)SDL_calloc(1, sizeof(*texture_palette)); 1978 if (!texture_palette) { 1979 SDL_SetTexturePalette(texture, NULL); 1980 return false; 1981 } 1982 if (!renderer->CreatePalette(renderer, texture_palette)) { 1983 renderer->DestroyPalette(renderer, texture_palette); 1984 SDL_SetTexturePalette(texture, NULL); 1985 return false; 1986 } 1987 texture->palette = texture_palette; 1988 texture->palette->refcount = 1; 1989 1990 if (!SDL_InsertIntoHashTable(renderer->palettes, palette, texture->palette, false)) { 1991 SDL_SetTexturePalette(texture, NULL); 1992 return false; 1993 } 1994 } 1995 } 1996 1997 if (!texture->native && renderer->ChangeTexturePalette) { 1998 renderer->ChangeTexturePalette(renderer, texture); 1999 } 2000 } 2001 2002 if (texture->palette_surface) { 2003 SDL_SetSurfacePalette(texture->palette_surface, palette); 2004 } 2005 } 2006 return true; 2007} 2008 2009SDL_Palette *SDL_GetTexturePalette(SDL_Texture *texture) 2010{ 2011 CHECK_TEXTURE_MAGIC(texture, NULL); 2012 2013 return texture->public_palette; 2014} 2015 2016bool SDL_SetTextureColorMod(SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b) 2017{ 2018 const float fR = (float)r / 255.0f; 2019 const float fG = (float)g / 255.0f; 2020 const float fB = (float)b / 255.0f; 2021 2022 return SDL_SetTextureColorModFloat(texture, fR, fG, fB); 2023} 2024 2025bool SDL_SetTextureColorModFloat(SDL_Texture *texture, float r, float g, float b) 2026{ 2027 CHECK_TEXTURE_MAGIC(texture, false); 2028 2029 texture->color.r = r; 2030 texture->color.g = g; 2031 texture->color.b = b; 2032 if (texture->native) { 2033 return SDL_SetTextureColorModFloat(texture->native, r, g, b); 2034 } 2035 return true; 2036} 2037 2038bool SDL_GetTextureColorMod(SDL_Texture *texture, Uint8 *r, Uint8 *g, Uint8 *b) 2039{ 2040 float fR = 1.0f, fG = 1.0f, fB = 1.0f; 2041 2042 if (!SDL_GetTextureColorModFloat(texture, &fR, &fG, &fB)) { 2043 if (r) { 2044 *r = 255; 2045 } 2046 if (g) { 2047 *g = 255; 2048 } 2049 if (b) { 2050 *b = 255; 2051 } 2052 return false; 2053 } 2054 2055 if (r) { 2056 *r = (Uint8)SDL_roundf(SDL_clamp(fR, 0.0f, 1.0f) * 255.0f); 2057 } 2058 if (g) { 2059 *g = (Uint8)SDL_roundf(SDL_clamp(fG, 0.0f, 1.0f) * 255.0f); 2060 } 2061 if (b) { 2062 *b = (Uint8)SDL_roundf(SDL_clamp(fB, 0.0f, 1.0f) * 255.0f); 2063 } 2064 return true; 2065} 2066 2067bool SDL_GetTextureColorModFloat(SDL_Texture *texture, float *r, float *g, float *b) 2068{ 2069 SDL_FColor color; 2070 2071 if (r) { 2072 *r = 1.0f; 2073 } 2074 if (g) { 2075 *g = 1.0f; 2076 } 2077 if (b) { 2078 *b = 1.0f; 2079 } 2080 2081 CHECK_TEXTURE_MAGIC(texture, false); 2082 2083 color = texture->color; 2084 2085 if (r) { 2086 *r = color.r; 2087 } 2088 if (g) { 2089 *g = color.g; 2090 } 2091 if (b) { 2092 *b = color.b; 2093 } 2094 return true; 2095} 2096 2097bool SDL_SetTextureAlphaMod(SDL_Texture *texture, Uint8 alpha) 2098{ 2099 const float fA = (float)alpha / 255.0f; 2100 2101 return SDL_SetTextureAlphaModFloat(texture, fA); 2102} 2103 2104bool SDL_SetTextureAlphaModFloat(SDL_Texture *texture, float alpha) 2105{ 2106 CHECK_TEXTURE_MAGIC(texture, false); 2107 2108 texture->color.a = alpha; 2109 if (texture->native) { 2110 return SDL_SetTextureAlphaModFloat(texture->native, alpha); 2111 } 2112 return true; 2113} 2114 2115bool SDL_GetTextureAlphaMod(SDL_Texture *texture, Uint8 *alpha) 2116{ 2117 float fA = 1.0f; 2118 2119 if (!SDL_GetTextureAlphaModFloat(texture, &fA)) { 2120 if (alpha) { 2121 *alpha = 255; 2122 } 2123 return false; 2124 } 2125 2126 if (alpha) { 2127 *alpha = (Uint8)SDL_roundf(SDL_clamp(fA, 0.0f, 1.0f) * 255.0f); 2128 } 2129 return true; 2130} 2131 2132bool SDL_GetTextureAlphaModFloat(SDL_Texture *texture, float *alpha) 2133{ 2134 if (alpha) { 2135 *alpha = 1.0f; 2136 } 2137 2138 CHECK_TEXTURE_MAGIC(texture, false); 2139 2140 if (alpha) { 2141 *alpha = texture->color.a; 2142 } 2143 return true; 2144} 2145 2146bool SDL_SetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode blendMode) 2147{ 2148 SDL_Renderer *renderer; 2149 2150 CHECK_TEXTURE_MAGIC(texture, false); 2151 2152 CHECK_PARAM(blendMode == SDL_BLENDMODE_INVALID) { 2153 return SDL_InvalidParamError("blendMode"); 2154 } 2155 2156 renderer = texture->renderer; 2157 if (!IsSupportedBlendMode(renderer, blendMode)) { 2158 return SDL_Unsupported(); 2159 } 2160 texture->blendMode = blendMode; 2161 if (texture->native) { 2162 return SDL_SetTextureBlendMode(texture->native, blendMode); 2163 } 2164 return true; 2165} 2166 2167bool SDL_GetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode *blendMode) 2168{ 2169 if (blendMode) { 2170 *blendMode = SDL_BLENDMODE_INVALID; 2171 } 2172 2173 CHECK_TEXTURE_MAGIC(texture, false); 2174 2175 if (blendMode) { 2176 *blendMode = texture->blendMode; 2177 } 2178 return true; 2179} 2180 2181bool SDL_SetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode scaleMode) 2182{ 2183 CHECK_TEXTURE_MAGIC(texture, false); 2184 2185 switch (scaleMode) { 2186 case SDL_SCALEMODE_NEAREST: 2187 case SDL_SCALEMODE_PIXELART: 2188 case SDL_SCALEMODE_LINEAR: 2189 break; 2190 default: 2191 return SDL_InvalidParamError("scaleMode"); 2192 } 2193 2194 texture->scaleMode = scaleMode; 2195 2196 if (texture->native) { 2197 return SDL_SetTextureScaleMode(texture->native, scaleMode); 2198 } 2199 return true; 2200} 2201 2202bool SDL_GetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode *scaleMode) 2203{ 2204 if (scaleMode) { 2205 *scaleMode = SDL_SCALEMODE_INVALID; 2206 } 2207 2208 CHECK_TEXTURE_MAGIC(texture, false); 2209 2210 if (scaleMode) { 2211 *scaleMode = texture->scaleMode; 2212 } 2213 return true; 2214} 2215 2216#ifdef SDL_HAVE_YUV 2217static bool SDL_UpdateTextureYUV(SDL_Texture *texture, const SDL_Rect *rect, 2218 const void *pixels, int pitch) 2219{ 2220 SDL_Texture *native = texture->native; 2221 SDL_Rect full_rect; 2222 bool result = true; 2223 2224 if (!SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch)) { 2225 return false; 2226 } 2227 2228 full_rect.x = 0; 2229 full_rect.y = 0; 2230 full_rect.w = texture->w; 2231 full_rect.h = texture->h; 2232 rect = &full_rect; 2233 2234 if (texture->access == SDL_TEXTUREACCESS_STREAMING) { 2235 // We can lock the texture and copy to it 2236 void *native_pixels = NULL; 2237 int native_pitch = 0; 2238 2239 if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) { 2240 return false; 2241 } 2242 result = SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, rect->w, rect->h, native_pixels, native_pitch); 2243 SDL_UnlockTexture(native); 2244 } else { 2245 // Use a temporary buffer for updating 2246 const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3); 2247 const size_t alloclen = (size_t)rect->h * temp_pitch; 2248 if (alloclen > 0) { 2249 void *temp_pixels = SDL_malloc(alloclen); 2250 if (!temp_pixels) { 2251 return false; 2252 } 2253 result = SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, rect->w, rect->h, temp_pixels, temp_pitch); 2254 if (result) { 2255 SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch); 2256 } 2257 SDL_free(temp_pixels); 2258 } 2259 } 2260 return result; 2261} 2262#endif // SDL_HAVE_YUV 2263 2264static bool SDL_UpdateTexturePaletteSurface(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) 2265{ 2266 SDL_Surface *surface = texture->palette_surface; 2267 2268 const Uint8 *src = (const Uint8 *)pixels; 2269 Uint8 *dst = ((Uint8 *)surface->pixels) + rect->y * surface->pitch + (rect->x * SDL_BITSPERPIXEL(texture->format)) / 8; 2270 int w = ((rect->w * SDL_BITSPERPIXEL(texture->format)) + 7) / 8; 2271 int h = rect->h; 2272 while (h--) { 2273 SDL_memcpy(dst, src, w); 2274 src += pitch; 2275 dst += surface->pitch; 2276 } 2277 texture->palette_version = 0; 2278 return true; 2279} 2280 2281static bool SDL_UpdateTextureNative(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) 2282{ 2283 SDL_Texture *native = texture->native; 2284 2285 if (texture->access == SDL_TEXTUREACCESS_STREAMING) { 2286 // We can lock the texture and copy to it 2287 void *native_pixels = NULL; 2288 int native_pitch = 0; 2289 2290 if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) { 2291 return false; 2292 } 2293 SDL_ConvertPixelsAndColorspace(rect->w, rect->h, 2294 texture->format, texture->colorspace, 0, pixels, pitch, 2295 native->format, native->colorspace, 0, native_pixels, native_pitch); 2296 SDL_UnlockTexture(native); 2297 } else { 2298 // Use a temporary buffer for updating 2299 const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3); 2300 const size_t alloclen = (size_t)rect->h * temp_pitch; 2301 if (alloclen > 0) { 2302 void *temp_pixels = SDL_malloc(alloclen); 2303 if (!temp_pixels) { 2304 return false; 2305 } 2306 SDL_ConvertPixelsAndColorspace(rect->w, rect->h, 2307 texture->format, texture->colorspace, 0, pixels, pitch, 2308 native->format, native->colorspace, 0, temp_pixels, temp_pitch); 2309 SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch); 2310 SDL_free(temp_pixels); 2311 } 2312 } 2313 return true; 2314} 2315 2316bool SDL_UpdateTexture(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) 2317{ 2318 SDL_Rect real_rect; 2319 2320 CHECK_TEXTURE_MAGIC(texture, false); 2321 2322 CHECK_PARAM(!pixels) { 2323 return SDL_InvalidParamError("pixels"); 2324 } 2325 CHECK_PARAM(!pitch) { 2326 return SDL_InvalidParamError("pitch"); 2327 } 2328 2329 real_rect.x = 0; 2330 real_rect.y = 0; 2331 real_rect.w = texture->w; 2332 real_rect.h = texture->h; 2333 if (rect) { 2334 if (!SDL_GetRectIntersection(rect, &real_rect, &real_rect)) { 2335 return true; 2336 } 2337 } 2338 2339 if (real_rect.w == 0 || real_rect.h == 0) { 2340 return true; // nothing to do. 2341#ifdef SDL_HAVE_YUV 2342 } else if (texture->yuv) { 2343 return SDL_UpdateTextureYUV(texture, &real_rect, pixels, pitch); 2344#endif 2345 } else if (texture->palette_surface) { 2346 return SDL_UpdateTexturePaletteSurface(texture, &real_rect, pixels, pitch); 2347 } else if (texture->native) { 2348 return SDL_UpdateTextureNative(texture, &real_rect, pixels, pitch); 2349 } else { 2350 SDL_Renderer *renderer = texture->renderer; 2351 if (!FlushRenderCommandsIfTextureNeeded(texture)) { 2352 return false; 2353 } 2354 return renderer->UpdateTexture(renderer, texture, &real_rect, pixels, pitch); 2355 } 2356} 2357 2358#ifdef SDL_HAVE_YUV 2359static bool SDL_UpdateTextureYUVPlanar(SDL_Texture *texture, const SDL_Rect *rect, 2360 const Uint8 *Yplane, int Ypitch, 2361 const Uint8 *Uplane, int Upitch, 2362 const Uint8 *Vplane, int Vpitch) 2363{ 2364 SDL_Texture *native = texture->native; 2365 SDL_Rect full_rect; 2366 bool result = true; 2367 2368 if (!SDL_SW_UpdateYUVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch)) { 2369 return false; 2370 } 2371 2372 full_rect.x = 0; 2373 full_rect.y = 0; 2374 full_rect.w = texture->w; 2375 full_rect.h = texture->h; 2376 rect = &full_rect; 2377 2378 if (!rect->w || !rect->h) { 2379 return true; // nothing to do. 2380 } 2381 2382 if (texture->access == SDL_TEXTUREACCESS_STREAMING) { 2383 // We can lock the texture and copy to it 2384 void *native_pixels = NULL; 2385 int native_pitch = 0; 2386 2387 if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) { 2388 return false; 2389 } 2390 result = SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, rect->w, rect->h, native_pixels, native_pitch); 2391 SDL_UnlockTexture(native); 2392 } else { 2393 // Use a temporary buffer for updating 2394 const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3); 2395 const size_t alloclen = (size_t)rect->h * temp_pitch; 2396 if (alloclen > 0) { 2397 void *temp_pixels = SDL_malloc(alloclen); 2398 if (!temp_pixels) { 2399 return false; 2400 } 2401 result = SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, rect->w, rect->h, temp_pixels, temp_pitch); 2402 if (result) { 2403 SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch); 2404 } 2405 SDL_free(temp_pixels); 2406 } 2407 } 2408 return result; 2409} 2410 2411static bool SDL_UpdateTextureNVPlanar(SDL_Texture *texture, const SDL_Rect *rect, 2412 const Uint8 *Yplane, int Ypitch, 2413 const Uint8 *UVplane, int UVpitch) 2414{ 2415 SDL_Texture *native = texture->native; 2416 SDL_Rect full_rect; 2417 bool result = true; 2418 2419 if (!SDL_SW_UpdateNVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, UVplane, UVpitch)) { 2420 return false; 2421 } 2422 2423 full_rect.x = 0; 2424 full_rect.y = 0; 2425 full_rect.w = texture->w; 2426 full_rect.h = texture->h; 2427 rect = &full_rect; 2428 2429 if (!rect->w || !rect->h) { 2430 return true; // nothing to do. 2431 } 2432 2433 if (texture->access == SDL_TEXTUREACCESS_STREAMING) { 2434 // We can lock the texture and copy to it 2435 void *native_pixels = NULL; 2436 int native_pitch = 0; 2437 2438 if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) { 2439 return false; 2440 } 2441 result = SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, rect->w, rect->h, native_pixels, native_pitch); 2442 SDL_UnlockTexture(native); 2443 } else { 2444 // Use a temporary buffer for updating 2445 const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3); 2446 const size_t alloclen = (size_t)rect->h * temp_pitch; 2447 if (alloclen > 0) { 2448 void *temp_pixels = SDL_malloc(alloclen); 2449 if (!temp_pixels) { 2450 return false; 2451 } 2452 result = SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, rect->w, rect->h, temp_pixels, temp_pitch); 2453 if (result) { 2454 SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch); 2455 } 2456 SDL_free(temp_pixels); 2457 } 2458 } 2459 return result; 2460} 2461 2462#endif // SDL_HAVE_YUV 2463 2464bool SDL_UpdateYUVTexture(SDL_Texture *texture, const SDL_Rect *rect, 2465 const Uint8 *Yplane, int Ypitch, 2466 const Uint8 *Uplane, int Upitch, 2467 const Uint8 *Vplane, int Vpitch) 2468{ 2469#ifdef SDL_HAVE_YUV 2470 SDL_Renderer *renderer; 2471 SDL_Rect real_rect; 2472 2473 CHECK_TEXTURE_MAGIC(texture, false); 2474 2475 CHECK_PARAM(!Yplane) { 2476 return SDL_InvalidParamError("Yplane"); 2477 } 2478 CHECK_PARAM(!Ypitch) { 2479 return SDL_InvalidParamError("Ypitch"); 2480 } 2481 CHECK_PARAM(!Uplane) { 2482 return SDL_InvalidParamError("Uplane"); 2483 } 2484 CHECK_PARAM(!Upitch) { 2485 return SDL_InvalidParamError("Upitch"); 2486 } 2487 CHECK_PARAM(!Vplane) { 2488 return SDL_InvalidParamError("Vplane"); 2489 } 2490 CHECK_PARAM(!Vpitch) { 2491 return SDL_InvalidParamError("Vpitch"); 2492 } 2493 2494 CHECK_PARAM(texture->format != SDL_PIXELFORMAT_YV12 && 2495 texture->format != SDL_PIXELFORMAT_IYUV) { 2496 return SDL_SetError("Texture format must be YV12 or IYUV"); 2497 } 2498 2499 real_rect.x = 0; 2500 real_rect.y = 0; 2501 real_rect.w = texture->w; 2502 real_rect.h = texture->h; 2503 if (rect) { 2504 SDL_GetRectIntersection(rect, &real_rect, &real_rect); 2505 } 2506 2507 if (real_rect.w == 0 || real_rect.h == 0) { 2508 return true; // nothing to do. 2509 } 2510 2511 if (texture->yuv) { 2512 return SDL_UpdateTextureYUVPlanar(texture, &real_rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch); 2513 } else { 2514 SDL_assert(!texture->native); 2515 renderer = texture->renderer; 2516 SDL_assert(renderer->UpdateTextureYUV); 2517 if (renderer->UpdateTextureYUV) { 2518 if (!FlushRenderCommandsIfTextureNeeded(texture)) { 2519 return false; 2520 } 2521 return renderer->UpdateTextureYUV(renderer, texture, &real_rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch); 2522 } else { 2523 return SDL_Unsupported(); 2524 } 2525 } 2526#else 2527 return false; 2528#endif 2529} 2530 2531bool SDL_UpdateNVTexture(SDL_Texture *texture, const SDL_Rect *rect, 2532 const Uint8 *Yplane, int Ypitch, 2533 const Uint8 *UVplane, int UVpitch) 2534{ 2535#ifdef SDL_HAVE_YUV 2536 SDL_Renderer *renderer; 2537 SDL_Rect real_rect; 2538 2539 CHECK_TEXTURE_MAGIC(texture, false); 2540 2541 CHECK_PARAM(!Yplane) { 2542 return SDL_InvalidParamError("Yplane"); 2543 } 2544 CHECK_PARAM(!Ypitch) { 2545 return SDL_InvalidParamError("Ypitch"); 2546 } 2547 CHECK_PARAM(!UVplane) { 2548 return SDL_InvalidParamError("UVplane"); 2549 } 2550 CHECK_PARAM(!UVpitch) { 2551 return SDL_InvalidParamError("UVpitch"); 2552 } 2553 2554 CHECK_PARAM(texture->format != SDL_PIXELFORMAT_NV12 && 2555 texture->format != SDL_PIXELFORMAT_NV21 && 2556 texture->format != SDL_PIXELFORMAT_P010) { 2557 return SDL_SetError("Texture format must be NV12, NV21, or P010"); 2558 } 2559 2560 real_rect.x = 0; 2561 real_rect.y = 0; 2562 real_rect.w = texture->w; 2563 real_rect.h = texture->h; 2564 if (rect) { 2565 SDL_GetRectIntersection(rect, &real_rect, &real_rect); 2566 } 2567 2568 if (real_rect.w == 0 || real_rect.h == 0) { 2569 return true; // nothing to do. 2570 } 2571 2572 if (texture->yuv) { 2573 return SDL_UpdateTextureNVPlanar(texture, &real_rect, Yplane, Ypitch, UVplane, UVpitch); 2574 } else { 2575 SDL_assert(!texture->native); 2576 renderer = texture->renderer; 2577 SDL_assert(renderer->UpdateTextureNV); 2578 if (renderer->UpdateTextureNV) { 2579 if (!FlushRenderCommandsIfTextureNeeded(texture)) { 2580 return false; 2581 } 2582 return renderer->UpdateTextureNV(renderer, texture, &real_rect, Yplane, Ypitch, UVplane, UVpitch); 2583 } else { 2584 return SDL_Unsupported(); 2585 } 2586 } 2587#else 2588 return false; 2589#endif 2590} 2591 2592#ifdef SDL_HAVE_YUV 2593static bool SDL_LockTextureYUV(SDL_Texture *texture, const SDL_Rect *rect, 2594 void **pixels, int *pitch) 2595{ 2596 return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch); 2597} 2598#endif // SDL_HAVE_YUV 2599 2600static bool SDL_LockTexturePaletteSurface(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch) 2601{ 2602 SDL_Surface *surface = texture->palette_surface; 2603 2604 *pixels = ((Uint8 *)surface->pixels) + rect->y * surface->pitch + (rect->x * SDL_BITSPERPIXEL(texture->format)) / 8; 2605 *pitch = surface->pitch; 2606 return true; 2607} 2608 2609static bool SDL_LockTextureNative(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch) 2610{ 2611 texture->locked_rect = *rect; 2612 *pixels = (void *)((Uint8 *)texture->pixels + 2613 rect->y * texture->pitch + 2614 rect->x * SDL_BYTESPERPIXEL(texture->format)); 2615 *pitch = texture->pitch; 2616 return true; 2617} 2618 2619bool SDL_LockTexture(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch) 2620{ 2621 SDL_Rect full_rect; 2622 2623 CHECK_TEXTURE_MAGIC(texture, false); 2624 2625 CHECK_PARAM(texture->access != SDL_TEXTUREACCESS_STREAMING) { 2626 return SDL_SetError("SDL_LockTexture(): texture must be streaming"); 2627 } 2628 2629 if (!rect) { 2630 full_rect.x = 0; 2631 full_rect.y = 0; 2632 full_rect.w = texture->w; 2633 full_rect.h = texture->h; 2634 rect = &full_rect; 2635 } 2636 2637#ifdef SDL_HAVE_YUV 2638 if (texture->yuv) { 2639 if (!FlushRenderCommandsIfTextureNeeded(texture)) { 2640 return false; 2641 } 2642 return SDL_LockTextureYUV(texture, rect, pixels, pitch); 2643 } else 2644#endif 2645 if (texture->palette_surface) { 2646 return SDL_LockTexturePaletteSurface(texture, rect, pixels, pitch); 2647 } else if (texture->native) { 2648 // Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. 2649 return SDL_LockTextureNative(texture, rect, pixels, pitch); 2650 } else { 2651 SDL_Renderer *renderer = texture->renderer; 2652 if (!FlushRenderCommandsIfTextureNeeded(texture)) { 2653 return false; 2654 } 2655 return renderer->LockTexture(renderer, texture, rect, pixels, pitch); 2656 } 2657} 2658 2659bool SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect, SDL_Surface **surface) 2660{ 2661 SDL_Rect real_rect; 2662 void *pixels = NULL; 2663 int pitch = 0; // fix static analysis 2664 2665 CHECK_TEXTURE_MAGIC(texture, false); 2666 2667 CHECK_PARAM(!surface) { 2668 return SDL_InvalidParamError("surface"); 2669 } 2670 2671 real_rect.x = 0; 2672 real_rect.y = 0; 2673 real_rect.w = texture->w; 2674 real_rect.h = texture->h; 2675 if (rect) { 2676 SDL_GetRectIntersection(rect, &real_rect, &real_rect); 2677 } 2678 2679 if (!SDL_LockTexture(texture, &real_rect, &pixels, &pitch)) { 2680 return false; 2681 } 2682 2683 texture->locked_surface = SDL_CreateSurfaceFrom(real_rect.w, real_rect.h, texture->format, pixels, pitch); 2684 if (!texture->locked_surface) { 2685 SDL_UnlockTexture(texture); 2686 return false; 2687 } 2688 if (texture->public_palette) { 2689 SDL_SetSurfacePalette(texture->locked_surface, texture->public_palette); 2690 } 2691 2692 *surface = texture->locked_surface; 2693 return true; 2694} 2695 2696#ifdef SDL_HAVE_YUV 2697static void SDL_UnlockTextureYUV(SDL_Texture *texture) 2698{ 2699 SDL_Texture *native = texture->native; 2700 void *native_pixels = NULL; 2701 int native_pitch = 0; 2702 SDL_Rect rect; 2703 2704 rect.x = 0; 2705 rect.y = 0; 2706 rect.w = texture->w; 2707 rect.h = texture->h; 2708 2709 if (!SDL_LockTexture(native, &rect, &native_pixels, &native_pitch)) { 2710 return; 2711 } 2712 SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format, 2713 rect.w, rect.h, native_pixels, native_pitch); 2714 SDL_UnlockTexture(native); 2715} 2716#endif // SDL_HAVE_YUV 2717 2718static void SDL_UnlockTexturePaletteSurface(SDL_Texture *texture) 2719{ 2720 texture->palette_version = 0; 2721} 2722 2723static void SDL_UnlockTextureNative(SDL_Texture *texture) 2724{ 2725 SDL_Texture *native = texture->native; 2726 void *native_pixels = NULL; 2727 int native_pitch = 0; 2728 const SDL_Rect *rect = &texture->locked_rect; 2729 const void *pixels = (void *)((Uint8 *)texture->pixels + 2730 rect->y * texture->pitch + 2731 rect->x * SDL_BYTESPERPIXEL(texture->format)); 2732 int pitch = texture->pitch; 2733 2734 if (!SDL_LockTexture(native, rect, &native_pixels, &native_pitch)) { 2735 return; 2736 } 2737 SDL_ConvertPixels(rect->w, rect->h, 2738 texture->format, pixels, pitch, 2739 native->format, native_pixels, native_pitch); 2740 SDL_UnlockTexture(native); 2741} 2742 2743void SDL_UnlockTexture(SDL_Texture *texture) 2744{ 2745 CHECK_TEXTURE_MAGIC(texture,); 2746 2747 if (texture->access != SDL_TEXTUREACCESS_STREAMING) { 2748 return; 2749 } 2750 2751#ifdef SDL_HAVE_YUV 2752 if (texture->yuv) { 2753 SDL_UnlockTextureYUV(texture); 2754 } else 2755#endif 2756 if (texture->palette_surface) { 2757 SDL_UnlockTexturePaletteSurface(texture); 2758 } else if (texture->native) { 2759 SDL_UnlockTextureNative(texture); 2760 } else { 2761 SDL_Renderer *renderer = texture->renderer; 2762 renderer->UnlockTexture(renderer, texture); 2763 } 2764 2765 if (texture->locked_surface) { 2766 SDL_DestroySurface(texture->locked_surface); 2767 texture->locked_surface = NULL; 2768 } 2769} 2770 2771bool SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) 2772{ 2773 CHECK_RENDERER_MAGIC(renderer, false); 2774 2775 // texture == NULL is valid and means reset the target to the window 2776 if (texture) { 2777 CHECK_TEXTURE_MAGIC(texture, false); 2778 2779 CHECK_PARAM(renderer != texture->renderer) { 2780 return SDL_SetError("Texture was not created with this renderer"); 2781 } 2782 CHECK_PARAM(texture->access != SDL_TEXTUREACCESS_TARGET) { 2783 return SDL_SetError("Texture not created with SDL_TEXTUREACCESS_TARGET"); 2784 } 2785 2786 if (texture->native) { 2787 // Always render to the native texture 2788 texture = texture->native; 2789 } 2790 } 2791 2792 if (texture == renderer->target) { 2793 // Nothing to do! 2794 return true; 2795 } 2796 2797 FlushRenderCommands(renderer); // time to send everything to the GPU! 2798 2799 SDL_LockMutex(renderer->target_mutex); 2800 2801 renderer->target = texture; 2802 if (texture) { 2803 renderer->view = &texture->view; 2804 } else { 2805 renderer->view = &renderer->main_view; 2806 } 2807 UpdateColorScale(renderer); 2808 2809 if (!renderer->SetRenderTarget(renderer, texture)) { 2810 SDL_UnlockMutex(renderer->target_mutex); 2811 return false; 2812 } 2813 2814 SDL_UnlockMutex(renderer->target_mutex); 2815 2816 if (!QueueCmdSetViewport(renderer)) { 2817 return false; 2818 } 2819 if (!QueueCmdSetClipRect(renderer)) { 2820 return false; 2821 } 2822 2823 // All set! 2824 return true; 2825} 2826 2827SDL_Texture *SDL_GetRenderTarget(SDL_Renderer *renderer) 2828{ 2829 CHECK_RENDERER_MAGIC(renderer, NULL); 2830 2831 if (!renderer->target) { 2832 return NULL; 2833 } 2834 return (SDL_Texture *) SDL_GetPointerProperty(SDL_GetTextureProperties(renderer->target), SDL_PROP_TEXTURE_PARENT_POINTER, renderer->target); 2835} 2836 2837static void UpdateLogicalPresentation(SDL_Renderer *renderer) 2838{ 2839 SDL_RenderViewState *view = renderer->view; 2840 const bool is_main_view = (view == &renderer->main_view); 2841 const float logical_w = view->logical_w; 2842 const float logical_h = view->logical_h; 2843 int iwidth, iheight; 2844 2845 if (is_main_view) { 2846 SDL_GetRenderOutputSize(renderer, &iwidth, &iheight); 2847 } else { 2848 SDL_assert(renderer->target != NULL); 2849 iwidth = (int)renderer->target->w; 2850 iheight = (int)renderer->target->h; 2851 } 2852 2853 view->logical_src_rect.x = 0.0f; 2854 view->logical_src_rect.y = 0.0f; 2855 view->logical_src_rect.w = logical_w; 2856 view->logical_src_rect.h = logical_h; 2857 2858 if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_DISABLED) { 2859 view->logical_dst_rect.x = 0.0f; 2860 view->logical_dst_rect.y = 0.0f; 2861 view->logical_dst_rect.w = iwidth; 2862 view->logical_dst_rect.h = iheight; 2863 view->logical_offset.x = view->logical_offset.y = 0.0f; 2864 view->logical_scale.x = view->logical_scale.y = 1.0f; 2865 view->current_scale.x = view->scale.x; // skip the multiplications against 1.0f. 2866 view->current_scale.y = view->scale.y; 2867 } else { 2868 const float output_w = (float)iwidth; 2869 const float output_h = (float)iheight; 2870 const float want_aspect = logical_w / logical_h; 2871 const float real_aspect = output_w / output_h; 2872 2873 if ((logical_w <= 0.0f) || (logical_h <= 0.0f)) { 2874 view->logical_dst_rect.x = 0.0f; 2875 view->logical_dst_rect.y = 0.0f; 2876 view->logical_dst_rect.w = output_w; 2877 view->logical_dst_rect.h = output_h; 2878 } else if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE) { 2879 float scale; 2880 if (want_aspect > real_aspect) { 2881 scale = (float)((int)output_w / (int)logical_w); // This an integer division! 2882 } else { 2883 scale = (float)((int)output_h / (int)logical_h); // This an integer division! 2884 } 2885 2886 if (scale < 1.0f) { 2887 scale = 1.0f; 2888 } 2889 2890 view->logical_dst_rect.w = SDL_floorf(logical_w * scale); 2891 view->logical_dst_rect.x = (output_w - view->logical_dst_rect.w) / 2.0f; 2892 view->logical_dst_rect.h = SDL_floorf(logical_h * scale); 2893 view->logical_dst_rect.y = (output_h - view->logical_dst_rect.h) / 2.0f; 2894 2895 } else if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_STRETCH || SDL_fabsf(want_aspect - real_aspect) < 0.0001f) { 2896 view->logical_dst_rect.x = 0.0f; 2897 view->logical_dst_rect.y = 0.0f; 2898 view->logical_dst_rect.w = output_w; 2899 view->logical_dst_rect.h = output_h; 2900 2901 } else if (want_aspect > real_aspect) { 2902 if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) { 2903 // We want a wider aspect ratio than is available - letterbox it 2904 const float scale = output_w / logical_w; 2905 view->logical_dst_rect.x = 0.0f; 2906 view->logical_dst_rect.w = output_w; 2907 view->logical_dst_rect.h = SDL_floorf(logical_h * scale); 2908 view->logical_dst_rect.y = (output_h - view->logical_dst_rect.h) / 2.0f; 2909 } else { // view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_OVERSCAN 2910 /* We want a wider aspect ratio than is available - 2911 zoom so logical height matches the real height 2912 and the width will grow off the screen 2913 */ 2914 const float scale = output_h / logical_h; 2915 view->logical_dst_rect.y = 0.0f; 2916 view->logical_dst_rect.h = output_h; 2917 view->logical_dst_rect.w = SDL_floorf(logical_w * scale); 2918 view->logical_dst_rect.x = (output_w - view->logical_dst_rect.w) / 2.0f; 2919 } 2920 } else { 2921 if (view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) { 2922 // We want a narrower aspect ratio than is available - use side-bars 2923 const float scale = output_h / logical_h; 2924 view->logical_dst_rect.y = 0.0f; 2925 view->logical_dst_rect.h = output_h; 2926 view->logical_dst_rect.w = SDL_floorf(logical_w * scale); 2927 view->logical_dst_rect.x = (output_w - view->logical_dst_rect.w) / 2.0f; 2928 } else { // view->logical_presentation_mode == SDL_LOGICAL_PRESENTATION_OVERSCAN 2929 /* We want a narrower aspect ratio than is available - 2930 zoom so logical width matches the real width 2931 and the height will grow off the screen 2932 */ 2933 const float scale = output_w / logical_w; 2934 view->logical_dst_rect.x = 0.0f; 2935 view->logical_dst_rect.w = output_w; 2936 view->logical_dst_rect.h = SDL_floorf(logical_h * scale); 2937 view->logical_dst_rect.y = (output_h - view->logical_dst_rect.h) / 2.0f; 2938 } 2939 } 2940 2941 view->logical_scale.x = (logical_w > 0.0f) ? view->logical_dst_rect.w / logical_w : 0.0f; 2942 view->logical_scale.y = (logical_h > 0.0f) ? view->logical_dst_rect.h / logical_h : 0.0f; 2943 view->current_scale.x = view->scale.x * view->logical_scale.x; 2944 view->current_scale.y = view->scale.y * view->logical_scale.y; 2945 view->logical_offset.x = view->logical_dst_rect.x; 2946 view->logical_offset.y = view->logical_dst_rect.y; 2947 } 2948 2949 if (is_main_view) { 2950 // 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. 2951 UpdateMainViewDimensions(renderer); 2952 } 2953 2954 view->pixel_w = (int) view->logical_dst_rect.w; 2955 view->pixel_h = (int) view->logical_dst_rect.h; 2956 UpdatePixelViewport(renderer, view); 2957 UpdatePixelClipRect(renderer, view); 2958 QueueCmdSetViewport(renderer); 2959 QueueCmdSetClipRect(renderer); 2960} 2961 2962bool SDL_SetRenderLogicalPresentation(SDL_Renderer *renderer, int w, int h, SDL_RendererLogicalPresentation mode) 2963{ 2964 CHECK_RENDERER_MAGIC(renderer, false); 2965 2966 SDL_RenderViewState *view = renderer->view; 2967 if (mode == SDL_LOGICAL_PRESENTATION_DISABLED) { 2968 view->logical_w = 0; 2969 view->logical_h = 0; 2970 } else { 2971 view->logical_w = w; 2972 view->logical_h = h; 2973 } 2974 view->logical_presentation_mode = mode; 2975 2976 UpdateLogicalPresentation(renderer); 2977 2978 return true; 2979} 2980 2981bool SDL_GetRenderLogicalPresentation(SDL_Renderer *renderer, int *w, int *h, SDL_RendererLogicalPresentation *mode) 2982{ 2983 #define SETVAL(ptr, val) if (ptr) { *ptr = val; } 2984 2985 SETVAL(w, 0); 2986 SETVAL(h, 0); 2987 SETVAL(mode, SDL_LOGICAL_PRESENTATION_DISABLED); 2988 2989 CHECK_RENDERER_MAGIC(renderer, false); 2990 2991 const SDL_RenderViewState *view = renderer->view; 2992 SETVAL(w, view->logical_w); 2993 SETVAL(h, view->logical_h); 2994 SETVAL(mode, view->logical_presentation_mode); 2995 2996 #undef SETVAL 2997 2998 return true; 2999} 3000 3001bool SDL_GetRenderLogicalPresentationRect(SDL_Renderer *renderer, SDL_FRect *rect) 3002{ 3003 if (rect) { 3004 SDL_zerop(rect); 3005 } 3006 3007 CHECK_RENDERER_MAGIC(renderer, false); 3008 3009 if (rect) { 3010 SDL_copyp(rect, &renderer->view->logical_dst_rect); 3011 } 3012 return true; 3013} 3014 3015static bool SDL_RenderVectorFromWindow(SDL_Renderer *renderer, float window_dx, float window_dy, float *dx, float *dy) 3016{ 3017 // Convert from window coordinates to pixels within the window 3018 window_dx *= renderer->dpi_scale.x; 3019 window_dy *= renderer->dpi_scale.y; 3020 3021 // Convert from pixels within the window to pixels within the view 3022 const SDL_RenderViewState *view = &renderer->main_view; 3023 if (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) { 3024 const SDL_FRect *src = &view->logical_src_rect; 3025 const SDL_FRect *dst = &view->logical_dst_rect; 3026 window_dx = (window_dx * src->w) / dst->w; 3027 window_dy = (window_dy * src->h) / dst->h; 3028 } 3029 3030 window_dx /= view->scale.x; 3031 window_dy /= view->scale.y; 3032 3033 *dx = window_dx; 3034 *dy = window_dy; 3035 return true; 3036} 3037 3038bool SDL_RenderCoordinatesFromWindow(SDL_Renderer *renderer, float window_x, float window_y, float *x, float *y) 3039{ 3040 float render_x, render_y; 3041 3042 CHECK_RENDERER_MAGIC(renderer, false); 3043 3044 // Convert from window coordinates to pixels within the window 3045 render_x = window_x * renderer->dpi_scale.x; 3046 render_y = window_y * renderer->dpi_scale.y; 3047 3048 // Convert from pixels within the window to pixels within the view 3049 const SDL_RenderViewState *view = &renderer->main_view; 3050 if (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) { 3051 const SDL_FRect *src = &view->logical_src_rect; 3052 const SDL_FRect *dst = &view->logical_dst_rect; 3053 render_x = ((render_x - dst->x) * src->w) / dst->w; 3054 render_y = ((render_y - dst->y) * src->h) / dst->h; 3055 } 3056 3057 render_x = (render_x / view->scale.x) - view->viewport.x; 3058 render_y = (render_y / view->scale.y) - view->viewport.y; 3059 3060 if (x) { 3061 *x = render_x; 3062 } 3063 if (y) { 3064 *y = render_y; 3065 } 3066 return true; 3067} 3068 3069bool SDL_RenderCoordinatesToWindow(SDL_Renderer *renderer, float x, float y, float *window_x, float *window_y) 3070{ 3071 CHECK_RENDERER_MAGIC(renderer, false); 3072 3073 const SDL_RenderViewState *view = &renderer->main_view; 3074 x = (view->viewport.x + x) * view->scale.x; 3075 y = (view->viewport.y + y) * view->scale.y; 3076 3077 // Convert from render coordinates to pixels within the window 3078 if (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED) { 3079 const SDL_FRect *src = &view->logical_src_rect; 3080 const SDL_FRect *dst = &view->logical_dst_rect; 3081 x = dst->x + ((x * dst->w) / src->w); 3082 y = dst->y + ((y * dst->h) / src->h); 3083 } 3084 3085 // Convert from pixels within the window to window coordinates 3086 x /= renderer->dpi_scale.x; 3087 y /= renderer->dpi_scale.y; 3088 3089 if (window_x) { 3090 *window_x = x; 3091 } 3092 if (window_y) { 3093 *window_y = y; 3094 } 3095 return true; 3096} 3097 3098bool SDL_ConvertEventToRenderCoordinates(SDL_Renderer *renderer, SDL_Event *event) 3099{ 3100 CHECK_RENDERER_MAGIC(renderer, false); 3101 3102 if (event->type == SDL_EVENT_MOUSE_MOTION) { 3103 SDL_Window *window = SDL_GetWindowFromID(event->motion.windowID); 3104 if (window == renderer->window) { 3105 SDL_RenderCoordinatesFromWindow(renderer, event->motion.x, event->motion.y, &event->motion.x, &event->motion.y); 3106 SDL_RenderVectorFromWindow(renderer, event->motion.xrel, event->motion.yrel, &event->motion.xrel, &event->motion.yrel); 3107 } 3108 } else if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN || 3109 event->type == SDL_EVENT_MOUSE_BUTTON_UP) { 3110 SDL_Window *window = SDL_GetWindowFromID(event->button.windowID); 3111 if (window == renderer->window) { 3112 SDL_RenderCoordinatesFromWindow(renderer, event->button.x, event->button.y, &event->button.x, &event->button.y); 3113 } 3114 } else if (event->type == SDL_EVENT_MOUSE_WHEEL) { 3115 SDL_Window *window = SDL_GetWindowFromID(event->wheel.windowID); 3116 if (window == renderer->window) { 3117 SDL_RenderCoordinatesFromWindow(renderer, event->wheel.mouse_x, 3118 event->wheel.mouse_y, 3119 &event->wheel.mouse_x, 3120 &event->wheel.mouse_y); 3121 } 3122 } else if (event->type == SDL_EVENT_FINGER_DOWN || 3123 event->type == SDL_EVENT_FINGER_UP || 3124 event->type == SDL_EVENT_FINGER_CANCELED || 3125 event->type == SDL_EVENT_FINGER_MOTION) { 3126 // FIXME: Are these events guaranteed to be window relative? 3127 if (renderer->window) { 3128 int w, h; 3129 if (!SDL_GetWindowSize(renderer->window, &w, &h)) { 3130 return false; 3131 } 3132 SDL_RenderCoordinatesFromWindow(renderer, event->tfinger.x * w, event->tfinger.y * h, &event->tfinger.x, &event->tfinger.y); 3133 SDL_RenderVectorFromWindow(renderer, event->tfinger.dx * w, event->tfinger.dy * h, &event->tfinger.dx, &event->tfinger.dy); 3134 } 3135 } else if (event->type == SDL_EVENT_PEN_MOTION) { 3136 SDL_Window *window = SDL_GetWindowFromID(event->pmotion.windowID); 3137 if (window == renderer->window) { 3138 SDL_RenderCoordinatesFromWindow(renderer, event->pmotion.x, event->pmotion.y, &event->pmotion.x, &event->pmotion.y); 3139 } 3140 } else if ((event->type == SDL_EVENT_PEN_DOWN) || (event->type == SDL_EVENT_PEN_UP)) { 3141 SDL_Window *window = SDL_GetWindowFromID(event->ptouch.windowID); 3142 if (window == renderer->window) { 3143 SDL_RenderCoordinatesFromWindow(renderer, event->ptouch.x, event->ptouch.y, &event->ptouch.x, &event->ptouch.y); 3144 } 3145 } else if ((event->type == SDL_EVENT_PEN_BUTTON_DOWN) || (event->type == SDL_EVENT_PEN_BUTTON_UP)) { 3146 SDL_Window *window = SDL_GetWindowFromID(event->pbutton.windowID); 3147 if (window == renderer->window) { 3148 SDL_RenderCoordinatesFromWindow(renderer, event->pbutton.x, event->pbutton.y, &event->pbutton.x, &event->pbutton.y); 3149 } 3150 } else if (event->type == SDL_EVENT_PEN_AXIS) { 3151 SDL_Window *window = SDL_GetWindowFromID(event->paxis.windowID); 3152 if (window == renderer->window) { 3153 SDL_RenderCoordinatesFromWindow(renderer, event->paxis.x, event->paxis.y, &event->paxis.x, &event->paxis.y); 3154 } 3155 } else if (event->type == SDL_EVENT_DROP_POSITION || 3156 event->type == SDL_EVENT_DROP_FILE || 3157 event->type == SDL_EVENT_DROP_TEXT || 3158 event->type == SDL_EVENT_DROP_COMPLETE) { 3159 SDL_Window *window = SDL_GetWindowFromID(event->drop.windowID); 3160 if (window == renderer->window) { 3161 SDL_RenderCoordinatesFromWindow(renderer, event->drop.x, event->drop.y, &event->drop.x, &event->drop.y); 3162 } 3163 } 3164 return true; 3165} 3166 3167bool SDL_SetRenderViewport(SDL_Renderer *renderer, const SDL_Rect *rect) 3168{ 3169 CHECK_RENDERER_MAGIC(renderer, false); 3170 3171 SDL_RenderViewState *view = renderer->view; 3172 if (rect) { 3173 if ((rect->w < 0) || (rect->h < 0)) { 3174 return SDL_SetError("rect has a negative size"); 3175 } 3176 SDL_copyp(&view->viewport, rect); 3177 } else { 3178 view->viewport.x = view->viewport.y = 0; 3179 view->viewport.w = view->viewport.h = -1; 3180 } 3181 UpdatePixelViewport(renderer, view); 3182 3183 return QueueCmdSetViewport(renderer); 3184} 3185 3186bool SDL_GetRenderViewport(SDL_Renderer *renderer, SDL_Rect *rect) 3187{ 3188 if (rect) { 3189 SDL_zerop(rect); 3190 } 3191 3192 CHECK_RENDERER_MAGIC(renderer, false); 3193 3194 if (rect) { 3195 const SDL_RenderViewState *view = renderer->view; 3196 rect->x = view->viewport.x; 3197 rect->y = view->viewport.y; 3198 if (view->viewport.w >= 0) { 3199 rect->w = view->viewport.w; 3200 } else { 3201 rect->w = (int)SDL_ceilf(view->pixel_w / view->current_scale.x); 3202 } 3203 if (view->viewport.h >= 0) { 3204 rect->h = view->viewport.h; 3205 } else { 3206 rect->h = (int)SDL_ceilf(view->pixel_h / view->current_scale.y); 3207 } 3208 } 3209 return true; 3210} 3211 3212bool SDL_RenderViewportSet(SDL_Renderer *renderer) 3213{ 3214 CHECK_RENDERER_MAGIC(renderer, false); 3215 3216 const SDL_RenderViewState *view = renderer->view; 3217 return (view->viewport.w >= 0 && view->viewport.h >= 0); 3218} 3219 3220static void GetRenderViewportSize(SDL_Renderer *renderer, SDL_FRect *rect) 3221{ 3222 const SDL_RenderViewState *view = renderer->view; 3223 const float scale_x = view->current_scale.x; 3224 const float scale_y = view->current_scale.y; 3225 3226 rect->x = 0.0f; 3227 rect->y = 0.0f; 3228 3229 if (view->viewport.w >= 0) { 3230 rect->w = (float)view->viewport.w; 3231 } else { 3232 rect->w = view->pixel_w / scale_x; 3233 } 3234 3235 if (view->viewport.h >= 0) { 3236 rect->h = (float)view->viewport.h; 3237 } else { 3238 rect->h = view->pixel_h / scale_y; 3239 } 3240} 3241 3242bool SDL_GetRenderSafeArea(SDL_Renderer *renderer, SDL_Rect *rect) 3243{ 3244 if (rect) { 3245 SDL_zerop(rect); 3246 } 3247 3248 CHECK_RENDERER_MAGIC(renderer, false); 3249 3250 if (renderer->target || !renderer->window) { 3251 // The entire viewport is safe for rendering 3252 return SDL_GetRenderViewport(renderer, rect); 3253 } 3254 3255 if (rect) { 3256 // Get the window safe rect 3257 SDL_Rect safe; 3258 if (!SDL_GetWindowSafeArea(renderer->window, &safe)) { 3259 return false; 3260 } 3261 3262 // Convert the coordinates into the render space 3263 float minx = (float)safe.x; 3264 float miny = (float)safe.y; 3265 float maxx = (float)safe.x + safe.w; 3266 float maxy = (float)safe.y + safe.h; 3267 if (!SDL_RenderCoordinatesFromWindow(renderer, minx, miny, &minx, &miny) || 3268 !SDL_RenderCoordinatesFromWindow(renderer, maxx, maxy, &maxx, &maxy)) { 3269 return false; 3270 } 3271 3272 rect->x = (int)SDL_ceilf(minx); 3273 rect->y = (int)SDL_ceilf(miny); 3274 rect->w = (int)SDL_ceilf(maxx - minx); 3275 rect->h = (int)SDL_ceilf(maxy - miny); 3276 3277 // Clip with the viewport 3278 SDL_Rect viewport; 3279 if (!SDL_GetRenderViewport(renderer, &viewport)) { 3280 return false; 3281 } 3282 if (!SDL_GetRectIntersection(rect, &viewport, rect)) { 3283 return SDL_SetError("No safe area within viewport"); 3284 } 3285 } 3286 return true; 3287} 3288 3289bool SDL_SetRenderClipRect(SDL_Renderer *renderer, const SDL_Rect *rect) 3290{ 3291 CHECK_RENDERER_MAGIC(renderer, false) 3292 3293 SDL_RenderViewState *view = renderer->view; 3294 if (rect && rect->w >= 0 && rect->h >= 0) { 3295 view->clipping_enabled = true; 3296 SDL_copyp(&view->clip_rect, rect); 3297 } else { 3298 view->clipping_enabled = false; 3299 SDL_zero(view->clip_rect); 3300 } 3301 UpdatePixelClipRect(renderer, view); 3302 3303 return QueueCmdSetClipRect(renderer); 3304} 3305 3306bool SDL_GetRenderClipRect(SDL_Renderer *renderer, SDL_Rect *rect) 3307{ 3308 if (rect) { 3309 SDL_zerop(rect); 3310 } 3311 3312 CHECK_RENDERER_MAGIC(renderer, false) 3313 3314 if (rect) { 3315 SDL_copyp(rect, &renderer->view->clip_rect); 3316 } 3317 return true; 3318} 3319 3320bool SDL_RenderClipEnabled(SDL_Renderer *renderer) 3321{ 3322 CHECK_RENDERER_MAGIC(renderer, false) 3323 return renderer->view->clipping_enabled; 3324} 3325 3326bool SDL_SetRenderScale(SDL_Renderer *renderer, float scaleX, float scaleY) 3327{ 3328 bool result = true; 3329 3330 CHECK_RENDERER_MAGIC(renderer, false); 3331 3332 SDL_RenderViewState *view = renderer->view; 3333 3334 if ((view->scale.x == scaleX) && (view->scale.y == scaleY)) { 3335 return true; 3336 } 3337 3338 view->scale.x = scaleX; 3339 view->scale.y = scaleY; 3340 view->current_scale.x = scaleX * view->logical_scale.x; 3341 view->current_scale.y = scaleY * view->logical_scale.y; 3342 UpdatePixelViewport(renderer, view); 3343 UpdatePixelClipRect(renderer, view); 3344 3345 // The scale affects the existing viewport and clip rectangle 3346 result &= QueueCmdSetViewport(renderer); 3347 result &= QueueCmdSetClipRect(renderer); 3348 return result; 3349} 3350 3351bool SDL_GetRenderScale(SDL_Renderer *renderer, float *scaleX, float *scaleY) 3352{ 3353 if (scaleX) { 3354 *scaleX = 1.0f; 3355 } 3356 if (scaleY) { 3357 *scaleY = 1.0f; 3358 } 3359 3360 CHECK_RENDERER_MAGIC(renderer, false); 3361 3362 const SDL_RenderViewState *view = renderer->view; 3363 3364 if (scaleX) { 3365 *scaleX = view->scale.x; 3366 } 3367 if (scaleY) { 3368 *scaleY = view->scale.y; 3369 } 3370 return true; 3371} 3372 3373bool SDL_SetRenderDrawColor(SDL_Renderer *renderer, Uint8 r, Uint8 g, Uint8 b, Uint8 a) 3374{ 3375 const float fR = (float)r / 255.0f; 3376 const float fG = (float)g / 255.0f; 3377 const float fB = (float)b / 255.0f; 3378 const float fA = (float)a / 255.0f; 3379 3380 return SDL_SetRenderDrawColorFloat(renderer, fR, fG, fB, fA); 3381} 3382 3383bool SDL_SetRenderDrawColorFloat(SDL_Renderer *renderer, float r, float g, float b, float a) 3384{ 3385 CHECK_RENDERER_MAGIC(renderer, false); 3386 3387 renderer->color.r = r; 3388 renderer->color.g = g; 3389 renderer->color.b = b; 3390 renderer->color.a = a; 3391 return true; 3392} 3393 3394bool SDL_GetRenderDrawColor(SDL_Renderer *renderer, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a) 3395{ 3396 float fR, fG, fB, fA; 3397 3398 if (!SDL_GetRenderDrawColorFloat(renderer, &fR, &fG, &fB, &fA)) { 3399 if (r) { 3400 *r = 0; 3401 } 3402 if (g) { 3403 *g = 0; 3404 } 3405 if (b) { 3406 *b = 0; 3407 } 3408 if (a) { 3409 *a = 0; 3410 } 3411 return false; 3412 } 3413 3414 if (r) { 3415 *r = (Uint8)(fR * 255.0f); 3416 } 3417 if (g) { 3418 *g = (Uint8)(fG * 255.0f); 3419 } 3420 if (b) { 3421 *b = (Uint8)(fB * 255.0f); 3422 } 3423 if (a) { 3424 *a = (Uint8)(fA * 255.0f); 3425 } 3426 return true; 3427} 3428 3429bool SDL_GetRenderDrawColorFloat(SDL_Renderer *renderer, float *r, float *g, float *b, float *a) 3430{ 3431 SDL_FColor color; 3432 3433 if (r) { 3434 *r = 0.0f; 3435 } 3436 if (g) { 3437 *g = 0.0f; 3438 } 3439 if (b) { 3440 *b = 0.0f; 3441 } 3442 if (a) { 3443 *a = 0.0f; 3444 } 3445 3446 CHECK_RENDERER_MAGIC(renderer, false); 3447 3448 color = renderer->color; 3449 3450 if (r) { 3451 *r = color.r; 3452 } 3453 if (g) { 3454 *g = color.g; 3455 } 3456 if (b) { 3457 *b = color.b; 3458 } 3459 if (a) { 3460 *a = color.a; 3461 } 3462 return true; 3463} 3464 3465bool SDL_SetRenderColorScale(SDL_Renderer *renderer, float scale) 3466{ 3467 CHECK_RENDERER_MAGIC(renderer, false); 3468 3469 renderer->desired_color_scale = scale; 3470 UpdateColorScale(renderer); 3471 return true; 3472} 3473 3474bool SDL_GetRenderColorScale(SDL_Renderer *renderer, float *scale) 3475{ 3476 if (scale) { 3477 *scale = 1.0f; 3478 } 3479 3480 CHECK_RENDERER_MAGIC(renderer, false); 3481 3482 if (scale) { 3483 *scale = renderer->desired_color_scale; 3484 } 3485 return true; 3486} 3487 3488bool SDL_SetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode) 3489{ 3490 CHECK_RENDERER_MAGIC(renderer, false); 3491 3492 CHECK_PARAM(blendMode == SDL_BLENDMODE_INVALID) { 3493 return SDL_InvalidParamError("blendMode"); 3494 } 3495 3496 if (!IsSupportedBlendMode(renderer, blendMode)) { 3497 return SDL_Unsupported(); 3498 } 3499 3500 renderer->blendMode = blendMode; 3501 return true; 3502} 3503 3504bool SDL_GetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode *blendMode) 3505{ 3506 if (blendMode) { 3507 *blendMode = SDL_BLENDMODE_INVALID; 3508 } 3509 3510 CHECK_RENDERER_MAGIC(renderer, false); 3511 3512 if (blendMode) { 3513 *blendMode = renderer->blendMode; 3514 } 3515 return true; 3516} 3517 3518bool SDL_RenderClear(SDL_Renderer *renderer) 3519{ 3520 CHECK_RENDERER_MAGIC(renderer, false); 3521 3522 return QueueCmdClear(renderer); 3523} 3524 3525bool SDL_RenderPoint(SDL_Renderer *renderer, float x, float y) 3526{ 3527 SDL_FPoint fpoint; 3528 fpoint.x = x; 3529 fpoint.y = y; 3530 return SDL_RenderPoints(renderer, &fpoint, 1); 3531} 3532 3533static bool RenderPointsWithRects(SDL_Renderer *renderer, const SDL_FPoint *fpoints, const int count) 3534{ 3535 bool result; 3536 bool isstack; 3537 SDL_FRect *frects; 3538 int i; 3539 3540 if (count < 1) { 3541 return true; 3542 } 3543 3544 frects = SDL_small_alloc(SDL_FRect, count, &isstack); 3545 if (!frects) { 3546 return false; 3547 } 3548 3549 const SDL_RenderViewState *view = renderer->view; 3550 const float scale_x = view->current_scale.x; 3551 const float scale_y = view->current_scale.y; 3552 for (i = 0; i < count; ++i) { 3553 frects[i].x = fpoints[i].x * scale_x; 3554 frects[i].y = fpoints[i].y * scale_y; 3555 frects[i].w = scale_x; 3556 frects[i].h = scale_y; 3557 } 3558 3559 result = QueueCmdFillRects(renderer, frects, count); 3560 3561 SDL_small_free(frects, isstack); 3562 3563 return result; 3564} 3565 3566bool SDL_RenderPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int count) 3567{ 3568 bool result; 3569 3570 CHECK_RENDERER_MAGIC(renderer, false); 3571 3572 CHECK_PARAM(!points) { 3573 return SDL_InvalidParamError("SDL_RenderPoints(): points"); 3574 } 3575 3576 if (count < 1) { 3577 return true; 3578 } 3579 3580#if DONT_DRAW_WHILE_HIDDEN 3581 // Don't draw while we're hidden 3582 if (renderer->hidden) { 3583 return true; 3584 } 3585#endif 3586 3587 const SDL_RenderViewState *view = renderer->view; 3588 if ((view->current_scale.x != 1.0f) || (view->current_scale.y != 1.0f)) { 3589 result = RenderPointsWithRects(renderer, points, count); 3590 } else { 3591 result = QueueCmdDrawPoints(renderer, points, count); 3592 } 3593 return result; 3594} 3595 3596bool SDL_RenderLine(SDL_Renderer *renderer, float x1, float y1, float x2, float y2) 3597{ 3598 SDL_FPoint points[2]; 3599 points[0].x = x1; 3600 points[0].y = y1; 3601 points[1].x = x2; 3602 points[1].y = y2; 3603 return SDL_RenderLines(renderer, points, 2); 3604} 3605 3606static bool RenderLineBresenham(SDL_Renderer *renderer, int x1, int y1, int x2, int y2, bool draw_last) 3607{ 3608 const SDL_RenderViewState *view = renderer->view; 3609 const int MAX_PIXELS = SDL_max(view->pixel_w, view->pixel_h) * 4; 3610 int i, deltax, deltay, numpixels; 3611 int d, dinc1, dinc2; 3612 int x, xinc1, xinc2; 3613 int y, yinc1, yinc2; 3614 bool result; 3615 bool isstack; 3616 SDL_FPoint *points; 3617 SDL_Rect viewport; 3618 3619 /* the backend might clip this further to the clipping rect, but we 3620 just want a basic safety against generating millions of points for 3621 massive lines. */ 3622 viewport = view->pixel_viewport; 3623 viewport.x = 0; 3624 viewport.y = 0; 3625 if (!SDL_GetRectAndLineIntersection(&viewport, &x1, &y1, &x2, &y2)) { 3626 return true; 3627 } 3628 3629 deltax = SDL_abs(x2 - x1); 3630 deltay = SDL_abs(y2 - y1); 3631 3632 if (deltax >= deltay) { 3633 numpixels = deltax + 1; 3634 d = (2 * deltay) - deltax; 3635 dinc1 = deltay * 2; 3636 dinc2 = (deltay - deltax) * 2; 3637 xinc1 = 1; 3638 xinc2 = 1; 3639 yinc1 = 0; 3640 yinc2 = 1; 3641 } else { 3642 numpixels = deltay + 1; 3643 d = (2 * deltax) - deltay; 3644 dinc1 = deltax * 2; 3645 dinc2 = (deltax - deltay) * 2; 3646 xinc1 = 0; 3647 xinc2 = 1; 3648 yinc1 = 1; 3649 yinc2 = 1; 3650 } 3651 3652 if (x1 > x2) { 3653 xinc1 = -xinc1; 3654 xinc2 = -xinc2; 3655 } 3656 if (y1 > y2) { 3657 yinc1 = -yinc1; 3658 yinc2 = -yinc2; 3659 } 3660 3661 x = x1; 3662 y = y1; 3663 3664 if (!draw_last) { 3665 --numpixels; 3666 } 3667 3668 if (numpixels > MAX_PIXELS) { 3669 return SDL_SetError("Line too long (tried to draw %d pixels, max %d)", numpixels, MAX_PIXELS); 3670 } 3671 3672 points = SDL_small_alloc(SDL_FPoint, numpixels, &isstack); 3673 if (!points) { 3674 return false; 3675 } 3676 for (i = 0; i < numpixels; ++i) { 3677 points[i].x = (float)x; 3678 points[i].y = (float)y; 3679 3680 if (d < 0) { 3681 d += dinc1; 3682 x += xinc1; 3683 y += yinc1; 3684 } else { 3685 d += dinc2; 3686 x += xinc2; 3687 y += yinc2; 3688 } 3689 } 3690 3691 if ((view->current_scale.x != 1.0f) || (view->current_scale.y != 1.0f)) { 3692 result = RenderPointsWithRects(renderer, points, numpixels); 3693 } else { 3694 result = QueueCmdDrawPoints(renderer, points, numpixels); 3695 } 3696 3697 SDL_small_free(points, isstack); 3698 3699 return result; 3700} 3701 3702static bool RenderLinesWithRectsF(SDL_Renderer *renderer, const SDL_FPoint *points, const int count) 3703{ 3704 const SDL_RenderViewState *view = renderer->view; 3705 const float scale_x = view->current_scale.x; 3706 const float scale_y = view->current_scale.y; 3707 SDL_FRect *frect; 3708 SDL_FRect *frects; 3709 int i, nrects = 0; 3710 bool result = true; 3711 bool isstack; 3712 bool drew_line = false; 3713 bool draw_last = false; 3714 3715 frects = SDL_small_alloc(SDL_FRect, count - 1, &isstack); 3716 if (!frects) { 3717 return false; 3718 } 3719 3720 for (i = 0; i < count - 1; ++i) { 3721 bool same_x = (points[i].x == points[i + 1].x); 3722 bool same_y = (points[i].y == points[i + 1].y); 3723 3724 if (i == (count - 2)) { 3725 if (!drew_line || points[i + 1].x != points[0].x || points[i + 1].y != points[0].y) { 3726 draw_last = true; 3727 } 3728 } else { 3729 if (same_x && same_y) { 3730 continue; 3731 } 3732 } 3733 if (same_x) { 3734 const float minY = SDL_min(points[i].y, points[i + 1].y); 3735 const float maxY = SDL_max(points[i].y, points[i + 1].y); 3736 3737 frect = &frects[nrects++]; 3738 frect->x = points[i].x * scale_x; 3739 frect->y = minY * scale_y; 3740 frect->w = scale_x; 3741 frect->h = (maxY - minY + draw_last) * scale_y; 3742 if (!draw_last && points[i + 1].y < points[i].y) { 3743 frect->y += scale_y; 3744 } 3745 } else if (same_y) { 3746 const float minX = SDL_min(points[i].x, points[i + 1].x); 3747 const float maxX = SDL_max(points[i].x, points[i + 1].x); 3748 3749 frect = &frects[nrects++]; 3750 frect->x = minX * scale_x; 3751 frect->y = points[i].y * scale_y; 3752 frect->w = (maxX - minX + draw_last) * scale_x; 3753 frect->h = scale_y; 3754 if (!draw_last && points[i + 1].x < points[i].x) { 3755 frect->x += scale_x; 3756 } 3757 } else { 3758 result &= RenderLineBresenham(renderer, (int)SDL_roundf(points[i].x), (int)SDL_roundf(points[i].y), 3759 (int)SDL_roundf(points[i + 1].x), (int)SDL_roundf(points[i + 1].y), draw_last); 3760 } 3761 drew_line = true; 3762 } 3763 3764 if (nrects) { 3765 result &= QueueCmdFillRects(renderer, frects, nrects); 3766 } 3767 3768 SDL_small_free(frects, isstack); 3769 3770 return result; 3771} 3772 3773bool SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count) 3774{ 3775 bool result = true; 3776 3777 CHECK_RENDERER_MAGIC(renderer, false); 3778 3779 CHECK_PARAM(!points) { 3780 return SDL_InvalidParamError("SDL_RenderLines(): points"); 3781 } 3782 3783 if (count < 2) { 3784 return true; 3785 } 3786 3787#if DONT_DRAW_WHILE_HIDDEN 3788 // Don't draw while we're hidden 3789 if (renderer->hidden) { 3790 return true; 3791 } 3792#endif 3793 3794 SDL_RenderViewState *view = renderer->view; 3795 const bool islogical = (view->logical_presentation_mode != SDL_LOGICAL_PRESENTATION_DISABLED); 3796 3797 if (islogical || (renderer->line_method == SDL_RENDERLINEMETHOD_GEOMETRY)) { 3798 const float scale_x = view->current_scale.x; 3799 const float scale_y = view->current_scale.y; 3800 bool isstack1; 3801 bool isstack2; 3802 float *xy = SDL_small_alloc(float, 4 * 2 * count, &isstack1); 3803 int *indices = SDL_small_alloc(int, (4) * 3 * (count - 1) + (2) * 3 * (count), &isstack2); 3804 3805 if (xy && indices) { 3806 int i; 3807 float *ptr_xy = xy; 3808 int *ptr_indices = indices; 3809 const int xy_stride = 2 * sizeof(float); 3810 int num_vertices = 4 * count; 3811 int num_indices = 0; 3812 const int size_indices = 4; 3813 int cur_index = -4; 3814 const bool is_looping = (points[0].x == points[count - 1].x && points[0].y == points[count - 1].y); 3815 SDL_FPoint p; // previous point 3816 p.x = p.y = 0.0f; 3817 /* p q 3818 3819 0----1------ 4----5 3820 | \ |``\ | \ | 3821 | \ | ` `\| \ | 3822 3----2-------7----6 3823 */ 3824 for (i = 0; i < count; ++i) { 3825 SDL_FPoint q = points[i]; // current point 3826 3827 q.x *= scale_x; 3828 q.y *= scale_y; 3829 3830 *ptr_xy++ = q.x; 3831 *ptr_xy++ = q.y; 3832 *ptr_xy++ = q.x + scale_x; 3833 *ptr_xy++ = q.y; 3834 *ptr_xy++ = q.x + scale_x; 3835 *ptr_xy++ = q.y + scale_y; 3836 *ptr_xy++ = q.x; 3837 *ptr_xy++ = q.y + scale_y; 3838 3839#define ADD_TRIANGLE(i1, i2, i3) \ 3840 *ptr_indices++ = cur_index + (i1); \ 3841 *ptr_indices++ = cur_index + (i2); \ 3842 *ptr_indices++ = cur_index + (i3); \ 3843 num_indices += 3; 3844 3845 // closed polyline, don´t draw twice the point 3846 if (i || !is_looping) { 3847 ADD_TRIANGLE(4, 5, 6) 3848 ADD_TRIANGLE(4, 6, 7) 3849 } 3850 3851 // first point only, no segment 3852 if (i == 0) { 3853 p = q; 3854 cur_index += 4; 3855 continue; 3856 } 3857 3858 // draw segment 3859 if (p.y == q.y) { 3860 if (p.x < q.x) { 3861 ADD_TRIANGLE(1, 4, 7) 3862 ADD_TRIANGLE(1, 7, 2) 3863 } else { 3864 ADD_TRIANGLE(5, 0, 3) 3865 ADD_TRIANGLE(5, 3, 6) 3866 } 3867 } else if (p.x == q.x) { 3868 if (p.y < q.y) { 3869 ADD_TRIANGLE(2, 5, 4) 3870 ADD_TRIANGLE(2, 4, 3) 3871 } else { 3872 ADD_TRIANGLE(6, 1, 0) 3873 ADD_TRIANGLE(6, 0, 7) 3874 } 3875 } else { 3876 if (p.y < q.y) { 3877 if (p.x < q.x) { 3878 ADD_TRIANGLE(1, 5, 4) 3879 ADD_TRIANGLE(1, 4, 2) 3880 ADD_TRIANGLE(2, 4, 7) 3881 ADD_TRIANGLE(2, 7, 3) 3882 } else { 3883 ADD_TRIANGLE(4, 0, 5) 3884 ADD_TRIANGLE(5, 0, 3) 3885 ADD_TRIANGLE(5, 3, 6) 3886 ADD_TRIANGLE(6, 3, 2) 3887 } 3888 } else { 3889 if (p.x < q.x) { 3890 ADD_TRIANGLE(0, 4, 7) 3891 ADD_TRIANGLE(0, 7, 1) 3892 ADD_TRIANGLE(1, 7, 6) 3893 ADD_TRIANGLE(1, 6, 2) 3894 } else { 3895 ADD_TRIANGLE(6, 5, 1) 3896 ADD_TRIANGLE(6, 1, 0) 3897 ADD_TRIANGLE(7, 6, 0) 3898 ADD_TRIANGLE(7, 0, 3) 3899 } 3900 } 3901 } 3902 3903 p = q; 3904 cur_index += 4; 3905 } 3906 3907 result = QueueCmdGeometry(renderer, NULL, 3908 xy, xy_stride, &renderer->color, 0 /* color_stride */, NULL, 0, 3909 num_vertices, indices, num_indices, size_indices, 3910 1.0f, 1.0f, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); 3911 } 3912 3913 SDL_small_free(xy, isstack1); 3914 SDL_small_free(indices, isstack2); 3915 3916 } else if (renderer->line_method == SDL_RENDERLINEMETHOD_POINTS) { 3917 result = RenderLinesWithRectsF(renderer, points, count); 3918 } else if (view->scale.x != 1.0f || view->scale.y != 1.0f) { /* we checked for logical scale elsewhere. */ 3919 result = RenderLinesWithRectsF(renderer, points, count); 3920 } else { 3921 result = QueueCmdDrawLines(renderer, points, count); 3922 } 3923 3924 return result; 3925} 3926 3927bool SDL_RenderRect(SDL_Renderer *renderer, const SDL_FRect *rect) 3928{ 3929 SDL_FRect frect; 3930 SDL_FPoint points[5]; 3931 3932 CHECK_RENDERER_MAGIC(renderer, false); 3933 3934 // If 'rect' == NULL, then outline the whole surface 3935 if (!rect) { 3936 GetRenderViewportSize(renderer, &frect); 3937 rect = &frect; 3938 } 3939 3940 points[0].x = rect->x; 3941 points[0].y = rect->y; 3942 points[1].x = rect->x + rect->w - 1; 3943 points[1].y = rect->y; 3944 points[2].x = rect->x + rect->w - 1; 3945 points[2].y = rect->y + rect->h - 1; 3946 points[3].x = rect->x; 3947 points[3].y = rect->y + rect->h - 1; 3948 points[4].x = rect->x; 3949 points[4].y = rect->y; 3950 return SDL_RenderLines(renderer, points, 5); 3951} 3952 3953bool SDL_RenderRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count) 3954{ 3955 int i; 3956 3957 CHECK_RENDERER_MAGIC(renderer, false); 3958 3959 CHECK_PARAM(!rects) { 3960 return SDL_InvalidParamError("SDL_RenderRects(): rects"); 3961 } 3962 3963 if (count < 1) { 3964 return true; 3965 } 3966 3967#if DONT_DRAW_WHILE_HIDDEN 3968 // Don't draw while we're hidden 3969 if (renderer->hidden) { 3970 return true; 3971 } 3972#endif 3973 3974 for (i = 0; i < count; ++i) { 3975 if (!SDL_RenderRect(renderer, &rects[i])) { 3976 return false; 3977 } 3978 } 3979 return true; 3980} 3981 3982bool SDL_RenderFillRect(SDL_Renderer *renderer, const SDL_FRect *rect) 3983{ 3984 SDL_FRect frect; 3985 3986 CHECK_RENDERER_MAGIC(renderer, false); 3987 3988 // If 'rect' == NULL, then fill the whole surface 3989 if (!rect) { 3990 GetRenderViewportSize(renderer, &frect); 3991 rect = &frect; 3992 } 3993 return SDL_RenderFillRects(renderer, rect, 1); 3994} 3995 3996bool SDL_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count) 3997{ 3998 SDL_FRect *frects; 3999 int i; 4000 bool result; 4001 bool isstack; 4002 4003 CHECK_RENDERER_MAGIC(renderer, false); 4004 4005 CHECK_PARAM(!rects) { 4006 return SDL_InvalidParamError("SDL_RenderFillRects(): rects"); 4007 } 4008 4009 if (count < 1) { 4010 return true; 4011 } 4012 4013#if DONT_DRAW_WHILE_HIDDEN 4014 // Don't draw while we're hidden 4015 if (renderer->hidden) { 4016 return true; 4017 } 4018#endif 4019 4020 frects = SDL_small_alloc(SDL_FRect, count, &isstack); 4021 if (!frects) { 4022 return false; 4023 } 4024 4025 const SDL_RenderViewState *view = renderer->view; 4026 const float scale_x = view->current_scale.x; 4027 const float scale_y = view->current_scale.y; 4028 for (i = 0; i < count; ++i) { 4029 frects[i].x = rects[i].x * scale_x; 4030 frects[i].y = rects[i].y * scale_y; 4031 frects[i].w = rects[i].w * scale_x; 4032 frects[i].h = rects[i].h * scale_y; 4033 } 4034 4035 result = QueueCmdFillRects(renderer, frects, count); 4036 4037 SDL_small_free(frects, isstack); 4038 4039 return result; 4040} 4041 4042static bool SDL_RenderTextureInternal(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect) 4043{ 4044 const SDL_RenderViewState *view = renderer->view; 4045 const float scale_x = view->current_scale.x; 4046 const float scale_y = view->current_scale.y; 4047 const bool use_rendergeometry = (!renderer->QueueCopy); 4048 bool result; 4049 4050 if (use_rendergeometry) { 4051 float xy[8]; 4052 const int xy_stride = 2 * sizeof(float); 4053 float uv[8]; 4054 const int uv_stride = 2 * sizeof(float); 4055 const int num_vertices = 4; 4056 const int *indices = rect_index_order; 4057 const int num_indices = 6; 4058 const int size_indices = 4; 4059 float minu, minv, maxu, maxv; 4060 float minx, miny, maxx, maxy; 4061 4062 minu = srcrect->x / texture->w; 4063 minv = srcrect->y / texture->h; 4064 maxu = (srcrect->x + srcrect->w) / texture->w; 4065 maxv = (srcrect->y + srcrect->h) / texture->h; 4066 4067 minx = dstrect->x; 4068 miny = dstrect->y; 4069 maxx = dstrect->x + dstrect->w; 4070 maxy = dstrect->y + dstrect->h; 4071 4072 uv[0] = minu; 4073 uv[1] = minv; 4074 uv[2] = maxu; 4075 uv[3] = minv; 4076 uv[4] = maxu; 4077 uv[5] = maxv; 4078 uv[6] = minu; 4079 uv[7] = maxv; 4080 4081 xy[0] = minx; 4082 xy[1] = miny; 4083 xy[2] = maxx; 4084 xy[3] = miny; 4085 xy[4] = maxx; 4086 xy[5] = maxy; 4087 xy[6] = minx; 4088 xy[7] = maxy; 4089 4090 result = QueueCmdGeometry(renderer, texture, 4091 xy, xy_stride, &texture->color, 0 /* color_stride */, uv, uv_stride, 4092 num_vertices, indices, num_indices, size_indices, 4093 scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); 4094 } else { 4095 const SDL_FRect rect = { dstrect->x * scale_x, dstrect->y * scale_y, dstrect->w * scale_x, dstrect->h * scale_y }; 4096 result = QueueCmdCopy(renderer, texture, srcrect, &rect); 4097 } 4098 return result; 4099} 4100 4101bool SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect) 4102{ 4103 CHECK_RENDERER_MAGIC(renderer, false); 4104 CHECK_TEXTURE_MAGIC(texture, false); 4105 4106 CHECK_PARAM(renderer != texture->renderer) { 4107 return SDL_SetError("Texture was not created with this renderer"); 4108 } 4109 4110#if DONT_DRAW_WHILE_HIDDEN 4111 // Don't draw while we're hidden 4112 if (renderer->hidden) { 4113 return true; 4114 } 4115#endif 4116 4117 SDL_FRect real_srcrect; 4118 real_srcrect.x = 0.0f; 4119 real_srcrect.y = 0.0f; 4120 real_srcrect.w = (float)texture->w; 4121 real_srcrect.h = (float)texture->h; 4122 if (srcrect) { 4123 if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) { 4124 return true; 4125 } 4126 } 4127 4128 SDL_FRect full_dstrect; 4129 if (!dstrect) { 4130 GetRenderViewportSize(renderer, &full_dstrect); 4131 dstrect = &full_dstrect; 4132 } 4133 4134 if (!UpdateTexturePalette(texture)) { 4135 return false; 4136 } 4137 4138 if (texture->native) { 4139 texture = texture->native; 4140 } 4141 4142 texture->last_command_generation = renderer->render_command_generation; 4143 4144 return SDL_RenderTextureInternal(renderer, texture, &real_srcrect, dstrect); 4145} 4146 4147bool SDL_RenderTextureAffine(SDL_Renderer *renderer, SDL_Texture *texture, 4148 const SDL_FRect *srcrect, const SDL_FPoint *origin, const SDL_FPoint *right, const SDL_FPoint *down) 4149{ 4150 SDL_FRect real_srcrect; 4151 SDL_FRect real_dstrect; 4152 bool result; 4153 4154 CHECK_RENDERER_MAGIC(renderer, false); 4155 CHECK_TEXTURE_MAGIC(texture, false); 4156 4157 CHECK_PARAM(renderer != texture->renderer) { 4158 return SDL_SetError("Texture was not created with this renderer"); 4159 } 4160 if (!renderer->QueueCopyEx && !renderer->QueueGeometry) { 4161 return SDL_SetError("Renderer does not support RenderCopyEx"); 4162 } 4163 4164#if DONT_DRAW_WHILE_HIDDEN 4165 // Don't draw while we're hidden 4166 if (renderer->hidden) { 4167 return true; 4168 } 4169#endif 4170 4171 real_srcrect.x = 0.0f; 4172 real_srcrect.y = 0.0f; 4173 real_srcrect.w = (float)texture->w; 4174 real_srcrect.h = (float)texture->h; 4175 if (srcrect) { 4176 if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) { 4177 return true; 4178 } 4179 } 4180 4181 GetRenderViewportSize(renderer, &real_dstrect); 4182 4183 if (!UpdateTexturePalette(texture)) { 4184 return false; 4185 } 4186 4187 if (texture->native) { 4188 texture = texture->native; 4189 } 4190 4191 texture->last_command_generation = renderer->render_command_generation; 4192 4193 const SDL_RenderViewState *view = renderer->view; 4194 const float scale_x = view->current_scale.x; 4195 const float scale_y = view->current_scale.y; 4196 4197 { 4198 float xy[8]; 4199 const int xy_stride = 2 * sizeof(float); 4200 float uv[8]; 4201 const int uv_stride = 2 * sizeof(float); 4202 const int num_vertices = 4; 4203 const int *indices = rect_index_order; 4204 const int num_indices = 6; 4205 const int size_indices = 4; 4206 4207 float minu = real_srcrect.x / texture->w; 4208 float minv = real_srcrect.y / texture->h; 4209 float maxu = (real_srcrect.x + real_srcrect.w) / texture->w; 4210 float maxv = (real_srcrect.y + real_srcrect.h) / texture->h; 4211 4212 uv[0] = minu; 4213 uv[1] = minv; 4214 uv[2] = maxu; 4215 uv[3] = minv; 4216 uv[4] = maxu; 4217 uv[5] = maxv; 4218 uv[6] = minu; 4219 uv[7] = maxv; 4220 4221 // (minx, miny) 4222 if (origin) { 4223 xy[0] = origin->x; 4224 xy[1] = origin->y; 4225 } else { 4226 xy[0] = real_dstrect.x; 4227 xy[1] = real_dstrect.y; 4228 } 4229 4230 // (maxx, miny) 4231 if (right) { 4232 xy[2] = right->x; 4233 xy[3] = right->y; 4234 } else { 4235 xy[2] = real_dstrect.x + real_dstrect.w; 4236 xy[3] = real_dstrect.y; 4237 } 4238 4239 // (minx, maxy) 4240 if (down) { 4241 xy[6] = down->x; 4242 xy[7] = down->y; 4243 } else { 4244 xy[6] = real_dstrect.x; 4245 xy[7] = real_dstrect.y + real_dstrect.h; 4246 } 4247 4248 // (maxx, maxy) 4249 if (origin || right || down) { 4250 xy[4] = xy[2] + xy[6] - xy[0]; 4251 xy[5] = xy[3] + xy[7] - xy[1]; 4252 } else { 4253 xy[4] = real_dstrect.x + real_dstrect.w; 4254 xy[5] = real_dstrect.y + real_dstrect.h; 4255 } 4256 4257 result = QueueCmdGeometry( 4258 renderer, texture, 4259 xy, xy_stride, 4260 &texture->color, 0 /* color_stride */, 4261 uv, uv_stride, 4262 num_vertices, indices, num_indices, size_indices, 4263 scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP 4264 ); 4265 } 4266 return result; 4267} 4268 4269bool SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture, 4270 const SDL_FRect *srcrect, const SDL_FRect *dstrect, 4271 const double angle, const SDL_FPoint *center, const SDL_FlipMode flip) 4272{ 4273 SDL_FRect real_srcrect; 4274 SDL_FPoint real_center; 4275 bool result; 4276 4277 if (flip == SDL_FLIP_NONE && (int)(angle / 360) == angle / 360) { // fast path when we don't need rotation or flipping 4278 return SDL_RenderTexture(renderer, texture, srcrect, dstrect); 4279 } 4280 4281 CHECK_RENDERER_MAGIC(renderer, false); 4282 CHECK_TEXTURE_MAGIC(texture, false); 4283 4284 CHECK_PARAM(renderer != texture->renderer) { 4285 return SDL_SetError("Texture was not created with this renderer"); 4286 } 4287 if (!renderer->QueueCopyEx && !renderer->QueueGeometry) { 4288 return SDL_SetError("Renderer does not support RenderCopyEx"); 4289 } 4290 4291#if DONT_DRAW_WHILE_HIDDEN 4292 // Don't draw while we're hidden 4293 if (renderer->hidden) { 4294 return true; 4295 } 4296#endif 4297 4298 real_srcrect.x = 0.0f; 4299 real_srcrect.y = 0.0f; 4300 real_srcrect.w = (float)texture->w; 4301 real_srcrect.h = (float)texture->h; 4302 if (srcrect) { 4303 if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) { 4304 return true; 4305 } 4306 } 4307 4308 // We don't intersect the dstrect with the viewport as RenderCopy does because of potential rotation clipping issues... TODO: should we? 4309 SDL_FRect full_dstrect; 4310 if (!dstrect) { 4311 GetRenderViewportSize(renderer, &full_dstrect); 4312 dstrect = &full_dstrect; 4313 } 4314 4315 if (!UpdateTexturePalette(texture)) { 4316 return false; 4317 } 4318 4319 if (texture->native) { 4320 texture = texture->native; 4321 } 4322 4323 if (center) { 4324 real_center = *center; 4325 } else { 4326 real_center.x = dstrect->w / 2.0f; 4327 real_center.y = dstrect->h / 2.0f; 4328 } 4329 4330 texture->last_command_generation = renderer->render_command_generation; 4331 4332 const SDL_RenderViewState *view = renderer->view; 4333 const float scale_x = view->current_scale.x; 4334 const float scale_y = view->current_scale.y; 4335 4336 const bool use_rendergeometry = (!renderer->QueueCopyEx); 4337 if (use_rendergeometry) { 4338 float xy[8]; 4339 const int xy_stride = 2 * sizeof(float); 4340 float uv[8]; 4341 const int uv_stride = 2 * sizeof(float); 4342 const int num_vertices = 4; 4343 const int *indices = rect_index_order; 4344 const int num_indices = 6; 4345 const int size_indices = 4; 4346 float minu, minv, maxu, maxv; 4347 float minx, miny, maxx, maxy; 4348 float centerx, centery; 4349 4350 float s_minx, s_miny, s_maxx, s_maxy; 4351 float c_minx, c_miny, c_maxx, c_maxy; 4352 4353 const float radian_angle = (float)((SDL_PI_D * angle) / 180.0); 4354 const float s = SDL_sinf(radian_angle); 4355 const float c = SDL_cosf(radian_angle); 4356 4357 minu = real_srcrect.x / texture->w; 4358 minv = real_srcrect.y / texture->h; 4359 maxu = (real_srcrect.x + real_srcrect.w) / texture->w; 4360 maxv = (real_srcrect.y + real_srcrect.h) / texture->h; 4361 4362 centerx = real_center.x + dstrect->x; 4363 centery = real_center.y + dstrect->y; 4364 4365 if (flip & SDL_FLIP_HORIZONTAL) { 4366 minx = dstrect->x + dstrect->w; 4367 maxx = dstrect->x; 4368 } else { 4369 minx = dstrect->x; 4370 maxx = dstrect->x + dstrect->w; 4371 } 4372 4373 if (flip & SDL_FLIP_VERTICAL) { 4374 miny = dstrect->y + dstrect->h; 4375 maxy = dstrect->y; 4376 } else { 4377 miny = dstrect->y; 4378 maxy = dstrect->y + dstrect->h; 4379 } 4380 4381 uv[0] = minu; 4382 uv[1] = minv; 4383 uv[2] = maxu; 4384 uv[3] = minv; 4385 uv[4] = maxu; 4386 uv[5] = maxv; 4387 uv[6] = minu; 4388 uv[7] = maxv; 4389 4390 /* apply rotation with 2x2 matrix ( c -s ) 4391 * ( s c ) */ 4392 s_minx = s * (minx - centerx); 4393 s_miny = s * (miny - centery); 4394 s_maxx = s * (maxx - centerx); 4395 s_maxy = s * (maxy - centery); 4396 c_minx = c * (minx - centerx); 4397 c_miny = c * (miny - centery); 4398 c_maxx = c * (maxx - centerx); 4399 c_maxy = c * (maxy - centery); 4400 4401 // (minx, miny) 4402 xy[0] = (c_minx - s_miny) + centerx; 4403 xy[1] = (s_minx + c_miny) + centery; 4404 // (maxx, miny) 4405 xy[2] = (c_maxx - s_miny) + centerx; 4406 xy[3] = (s_maxx + c_miny) + centery; 4407 // (maxx, maxy) 4408 xy[4] = (c_maxx - s_maxy) + centerx; 4409 xy[5] = (s_maxx + c_maxy) + centery; 4410 // (minx, maxy) 4411 xy[6] = (c_minx - s_maxy) + centerx; 4412 xy[7] = (s_minx + c_maxy) + centery; 4413 4414 result = QueueCmdGeometry(renderer, texture, 4415 xy, xy_stride, &texture->color, 0 /* color_stride */, uv, uv_stride, 4416 num_vertices, indices, num_indices, size_indices, 4417 scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); 4418 } else { 4419 result = QueueCmdCopyEx(renderer, texture, &real_srcrect, dstrect, angle, &real_center, flip, scale_x, scale_y); 4420 } 4421 return result; 4422} 4423 4424static bool SDL_RenderTextureTiled_Wrap(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float scale, const SDL_FRect *dstrect) 4425{ 4426 float xy[8]; 4427 const int xy_stride = 2 * sizeof(float); 4428 float uv[8]; 4429 const int uv_stride = 2 * sizeof(float); 4430 const int num_vertices = 4; 4431 const int *indices = rect_index_order; 4432 const int num_indices = 6; 4433 const int size_indices = 4; 4434 float minu, minv, maxu, maxv; 4435 float minx, miny, maxx, maxy; 4436 4437 minu = 0.0f; 4438 minv = 0.0f; 4439 maxu = dstrect->w / (srcrect->w * scale); 4440 maxv = dstrect->h / (srcrect->h * scale); 4441 4442 minx = dstrect->x; 4443 miny = dstrect->y; 4444 maxx = dstrect->x + dstrect->w; 4445 maxy = dstrect->y + dstrect->h; 4446 4447 uv[0] = minu; 4448 uv[1] = minv; 4449 uv[2] = maxu; 4450 uv[3] = minv; 4451 uv[4] = maxu; 4452 uv[5] = maxv; 4453 uv[6] = minu; 4454 uv[7] = maxv; 4455 4456 xy[0] = minx; 4457 xy[1] = miny; 4458 xy[2] = maxx; 4459 xy[3] = miny; 4460 xy[4] = maxx; 4461 xy[5] = maxy; 4462 xy[6] = minx; 4463 xy[7] = maxy; 4464 4465 const SDL_RenderViewState *view = renderer->view; 4466 return QueueCmdGeometry(renderer, texture, 4467 xy, xy_stride, &texture->color, 0 /* color_stride */, uv, uv_stride, 4468 num_vertices, indices, num_indices, size_indices, 4469 view->current_scale.x, view->current_scale.y, 4470 SDL_TEXTURE_ADDRESS_WRAP, SDL_TEXTURE_ADDRESS_WRAP); 4471} 4472 4473static bool SDL_RenderTextureTiled_Iterate(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float scale, const SDL_FRect *dstrect) 4474{ 4475 float tile_width = srcrect->w * scale; 4476 float tile_height = srcrect->h * scale; 4477 float float_rows, float_cols; 4478 float remaining_w = SDL_modff(dstrect->w / tile_width, &float_cols); 4479 float remaining_h = SDL_modff(dstrect->h / tile_height, &float_rows); 4480 float remaining_src_w = remaining_w * srcrect->w; 4481 float remaining_src_h = remaining_h * srcrect->h; 4482 float remaining_dst_w = remaining_w * tile_width; 4483 float remaining_dst_h = remaining_h * tile_height; 4484 int rows = (int)float_rows; 4485 int cols = (int)float_cols; 4486 SDL_FRect curr_src, curr_dst; 4487 4488 SDL_copyp(&curr_src, srcrect); 4489 curr_dst.y = dstrect->y; 4490 curr_dst.w = tile_width; 4491 curr_dst.h = tile_height; 4492 for (int y = 0; y < rows; ++y) { 4493 curr_dst.x = dstrect->x; 4494 for (int x = 0; x < cols; ++x) { 4495 if (!SDL_RenderTextureInternal(renderer, texture, &curr_src, &curr_dst)) { 4496 return false; 4497 } 4498 curr_dst.x += curr_dst.w; 4499 } 4500 if (remaining_dst_w > 0.0f) { 4501 curr_src.w = remaining_src_w; 4502 curr_dst.w = remaining_dst_w; 4503 if (!SDL_RenderTextureInternal(renderer, texture, &curr_src, &curr_dst)) { 4504 return false; 4505 } 4506 curr_src.w = srcrect->w; 4507 curr_dst.w = tile_width; 4508 } 4509 curr_dst.y += curr_dst.h; 4510 } 4511 if (remaining_dst_h > 0.0f) { 4512 curr_src.h = remaining_src_h; 4513 curr_dst.h = remaining_dst_h; 4514 curr_dst.x = dstrect->x; 4515 for (int x = 0; x < cols; ++x) { 4516 if (!SDL_RenderTextureInternal(renderer, texture, &curr_src, &curr_dst)) { 4517 return false; 4518 } 4519 curr_dst.x += curr_dst.w; 4520 } 4521 if (remaining_dst_w > 0.0f) { 4522 curr_src.w = remaining_src_w; 4523 curr_dst.w = remaining_dst_w; 4524 if (!SDL_RenderTextureInternal(renderer, texture, &curr_src, &curr_dst)) { 4525 return false; 4526 } 4527 } 4528 } 4529 return true; 4530} 4531 4532static bool IsNPOT(int x) 4533{ 4534 return (x <= 0) || ((x & (x - 1)) != 0); 4535} 4536 4537bool SDL_RenderTextureTiled(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float scale, const SDL_FRect *dstrect) 4538{ 4539 SDL_FRect real_srcrect; 4540 4541 CHECK_RENDERER_MAGIC(renderer, false); 4542 CHECK_TEXTURE_MAGIC(texture, false); 4543 4544 CHECK_PARAM(renderer != texture->renderer) { 4545 return SDL_SetError("Texture was not created with this renderer"); 4546 } 4547 4548 CHECK_PARAM(scale <= 0.0f) { 4549 return SDL_InvalidParamError("scale"); 4550 } 4551 4552#if DONT_DRAW_WHILE_HIDDEN 4553 // Don't draw while we're hidden 4554 if (renderer->hidden) { 4555 return true; 4556 } 4557#endif 4558 4559 real_srcrect.x = 0.0f; 4560 real_srcrect.y = 0.0f; 4561 real_srcrect.w = (float)texture->w; 4562 real_srcrect.h = (float)texture->h; 4563 if (srcrect) { 4564 if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) { 4565 return true; 4566 } 4567 } 4568 4569 SDL_FRect full_dstrect; 4570 if (!dstrect) { 4571 GetRenderViewportSize(renderer, &full_dstrect); 4572 dstrect = &full_dstrect; 4573 } 4574 4575 if (!UpdateTexturePalette(texture)) { 4576 return false; 4577 } 4578 4579 if (texture->native) { 4580 texture = texture->native; 4581 } 4582 4583 texture->last_command_generation = renderer->render_command_generation; 4584 4585 bool do_wrapping = !renderer->software && 4586 (!srcrect || 4587 (real_srcrect.x == 0.0f && real_srcrect.y == 0.0f && 4588 real_srcrect.w == (float)texture->w && real_srcrect.h == (float)texture->h)); 4589 if (do_wrapping && renderer->npot_texture_wrap_unsupported) { 4590 if (IsNPOT(texture->w) || IsNPOT(texture->h)) { 4591 do_wrapping = false; 4592 } 4593 } 4594 4595 // See if we can use geometry with repeating texture coordinates 4596 if (do_wrapping) { 4597 return SDL_RenderTextureTiled_Wrap(renderer, texture, &real_srcrect, scale, dstrect); 4598 } else { 4599 return SDL_RenderTextureTiled_Iterate(renderer, texture, &real_srcrect, scale, dstrect); 4600 } 4601} 4602 4603bool 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) 4604{ 4605 SDL_FRect full_src, full_dst; 4606 SDL_FRect curr_src, curr_dst; 4607 float dst_left_width; 4608 float dst_right_width; 4609 float dst_top_height; 4610 float dst_bottom_height; 4611 4612 CHECK_RENDERER_MAGIC(renderer, false); 4613 CHECK_TEXTURE_MAGIC(texture, false); 4614 4615 CHECK_PARAM(renderer != texture->renderer) { 4616 return SDL_SetError("Texture was not created with this renderer"); 4617 } 4618 4619 if (!srcrect) { 4620 full_src.x = 0; 4621 full_src.y = 0; 4622 full_src.w = (float)texture->w; 4623 full_src.h = (float)texture->h; 4624 srcrect = &full_src; 4625 } 4626 4627 if (!dstrect) { 4628 GetRenderViewportSize(renderer, &full_dst); 4629 dstrect = &full_dst; 4630 } 4631 4632 if (scale <= 0.0f || scale == 1.0f) { 4633 dst_left_width = SDL_ceilf(left_width); 4634 dst_right_width = SDL_ceilf(right_width); 4635 dst_top_height = SDL_ceilf(top_height); 4636 dst_bottom_height = SDL_ceilf(bottom_height); 4637 } else { 4638 dst_left_width = SDL_ceilf(left_width * scale); 4639 dst_right_width = SDL_ceilf(right_width * scale); 4640 dst_top_height = SDL_ceilf(top_height * scale); 4641 dst_bottom_height = SDL_ceilf(bottom_height * scale); 4642 } 4643 4644 // Center 4645 curr_src.x = srcrect->x + left_width; 4646 curr_src.y = srcrect->y + top_height; 4647 curr_src.w = srcrect->w - left_width - right_width; 4648 curr_src.h = srcrect->h - top_height - bottom_height; 4649 curr_dst.x = dstrect->x + dst_left_width; 4650 curr_dst.y = dstrect->y + dst_top_height; 4651 curr_dst.w = dstrect->w - dst_left_width - dst_right_width; 4652 curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; 4653 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4654 return false; 4655 } 4656 4657 // Upper-left corner 4658 curr_src.x = srcrect->x; 4659 curr_src.y = srcrect->y; 4660 curr_src.w = left_width; 4661 curr_src.h = top_height; 4662 curr_dst.x = dstrect->x; 4663 curr_dst.y = dstrect->y; 4664 curr_dst.w = dst_left_width; 4665 curr_dst.h = dst_top_height; 4666 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4667 return false; 4668 } 4669 4670 // Upper-right corner 4671 curr_src.x = srcrect->x + srcrect->w - right_width; 4672 curr_src.w = right_width; 4673 curr_dst.x = dstrect->x + dstrect->w - dst_right_width; 4674 curr_dst.w = dst_right_width; 4675 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4676 return false; 4677 } 4678 4679 // Lower-right corner 4680 curr_src.y = srcrect->y + srcrect->h - bottom_height; 4681 curr_src.h = bottom_height; 4682 curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height; 4683 curr_dst.h = dst_bottom_height; 4684 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4685 return false; 4686 } 4687 4688 // Lower-left corner 4689 curr_src.x = srcrect->x; 4690 curr_src.w = left_width; 4691 curr_dst.x = dstrect->x; 4692 curr_dst.w = dst_left_width; 4693 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4694 return false; 4695 } 4696 4697 // Left 4698 curr_src.y = srcrect->y + top_height; 4699 curr_src.h = srcrect->h - top_height - bottom_height; 4700 curr_dst.y = dstrect->y + dst_top_height; 4701 curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; 4702 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4703 return false; 4704 } 4705 4706 // Right 4707 curr_src.x = srcrect->x + srcrect->w - right_width; 4708 curr_src.w = right_width; 4709 curr_dst.x = dstrect->x + dstrect->w - dst_right_width; 4710 curr_dst.w = dst_right_width; 4711 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4712 return false; 4713 } 4714 4715 // Top 4716 curr_src.x = srcrect->x + left_width; 4717 curr_src.y = srcrect->y; 4718 curr_src.w = srcrect->w - left_width - right_width; 4719 curr_src.h = top_height; 4720 curr_dst.x = dstrect->x + dst_left_width; 4721 curr_dst.y = dstrect->y; 4722 curr_dst.w = dstrect->w - dst_left_width - dst_right_width; 4723 curr_dst.h = dst_top_height; 4724 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4725 return false; 4726 } 4727 4728 // Bottom 4729 curr_src.y = srcrect->y + srcrect->h - bottom_height; 4730 curr_src.h = bottom_height; 4731 curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height; 4732 curr_dst.h = dst_bottom_height; 4733 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4734 return false; 4735 } 4736 4737 return true; 4738} 4739 4740bool 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) 4741{ 4742 SDL_FRect full_src, full_dst; 4743 SDL_FRect curr_src, curr_dst; 4744 float dst_left_width; 4745 float dst_right_width; 4746 float dst_top_height; 4747 float dst_bottom_height; 4748 4749 CHECK_RENDERER_MAGIC(renderer, false); 4750 CHECK_TEXTURE_MAGIC(texture, false); 4751 4752 CHECK_PARAM(renderer != texture->renderer) { 4753 return SDL_SetError("Texture was not created with this renderer"); 4754 } 4755 4756 if (!srcrect) { 4757 full_src.x = 0; 4758 full_src.y = 0; 4759 full_src.w = (float)texture->w; 4760 full_src.h = (float)texture->h; 4761 srcrect = &full_src; 4762 } 4763 4764 if (!dstrect) { 4765 GetRenderViewportSize(renderer, &full_dst); 4766 dstrect = &full_dst; 4767 } 4768 4769 if (scale <= 0.0f || scale == 1.0f) { 4770 dst_left_width = SDL_ceilf(left_width); 4771 dst_right_width = SDL_ceilf(right_width); 4772 dst_top_height = SDL_ceilf(top_height); 4773 dst_bottom_height = SDL_ceilf(bottom_height); 4774 } else { 4775 dst_left_width = SDL_ceilf(left_width * scale); 4776 dst_right_width = SDL_ceilf(right_width * scale); 4777 dst_top_height = SDL_ceilf(top_height * scale); 4778 dst_bottom_height = SDL_ceilf(bottom_height * scale); 4779 } 4780 4781 // Center 4782 curr_src.x = srcrect->x + left_width; 4783 curr_src.y = srcrect->y + top_height; 4784 curr_src.w = srcrect->w - left_width - right_width; 4785 curr_src.h = srcrect->h - top_height - bottom_height; 4786 curr_dst.x = dstrect->x + dst_left_width; 4787 curr_dst.y = dstrect->y + dst_top_height; 4788 curr_dst.w = dstrect->w - dst_left_width - dst_right_width; 4789 curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; 4790 if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) { 4791 return false; 4792 } 4793 4794 // Upper-left corner 4795 curr_src.x = srcrect->x; 4796 curr_src.y = srcrect->y; 4797 curr_src.w = left_width; 4798 curr_src.h = top_height; 4799 curr_dst.x = dstrect->x; 4800 curr_dst.y = dstrect->y; 4801 curr_dst.w = dst_left_width; 4802 curr_dst.h = dst_top_height; 4803 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4804 return false; 4805 } 4806 4807 // Upper-right corner 4808 curr_src.x = srcrect->x + srcrect->w - right_width; 4809 curr_src.w = right_width; 4810 curr_dst.x = dstrect->x + dstrect->w - dst_right_width; 4811 curr_dst.w = dst_right_width; 4812 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4813 return false; 4814 } 4815 4816 // Lower-right corner 4817 curr_src.y = srcrect->y + srcrect->h - bottom_height; 4818 curr_src.h = bottom_height; 4819 curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height; 4820 curr_dst.h = dst_bottom_height; 4821 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4822 return false; 4823 } 4824 4825 // Lower-left corner 4826 curr_src.x = srcrect->x; 4827 curr_src.w = left_width; 4828 curr_dst.x = dstrect->x; 4829 curr_dst.w = dst_left_width; 4830 if (!SDL_RenderTexture(renderer, texture, &curr_src, &curr_dst)) { 4831 return false; 4832 } 4833 4834 // Left 4835 curr_src.y = srcrect->y + top_height; 4836 curr_src.h = srcrect->h - top_height - bottom_height; 4837 curr_dst.y = dstrect->y + dst_top_height; 4838 curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; 4839 if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) { 4840 return false; 4841 } 4842 4843 // Right 4844 curr_src.x = srcrect->x + srcrect->w - right_width; 4845 curr_src.w = right_width; 4846 curr_dst.x = dstrect->x + dstrect->w - dst_right_width; 4847 curr_dst.w = dst_right_width; 4848 if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) { 4849 return false; 4850 } 4851 4852 // Top 4853 curr_src.x = srcrect->x + left_width; 4854 curr_src.y = srcrect->y; 4855 curr_src.w = srcrect->w - left_width - right_width; 4856 curr_src.h = top_height; 4857 curr_dst.x = dstrect->x + dst_left_width; 4858 curr_dst.y = dstrect->y; 4859 curr_dst.w = dstrect->w - dst_left_width - dst_right_width; 4860 curr_dst.h = dst_top_height; 4861 if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) { 4862 return false; 4863 } 4864 4865 // Bottom 4866 curr_src.y = srcrect->y + srcrect->h - bottom_height; 4867 curr_src.h = bottom_height; 4868 curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height; 4869 curr_dst.h = dst_bottom_height; 4870 if (!SDL_RenderTextureTiled(renderer, texture, &curr_src, tileScale, &curr_dst)) { 4871 return false; 4872 } 4873 4874 return true; 4875} 4876 4877bool SDL_RenderGeometry(SDL_Renderer *renderer, 4878 SDL_Texture *texture, 4879 const SDL_Vertex *vertices, int num_vertices, 4880 const int *indices, int num_indices) 4881{ 4882 if (vertices) { 4883 const float *xy = &vertices->position.x; 4884 int xy_stride = sizeof(SDL_Vertex); 4885 const SDL_FColor *color = &vertices->color; 4886 int color_stride = sizeof(SDL_Vertex); 4887 const float *uv = &vertices->tex_coord.x; 4888 int uv_stride = sizeof(SDL_Vertex); 4889 int size_indices = 4; 4890 return SDL_RenderGeometryRaw(renderer, texture, xy, xy_stride, color, color_stride, uv, uv_stride, num_vertices, indices, num_indices, size_indices); 4891 } else { 4892 return SDL_InvalidParamError("vertices"); 4893 } 4894} 4895 4896#ifdef SDL_VIDEO_RENDER_SW 4897static int remap_one_indice( 4898 int prev, 4899 int k, 4900 SDL_Texture *texture, 4901 const float *xy, int xy_stride, 4902 const SDL_FColor *color, int color_stride, 4903 const float *uv, int uv_stride) 4904{ 4905 const float *xy0_, *xy1_, *uv0_, *uv1_; 4906 const SDL_FColor *col0_, *col1_; 4907 xy0_ = (const float *)((const char *)xy + prev * xy_stride); 4908 xy1_ = (const float *)((const char *)xy + k * xy_stride); 4909 if (xy0_[0] != xy1_[0]) { 4910 return k; 4911 } 4912 if (xy0_[1] != xy1_[1]) { 4913 return k; 4914 } 4915 if (texture) { 4916 uv0_ = (const float *)((const char *)uv + prev * uv_stride); 4917 uv1_ = (const float *)((const char *)uv + k * uv_stride); 4918 if (uv0_[0] != uv1_[0]) { 4919 return k; 4920 } 4921 if (uv0_[1] != uv1_[1]) { 4922 return k; 4923 } 4924 } 4925 col0_ = (const SDL_FColor *)((const char *)color + prev * color_stride); 4926 col1_ = (const SDL_FColor *)((const char *)color + k * color_stride); 4927 4928 if (SDL_memcmp(col0_, col1_, sizeof(*col0_)) != 0) { 4929 return k; 4930 } 4931 4932 return prev; 4933} 4934 4935static int remap_indices( 4936 int prev[3], 4937 int k, 4938 SDL_Texture *texture, 4939 const float *xy, int xy_stride, 4940 const SDL_FColor *color, int color_stride, 4941 const float *uv, int uv_stride) 4942{ 4943 int i; 4944 if (prev[0] == -1) { 4945 return k; 4946 } 4947 4948 for (i = 0; i < 3; i++) { 4949 int new_k = remap_one_indice(prev[i], k, texture, xy, xy_stride, color, color_stride, uv, uv_stride); 4950 if (new_k != k) { 4951 return new_k; 4952 } 4953 } 4954 return k; 4955} 4956 4957#define DEBUG_SW_RENDER_GEOMETRY 0 4958// For the software renderer, try to reinterpret triangles as SDL_Rect 4959static bool SDLCALL SDL_SW_RenderGeometryRaw(SDL_Renderer *renderer, 4960 SDL_Texture *texture, 4961 const float *xy, int xy_stride, 4962 const SDL_FColor *color, int color_stride, 4963 const float *uv, int uv_stride, 4964 int num_vertices, 4965 const void *indices, int num_indices, int size_indices) 4966{ 4967 int i; 4968 bool result = true; 4969 int count = indices ? num_indices : num_vertices; 4970 int prev[3]; // Previous triangle vertex indices 4971 float texw = 0.0f, texh = 0.0f; 4972 SDL_BlendMode blendMode = SDL_BLENDMODE_NONE; 4973 float r = 0, g = 0, b = 0, a = 0; 4974 const SDL_RenderViewState *view = renderer->view; 4975 const float scale_x = view->current_scale.x; 4976 const float scale_y = view->current_scale.y; 4977 4978 // Save 4979 SDL_GetRenderDrawBlendMode(renderer, &blendMode); 4980 SDL_GetRenderDrawColorFloat(renderer, &r, &g, &b, &a); 4981 4982 if (texture) { 4983 SDL_GetTextureSize(texture, &texw, &texh); 4984 } 4985 4986 prev[0] = -1; 4987 prev[1] = -1; 4988 prev[2] = -1; 4989 size_indices = indices ? size_indices : 0; 4990 4991 for (i = 0; i < count; i += 3) { 4992 int k0, k1, k2; // Current triangle indices 4993 int is_quad = 1; 4994#if DEBUG_SW_RENDER_GEOMETRY 4995 int is_uniform = 1; 4996 int is_rectangle = 1; 4997#endif 4998 int A = -1; // Top left vertex 4999 int B = -1; // Bottom right vertex 5000 int C = -1; // Third vertex of current triangle 5001 int C2 = -1; // Last, vertex of previous triangle 5002 5003 if (size_indices == 4) { 5004 k0 = ((const Uint32 *)indices)[i]; 5005 k1 = ((const Uint32 *)indices)[i + 1]; 5006 k2 = ((const Uint32 *)indices)[i + 2]; 5007 } else if (size_indices == 2) { 5008 k0 = ((const Uint16 *)indices)[i]; 5009 k1 = ((const Uint16 *)indices)[i + 1]; 5010 k2 = ((const Uint16 *)indices)[i + 2]; 5011 } else if (size_indices == 1) { 5012 k0 = ((const Uint8 *)indices)[i]; 5013 k1 = ((const Uint8 *)indices)[i + 1]; 5014 k2 = ((const Uint8 *)indices)[i + 2]; 5015 } else { 5016 /* Vertices were not provided by indices. Maybe some are duplicated. 5017 * We try to indentificate the duplicates by comparing with the previous three vertices */ 5018 k0 = remap_indices(prev, i, texture, xy, xy_stride, color, color_stride, uv, uv_stride); 5019 k1 = remap_indices(prev, i + 1, texture, xy, xy_stride, color, color_stride, uv, uv_stride); 5020 k2 = remap_indices(prev, i + 2, texture, xy, xy_stride, color, color_stride, uv, uv_stride); 5021 } 5022 5023 if (prev[0] == -1) { 5024 prev[0] = k0; 5025 prev[1] = k1; 5026 prev[2] = k2; 5027 continue; 5028 } 5029 5030 /* Two triangles forming a quadrilateral, 5031 * prev and current triangles must have exactly 2 common vertices */ 5032 { 5033 int cnt = 0, j = 3; 5034 while (j--) { 5035 int p = prev[j]; 5036 if (p == k0 || p == k1 || p == k2) { 5037 cnt++; 5038 } 5039 } 5040 is_quad = (cnt == 2); 5041 } 5042 5043 // Identify vertices 5044 if (is_quad) { 5045 const float *xy0_, *xy1_, *xy2_; 5046 float x0, x1, x2; 5047 float y0, y1, y2; 5048 xy0_ = (const float *)((const char *)xy + k0 * xy_stride); 5049 xy1_ = (const float *)((const char *)xy + k1 * xy_stride); 5050 xy2_ = (const float *)((const char *)xy + k2 * xy_stride); 5051 x0 = xy0_[0]; 5052 y0 = xy0_[1]; 5053 x1 = xy1_[0]; 5054 y1 = xy1_[1]; 5055 x2 = xy2_[0]; 5056 y2 = xy2_[1]; 5057 5058 // Find top-left 5059 if (x0 <= x1 && y0 <= y1) { 5060 if (x0 <= x2 && y0 <= y2) { 5061 A = k0; 5062 } else { 5063 A = k2; 5064 } 5065 } else { 5066 if (x1 <= x2 && y1 <= y2) { 5067 A = k1; 5068 } else { 5069 A = k2; 5070 } 5071 } 5072 5073 // Find bottom-right 5074 if (x0 >= x1 && y0 >= y1) { 5075 if (x0 >= x2 && y0 >= y2) { 5076 B = k0; 5077 } else { 5078 B = k2; 5079 } 5080 } else { 5081 if (x1 >= x2 && y1 >= y2) { 5082 B = k1; 5083 } else { 5084 B = k2; 5085 } 5086 } 5087 5088 // Find C 5089 if (k0 != A && k0 != B) { 5090 C = k0; 5091 } else if (k1 != A && k1 != B) { 5092 C = k1; 5093 } else { 5094 C = k2; 5095 } 5096 5097 // Find C2 5098 if (prev[0] != A && prev[0] != B) { 5099 C2 = prev[0]; 5100 } else if (prev[1] != A && prev[1] != B) { 5101 C2 = prev[1]; 5102 } else { 5103 C2 = prev[2]; 5104 } 5105 5106 xy0_ = (const float *)((const char *)xy + A * xy_stride); 5107 xy1_ = (const float *)((const char *)xy + B * xy_stride); 5108 xy2_ = (const float *)((const char *)xy + C * xy_stride); 5109 x0 = xy0_[0]; 5110 y0 = xy0_[1]; 5111 x1 = xy1_[0]; 5112 y1 = xy1_[1]; 5113 x2 = xy2_[0]; 5114 y2 = xy2_[1]; 5115 5116 // Check if triangle A B C is rectangle 5117 if ((x0 == x2 && y1 == y2) || (y0 == y2 && x1 == x2)) { 5118 // ok 5119 } else { 5120 is_quad = 0; 5121#if DEBUG_SW_RENDER_GEOMETRY 5122 is_rectangle = 0; 5123#endif 5124 } 5125 5126 xy2_ = (const float *)((const char *)xy + C2 * xy_stride); 5127 x2 = xy2_[0]; 5128 y2 = xy2_[1]; 5129 5130 // Check if triangle A B C2 is rectangle 5131 if ((x0 == x2 && y1 == y2) || (y0 == y2 && x1 == x2)) { 5132 // ok 5133 } else { 5134 is_quad = 0; 5135#if DEBUG_SW_RENDER_GEOMETRY 5136 is_rectangle = 0; 5137#endif 5138 } 5139 } 5140 5141 // Check if uniformly colored 5142 if (is_quad) { 5143 const SDL_FColor *col0_ = (const SDL_FColor *)((const char *)color + A * color_stride); 5144 const SDL_FColor *col1_ = (const SDL_FColor *)((const char *)color + B * color_stride); 5145 const SDL_FColor *col2_ = (const SDL_FColor *)((const char *)color + C * color_stride); 5146 const SDL_FColor *col3_ = (const SDL_FColor *)((const char *)color + C2 * color_stride); 5147 if (SDL_memcmp(col0_, col1_, sizeof(*col0_)) == 0 && 5148 SDL_memcmp(col0_, col2_, sizeof(*col0_)) == 0 && 5149 SDL_memcmp(col0_, col3_, sizeof(*col0_)) == 0) { 5150 // ok 5151 } else { 5152 is_quad = 0; 5153#if DEBUG_SW_RENDER_GEOMETRY 5154 is_uniform = 0; 5155#endif 5156 } 5157 } 5158 5159 // Check if UVs within range 5160 if (is_quad) { 5161 const float *uv0_ = (const float *)((const char *)uv + A * color_stride); 5162 const float *uv1_ = (const float *)((const char *)uv + B * color_stride); 5163 const float *uv2_ = (const float *)((const char *)uv + C * color_stride); 5164 const float *uv3_ = (const float *)((const char *)uv + C2 * color_stride); 5165 if (uv0_[0] >= 0.0f && uv0_[0] <= 1.0f && 5166 uv1_[0] >= 0.0f && uv1_[0] <= 1.0f && 5167 uv2_[0] >= 0.0f && uv2_[0] <= 1.0f && 5168 uv3_[0] >= 0.0f && uv3_[0] <= 1.0f) { 5169 // ok 5170 } else { 5171 is_quad = 0; 5172 } 5173 } 5174 5175 // Start rendering rect 5176 if (is_quad) { 5177 SDL_FRect s; 5178 SDL_FRect d; 5179 const float *xy0_, *xy1_, *uv0_, *uv1_; 5180 const SDL_FColor *col0_ = (const SDL_FColor *)((const char *)color + k0 * color_stride); 5181 5182 xy0_ = (const float *)((const char *)xy + A * xy_stride); 5183 xy1_ = (const float *)((const char *)xy + B * xy_stride); 5184 5185 if (texture) { 5186 uv0_ = (const float *)((const char *)uv + A * uv_stride); 5187 uv1_ = (const float *)((const char *)uv + B * uv_stride); 5188 s.x = uv0_[0] * texw; 5189 s.y = uv0_[1] * texh; 5190 s.w = uv1_[0] * texw - s.x; 5191 s.h = uv1_[1] * texh - s.y; 5192 } else { 5193 s.x = s.y = s.w = s.h = 0; 5194 } 5195 5196 d.x = xy0_[0]; 5197 d.y = xy0_[1]; 5198 d.w = xy1_[0] - d.x; 5199 d.h = xy1_[1] - d.y; 5200 5201 // Rect + texture 5202 if (texture && s.w != 0 && s.h != 0) { 5203 SDL_SetTextureAlphaModFloat(texture, col0_->a); 5204 SDL_SetTextureColorModFloat(texture, col0_->r, col0_->g, col0_->b); 5205 if (s.w > 0 && s.h > 0) { 5206 SDL_RenderTexture(renderer, texture, &s, &d); 5207 } else { 5208 int flags = 0; 5209 if (s.w < 0) { 5210 flags |= SDL_FLIP_HORIZONTAL; 5211 s.w *= -1; 5212 s.x -= s.w; 5213 } 5214 if (s.h < 0) { 5215 flags |= SDL_FLIP_VERTICAL; 5216 s.h *= -1; 5217 s.y -= s.h; 5218 } 5219 SDL_RenderTextureRotated(renderer, texture, &s, &d, 0, NULL, (SDL_FlipMode)flags); 5220 } 5221 5222#if DEBUG_SW_RENDER_GEOMETRY 5223 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, 5224 (void *)texture, s.x, s.y, s.w, s.h, d.x, d.y, d.w, d.h); 5225#endif 5226 } else if (d.w != 0.0f && d.h != 0.0f) { // Rect, no texture 5227 SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); 5228 SDL_SetRenderDrawColorFloat(renderer, col0_->r, col0_->g, col0_->b, col0_->a); 5229 SDL_RenderFillRect(renderer, &d); 5230#if DEBUG_SW_RENDER_GEOMETRY 5231 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, 5232 (void *)texture, d.x, d.y, d.w, d.h); 5233 } else { 5234 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, 5235 (void *)texture, s.x, s.y, s.w, s.h, d.x, d.y, d.w, d.h); 5236#endif 5237 } 5238 5239 prev[0] = -1; 5240 } else { 5241 // Render triangles 5242 if (prev[0] != -1) { 5243#if DEBUG_SW_RENDER_GEOMETRY 5244 SDL_Log("Triangle %d %d %d - is_uniform:%d is_rectangle:%d", prev[0], prev[1], prev[2], is_uniform, is_rectangle); 5245#endif 5246 result = QueueCmdGeometry(renderer, texture, 5247 xy, xy_stride, color, color_stride, uv, uv_stride, 5248 num_vertices, prev, 3, 4, 5249 scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); 5250 if (!result) { 5251 goto end; 5252 } 5253 } 5254 5255 prev[0] = k0; 5256 prev[1] = k1; 5257 prev[2] = k2; 5258 } 5259 } // End for (), next triangle 5260 5261 if (prev[0] != -1) { 5262 // flush the last triangle 5263#if DEBUG_SW_RENDER_GEOMETRY 5264 SDL_Log("Last triangle %d %d %d", prev[0], prev[1], prev[2]); 5265#endif 5266 result = QueueCmdGeometry(renderer, texture, 5267 xy, xy_stride, color, color_stride, uv, uv_stride, 5268 num_vertices, prev, 3, 4, 5269 scale_x, scale_y, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); 5270 if (!result) { 5271 goto end; 5272 } 5273 } 5274 5275end: 5276 // Restore 5277 SDL_SetRenderDrawBlendMode(renderer, blendMode); 5278 SDL_SetRenderDrawColorFloat(renderer, r, g, b, a); 5279 5280 return result; 5281} 5282#endif // SDL_VIDEO_RENDER_SW 5283 5284bool SDL_RenderGeometryRaw(SDL_Renderer *renderer, 5285 SDL_Texture *texture, 5286 const float *xy, int xy_stride, 5287 const SDL_FColor *color, int color_stride, 5288 const float *uv, int uv_stride, 5289 int num_vertices, 5290 const void *indices, int num_indices, int size_indices) 5291{ 5292 int i; 5293 int count = indices ? num_indices : num_vertices; 5294 SDL_TextureAddressMode texture_address_mode_u = SDL_TEXTURE_ADDRESS_CLAMP; 5295 SDL_TextureAddressMode texture_address_mode_v = SDL_TEXTURE_ADDRESS_CLAMP; 5296 5297 CHECK_RENDERER_MAGIC(renderer, false); 5298 5299 if (texture) { 5300 CHECK_TEXTURE_MAGIC(texture, false); 5301 5302 CHECK_PARAM(renderer != texture->renderer) { 5303 return SDL_SetError("Texture was not created with this renderer"); 5304 } 5305 } 5306 5307 CHECK_PARAM(!xy) { 5308 return SDL_InvalidParamError("xy"); 5309 } 5310 5311 CHECK_PARAM(!color) { 5312 return SDL_InvalidParamError("color"); 5313 } 5314 5315 CHECK_PARAM(texture && !uv) { 5316 return SDL_InvalidParamError("uv"); 5317 } 5318 5319 (void)count; // In case parameter checking is disabled 5320 CHECK_PARAM(count % 3 != 0) { 5321 return SDL_InvalidParamError(indices ? "num_indices" : "num_vertices"); 5322 } 5323 5324 if (indices) { 5325 CHECK_PARAM(size_indices != 1 && size_indices != 2 && size_indices != 4) { 5326 return SDL_InvalidParamError("size_indices"); 5327 } 5328 } 5329 5330 if (!indices) { 5331 size_indices = 0; 5332 } 5333 5334 if (!renderer->QueueGeometry) { 5335 return SDL_Unsupported(); 5336 } 5337 5338#if DONT_DRAW_WHILE_HIDDEN 5339 // Don't draw while we're hidden 5340 if (renderer->hidden) { 5341 return true; 5342 } 5343#endif 5344 5345 if (num_vertices < 3) { 5346 return true; 5347 } 5348 5349 if (texture) { 5350 if (!UpdateTexturePalette(texture)) { 5351 return false; 5352 } 5353 5354 if (texture->native) { 5355 texture = texture->native; 5356 } 5357 5358 if (renderer->npot_texture_wrap_unsupported && IsNPOT(texture->w)) { 5359 texture_address_mode_u = SDL_TEXTURE_ADDRESS_CLAMP; 5360 } else { 5361 texture_address_mode_u = renderer->texture_address_mode_u; 5362 } 5363 if (renderer->npot_texture_wrap_unsupported && IsNPOT(texture->h)) { 5364 texture_address_mode_v = SDL_TEXTURE_ADDRESS_CLAMP; 5365 } else { 5366 texture_address_mode_v = renderer->texture_address_mode_v; 5367 } 5368 5369 if (texture_address_mode_u == SDL_TEXTURE_ADDRESS_AUTO || 5370 texture_address_mode_v == SDL_TEXTURE_ADDRESS_AUTO) { 5371 for (i = 0; i < num_vertices; ++i) { 5372 const float *uv_ = (const float *)((const char *)uv + i * uv_stride); 5373 float u = uv_[0]; 5374 float v = uv_[1]; 5375 if (u < 0.0f || u > 1.0f) { 5376 if (texture_address_mode_u == SDL_TEXTURE_ADDRESS_AUTO) { 5377 texture_address_mode_u = SDL_TEXTURE_ADDRESS_WRAP; 5378 if (texture_address_mode_v != SDL_TEXTURE_ADDRESS_AUTO) { 5379 break; 5380 } 5381 } 5382 } 5383 if (v < 0.0f || v > 1.0f) { 5384 if (texture_address_mode_v == SDL_TEXTURE_ADDRESS_AUTO) { 5385 texture_address_mode_v = SDL_TEXTURE_ADDRESS_WRAP; 5386 if (texture_address_mode_u != SDL_TEXTURE_ADDRESS_AUTO) { 5387 break; 5388 } 5389 } 5390 } 5391 } 5392 if (texture_address_mode_u == SDL_TEXTURE_ADDRESS_AUTO) { 5393 texture_address_mode_u = SDL_TEXTURE_ADDRESS_CLAMP; 5394 } 5395 if (texture_address_mode_v == SDL_TEXTURE_ADDRESS_AUTO) { 5396 texture_address_mode_v = SDL_TEXTURE_ADDRESS_CLAMP; 5397 } 5398 } 5399 } 5400 5401 if (indices) { 5402 for (i = 0; i < num_indices; ++i) { 5403 int j; 5404 if (size_indices == 4) { 5405 j = ((const Uint32 *)indices)[i]; 5406 } else if (size_indices == 2) { 5407 j = ((const Uint16 *)indices)[i]; 5408 } else { 5409 j = ((const Uint8 *)indices)[i]; 5410 } 5411 if (j < 0 || j >= num_vertices) { 5412 return SDL_SetError("Values of 'indices' out of bounds"); 5413 } 5414 } 5415 } 5416 5417 if (texture) { 5418 texture->last_command_generation = renderer->render_command_generation; 5419 } 5420 5421 // For the software renderer, try to reinterpret triangles as SDL_Rect 5422#ifdef SDL_VIDEO_RENDER_SW 5423 if (renderer->software && 5424 texture_address_mode_u == SDL_TEXTURE_ADDRESS_CLAMP && 5425 texture_address_mode_v == SDL_TEXTURE_ADDRESS_CLAMP) { 5426 return SDL_SW_RenderGeometryRaw(renderer, texture, 5427 xy, xy_stride, color, color_stride, uv, uv_stride, num_vertices, 5428 indices, num_indices, size_indices); 5429 } 5430#endif 5431 5432 const SDL_RenderViewState *view = renderer->view; 5433 return QueueCmdGeometry(renderer, texture, 5434 xy, xy_stride, color, color_stride, uv, uv_stride, 5435 num_vertices, indices, num_indices, size_indices, 5436 view->current_scale.x, view->current_scale.y, 5437 texture_address_mode_u, texture_address_mode_v); 5438} 5439 5440bool SDL_SetRenderTextureAddressMode(SDL_Renderer *renderer, SDL_TextureAddressMode u_mode, SDL_TextureAddressMode v_mode) 5441{ 5442 CHECK_RENDERER_MAGIC(renderer, false); 5443 5444 renderer->texture_address_mode_u = u_mode; 5445 renderer->texture_address_mode_v = v_mode; 5446 return true; 5447} 5448 5449bool SDL_GetRenderTextureAddressMode(SDL_Renderer *renderer, SDL_TextureAddressMode *u_mode, SDL_TextureAddressMode *v_mode) 5450{ 5451 if (u_mode) { 5452 *u_mode = SDL_TEXTURE_ADDRESS_INVALID; 5453 } 5454 if (v_mode) { 5455 *v_mode = SDL_TEXTURE_ADDRESS_INVALID; 5456 } 5457 5458 CHECK_RENDERER_MAGIC(renderer, false); 5459 5460 if (u_mode) { 5461 *u_mode = renderer->texture_address_mode_u; 5462 } 5463 if (v_mode) { 5464 *v_mode = renderer->texture_address_mode_v; 5465 } 5466 return true; 5467} 5468 5469SDL_Surface *SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect) 5470{ 5471 CHECK_RENDERER_MAGIC(renderer, NULL); 5472 5473 if (!renderer->RenderReadPixels) { 5474 SDL_Unsupported(); 5475 return NULL; 5476 } 5477 5478 FlushRenderCommands(renderer); // we need to render before we read the results. 5479 5480 SDL_Rect real_rect = renderer->view->pixel_viewport; 5481 5482 if (rect) { 5483 if (!SDL_GetRectIntersection(rect, &real_rect, &real_rect)) { 5484 SDL_SetError("Can't read outside the current viewport"); 5485 return NULL; 5486 } 5487 } 5488 5489 SDL_Surface *surface = renderer->RenderReadPixels(renderer, &real_rect); 5490 if (surface) { 5491 SDL_PropertiesID props = SDL_GetSurfaceProperties(surface); 5492 5493 if (renderer->target) { 5494 SDL_Texture *target = renderer->target; 5495 SDL_Texture *parent = SDL_GetPointerProperty(SDL_GetTextureProperties(target), SDL_PROP_TEXTURE_PARENT_POINTER, NULL); 5496 SDL_PixelFormat expected_format = (parent ? parent->format : target->format); 5497 5498 SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, target->SDR_white_point); 5499 SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, target->HDR_headroom); 5500 5501 // Set the expected surface format 5502 if ((surface->format == SDL_PIXELFORMAT_ARGB8888 && expected_format == SDL_PIXELFORMAT_XRGB8888) || 5503 (surface->format == SDL_PIXELFORMAT_RGBA8888 && expected_format == SDL_PIXELFORMAT_RGBX8888) || 5504 (surface->format == SDL_PIXELFORMAT_ABGR8888 && expected_format == SDL_PIXELFORMAT_XBGR8888) || 5505 (surface->format == SDL_PIXELFORMAT_BGRA8888 && expected_format == SDL_PIXELFORMAT_BGRX8888)) { 5506 surface->format = expected_format; 5507 surface->fmt = SDL_GetPixelFormatDetails(expected_format); 5508 } 5509 } else { 5510 SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point); 5511 SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, renderer->HDR_headroom); 5512 } 5513 } 5514 return surface; 5515} 5516 5517static void SDL_RenderApplyWindowShape(SDL_Renderer *renderer) 5518{ 5519 SDL_Surface *shape = (SDL_Surface *)SDL_GetPointerProperty(SDL_GetWindowProperties(renderer->window), SDL_PROP_WINDOW_SHAPE_POINTER, NULL); 5520 if (shape != renderer->shape_surface) { 5521 if (renderer->shape_texture) { 5522 SDL_DestroyTexture(renderer->shape_texture); 5523 renderer->shape_texture = NULL; 5524 } 5525 5526 if (shape) { 5527 // There's nothing we can do if this fails, so just keep on going 5528 renderer->shape_texture = SDL_CreateTextureFromSurface(renderer, shape); 5529 5530 SDL_SetTextureBlendMode(renderer->shape_texture, 5531 SDL_ComposeCustomBlendMode( 5532 SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD, 5533 SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD)); 5534 } 5535 renderer->shape_surface = shape; 5536 } 5537 5538 if (renderer->shape_texture) { 5539 SDL_RenderTexture(renderer, renderer->shape_texture, NULL, NULL); 5540 } 5541} 5542 5543static void SDL_SimulateRenderVSync(SDL_Renderer *renderer) 5544{ 5545 Uint64 now, elapsed; 5546 const Uint64 interval = renderer->simulate_vsync_interval_ns; 5547 5548 if (!interval) { 5549 // We can't do sub-ns delay, so just return here 5550 return; 5551 } 5552 5553 now = SDL_GetTicksNS(); 5554 elapsed = (now - renderer->last_present); 5555 if (elapsed < interval) { 5556 Uint64 duration = (interval - elapsed); 5557 SDL_DelayPrecise(duration); 5558 now = SDL_GetTicksNS(); 5559 } 5560 5561 elapsed = (now - renderer->last_present); 5562 if (!renderer->last_present || elapsed > SDL_MS_TO_NS(1000)) { 5563 // It's been too long, reset the presentation timeline 5564 renderer->last_present = now; 5565 } else { 5566 renderer->last_present += (elapsed / interval) * interval; 5567 } 5568} 5569 5570bool SDL_RenderPresent(SDL_Renderer *renderer) 5571{ 5572 bool presented = true; 5573 5574 CHECK_RENDERER_MAGIC(renderer, false); 5575 5576 CHECK_PARAM(renderer->target) { 5577 if (!renderer->window && SDL_strcmp(renderer->name, SDL_GPU_RENDERER) == 0) { 5578 // We're an offscreen renderer, we must submit the command queue 5579 } else { 5580 return SDL_SetError("You can't present on a render target"); 5581 } 5582 } 5583 5584 if (renderer->transparent_window) { 5585 SDL_RenderApplyWindowShape(renderer); 5586 } 5587 5588 FlushRenderCommands(renderer); // time to send everything to the GPU! 5589 5590#if DONT_DRAW_WHILE_HIDDEN 5591 // Don't present while we're hidden 5592 if (renderer->hidden) { 5593 presented = false; 5594 } else 5595#endif 5596 if (!renderer->RenderPresent(renderer)) { 5597 presented = false; 5598 } 5599 5600 if (renderer->simulate_vsync || 5601 (!presented && renderer->wanted_vsync)) { 5602 SDL_SimulateRenderVSync(renderer); 5603 } 5604 return true; 5605} 5606 5607static void SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying) 5608{ 5609 SDL_Renderer *renderer; 5610 5611 if (texture->public_palette) { 5612 SDL_SetTexturePalette(texture, NULL); 5613 } 5614 5615 SDL_DestroyProperties(texture->props); 5616 5617 renderer = texture->renderer; 5618 if (is_destroying) { 5619 // Renderer get destroyed, avoid to queue more commands 5620 } else { 5621 if (texture == renderer->target) { 5622 SDL_SetRenderTarget(renderer, NULL); // implies command queue flush 5623 } else { 5624 FlushRenderCommandsIfTextureNeeded(texture); 5625 } 5626 } 5627 5628 SDL_SetObjectValid(texture, SDL_OBJECT_TYPE_TEXTURE, false); 5629 5630 if (texture->next) { 5631 texture->next->prev = texture->prev; 5632 } 5633 if (texture->prev) { 5634 texture->prev->next = texture->next; 5635 } else { 5636 renderer->textures = texture->next; 5637 } 5638 5639 if (texture->native) { 5640 SDL_DestroyTextureInternal(texture->native, is_destroying); 5641 } 5642#ifdef SDL_HAVE_YUV 5643 if (texture->yuv) { 5644 SDL_SW_DestroyYUVTexture(texture->yuv); 5645 } 5646#endif 5647 SDL_free(texture->pixels); 5648 5649 renderer->DestroyTexture(renderer, texture); 5650 5651 if (texture->palette_surface) { 5652 SDL_DestroySurface(texture->palette_surface); 5653 texture->palette_surface = NULL; 5654 } 5655 if (texture->locked_surface) { 5656 SDL_DestroySurface(texture->locked_surface); 5657 texture->locked_surface = NULL; 5658 } 5659 5660 SDL_free(texture); 5661} 5662 5663void SDL_DestroyTexture(SDL_Texture *texture) 5664{ 5665 CHECK_TEXTURE_MAGIC(texture, ); 5666 5667 if (--texture->refcount > 0) { 5668 return; 5669 } 5670 5671 SDL_DestroyTextureInternal(texture, false /* is_destroying */); 5672} 5673 5674static void SDL_DiscardAllCommands(SDL_Renderer *renderer) 5675{ 5676 SDL_RenderCommand *cmd; 5677 5678 if (renderer->render_commands_tail) { 5679 renderer->render_commands_tail->next = renderer->render_commands_pool; 5680 cmd = renderer->render_commands; 5681 } else { 5682 cmd = renderer->render_commands_pool; 5683 } 5684 5685 renderer->render_commands_pool = NULL; 5686 renderer->render_commands_tail = NULL; 5687 renderer->render_commands = NULL; 5688 renderer->vertex_data_used = 0; 5689 5690 while (cmd) { 5691 SDL_RenderCommand *next = cmd->next; 5692 SDL_free(cmd); 5693 cmd = next; 5694 } 5695} 5696 5697void SDL_DestroyRendererWithoutFreeing(SDL_Renderer *renderer) 5698{ 5699 SDL_assert(renderer != NULL); 5700 SDL_assert(!renderer->destroyed); 5701 5702 renderer->destroyed = true; 5703 5704 SDL_RemoveWindowEventWatch(SDL_WINDOW_EVENT_WATCH_NORMAL, SDL_RendererEventWatch, renderer); 5705 5706 if (renderer->window) { 5707 SDL_PropertiesID props = SDL_GetWindowProperties(renderer->window); 5708 if (SDL_GetPointerProperty(props, SDL_PROP_WINDOW_RENDERER_POINTER, NULL) == renderer) { 5709 SDL_ClearProperty(props, SDL_PROP_WINDOW_RENDERER_POINTER); 5710 } 5711 SDL_RemoveWindowRenderer(renderer->window, renderer); 5712 } 5713 5714 if (renderer->software) { 5715 // Make sure all drawing to a surface is complete 5716 FlushRenderCommands(renderer); 5717 } 5718 SDL_DiscardAllCommands(renderer); 5719 5720 if (renderer->debug_char_texture_atlas) { 5721 SDL_DestroyTexture(renderer->debug_char_texture_atlas); 5722 renderer->debug_char_texture_atlas = NULL; 5723 } 5724 5725 // Free existing textures for this renderer 5726 while (renderer->textures) { 5727 SDL_Texture *tex = renderer->textures; 5728 SDL_DestroyTextureInternal(renderer->textures, true /* is_destroying */); 5729 SDL_assert(tex != renderer->textures); // satisfy static analysis. 5730 } 5731 5732 // Free palette cache, which should be empty now 5733 if (renderer->palettes) { 5734 SDL_assert(SDL_HashTableEmpty(renderer->palettes)); 5735 SDL_DestroyHashTable(renderer->palettes); 5736 renderer->palettes = NULL; 5737 } 5738 5739 // Clean up renderer-specific resources 5740 if (renderer->DestroyRenderer) { 5741 renderer->DestroyRenderer(renderer); 5742 } 5743 5744 if (renderer->target_mutex) { 5745 SDL_DestroyMutex(renderer->target_mutex); 5746 renderer->target_mutex = NULL; 5747 } 5748 if (renderer->vertex_data) { 5749 SDL_free(renderer->vertex_data); 5750 renderer->vertex_data = NULL; 5751 } 5752 if (renderer->texture_formats) { 5753 SDL_free(renderer->texture_formats); 5754 renderer->texture_formats = NULL; 5755 } 5756 if (renderer->props) { 5757 SDL_DestroyProperties(renderer->props); 5758 renderer->props = 0; 5759 } 5760} 5761 5762void SDL_DestroyRenderer(SDL_Renderer *renderer) 5763{ 5764 CHECK_RENDERER_MAGIC_BUT_NOT_DESTROYED_FLAG(renderer,); 5765 5766 // if we've already destroyed the renderer through SDL_DestroyWindow, we just need 5767 // to free the renderer pointer. This lets apps destroy the window and renderer 5768 // in either order. 5769 if (!renderer->destroyed) { 5770 SDL_DestroyRendererWithoutFreeing(renderer); 5771 } 5772 5773 SDL_Renderer *curr = SDL_renderers; 5774 SDL_Renderer *prev = NULL; 5775 while (curr) { 5776 if (curr == renderer) { 5777 if (prev) { 5778 prev->next = renderer->next; 5779 } else { 5780 SDL_renderers = renderer->next; 5781 } 5782 break; 5783 } 5784 prev = curr; 5785 curr = curr->next; 5786 } 5787 5788 SDL_SetObjectValid(renderer, SDL_OBJECT_TYPE_RENDERER, false); // It's no longer magical... 5789 5790 SDL_free(renderer); 5791} 5792 5793void *SDL_GetRenderMetalLayer(SDL_Renderer *renderer) 5794{ 5795 CHECK_RENDERER_MAGIC(renderer, NULL); 5796 5797 if (renderer->GetMetalLayer) { 5798 FlushRenderCommands(renderer); // in case the app is going to mess with it. 5799 return renderer->GetMetalLayer(renderer); 5800 } 5801 return NULL; 5802} 5803 5804void *SDL_GetRenderMetalCommandEncoder(SDL_Renderer *renderer) 5805{ 5806 CHECK_RENDERER_MAGIC(renderer, NULL); 5807 5808 if (renderer->GetMetalCommandEncoder) { 5809 FlushRenderCommands(renderer); // in case the app is going to mess with it. 5810 return renderer->GetMetalCommandEncoder(renderer); 5811 } 5812 return NULL; 5813} 5814 5815bool SDL_AddVulkanRenderSemaphores(SDL_Renderer *renderer, Uint32 wait_stage_mask, Sint64 wait_semaphore, Sint64 signal_semaphore) 5816{ 5817 CHECK_RENDERER_MAGIC(renderer, false); 5818 5819 if (!renderer->AddVulkanRenderSemaphores) { 5820 return SDL_Unsupported(); 5821 } 5822 return renderer->AddVulkanRenderSemaphores(renderer, wait_stage_mask, wait_semaphore, signal_semaphore); 5823} 5824 5825static SDL_BlendMode SDL_GetShortBlendMode(SDL_BlendMode blendMode) 5826{ 5827 if (blendMode == SDL_BLENDMODE_NONE_FULL) { 5828 return SDL_BLENDMODE_NONE; 5829 } 5830 if (blendMode == SDL_BLENDMODE_BLEND_FULL) { 5831 return SDL_BLENDMODE_BLEND; 5832 } 5833 if (blendMode == SDL_BLENDMODE_BLEND_PREMULTIPLIED_FULL) { 5834 return SDL_BLENDMODE_BLEND_PREMULTIPLIED; 5835 } 5836 if (blendMode == SDL_BLENDMODE_ADD_FULL) { 5837 return SDL_BLENDMODE_ADD; 5838 } 5839 if (blendMode == SDL_BLENDMODE_ADD_PREMULTIPLIED_FULL) { 5840 return SDL_BLENDMODE_ADD_PREMULTIPLIED; 5841 } 5842 if (blendMode == SDL_BLENDMODE_MOD_FULL) { 5843 return SDL_BLENDMODE_MOD; 5844 } 5845 if (blendMode == SDL_BLENDMODE_MUL_FULL) { 5846 return SDL_BLENDMODE_MUL; 5847 } 5848 return blendMode; 5849} 5850 5851static SDL_BlendMode SDL_GetLongBlendMode(SDL_BlendMode blendMode) 5852{ 5853 if (blendMode == SDL_BLENDMODE_NONE) { 5854 return SDL_BLENDMODE_NONE_FULL; 5855 } 5856 if (blendMode == SDL_BLENDMODE_BLEND) { 5857 return SDL_BLENDMODE_BLEND_FULL; 5858 } 5859 if (blendMode == SDL_BLENDMODE_BLEND_PREMULTIPLIED) { 5860 return SDL_BLENDMODE_BLEND_PREMULTIPLIED_FULL; 5861 } 5862 if (blendMode == SDL_BLENDMODE_ADD) { 5863 return SDL_BLENDMODE_ADD_FULL; 5864 } 5865 if (blendMode == SDL_BLENDMODE_ADD_PREMULTIPLIED) { 5866 return SDL_BLENDMODE_ADD_PREMULTIPLIED_FULL; 5867 } 5868 if (blendMode == SDL_BLENDMODE_MOD) { 5869 return SDL_BLENDMODE_MOD_FULL; 5870 } 5871 if (blendMode == SDL_BLENDMODE_MUL) { 5872 return SDL_BLENDMODE_MUL_FULL; 5873 } 5874 return blendMode; 5875} 5876 5877SDL_BlendMode SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor, 5878 SDL_BlendOperation colorOperation, 5879 SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor, 5880 SDL_BlendOperation alphaOperation) 5881{ 5882 SDL_BlendMode blendMode = SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation, 5883 srcAlphaFactor, dstAlphaFactor, alphaOperation); 5884 return SDL_GetShortBlendMode(blendMode); 5885} 5886 5887SDL_BlendFactor SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode) 5888{ 5889 blendMode = SDL_GetLongBlendMode(blendMode); 5890 return (SDL_BlendFactor)(((Uint32)blendMode >> 4) & 0xF); 5891} 5892 5893SDL_BlendFactor SDL_GetBlendModeDstColorFactor(SDL_BlendMode blendMode) 5894{ 5895 blendMode = SDL_GetLongBlendMode(blendMode); 5896 return (SDL_BlendFactor)(((Uint32)blendMode >> 8) & 0xF); 5897} 5898 5899SDL_BlendOperation SDL_GetBlendModeColorOperation(SDL_BlendMode blendMode) 5900{ 5901 blendMode = SDL_GetLongBlendMode(blendMode); 5902 return (SDL_BlendOperation)(((Uint32)blendMode >> 0) & 0xF); 5903} 5904 5905SDL_BlendFactor SDL_GetBlendModeSrcAlphaFactor(SDL_BlendMode blendMode) 5906{ 5907 blendMode = SDL_GetLongBlendMode(blendMode); 5908 return (SDL_BlendFactor)(((Uint32)blendMode >> 20) & 0xF); 5909} 5910 5911SDL_BlendFactor SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode) 5912{ 5913 blendMode = SDL_GetLongBlendMode(blendMode); 5914 return (SDL_BlendFactor)(((Uint32)blendMode >> 24) & 0xF); 5915} 5916 5917SDL_BlendOperation SDL_GetBlendModeAlphaOperation(SDL_BlendMode blendMode) 5918{ 5919 blendMode = SDL_GetLongBlendMode(blendMode); 5920 return (SDL_BlendOperation)(((Uint32)blendMode >> 16) & 0xF); 5921} 5922 5923bool SDL_SetRenderVSync(SDL_Renderer *renderer, int vsync) 5924{ 5925 CHECK_RENDERER_MAGIC(renderer, false); 5926 5927 renderer->wanted_vsync = vsync ? true : false; 5928 5929 // for the software renderer, forward the call to the WindowTexture renderer 5930#ifdef SDL_VIDEO_RENDER_SW 5931 if (renderer->software) { 5932 if (!renderer->window) { 5933 if (!vsync) { 5934 return true; 5935 } else { 5936 return SDL_Unsupported(); 5937 } 5938 } 5939 if (SDL_SetWindowTextureVSync(NULL, renderer->window, vsync)) { 5940 renderer->simulate_vsync = false; 5941 return true; 5942 } 5943 } 5944#endif 5945 5946 if (!renderer->SetVSync || 5947 !renderer->SetVSync(renderer, vsync)) { 5948 switch (vsync) { 5949 case 0: 5950 renderer->simulate_vsync = false; 5951 break; 5952 case 1: 5953 renderer->simulate_vsync = true; 5954 break; 5955 default: 5956 return SDL_Unsupported(); 5957 } 5958 } 5959 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_VSYNC_NUMBER, vsync); 5960 return true; 5961} 5962 5963bool SDL_GetRenderVSync(SDL_Renderer *renderer, int *vsync) 5964{ 5965 if (vsync) { 5966 *vsync = 0; 5967 } 5968 5969 CHECK_RENDERER_MAGIC(renderer, false); 5970 5971 if (vsync) { 5972 *vsync = (int)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_VSYNC_NUMBER, 0); 5973 } 5974 return true; 5975} 5976 5977 5978#define SDL_DEBUG_FONT_GLYPHS_PER_ROW 14 5979 5980static bool CreateDebugTextAtlas(SDL_Renderer *renderer) 5981{ 5982 SDL_assert(renderer->debug_char_texture_atlas == NULL); // don't double-create it! 5983 5984 const int charWidth = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; 5985 const int charHeight = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; 5986 5987 // actually make each glyph two pixels taller/wider, to prevent scaling artifacts. 5988 const int rows = (SDL_DEBUG_FONT_NUM_GLYPHS / SDL_DEBUG_FONT_GLYPHS_PER_ROW) + 1; 5989 SDL_Surface *atlas = SDL_CreateSurface((charWidth + 2) * SDL_DEBUG_FONT_GLYPHS_PER_ROW, rows * (charHeight + 2), SDL_PIXELFORMAT_RGBA8888); 5990 if (!atlas) { 5991 return false; 5992 } 5993 5994 const int pitch = atlas->pitch; 5995 SDL_memset(atlas->pixels, '\0', atlas->h * atlas->pitch); 5996 5997 int column = 0; 5998 int row = 0; 5999 for (int glyph = 0; glyph < SDL_DEBUG_FONT_NUM_GLYPHS; glyph++) { 6000 // find top-left of this glyph in destination surface. The +2's account for glyph padding. 6001 Uint8 *linepos = (((Uint8 *)atlas->pixels) + ((row * (charHeight + 2) + 1) * pitch)) + ((column * (charWidth + 2) + 1) * sizeof (Uint32)); 6002 const Uint8 *charpos = SDL_RenderDebugTextFontData + (glyph * 8); 6003 6004 // Draw the glyph to the surface... 6005 for (int iy = 0; iy < charHeight; iy++) { 6006 Uint32 *curpos = (Uint32 *)linepos; 6007 for (int ix = 0; ix < charWidth; ix++) { 6008 if ((*charpos) & (1 << ix)) { 6009 *curpos = 0xffffffff; 6010 } else { 6011 *curpos = 0; 6012 } 6013 ++curpos; 6014 } 6015 linepos += pitch; 6016 ++charpos; 6017 } 6018 6019 // move to next position (and if too far, start the next row). 6020 column++; 6021 if (column >= SDL_DEBUG_FONT_GLYPHS_PER_ROW) { 6022 row++; 6023 column = 0; 6024 } 6025 } 6026 6027 SDL_assert((row < rows) || ((row == rows) && (column == 0))); // make sure we didn't overflow the surface. 6028 6029 // Convert temp surface into texture 6030 SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, atlas); 6031 if (texture) { 6032 SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_PIXELART); 6033 renderer->debug_char_texture_atlas = texture; 6034 } 6035 SDL_DestroySurface(atlas); 6036 6037 return texture != NULL; 6038} 6039 6040static bool DrawDebugCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c) 6041{ 6042 SDL_assert(renderer->debug_char_texture_atlas != NULL); // should have been created by now! 6043 6044 const int charWidth = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; 6045 const int charHeight = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; 6046 6047 // Character index in cache 6048 Uint32 ci = c; 6049 if ((ci <= 32) || ((ci >= 127) && (ci <= 160))) { 6050 return true; // these are just completely blank chars, don't bother doing anything. 6051 } else if (ci >= SDL_DEBUG_FONT_NUM_GLYPHS) { 6052 ci = SDL_DEBUG_FONT_NUM_GLYPHS - 1; // use our "not a valid/supported character" glyph. 6053 } else if (ci < 127) { 6054 ci -= 33; // adjust for the 33 blank glyphs at the start 6055 } else { 6056 ci -= 67; // adjust for the 33 blank glyphs at the start AND the 34 gap in the middle. 6057 } 6058 6059 const float src_x = (float) (((ci % SDL_DEBUG_FONT_GLYPHS_PER_ROW) * (charWidth + 2)) + 1); 6060 const float src_y = (float) (((ci / SDL_DEBUG_FONT_GLYPHS_PER_ROW) * (charHeight + 2)) + 1); 6061 6062 // Draw texture onto destination 6063 const SDL_FRect srect = { src_x, src_y, (float) charWidth, (float) charHeight }; 6064 const SDL_FRect drect = { x, y, (float) charWidth, (float) charHeight }; 6065 return SDL_RenderTexture(renderer, renderer->debug_char_texture_atlas, &srect, &drect); 6066} 6067 6068bool SDL_RenderDebugText(SDL_Renderer *renderer, float x, float y, const char *s) 6069{ 6070 CHECK_RENDERER_MAGIC(renderer, false); 6071 6072 // Allocate a texture atlas for this renderer if needed. 6073 if (!renderer->debug_char_texture_atlas) { 6074 if (!CreateDebugTextAtlas(renderer)) { 6075 return false; 6076 } 6077 } 6078 6079 bool result = true; 6080 6081 Uint8 r, g, b, a; 6082 result &= SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); 6083 result &= SDL_SetTextureColorMod(renderer->debug_char_texture_atlas, r, g, b); 6084 result &= SDL_SetTextureAlphaMod(renderer->debug_char_texture_atlas, a); 6085 6086 float curx = x; 6087 Uint32 ch; 6088 6089 while (result && ((ch = SDL_StepUTF8(&s, NULL)) != 0)) { 6090 result &= DrawDebugCharacter(renderer, curx, y, ch); 6091 curx += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; 6092 } 6093 6094 return result; 6095} 6096 6097bool SDL_RenderDebugTextFormat(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 6098{ 6099 va_list ap; 6100 va_start(ap, fmt); 6101 6102 // fast path to avoid unnecessary allocation and copy. If you're going through the dynapi, there's a good chance 6103 // you _always_ hit this path, since it probably had to process varargs before calling into the jumptable. 6104 if (SDL_strcmp(fmt, "%s") == 0) { 6105 const char *str = va_arg(ap, const char *); 6106 va_end(ap); 6107 return SDL_RenderDebugText(renderer, x, y, str); 6108 } 6109 6110 char *str = NULL; 6111 const int rc = SDL_vasprintf(&str, fmt, ap); 6112 va_end(ap); 6113 6114 if (rc == -1) { 6115 return false; 6116 } 6117 6118 const bool retval = SDL_RenderDebugText(renderer, x, y, str); 6119 SDL_free(str); 6120 return retval; 6121} 6122 6123bool SDL_SetDefaultTextureScaleMode(SDL_Renderer *renderer, SDL_ScaleMode scale_mode) 6124{ 6125 CHECK_RENDERER_MAGIC(renderer, false); 6126 6127 renderer->scale_mode = scale_mode; 6128 6129 return true; 6130} 6131 6132bool SDL_GetDefaultTextureScaleMode(SDL_Renderer *renderer, SDL_ScaleMode *scale_mode) 6133{ 6134 if (scale_mode) { 6135 *scale_mode = SDL_SCALEMODE_LINEAR; 6136 } 6137 6138 CHECK_RENDERER_MAGIC(renderer, false); 6139 6140 if (scale_mode) { 6141 *scale_mode = renderer->scale_mode; 6142 } 6143 return true; 6144} 6145 6146SDL_GPURenderState *SDL_CreateGPURenderState(SDL_Renderer *renderer, SDL_GPURenderStateCreateInfo *createinfo) 6147{ 6148 CHECK_RENDERER_MAGIC(renderer, NULL); 6149 6150 CHECK_PARAM(!createinfo) { 6151 SDL_InvalidParamError("createinfo"); 6152 return NULL; 6153 } 6154 6155 CHECK_PARAM(!createinfo->fragment_shader) { 6156 SDL_SetError("A fragment_shader is required"); 6157 return NULL; 6158 } 6159 6160 SDL_GPUDevice *device = (SDL_GPUDevice *)SDL_GetPointerProperty(renderer->props, SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL); 6161 if (!device) { 6162 SDL_SetError("Renderer isn't associated with a GPU device"); 6163 return NULL; 6164 } 6165 6166 SDL_GPURenderState *state = (SDL_GPURenderState *)SDL_calloc(1, sizeof(*state)); 6167 if (!state) { 6168 return NULL; 6169 } 6170 6171 state->renderer = renderer; 6172 state->fragment_shader = createinfo->fragment_shader; 6173 6174 if (createinfo->num_sampler_bindings > 0) { 6175 state->sampler_bindings = (SDL_GPUTextureSamplerBinding *)SDL_calloc(createinfo->num_sampler_bindings, sizeof(*state->sampler_bindings)); 6176 if (!state->sampler_bindings) { 6177 SDL_DestroyGPURenderState(state); 6178 return NULL; 6179 } 6180 SDL_memcpy(state->sampler_bindings, createinfo->sampler_bindings, createinfo->num_sampler_bindings * sizeof(*state->sampler_bindings)); 6181 state->num_sampler_bindings = createinfo->num_sampler_bindings; 6182 } 6183 6184 if (createinfo->num_storage_textures > 0) { 6185 state->storage_textures = (SDL_GPUTexture **)SDL_calloc(createinfo->num_storage_textures, sizeof(*state->storage_textures)); 6186 if (!state->storage_textures) { 6187 SDL_DestroyGPURenderState(state); 6188 return NULL; 6189 } 6190 SDL_memcpy(state->storage_textures, createinfo->storage_textures, createinfo->num_storage_textures * sizeof(*state->storage_textures)); 6191 state->num_storage_textures = createinfo->num_storage_textures; 6192 } 6193 6194 if (createinfo->num_storage_buffers > 0) { 6195 state->storage_buffers = (SDL_GPUBuffer **)SDL_calloc(createinfo->num_storage_buffers, sizeof(*state->storage_buffers)); 6196 if (!state->storage_buffers) { 6197 SDL_DestroyGPURenderState(state); 6198 return NULL; 6199 } 6200 SDL_memcpy(state->storage_buffers, createinfo->storage_buffers, createinfo->num_storage_buffers * sizeof(*state->storage_buffers)); 6201 state->num_storage_buffers = createinfo->num_storage_buffers; 6202 } 6203 6204 return state; 6205} 6206 6207bool SDL_SetGPURenderStateFragmentUniforms(SDL_GPURenderState *state, Uint32 slot_index, const void *data, Uint32 length) 6208{ 6209 if (!state) { 6210 return SDL_InvalidParamError("state"); 6211 } 6212 6213 if (!FlushRenderCommandsIfGPURenderStateNeeded(state)) { 6214 return false; 6215 } 6216 6217 for (int i = 0; i < state->num_uniform_buffers; i++) { 6218 SDL_GPURenderStateUniformBuffer *buffer = &state->uniform_buffers[i]; 6219 if (buffer->slot_index == slot_index) { 6220 void *new_data = SDL_realloc(buffer->data, length); 6221 if (!new_data) { 6222 return false; 6223 } 6224 SDL_memcpy(new_data, data, length); 6225 buffer->data = new_data; 6226 buffer->length = length; 6227 return true; 6228 } 6229 } 6230 6231 SDL_GPURenderStateUniformBuffer *buffers = (SDL_GPURenderStateUniformBuffer *)SDL_realloc(state->uniform_buffers, (state->num_uniform_buffers + 1) * sizeof(*state->uniform_buffers)); 6232 if (!buffers) { 6233 return false; 6234 } 6235 6236 SDL_GPURenderStateUniformBuffer *buffer = &buffers[state->num_uniform_buffers]; 6237 buffer->slot_index = slot_index; 6238 buffer->length = length; 6239 buffer->data = SDL_malloc(length); 6240 if (!buffer->data) { 6241 SDL_free(buffers); 6242 return false; 6243 } 6244 SDL_memcpy(buffer->data, data, length); 6245 6246 state->uniform_buffers = buffers; 6247 ++state->num_uniform_buffers; 6248 return true; 6249} 6250 6251bool SDL_SetGPURenderState(SDL_Renderer *renderer, SDL_GPURenderState *state) 6252{ 6253 CHECK_RENDERER_MAGIC(renderer, false); 6254 6255 renderer->gpu_render_state = state; 6256 return true; 6257} 6258 6259void SDL_DestroyGPURenderState(SDL_GPURenderState *state) 6260{ 6261 if (!state) { 6262 return; 6263 } 6264 6265 FlushRenderCommandsIfGPURenderStateNeeded(state); 6266 6267 if (state->num_uniform_buffers > 0) { 6268 for (int i = 0; i < state->num_uniform_buffers; i++) { 6269 SDL_free(state->uniform_buffers[i].data); 6270 } 6271 SDL_free(state->uniform_buffers); 6272 } 6273 SDL_free(state->sampler_bindings); 6274 SDL_free(state->storage_textures); 6275 SDL_free(state->storage_buffers); 6276 SDL_free(state); 6277} 6278[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.