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