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