SDL_render.c (104748B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 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_hints.h" 26 #include "SDL_render.h" 27 #include "SDL_sysrender.h" 28 #include "software/SDL_render_sw_c.h" 29 #include "../video/SDL_pixels_c.h" 30 31 #if defined(__ANDROID__) 32 # include "../core/android/SDL_android.h" 33 #endif 34 35 #define SDL_WINDOWRENDERDATA "_SDL_WindowRenderData" 36 37 #define CHECK_RENDERER_MAGIC(renderer, retval) \ 38 SDL_assert(renderer && renderer->magic == &renderer_magic); \ 39 if (!renderer || renderer->magic != &renderer_magic) { \ 40 SDL_SetError("Invalid renderer"); \ 41 return retval; \ 42 } 43 44 #define CHECK_TEXTURE_MAGIC(texture, retval) \ 45 SDL_assert(texture && texture->magic == &texture_magic); \ 46 if (!texture || texture->magic != &texture_magic) { \ 47 SDL_SetError("Invalid texture"); \ 48 return retval; \ 49 } 50 51 /* Predefined blend modes */ 52 #define SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation, \ 53 srcAlphaFactor, dstAlphaFactor, alphaOperation) \ 54 (SDL_BlendMode)(((Uint32)colorOperation << 0) | \ 55 ((Uint32)srcColorFactor << 4) | \ 56 ((Uint32)dstColorFactor << 8) | \ 57 ((Uint32)alphaOperation << 16) | \ 58 ((Uint32)srcAlphaFactor << 20) | \ 59 ((Uint32)dstAlphaFactor << 24)) 60 61 #define SDL_BLENDMODE_NONE_FULL \ 62 SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD, \ 63 SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD) 64 65 #define SDL_BLENDMODE_BLEND_FULL \ 66 SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \ 67 SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD) 68 69 #define SDL_BLENDMODE_ADD_FULL \ 70 SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, \ 71 SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD) 72 73 #define SDL_BLENDMODE_MOD_FULL \ 74 SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_COLOR, SDL_BLENDOPERATION_ADD, \ 75 SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD) 76 77 #define SDL_BLENDMODE_MUL_FULL \ 78 SDL_COMPOSE_BLENDMODE(SDL_BLENDFACTOR_DST_COLOR, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, \ 79 SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD) 80 81 #if !SDL_RENDER_DISABLED 82 static const SDL_RenderDriver *render_drivers[] = { 83 #if SDL_VIDEO_RENDER_D3D 84 &D3D_RenderDriver, 85 #endif 86 #if SDL_VIDEO_RENDER_D3D11 87 &D3D11_RenderDriver, 88 #endif 89 #if SDL_VIDEO_RENDER_METAL 90 &METAL_RenderDriver, 91 #endif 92 #if SDL_VIDEO_RENDER_OGL 93 &GL_RenderDriver, 94 #endif 95 #if SDL_VIDEO_RENDER_OGL_ES2 96 &GLES2_RenderDriver, 97 #endif 98 #if SDL_VIDEO_RENDER_OGL_ES 99 &GLES_RenderDriver, 100 #endif 101 #if SDL_VIDEO_RENDER_DIRECTFB 102 &DirectFB_RenderDriver, 103 #endif 104 #if SDL_VIDEO_RENDER_PSP 105 &PSP_RenderDriver, 106 #endif 107 #if SDL_VIDEO_RENDER_SW 108 &SW_RenderDriver 109 #endif 110 }; 111 #endif /* !SDL_RENDER_DISABLED */ 112 113 static char renderer_magic; 114 static char texture_magic; 115 116 static SDL_INLINE void 117 DebugLogRenderCommands(const SDL_RenderCommand *cmd) 118 { 119 #if 0 120 unsigned int i = 1; 121 SDL_Log("Render commands to flush:"); 122 while (cmd) { 123 switch (cmd->command) { 124 case SDL_RENDERCMD_NO_OP: 125 SDL_Log(" %u. no-op", i++); 126 break; 127 128 case SDL_RENDERCMD_SETVIEWPORT: 129 SDL_Log(" %u. set viewport (first=%u, rect={(%d, %d), %dx%d})", i++, 130 (unsigned int) cmd->data.viewport.first, 131 cmd->data.viewport.rect.x, cmd->data.viewport.rect.y, 132 cmd->data.viewport.rect.w, cmd->data.viewport.rect.h); 133 break; 134 135 case SDL_RENDERCMD_SETCLIPRECT: 136 SDL_Log(" %u. set cliprect (enabled=%s, rect={(%d, %d), %dx%d})", i++, 137 cmd->data.cliprect.enabled ? "true" : "false", 138 cmd->data.cliprect.rect.x, cmd->data.cliprect.rect.y, 139 cmd->data.cliprect.rect.w, cmd->data.cliprect.rect.h); 140 break; 141 142 case SDL_RENDERCMD_SETDRAWCOLOR: 143 SDL_Log(" %u. set draw color (first=%u, r=%d, g=%d, b=%d, a=%d)", i++, 144 (unsigned int) cmd->data.color.first, 145 (int) cmd->data.color.r, (int) cmd->data.color.g, 146 (int) cmd->data.color.b, (int) cmd->data.color.a); 147 break; 148 149 case SDL_RENDERCMD_CLEAR: 150 SDL_Log(" %u. clear (first=%u, r=%d, g=%d, b=%d, a=%d)", i++, 151 (unsigned int) cmd->data.color.first, 152 (int) cmd->data.color.r, (int) cmd->data.color.g, 153 (int) cmd->data.color.b, (int) cmd->data.color.a); 154 break; 155 156 case SDL_RENDERCMD_DRAW_POINTS: 157 SDL_Log(" %u. draw points (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++, 158 (unsigned int) cmd->data.draw.first, 159 (unsigned int) cmd->data.draw.count, 160 (int) cmd->data.draw.r, (int) cmd->data.draw.g, 161 (int) cmd->data.draw.b, (int) cmd->data.draw.a, 162 (int) cmd->data.draw.blend); 163 break; 164 165 case SDL_RENDERCMD_DRAW_LINES: 166 SDL_Log(" %u. draw lines (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++, 167 (unsigned int) cmd->data.draw.first, 168 (unsigned int) cmd->data.draw.count, 169 (int) cmd->data.draw.r, (int) cmd->data.draw.g, 170 (int) cmd->data.draw.b, (int) cmd->data.draw.a, 171 (int) cmd->data.draw.blend); 172 break; 173 174 case SDL_RENDERCMD_FILL_RECTS: 175 SDL_Log(" %u. fill rects (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++, 176 (unsigned int) cmd->data.draw.first, 177 (unsigned int) cmd->data.draw.count, 178 (int) cmd->data.draw.r, (int) cmd->data.draw.g, 179 (int) cmd->data.draw.b, (int) cmd->data.draw.a, 180 (int) cmd->data.draw.blend); 181 break; 182 183 case SDL_RENDERCMD_COPY: 184 SDL_Log(" %u. copy (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, tex=%p)", i++, 185 (unsigned int) cmd->data.draw.first, 186 (unsigned int) cmd->data.draw.count, 187 (int) cmd->data.draw.r, (int) cmd->data.draw.g, 188 (int) cmd->data.draw.b, (int) cmd->data.draw.a, 189 (int) cmd->data.draw.blend, cmd->data.draw.texture); 190 break; 191 192 193 case SDL_RENDERCMD_COPY_EX: 194 SDL_Log(" %u. copyex (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, tex=%p)", i++, 195 (unsigned int) cmd->data.draw.first, 196 (unsigned int) cmd->data.draw.count, 197 (int) cmd->data.draw.r, (int) cmd->data.draw.g, 198 (int) cmd->data.draw.b, (int) cmd->data.draw.a, 199 (int) cmd->data.draw.blend, cmd->data.draw.texture); 200 break; 201 } 202 cmd = cmd->next; 203 } 204 #endif 205 } 206 207 static int 208 FlushRenderCommands(SDL_Renderer *renderer) 209 { 210 int retval; 211 212 SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL)); 213 214 if (renderer->render_commands == NULL) { /* nothing to do! */ 215 SDL_assert(renderer->vertex_data_used == 0); 216 return 0; 217 } 218 219 DebugLogRenderCommands(renderer->render_commands); 220 221 retval = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used); 222 223 /* Move the whole render command queue to the unused pool so we can reuse them next time. */ 224 if (renderer->render_commands_tail != NULL) { 225 renderer->render_commands_tail->next = renderer->render_commands_pool; 226 renderer->render_commands_pool = renderer->render_commands; 227 renderer->render_commands_tail = NULL; 228 renderer->render_commands = NULL; 229 } 230 renderer->vertex_data_used = 0; 231 renderer->render_command_generation++; 232 renderer->color_queued = SDL_FALSE; 233 renderer->viewport_queued = SDL_FALSE; 234 renderer->cliprect_queued = SDL_FALSE; 235 return retval; 236 } 237 238 static int 239 FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture) 240 { 241 SDL_Renderer *renderer = texture->renderer; 242 if (texture->last_command_generation == renderer->render_command_generation) { 243 /* the current command queue depends on this texture, flush the queue now before it changes */ 244 return FlushRenderCommands(renderer); 245 } 246 return 0; 247 } 248 249 static SDL_INLINE int 250 FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer) 251 { 252 return renderer->batching ? 0 : FlushRenderCommands(renderer); 253 } 254 255 int 256 SDL_RenderFlush(SDL_Renderer * renderer) 257 { 258 return FlushRenderCommands(renderer); 259 } 260 261 void * 262 SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, const size_t alignment, size_t *offset) 263 { 264 const size_t needed = renderer->vertex_data_used + numbytes + alignment; 265 size_t current_offset = renderer->vertex_data_used; 266 267 size_t aligner = (alignment && ((current_offset & (alignment - 1)) != 0)) ? (alignment - (current_offset & (alignment - 1))) : 0; 268 size_t aligned = current_offset + aligner; 269 270 if (renderer->vertex_data_allocation < needed) { 271 const size_t current_allocation = renderer->vertex_data ? renderer->vertex_data_allocation : 1024; 272 size_t newsize = current_allocation * 2; 273 void *ptr; 274 while (newsize < needed) { 275 newsize *= 2; 276 } 277 ptr = SDL_realloc(renderer->vertex_data, newsize); 278 if (ptr == NULL) { 279 SDL_OutOfMemory(); 280 return NULL; 281 } 282 renderer->vertex_data = ptr; 283 renderer->vertex_data_allocation = newsize; 284 } 285 286 if (offset) { 287 *offset = aligned; 288 } 289 290 renderer->vertex_data_used += aligner + numbytes; 291 292 return ((Uint8 *) renderer->vertex_data) + aligned; 293 } 294 295 static SDL_RenderCommand * 296 AllocateRenderCommand(SDL_Renderer *renderer) 297 { 298 SDL_RenderCommand *retval = NULL; 299 300 /* !!! FIXME: are there threading limitations in SDL's render API? If not, we need to mutex this. */ 301 retval = renderer->render_commands_pool; 302 if (retval != NULL) { 303 renderer->render_commands_pool = retval->next; 304 retval->next = NULL; 305 } else { 306 retval = SDL_calloc(1, sizeof (*retval)); 307 if (!retval) { 308 SDL_OutOfMemory(); 309 return NULL; 310 } 311 } 312 313 SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL)); 314 if (renderer->render_commands_tail != NULL) { 315 renderer->render_commands_tail->next = retval; 316 } else { 317 renderer->render_commands = retval; 318 } 319 renderer->render_commands_tail = retval; 320 321 return retval; 322 } 323 324 static int 325 QueueCmdSetViewport(SDL_Renderer *renderer) 326 { 327 int retval = 0; 328 if (!renderer->viewport_queued || (SDL_memcmp(&renderer->viewport, &renderer->last_queued_viewport, sizeof (SDL_Rect)) != 0)) { 329 SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); 330 retval = -1; 331 if (cmd != NULL) { 332 cmd->command = SDL_RENDERCMD_SETVIEWPORT; 333 cmd->data.viewport.first = 0; /* render backend will fill this in. */ 334 SDL_memcpy(&cmd->data.viewport.rect, &renderer->viewport, sizeof (renderer->viewport)); 335 retval = renderer->QueueSetViewport(renderer, cmd); 336 if (retval < 0) { 337 cmd->command = SDL_RENDERCMD_NO_OP; 338 } else { 339 SDL_memcpy(&renderer->last_queued_viewport, &renderer->viewport, sizeof (SDL_Rect)); 340 renderer->viewport_queued = SDL_TRUE; 341 } 342 } 343 } 344 return retval; 345 } 346 347 static int 348 QueueCmdSetClipRect(SDL_Renderer *renderer) 349 { 350 int retval = 0; 351 if ((!renderer->cliprect_queued) || 352 (renderer->clipping_enabled != renderer->last_queued_cliprect_enabled) || 353 (SDL_memcmp(&renderer->clip_rect, &renderer->last_queued_cliprect, sizeof (SDL_Rect)) != 0)) { 354 SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); 355 if (cmd == NULL) { 356 retval = -1; 357 } else { 358 cmd->command = SDL_RENDERCMD_SETCLIPRECT; 359 cmd->data.cliprect.enabled = renderer->clipping_enabled; 360 SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (cmd->data.cliprect.rect)); 361 SDL_memcpy(&renderer->last_queued_cliprect, &renderer->clip_rect, sizeof (SDL_Rect)); 362 renderer->last_queued_cliprect_enabled = renderer->clipping_enabled; 363 renderer->cliprect_queued = SDL_TRUE; 364 } 365 } 366 return retval; 367 } 368 369 static int 370 QueueCmdSetDrawColor(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uint8 b, const Uint8 a) 371 { 372 const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); 373 int retval = 0; 374 375 if (!renderer->color_queued || (color != renderer->last_queued_color)) { 376 SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); 377 retval = -1; 378 379 if (cmd != NULL) { 380 cmd->command = SDL_RENDERCMD_SETDRAWCOLOR; 381 cmd->data.color.first = 0; /* render backend will fill this in. */ 382 cmd->data.color.r = r; 383 cmd->data.color.g = g; 384 cmd->data.color.b = b; 385 cmd->data.color.a = a; 386 retval = renderer->QueueSetDrawColor(renderer, cmd); 387 if (retval < 0) { 388 cmd->command = SDL_RENDERCMD_NO_OP; 389 } else { 390 renderer->last_queued_color = color; 391 renderer->color_queued = SDL_TRUE; 392 } 393 } 394 } 395 return retval; 396 } 397 398 static int 399 QueueCmdClear(SDL_Renderer *renderer) 400 { 401 SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); 402 if (cmd == NULL) { 403 return -1; 404 } 405 406 cmd->command = SDL_RENDERCMD_CLEAR; 407 cmd->data.color.first = 0; 408 cmd->data.color.r = renderer->r; 409 cmd->data.color.g = renderer->g; 410 cmd->data.color.b = renderer->b; 411 cmd->data.color.a = renderer->a; 412 return 0; 413 } 414 415 static int 416 PrepQueueCmdDraw(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uint8 b, const Uint8 a) 417 { 418 int retval = QueueCmdSetDrawColor(renderer, r, g, b, a); 419 420 /* Set the viewport and clip rect directly before draws, so the backends 421 * don't have to worry about that state not being valid at draw time. */ 422 if (retval == 0 && !renderer->viewport_queued) { 423 retval = QueueCmdSetViewport(renderer); 424 } 425 if (retval == 0 && !renderer->cliprect_queued) { 426 retval = QueueCmdSetClipRect(renderer); 427 } 428 return retval; 429 } 430 431 static SDL_RenderCommand * 432 PrepQueueCmdDrawSolid(SDL_Renderer *renderer, const SDL_RenderCommandType cmdtype) 433 { 434 /* !!! FIXME: drop this draw if viewport w or h is zero. */ 435 SDL_RenderCommand *cmd = NULL; 436 if (PrepQueueCmdDraw(renderer, renderer->r, renderer->g, renderer->b, renderer->a) == 0) { 437 cmd = AllocateRenderCommand(renderer); 438 if (cmd != NULL) { 439 cmd->command = cmdtype; 440 cmd->data.draw.first = 0; /* render backend will fill this in. */ 441 cmd->data.draw.count = 0; /* render backend will fill this in. */ 442 cmd->data.draw.r = renderer->r; 443 cmd->data.draw.g = renderer->g; 444 cmd->data.draw.b = renderer->b; 445 cmd->data.draw.a = renderer->a; 446 cmd->data.draw.blend = renderer->blendMode; 447 cmd->data.draw.texture = NULL; /* no texture. */ 448 } 449 } 450 return cmd; 451 } 452 453 static int 454 QueueCmdDrawPoints(SDL_Renderer *renderer, const SDL_FPoint * points, const int count) 455 { 456 SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_POINTS); 457 int retval = -1; 458 if (cmd != NULL) { 459 retval = renderer->QueueDrawPoints(renderer, cmd, points, count); 460 if (retval < 0) { 461 cmd->command = SDL_RENDERCMD_NO_OP; 462 } 463 } 464 return retval; 465 } 466 467 static int 468 QueueCmdDrawLines(SDL_Renderer *renderer, const SDL_FPoint * points, const int count) 469 { 470 SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_LINES); 471 int retval = -1; 472 if (cmd != NULL) { 473 retval = renderer->QueueDrawLines(renderer, cmd, points, count); 474 if (retval < 0) { 475 cmd->command = SDL_RENDERCMD_NO_OP; 476 } 477 } 478 return retval; 479 } 480 481 static int 482 QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect * rects, const int count) 483 { 484 SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_FILL_RECTS); 485 int retval = -1; 486 if (cmd != NULL) { 487 retval = renderer->QueueFillRects(renderer, cmd, rects, count); 488 if (retval < 0) { 489 cmd->command = SDL_RENDERCMD_NO_OP; 490 } 491 } 492 return retval; 493 } 494 495 static SDL_RenderCommand * 496 PrepQueueCmdDrawTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_RenderCommandType cmdtype) 497 { 498 /* !!! FIXME: drop this draw if viewport w or h is zero. */ 499 SDL_RenderCommand *cmd = NULL; 500 if (PrepQueueCmdDraw(renderer, texture->r, texture->g, texture->b, texture->a) == 0) { 501 cmd = AllocateRenderCommand(renderer); 502 if (cmd != NULL) { 503 cmd->command = cmdtype; 504 cmd->data.draw.first = 0; /* render backend will fill this in. */ 505 cmd->data.draw.count = 0; /* render backend will fill this in. */ 506 cmd->data.draw.r = texture->r; 507 cmd->data.draw.g = texture->g; 508 cmd->data.draw.b = texture->b; 509 cmd->data.draw.a = texture->a; 510 cmd->data.draw.blend = texture->blendMode; 511 cmd->data.draw.texture = texture; 512 } 513 } 514 return cmd; 515 } 516 517 static int 518 QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_FRect * dstrect) 519 { 520 SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY); 521 int retval = -1; 522 if (cmd != NULL) { 523 retval = renderer->QueueCopy(renderer, cmd, texture, srcrect, dstrect); 524 if (retval < 0) { 525 cmd->command = SDL_RENDERCMD_NO_OP; 526 } 527 } 528 return retval; 529 } 530 531 static int 532 QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture * texture, 533 const SDL_Rect * srcquad, const SDL_FRect * dstrect, 534 const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) 535 { 536 SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY_EX); 537 int retval = -1; 538 SDL_assert(renderer->QueueCopyEx != NULL); /* should have caught at higher level. */ 539 if (cmd != NULL) { 540 retval = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip); 541 if (retval < 0) { 542 cmd->command = SDL_RENDERCMD_NO_OP; 543 } 544 } 545 return retval; 546 } 547 548 549 static int UpdateLogicalSize(SDL_Renderer *renderer); 550 551 int 552 SDL_GetNumRenderDrivers(void) 553 { 554 #if !SDL_RENDER_DISABLED 555 return SDL_arraysize(render_drivers); 556 #else 557 return 0; 558 #endif 559 } 560 561 int 562 SDL_GetRenderDriverInfo(int index, SDL_RendererInfo * info) 563 { 564 #if !SDL_RENDER_DISABLED 565 if (index < 0 || index >= SDL_GetNumRenderDrivers()) { 566 return SDL_SetError("index must be in the range of 0 - %d", 567 SDL_GetNumRenderDrivers() - 1); 568 } 569 *info = render_drivers[index]->info; 570 return 0; 571 #else 572 return SDL_SetError("SDL not built with rendering support"); 573 #endif 574 } 575 576 static void GetWindowViewportValues(SDL_Renderer *renderer, int *logical_w, int *logical_h, SDL_Rect *viewport, SDL_FPoint *scale) 577 { 578 SDL_LockMutex(renderer->target_mutex); 579 *logical_w = renderer->target ? renderer->logical_w_backup : renderer->logical_w; 580 *logical_h = renderer->target ? renderer->logical_h_backup : renderer->logical_h; 581 *viewport = renderer->target ? renderer->viewport_backup : renderer->viewport; 582 *scale = renderer->target ? renderer->scale_backup : renderer->scale; 583 SDL_UnlockMutex(renderer->target_mutex); 584 } 585 586 static int SDLCALL 587 SDL_RendererEventWatch(void *userdata, SDL_Event *event) 588 { 589 SDL_Renderer *renderer = (SDL_Renderer *)userdata; 590 591 if (event->type == SDL_WINDOWEVENT) { 592 SDL_Window *window = SDL_GetWindowFromID(event->window.windowID); 593 if (window == renderer->window) { 594 if (renderer->WindowEvent) { 595 renderer->WindowEvent(renderer, &event->window); 596 } 597 598 if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { 599 /* Make sure we're operating on the default render target */ 600 SDL_Texture *saved_target = SDL_GetRenderTarget(renderer); 601 if (saved_target) { 602 SDL_SetRenderTarget(renderer, NULL); 603 } 604 605 if (renderer->logical_w) { 606 UpdateLogicalSize(renderer); 607 } else { 608 /* Window was resized, reset viewport */ 609 int w, h; 610 611 if (renderer->GetOutputSize) { 612 renderer->GetOutputSize(renderer, &w, &h); 613 } else { 614 SDL_GetWindowSize(renderer->window, &w, &h); 615 } 616 617 if (renderer->target) { 618 renderer->viewport_backup.x = 0; 619 renderer->viewport_backup.y = 0; 620 renderer->viewport_backup.w = w; 621 renderer->viewport_backup.h = h; 622 } else { 623 renderer->viewport.x = 0; 624 renderer->viewport.y = 0; 625 renderer->viewport.w = w; 626 renderer->viewport.h = h; 627 QueueCmdSetViewport(renderer); 628 FlushRenderCommandsIfNotBatching(renderer); 629 } 630 } 631 632 if (saved_target) { 633 SDL_SetRenderTarget(renderer, saved_target); 634 } 635 } else if (event->window.event == SDL_WINDOWEVENT_HIDDEN) { 636 renderer->hidden = SDL_TRUE; 637 } else if (event->window.event == SDL_WINDOWEVENT_SHOWN) { 638 if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)) { 639 renderer->hidden = SDL_FALSE; 640 } 641 } else if (event->window.event == SDL_WINDOWEVENT_MINIMIZED) { 642 renderer->hidden = SDL_TRUE; 643 } else if (event->window.event == SDL_WINDOWEVENT_RESTORED || 644 event->window.event == SDL_WINDOWEVENT_MAXIMIZED) { 645 if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN)) { 646 renderer->hidden = SDL_FALSE; 647 } 648 } 649 } 650 } else if (event->type == SDL_MOUSEMOTION) { 651 SDL_Window *window = SDL_GetWindowFromID(event->motion.windowID); 652 if (window == renderer->window) { 653 int logical_w, logical_h; 654 SDL_Rect viewport; 655 SDL_FPoint scale; 656 GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale); 657 if (logical_w) { 658 event->motion.x -= (int)(viewport.x * renderer->dpi_scale.x); 659 event->motion.y -= (int)(viewport.y * renderer->dpi_scale.y); 660 event->motion.x = (int)(event->motion.x / (scale.x * renderer->dpi_scale.x)); 661 event->motion.y = (int)(event->motion.y / (scale.y * renderer->dpi_scale.y)); 662 if (event->motion.xrel != 0 && renderer->relative_scaling) { 663 float rel = renderer->xrel + event->motion.xrel / (scale.x * renderer->dpi_scale.x); 664 float trunc = SDL_truncf(rel); 665 renderer->xrel = rel - trunc; 666 event->motion.xrel = (Sint32) trunc; 667 } 668 if (event->motion.yrel != 0 && renderer->relative_scaling) { 669 float rel = renderer->yrel + event->motion.yrel / (scale.y * renderer->dpi_scale.y); 670 float trunc = SDL_truncf(rel); 671 renderer->yrel = rel - trunc; 672 event->motion.yrel = (Sint32) trunc; 673 } 674 } 675 } 676 } else if (event->type == SDL_MOUSEBUTTONDOWN || 677 event->type == SDL_MOUSEBUTTONUP) { 678 SDL_Window *window = SDL_GetWindowFromID(event->button.windowID); 679 if (window == renderer->window) { 680 int logical_w, logical_h; 681 SDL_Rect viewport; 682 SDL_FPoint scale; 683 GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale); 684 if (logical_w) { 685 event->button.x -= (int)(viewport.x * renderer->dpi_scale.x); 686 event->button.y -= (int)(viewport.y * renderer->dpi_scale.y); 687 event->button.x = (int)(event->button.x / (scale.x * renderer->dpi_scale.x)); 688 event->button.y = (int)(event->button.y / (scale.y * renderer->dpi_scale.y)); 689 } 690 } 691 } else if (event->type == SDL_FINGERDOWN || 692 event->type == SDL_FINGERUP || 693 event->type == SDL_FINGERMOTION) { 694 int logical_w, logical_h; 695 float physical_w, physical_h; 696 SDL_Rect viewport; 697 SDL_FPoint scale; 698 GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale); 699 700 /* !!! FIXME: we probably should drop events that are outside of the 701 !!! FIXME: viewport, but we can't do that from an event watcher, 702 !!! FIXME: and we would have to track if a touch happened outside 703 !!! FIXME: the viewport and then slid into it to insert extra 704 !!! FIXME: events, which is a mess, so for now we just clamp these 705 !!! FIXME: events to the edge. */ 706 707 if (renderer->GetOutputSize) { 708 int w, h; 709 renderer->GetOutputSize(renderer, &w, &h); 710 physical_w = (float) w; 711 physical_h = (float) h; 712 } else { 713 int w, h; 714 SDL_GetWindowSize(renderer->window, &w, &h); 715 physical_w = ((float) w) * renderer->dpi_scale.x; 716 physical_h = ((float) h) * renderer->dpi_scale.y; 717 } 718 719 if (physical_w == 0.0f) { /* nowhere for the touch to go, avoid division by zero and put it dead center. */ 720 event->tfinger.x = 0.5f; 721 } else { 722 const float normalized_viewport_x = ((float) viewport.x) / physical_w; 723 const float normalized_viewport_w = ((float) viewport.w) / physical_w; 724 if (event->tfinger.x <= normalized_viewport_x) { 725 event->tfinger.x = 0.0f; /* to the left of the viewport, clamp to the edge. */ 726 } else if (event->tfinger.x >= (normalized_viewport_x + normalized_viewport_w)) { 727 event->tfinger.x = 1.0f; /* to the right of the viewport, clamp to the edge. */ 728 } else { 729 event->tfinger.x = (event->tfinger.x - normalized_viewport_x) / normalized_viewport_w; 730 } 731 } 732 733 if (physical_h == 0.0f) { /* nowhere for the touch to go, avoid division by zero and put it dead center. */ 734 event->tfinger.y = 0.5f; 735 } else { 736 const float normalized_viewport_y = ((float) viewport.y) / physical_h; 737 const float normalized_viewport_h = ((float) viewport.h) / physical_h; 738 if (event->tfinger.y <= normalized_viewport_y) { 739 event->tfinger.y = 0.0f; /* to the left of the viewport, clamp to the edge. */ 740 } else if (event->tfinger.y >= (normalized_viewport_y + normalized_viewport_h)) { 741 event->tfinger.y = 1.0f; /* to the right of the viewport, clamp to the edge. */ 742 } else { 743 event->tfinger.y = (event->tfinger.y - normalized_viewport_y) / normalized_viewport_h; 744 } 745 } 746 } 747 748 return 0; 749 } 750 751 int 752 SDL_CreateWindowAndRenderer(int width, int height, Uint32 window_flags, 753 SDL_Window **window, SDL_Renderer **renderer) 754 { 755 *window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, 756 SDL_WINDOWPOS_UNDEFINED, 757 width, height, window_flags); 758 if (!*window) { 759 *renderer = NULL; 760 return -1; 761 } 762 763 *renderer = SDL_CreateRenderer(*window, -1, 0); 764 if (!*renderer) { 765 return -1; 766 } 767 768 return 0; 769 } 770 771 static SDL_INLINE 772 void VerifyDrawQueueFunctions(const SDL_Renderer *renderer) 773 { 774 /* all of these functions are required to be implemented, even as no-ops, so we don't 775 have to check that they aren't NULL over and over. */ 776 SDL_assert(renderer->QueueSetViewport != NULL); 777 SDL_assert(renderer->QueueSetDrawColor != NULL); 778 SDL_assert(renderer->QueueDrawPoints != NULL); 779 SDL_assert(renderer->QueueDrawLines != NULL); 780 SDL_assert(renderer->QueueFillRects != NULL); 781 SDL_assert(renderer->QueueCopy != NULL); 782 SDL_assert(renderer->RunCommandQueue != NULL); 783 } 784 785 SDL_Renderer * 786 SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags) 787 { 788 #if !SDL_RENDER_DISABLED 789 SDL_Renderer *renderer = NULL; 790 int n = SDL_GetNumRenderDrivers(); 791 SDL_bool batching = SDL_TRUE; 792 const char *hint; 793 794 #if defined(__ANDROID__) 795 Android_ActivityMutex_Lock_Running(); 796 #endif 797 798 if (!window) { 799 SDL_SetError("Invalid window"); 800 goto error; 801 } 802 803 if (SDL_GetRenderer(window)) { 804 SDL_SetError("Renderer already associated with window"); 805 goto error; 806 } 807 808 if (SDL_GetHint(SDL_HINT_RENDER_VSYNC)) { 809 if (SDL_GetHintBoolean(SDL_HINT_RENDER_VSYNC, SDL_TRUE)) { 810 flags |= SDL_RENDERER_PRESENTVSYNC; 811 } else { 812 flags &= ~SDL_RENDERER_PRESENTVSYNC; 813 } 814 } 815 816 if (index < 0) { 817 hint = SDL_GetHint(SDL_HINT_RENDER_DRIVER); 818 if (hint) { 819 for (index = 0; index < n; ++index) { 820 const SDL_RenderDriver *driver = render_drivers[index]; 821 822 if (SDL_strcasecmp(hint, driver->info.name) == 0) { 823 /* Create a new renderer instance */ 824 renderer = driver->CreateRenderer(window, flags); 825 if (renderer) { 826 batching = SDL_FALSE; 827 } 828 break; 829 } 830 } 831 } 832 833 if (!renderer) { 834 for (index = 0; index < n; ++index) { 835 const SDL_RenderDriver *driver = render_drivers[index]; 836 837 if ((driver->info.flags & flags) == flags) { 838 /* Create a new renderer instance */ 839 renderer = driver->CreateRenderer(window, flags); 840 if (renderer) { 841 /* Yay, we got one! */ 842 break; 843 } 844 } 845 } 846 } 847 if (index == n) { 848 SDL_SetError("Couldn't find matching render driver"); 849 goto error; 850 } 851 } else { 852 if (index >= SDL_GetNumRenderDrivers()) { 853 SDL_SetError("index must be -1 or in the range of 0 - %d", 854 SDL_GetNumRenderDrivers() - 1); 855 goto error; 856 } 857 /* Create a new renderer instance */ 858 renderer = render_drivers[index]->CreateRenderer(window, flags); 859 batching = SDL_FALSE; 860 } 861 862 if (!renderer) { 863 goto error; 864 } 865 866 VerifyDrawQueueFunctions(renderer); 867 868 /* let app/user override batching decisions. */ 869 if (renderer->always_batch) { 870 batching = SDL_TRUE; 871 } else if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) { 872 batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE); 873 } 874 875 renderer->batching = batching; 876 renderer->magic = &renderer_magic; 877 renderer->window = window; 878 renderer->target_mutex = SDL_CreateMutex(); 879 renderer->scale.x = 1.0f; 880 renderer->scale.y = 1.0f; 881 renderer->dpi_scale.x = 1.0f; 882 renderer->dpi_scale.y = 1.0f; 883 884 /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */ 885 renderer->render_command_generation = 1; 886 887 if (window && renderer->GetOutputSize) { 888 int window_w, window_h; 889 int output_w, output_h; 890 if (renderer->GetOutputSize(renderer, &output_w, &output_h) == 0) { 891 SDL_GetWindowSize(renderer->window, &window_w, &window_h); 892 renderer->dpi_scale.x = (float)window_w / output_w; 893 renderer->dpi_scale.y = (float)window_h / output_h; 894 } 895 } 896 897 renderer->relative_scaling = SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_SCALING, SDL_TRUE); 898 899 if (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN|SDL_WINDOW_MINIMIZED)) { 900 renderer->hidden = SDL_TRUE; 901 } else { 902 renderer->hidden = SDL_FALSE; 903 } 904 905 SDL_SetWindowData(window, SDL_WINDOWRENDERDATA, renderer); 906 907 SDL_RenderSetViewport(renderer, NULL); 908 909 SDL_AddEventWatch(SDL_RendererEventWatch, renderer); 910 911 SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, 912 "Created renderer: %s", renderer->info.name); 913 914 #if defined(__ANDROID__) 915 Android_ActivityMutex_Unlock(); 916 #endif 917 return renderer; 918 919 error: 920 921 #if defined(__ANDROID__) 922 Android_ActivityMutex_Unlock(); 923 #endif 924 return NULL; 925 926 #else 927 SDL_SetError("SDL not built with rendering support"); 928 return NULL; 929 #endif 930 } 931 932 SDL_Renderer * 933 SDL_CreateSoftwareRenderer(SDL_Surface * surface) 934 { 935 #if !SDL_RENDER_DISABLED && SDL_VIDEO_RENDER_SW 936 SDL_Renderer *renderer; 937 938 renderer = SW_CreateRendererForSurface(surface); 939 940 if (renderer) { 941 VerifyDrawQueueFunctions(renderer); 942 renderer->magic = &renderer_magic; 943 renderer->target_mutex = SDL_CreateMutex(); 944 renderer->scale.x = 1.0f; 945 renderer->scale.y = 1.0f; 946 947 /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */ 948 renderer->render_command_generation = 1; 949 950 SDL_RenderSetViewport(renderer, NULL); 951 } 952 return renderer; 953 #else 954 SDL_SetError("SDL not built with rendering support"); 955 return NULL; 956 #endif /* !SDL_RENDER_DISABLED */ 957 } 958 959 SDL_Renderer * 960 SDL_GetRenderer(SDL_Window * window) 961 { 962 return (SDL_Renderer *)SDL_GetWindowData(window, SDL_WINDOWRENDERDATA); 963 } 964 965 int 966 SDL_GetRendererInfo(SDL_Renderer * renderer, SDL_RendererInfo * info) 967 { 968 CHECK_RENDERER_MAGIC(renderer, -1); 969 970 *info = renderer->info; 971 return 0; 972 } 973 974 int 975 SDL_GetRendererOutputSize(SDL_Renderer * renderer, int *w, int *h) 976 { 977 CHECK_RENDERER_MAGIC(renderer, -1); 978 979 if (renderer->target) { 980 return SDL_QueryTexture(renderer->target, NULL, NULL, w, h); 981 } else if (renderer->GetOutputSize) { 982 return renderer->GetOutputSize(renderer, w, h); 983 } else if (renderer->window) { 984 SDL_GetWindowSize(renderer->window, w, h); 985 return 0; 986 } else { 987 SDL_assert(0 && "This should never happen"); 988 return SDL_SetError("Renderer doesn't support querying output size"); 989 } 990 } 991 992 static SDL_bool 993 IsSupportedBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode) 994 { 995 switch (blendMode) 996 { 997 /* These are required to be supported by all renderers */ 998 case SDL_BLENDMODE_NONE: 999 case SDL_BLENDMODE_BLEND: 1000 case SDL_BLENDMODE_ADD: 1001 case SDL_BLENDMODE_MOD: 1002 case SDL_BLENDMODE_MUL: 1003 return SDL_TRUE; 1004 1005 default: 1006 return renderer->SupportsBlendMode && renderer->SupportsBlendMode(renderer, blendMode); 1007 } 1008 } 1009 1010 static SDL_bool 1011 IsSupportedFormat(SDL_Renderer * renderer, Uint32 format) 1012 { 1013 Uint32 i; 1014 1015 for (i = 0; i < renderer->info.num_texture_formats; ++i) { 1016 if (renderer->info.texture_formats[i] == format) { 1017 return SDL_TRUE; 1018 } 1019 } 1020 return SDL_FALSE; 1021 } 1022 1023 static Uint32 1024 GetClosestSupportedFormat(SDL_Renderer * renderer, Uint32 format) 1025 { 1026 Uint32 i; 1027 1028 if (SDL_ISPIXELFORMAT_FOURCC(format)) { 1029 /* Look for an exact match */ 1030 for (i = 0; i < renderer->info.num_texture_formats; ++i) { 1031 if (renderer->info.texture_formats[i] == format) { 1032 return renderer->info.texture_formats[i]; 1033 } 1034 } 1035 } else { 1036 SDL_bool hasAlpha = SDL_ISPIXELFORMAT_ALPHA(format); 1037 1038 /* We just want to match the first format that has the same channels */ 1039 for (i = 0; i < renderer->info.num_texture_formats; ++i) { 1040 if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) && 1041 SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == hasAlpha) { 1042 return renderer->info.texture_formats[i]; 1043 } 1044 } 1045 } 1046 return renderer->info.texture_formats[0]; 1047 } 1048 1049 1050 static SDL_ScaleMode SDL_GetScaleMode(void) 1051 { 1052 const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY); 1053 1054 if (!hint || SDL_strcasecmp(hint, "nearest") == 0) { 1055 return SDL_ScaleModeNearest; 1056 } else if (SDL_strcasecmp(hint, "linear") == 0) { 1057 return SDL_ScaleModeLinear; 1058 } else if (SDL_strcasecmp(hint, "best") == 0) { 1059 return SDL_ScaleModeBest; 1060 } else { 1061 return (SDL_ScaleMode)SDL_atoi(hint); 1062 } 1063 } 1064 1065 SDL_Texture * 1066 SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h) 1067 { 1068 SDL_Texture *texture; 1069 1070 CHECK_RENDERER_MAGIC(renderer, NULL); 1071 1072 if (!format) { 1073 format = renderer->info.texture_formats[0]; 1074 } 1075 if (SDL_BYTESPERPIXEL(format) == 0) { 1076 SDL_SetError("Invalid texture format"); 1077 return NULL; 1078 } 1079 if (SDL_ISPIXELFORMAT_INDEXED(format)) { 1080 SDL_SetError("Palettized textures are not supported"); 1081 return NULL; 1082 } 1083 if (w <= 0 || h <= 0) { 1084 SDL_SetError("Texture dimensions can't be 0"); 1085 return NULL; 1086 } 1087 if ((renderer->info.max_texture_width && w > renderer->info.max_texture_width) || 1088 (renderer->info.max_texture_height && h > renderer->info.max_texture_height)) { 1089 SDL_SetError("Texture dimensions are limited to %dx%d", renderer->info.max_texture_width, renderer->info.max_texture_height); 1090 return NULL; 1091 } 1092 texture = (SDL_Texture *) SDL_calloc(1, sizeof(*texture)); 1093 if (!texture) { 1094 SDL_OutOfMemory(); 1095 return NULL; 1096 } 1097 texture->magic = &texture_magic; 1098 texture->format = format; 1099 texture->access = access; 1100 texture->w = w; 1101 texture->h = h; 1102 texture->r = 255; 1103 texture->g = 255; 1104 texture->b = 255; 1105 texture->a = 255; 1106 texture->scaleMode = SDL_GetScaleMode(); 1107 texture->renderer = renderer; 1108 texture->next = renderer->textures; 1109 if (renderer->textures) { 1110 renderer->textures->prev = texture; 1111 } 1112 renderer->textures = texture; 1113 1114 if (IsSupportedFormat(renderer, format)) { 1115 if (renderer->CreateTexture(renderer, texture) < 0) { 1116 SDL_DestroyTexture(texture); 1117 return NULL; 1118 } 1119 } else { 1120 texture->native = SDL_CreateTexture(renderer, 1121 GetClosestSupportedFormat(renderer, format), 1122 access, w, h); 1123 if (!texture->native) { 1124 SDL_DestroyTexture(texture); 1125 return NULL; 1126 } 1127 1128 /* Swap textures to have texture before texture->native in the list */ 1129 texture->native->next = texture->next; 1130 if (texture->native->next) { 1131 texture->native->next->prev = texture->native; 1132 } 1133 texture->prev = texture->native->prev; 1134 if (texture->prev) { 1135 texture->prev->next = texture; 1136 } 1137 texture->native->prev = texture; 1138 texture->next = texture->native; 1139 renderer->textures = texture; 1140 1141 if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) { 1142 #if SDL_HAVE_YUV 1143 texture->yuv = SDL_SW_CreateYUVTexture(format, w, h); 1144 #else 1145 SDL_SetError("SDL not built with YUV support"); 1146 #endif 1147 if (!texture->yuv) { 1148 SDL_DestroyTexture(texture); 1149 return NULL; 1150 } 1151 } else if (access == SDL_TEXTUREACCESS_STREAMING) { 1152 /* The pitch is 4 byte aligned */ 1153 texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3); 1154 texture->pixels = SDL_calloc(1, texture->pitch * h); 1155 if (!texture->pixels) { 1156 SDL_DestroyTexture(texture); 1157 return NULL; 1158 } 1159 } 1160 } 1161 return texture; 1162 } 1163 1164 SDL_Texture * 1165 SDL_CreateTextureFromSurface(SDL_Renderer * renderer, SDL_Surface * surface) 1166 { 1167 const SDL_PixelFormat *fmt; 1168 SDL_bool needAlpha; 1169 SDL_bool direct_update; 1170 int i; 1171 Uint32 format = SDL_PIXELFORMAT_UNKNOWN; 1172 SDL_Texture *texture; 1173 1174 CHECK_RENDERER_MAGIC(renderer, NULL); 1175 1176 if (!surface) { 1177 SDL_SetError("SDL_CreateTextureFromSurface() passed NULL surface"); 1178 return NULL; 1179 } 1180 1181 /* See what the best texture format is */ 1182 fmt = surface->format; 1183 if (fmt->Amask || SDL_HasColorKey(surface)) { 1184 needAlpha = SDL_TRUE; 1185 } else { 1186 needAlpha = SDL_FALSE; 1187 } 1188 1189 /* If Palette contains alpha values, promotes to alpha format */ 1190 if (fmt->palette) { 1191 SDL_bool is_opaque, has_alpha_channel; 1192 SDL_DetectPalette(fmt->palette, &is_opaque, &has_alpha_channel); 1193 if (!is_opaque) { 1194 needAlpha = SDL_TRUE; 1195 } 1196 } 1197 1198 /* Try to have the best pixel format for the texture */ 1199 /* No alpha, but a colorkey => promote to alpha */ 1200 if (!fmt->Amask && SDL_HasColorKey(surface)) { 1201 if (fmt->format == SDL_PIXELFORMAT_RGB888) { 1202 for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) { 1203 if (renderer->info.texture_formats[i] == SDL_PIXELFORMAT_ARGB8888) { 1204 format = SDL_PIXELFORMAT_ARGB8888; 1205 break; 1206 } 1207 } 1208 } else if (fmt->format == SDL_PIXELFORMAT_BGR888) { 1209 for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) { 1210 if (renderer->info.texture_formats[i] == SDL_PIXELFORMAT_ABGR8888) { 1211 format = SDL_PIXELFORMAT_ABGR8888; 1212 break; 1213 } 1214 } 1215 } 1216 } else { 1217 /* Exact match would be fine */ 1218 for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) { 1219 if (renderer->info.texture_formats[i] == fmt->format) { 1220 format = fmt->format; 1221 break; 1222 } 1223 } 1224 } 1225 1226 /* Fallback, choose a valid pixel format */ 1227 if (format == SDL_PIXELFORMAT_UNKNOWN) { 1228 format = renderer->info.texture_formats[0]; 1229 for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) { 1230 if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) && 1231 SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == needAlpha) { 1232 format = renderer->info.texture_formats[i]; 1233 break; 1234 } 1235 } 1236 } 1237 1238 texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STATIC, 1239 surface->w, surface->h); 1240 if (!texture) { 1241 return NULL; 1242 } 1243 1244 if (format == surface->format->format) { 1245 if (surface->format->Amask && SDL_HasColorKey(surface)) { 1246 /* Surface and Renderer formats are identicals. 1247 * Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */ 1248 direct_update = SDL_FALSE; 1249 } else { 1250 /* Update Texture directly */ 1251 direct_update = SDL_TRUE; 1252 } 1253 } else { 1254 /* Surface and Renderer formats are differents, it needs an intermediate conversion. */ 1255 direct_update = SDL_FALSE; 1256 } 1257 1258 if (direct_update) { 1259 if (SDL_MUSTLOCK(surface)) { 1260 SDL_LockSurface(surface); 1261 SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch); 1262 SDL_UnlockSurface(surface); 1263 } else { 1264 SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch); 1265 } 1266 } else { 1267 SDL_PixelFormat *dst_fmt; 1268 SDL_Surface *temp = NULL; 1269 1270 /* Set up a destination surface for the texture update */ 1271 dst_fmt = SDL_AllocFormat(format); 1272 if (!dst_fmt) { 1273 SDL_DestroyTexture(texture); 1274 return NULL; 1275 } 1276 temp = SDL_ConvertSurface(surface, dst_fmt, 0); 1277 SDL_FreeFormat(dst_fmt); 1278 if (temp) { 1279 SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch); 1280 SDL_FreeSurface(temp); 1281 } else { 1282 SDL_DestroyTexture(texture); 1283 return NULL; 1284 } 1285 } 1286 1287 { 1288 Uint8 r, g, b, a; 1289 SDL_BlendMode blendMode; 1290 1291 SDL_GetSurfaceColorMod(surface, &r, &g, &b); 1292 SDL_SetTextureColorMod(texture, r, g, b); 1293 1294 SDL_GetSurfaceAlphaMod(surface, &a); 1295 SDL_SetTextureAlphaMod(texture, a); 1296 1297 if (SDL_HasColorKey(surface)) { 1298 /* We converted to a texture with alpha format */ 1299 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); 1300 } else { 1301 SDL_GetSurfaceBlendMode(surface, &blendMode); 1302 SDL_SetTextureBlendMode(texture, blendMode); 1303 } 1304 } 1305 return texture; 1306 } 1307 1308 int 1309 SDL_QueryTexture(SDL_Texture * texture, Uint32 * format, int *access, 1310 int *w, int *h) 1311 { 1312 CHECK_TEXTURE_MAGIC(texture, -1); 1313 1314 if (format) { 1315 *format = texture->format; 1316 } 1317 if (access) { 1318 *access = texture->access; 1319 } 1320 if (w) { 1321 *w = texture->w; 1322 } 1323 if (h) { 1324 *h = texture->h; 1325 } 1326 return 0; 1327 } 1328 1329 int 1330 SDL_SetTextureColorMod(SDL_Texture * texture, Uint8 r, Uint8 g, Uint8 b) 1331 { 1332 CHECK_TEXTURE_MAGIC(texture, -1); 1333 1334 if (r < 255 || g < 255 || b < 255) { 1335 texture->modMode |= SDL_TEXTUREMODULATE_COLOR; 1336 } else { 1337 texture->modMode &= ~SDL_TEXTUREMODULATE_COLOR; 1338 } 1339 texture->r = r; 1340 texture->g = g; 1341 texture->b = b; 1342 if (texture->native) { 1343 return SDL_SetTextureColorMod(texture->native, r, g, b); 1344 } 1345 return 0; 1346 } 1347 1348 int 1349 SDL_GetTextureColorMod(SDL_Texture * texture, Uint8 * r, Uint8 * g, 1350 Uint8 * b) 1351 { 1352 CHECK_TEXTURE_MAGIC(texture, -1); 1353 1354 if (r) { 1355 *r = texture->r; 1356 } 1357 if (g) { 1358 *g = texture->g; 1359 } 1360 if (b) { 1361 *b = texture->b; 1362 } 1363 return 0; 1364 } 1365 1366 int 1367 SDL_SetTextureAlphaMod(SDL_Texture * texture, Uint8 alpha) 1368 { 1369 CHECK_TEXTURE_MAGIC(texture, -1); 1370 1371 if (alpha < 255) { 1372 texture->modMode |= SDL_TEXTUREMODULATE_ALPHA; 1373 } else { 1374 texture->modMode &= ~SDL_TEXTUREMODULATE_ALPHA; 1375 } 1376 texture->a = alpha; 1377 if (texture->native) { 1378 return SDL_SetTextureAlphaMod(texture->native, alpha); 1379 } 1380 return 0; 1381 } 1382 1383 int 1384 SDL_GetTextureAlphaMod(SDL_Texture * texture, Uint8 * alpha) 1385 { 1386 CHECK_TEXTURE_MAGIC(texture, -1); 1387 1388 if (alpha) { 1389 *alpha = texture->a; 1390 } 1391 return 0; 1392 } 1393 1394 int 1395 SDL_SetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode blendMode) 1396 { 1397 SDL_Renderer *renderer; 1398 1399 CHECK_TEXTURE_MAGIC(texture, -1); 1400 1401 renderer = texture->renderer; 1402 if (!IsSupportedBlendMode(renderer, blendMode)) { 1403 return SDL_Unsupported(); 1404 } 1405 texture->blendMode = blendMode; 1406 if (texture->native) { 1407 return SDL_SetTextureBlendMode(texture->native, blendMode); 1408 } 1409 return 0; 1410 } 1411 1412 int 1413 SDL_GetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode *blendMode) 1414 { 1415 CHECK_TEXTURE_MAGIC(texture, -1); 1416 1417 if (blendMode) { 1418 *blendMode = texture->blendMode; 1419 } 1420 return 0; 1421 } 1422 1423 int 1424 SDL_SetTextureScaleMode(SDL_Texture * texture, SDL_ScaleMode scaleMode) 1425 { 1426 SDL_Renderer *renderer; 1427 1428 CHECK_TEXTURE_MAGIC(texture, -1); 1429 1430 renderer = texture->renderer; 1431 renderer->SetTextureScaleMode(renderer, texture, scaleMode); 1432 texture->scaleMode = scaleMode; 1433 if (texture->native) { 1434 return SDL_SetTextureScaleMode(texture->native, scaleMode); 1435 } 1436 return 0; 1437 } 1438 1439 int 1440 SDL_GetTextureScaleMode(SDL_Texture * texture, SDL_ScaleMode *scaleMode) 1441 { 1442 CHECK_TEXTURE_MAGIC(texture, -1); 1443 1444 if (scaleMode) { 1445 *scaleMode = texture->scaleMode; 1446 } 1447 return 0; 1448 } 1449 1450 #if SDL_HAVE_YUV 1451 static int 1452 SDL_UpdateTextureYUV(SDL_Texture * texture, const SDL_Rect * rect, 1453 const void *pixels, int pitch) 1454 { 1455 SDL_Texture *native = texture->native; 1456 SDL_Rect full_rect; 1457 1458 if (SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch) < 0) { 1459 return -1; 1460 } 1461 1462 full_rect.x = 0; 1463 full_rect.y = 0; 1464 full_rect.w = texture->w; 1465 full_rect.h = texture->h; 1466 rect = &full_rect; 1467 1468 if (texture->access == SDL_TEXTUREACCESS_STREAMING) { 1469 /* We can lock the texture and copy to it */ 1470 void *native_pixels = NULL; 1471 int native_pitch = 0; 1472 1473 if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) { 1474 return -1; 1475 } 1476 SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, 1477 rect->w, rect->h, native_pixels, native_pitch); 1478 SDL_UnlockTexture(native); 1479 } else { 1480 /* Use a temporary buffer for updating */ 1481 const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3); 1482 const size_t alloclen = rect->h * temp_pitch; 1483 if (alloclen > 0) { 1484 void *temp_pixels = SDL_malloc(alloclen); 1485 if (!temp_pixels) { 1486 return SDL_OutOfMemory(); 1487 } 1488 SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, 1489 rect->w, rect->h, temp_pixels, temp_pitch); 1490 SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch); 1491 SDL_free(temp_pixels); 1492 } 1493 } 1494 return 0; 1495 } 1496 #endif /* SDL_HAVE_YUV */ 1497 1498 static int 1499 SDL_UpdateTextureNative(SDL_Texture * texture, const SDL_Rect * rect, 1500 const void *pixels, int pitch) 1501 { 1502 SDL_Texture *native = texture->native; 1503 1504 if (!rect->w || !rect->h) { 1505 return 0; /* nothing to do. */ 1506 } 1507 1508 if (texture->access == SDL_TEXTUREACCESS_STREAMING) { 1509 /* We can lock the texture and copy to it */ 1510 void *native_pixels = NULL; 1511 int native_pitch = 0; 1512 1513 if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) { 1514 return -1; 1515 } 1516 SDL_ConvertPixels(rect->w, rect->h, 1517 texture->format, pixels, pitch, 1518 native->format, native_pixels, native_pitch); 1519 SDL_UnlockTexture(native); 1520 } else { 1521 /* Use a temporary buffer for updating */ 1522 const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3); 1523 const size_t alloclen = rect->h * temp_pitch; 1524 if (alloclen > 0) { 1525 void *temp_pixels = SDL_malloc(alloclen); 1526 if (!temp_pixels) { 1527 return SDL_OutOfMemory(); 1528 } 1529 SDL_ConvertPixels(rect->w, rect->h, 1530 texture->format, pixels, pitch, 1531 native->format, temp_pixels, temp_pitch); 1532 SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch); 1533 SDL_free(temp_pixels); 1534 } 1535 } 1536 return 0; 1537 } 1538 1539 int 1540 SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect, 1541 const void *pixels, int pitch) 1542 { 1543 SDL_Rect full_rect; 1544 1545 CHECK_TEXTURE_MAGIC(texture, -1); 1546 1547 if (!pixels) { 1548 return SDL_InvalidParamError("pixels"); 1549 } 1550 if (!pitch) { 1551 return SDL_InvalidParamError("pitch"); 1552 } 1553 1554 if (!rect) { 1555 full_rect.x = 0; 1556 full_rect.y = 0; 1557 full_rect.w = texture->w; 1558 full_rect.h = texture->h; 1559 rect = &full_rect; 1560 } 1561 1562 if ((rect->w == 0) || (rect->h == 0)) { 1563 return 0; /* nothing to do. */ 1564 #if SDL_HAVE_YUV 1565 } else if (texture->yuv) { 1566 return SDL_UpdateTextureYUV(texture, rect, pixels, pitch); 1567 #endif 1568 } else if (texture->native) { 1569 return SDL_UpdateTextureNative(texture, rect, pixels, pitch); 1570 } else { 1571 SDL_Renderer *renderer = texture->renderer; 1572 if (FlushRenderCommandsIfTextureNeeded(texture) < 0) { 1573 return -1; 1574 } 1575 return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch); 1576 } 1577 } 1578 1579 #if SDL_HAVE_YUV 1580 static int 1581 SDL_UpdateTextureYUVPlanar(SDL_Texture * texture, const SDL_Rect * rect, 1582 const Uint8 *Yplane, int Ypitch, 1583 const Uint8 *Uplane, int Upitch, 1584 const Uint8 *Vplane, int Vpitch) 1585 { 1586 SDL_Texture *native = texture->native; 1587 SDL_Rect full_rect; 1588 1589 if (SDL_SW_UpdateYUVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch) < 0) { 1590 return -1; 1591 } 1592 1593 full_rect.x = 0; 1594 full_rect.y = 0; 1595 full_rect.w = texture->w; 1596 full_rect.h = texture->h; 1597 rect = &full_rect; 1598 1599 if (!rect->w || !rect->h) { 1600 return 0; /* nothing to do. */ 1601 } 1602 1603 if (texture->access == SDL_TEXTUREACCESS_STREAMING) { 1604 /* We can lock the texture and copy to it */ 1605 void *native_pixels = NULL; 1606 int native_pitch = 0; 1607 1608 if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) { 1609 return -1; 1610 } 1611 SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, 1612 rect->w, rect->h, native_pixels, native_pitch); 1613 SDL_UnlockTexture(native); 1614 } else { 1615 /* Use a temporary buffer for updating */ 1616 const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3); 1617 const size_t alloclen = rect->h * temp_pitch; 1618 if (alloclen > 0) { 1619 void *temp_pixels = SDL_malloc(alloclen); 1620 if (!temp_pixels) { 1621 return SDL_OutOfMemory(); 1622 } 1623 SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format, 1624 rect->w, rect->h, temp_pixels, temp_pitch); 1625 SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch); 1626 SDL_free(temp_pixels); 1627 } 1628 } 1629 return 0; 1630 } 1631 #endif /* SDL_HAVE_YUV */ 1632 1633 int SDL_UpdateYUVTexture(SDL_Texture * texture, const SDL_Rect * rect, 1634 const Uint8 *Yplane, int Ypitch, 1635 const Uint8 *Uplane, int Upitch, 1636 const Uint8 *Vplane, int Vpitch) 1637 { 1638 #if SDL_HAVE_YUV 1639 SDL_Renderer *renderer; 1640 SDL_Rect full_rect; 1641 1642 CHECK_TEXTURE_MAGIC(texture, -1); 1643 1644 if (!Yplane) { 1645 return SDL_InvalidParamError("Yplane"); 1646 } 1647 if (!Ypitch) { 1648 return SDL_InvalidParamError("Ypitch"); 1649 } 1650 if (!Uplane) { 1651 return SDL_InvalidParamError("Uplane"); 1652 } 1653 if (!Upitch) { 1654 return SDL_InvalidParamError("Upitch"); 1655 } 1656 if (!Vplane) { 1657 return SDL_InvalidParamError("Vplane"); 1658 } 1659 if (!Vpitch) { 1660 return SDL_InvalidParamError("Vpitch"); 1661 } 1662 1663 if (texture->format != SDL_PIXELFORMAT_YV12 && 1664 texture->format != SDL_PIXELFORMAT_IYUV) { 1665 return SDL_SetError("Texture format must by YV12 or IYUV"); 1666 } 1667 1668 if (!rect) { 1669 full_rect.x = 0; 1670 full_rect.y = 0; 1671 full_rect.w = texture->w; 1672 full_rect.h = texture->h; 1673 rect = &full_rect; 1674 } 1675 1676 if (!rect->w || !rect->h) { 1677 return 0; /* nothing to do. */ 1678 } 1679 1680 if (texture->yuv) { 1681 return SDL_UpdateTextureYUVPlanar(texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch); 1682 } else { 1683 SDL_assert(!texture->native); 1684 renderer = texture->renderer; 1685 SDL_assert(renderer->UpdateTextureYUV); 1686 if (renderer->UpdateTextureYUV) { 1687 if (FlushRenderCommandsIfTextureNeeded(texture) < 0) { 1688 return -1; 1689 } 1690 return renderer->UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch); 1691 } else { 1692 return SDL_Unsupported(); 1693 } 1694 } 1695 #else 1696 return -1; 1697 #endif 1698 } 1699 1700 #if SDL_HAVE_YUV 1701 static int 1702 SDL_LockTextureYUV(SDL_Texture * texture, const SDL_Rect * rect, 1703 void **pixels, int *pitch) 1704 { 1705 return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch); 1706 } 1707 #endif /* SDL_HAVE_YUV */ 1708 1709 static int 1710 SDL_LockTextureNative(SDL_Texture * texture, const SDL_Rect * rect, 1711 void **pixels, int *pitch) 1712 { 1713 texture->locked_rect = *rect; 1714 *pixels = (void *) ((Uint8 *) texture->pixels + 1715 rect->y * texture->pitch + 1716 rect->x * SDL_BYTESPERPIXEL(texture->format)); 1717 *pitch = texture->pitch; 1718 return 0; 1719 } 1720 1721 int 1722 SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect, 1723 void **pixels, int *pitch) 1724 { 1725 SDL_Rect full_rect; 1726 1727 CHECK_TEXTURE_MAGIC(texture, -1); 1728 1729 if (texture->access != SDL_TEXTUREACCESS_STREAMING) { 1730 return SDL_SetError("SDL_LockTexture(): texture must be streaming"); 1731 } 1732 1733 if (!rect) { 1734 full_rect.x = 0; 1735 full_rect.y = 0; 1736 full_rect.w = texture->w; 1737 full_rect.h = texture->h; 1738 rect = &full_rect; 1739 } 1740 1741 #if SDL_HAVE_YUV 1742 if (texture->yuv) { 1743 if (FlushRenderCommandsIfTextureNeeded(texture) < 0) { 1744 return -1; 1745 } 1746 return SDL_LockTextureYUV(texture, rect, pixels, pitch); 1747 } else 1748 #endif 1749 if (texture->native) { 1750 /* Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. */ 1751 return SDL_LockTextureNative(texture, rect, pixels, pitch); 1752 } else { 1753 SDL_Renderer *renderer = texture->renderer; 1754 if (FlushRenderCommandsIfTextureNeeded(texture) < 0) { 1755 return -1; 1756 } 1757 return renderer->LockTexture(renderer, texture, rect, pixels, pitch); 1758 } 1759 } 1760 1761 int 1762 SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect, 1763 SDL_Surface **surface) 1764 { 1765 SDL_Rect real_rect; 1766 void *pixels = NULL; 1767 int pitch = 0; /* fix static analysis */ 1768 int ret; 1769 1770 if (texture == NULL || surface == NULL) { 1771 return -1; 1772 } 1773 1774 real_rect.x = 0; 1775 real_rect.y = 0; 1776 real_rect.w = texture->w; 1777 real_rect.h = texture->h; 1778 1779 if (rect) { 1780 SDL_IntersectRect(rect, &real_rect, &real_rect); 1781 } 1782 1783 ret = SDL_LockTexture(texture, &real_rect, &pixels, &pitch); 1784 if (ret < 0) { 1785 return ret; 1786 } 1787 1788 texture->locked_surface = SDL_CreateRGBSurfaceWithFormatFrom(pixels, real_rect.w, real_rect.h, 0, pitch, texture->format); 1789 if (texture->locked_surface == NULL) { 1790 SDL_UnlockTexture(texture); 1791 return -1; 1792 } 1793 1794 *surface = texture->locked_surface; 1795 return 0; 1796 } 1797 1798 #if SDL_HAVE_YUV 1799 static void 1800 SDL_UnlockTextureYUV(SDL_Texture * texture) 1801 { 1802 SDL_Texture *native = texture->native; 1803 void *native_pixels = NULL; 1804 int native_pitch = 0; 1805 SDL_Rect rect; 1806 1807 rect.x = 0; 1808 rect.y = 0; 1809 rect.w = texture->w; 1810 rect.h = texture->h; 1811 1812 if (SDL_LockTexture(native, &rect, &native_pixels, &native_pitch) < 0) { 1813 return; 1814 } 1815 SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format, 1816 rect.w, rect.h, native_pixels, native_pitch); 1817 SDL_UnlockTexture(native); 1818 } 1819 #endif /* SDL_HAVE_YUV */ 1820 1821 static void 1822 SDL_UnlockTextureNative(SDL_Texture * texture) 1823 { 1824 SDL_Texture *native = texture->native; 1825 void *native_pixels = NULL; 1826 int native_pitch = 0; 1827 const SDL_Rect *rect = &texture->locked_rect; 1828 const void* pixels = (void *) ((Uint8 *) texture->pixels + 1829 rect->y * texture->pitch + 1830 rect->x * SDL_BYTESPERPIXEL(texture->format)); 1831 int pitch = texture->pitch; 1832 1833 if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) { 1834 return; 1835 } 1836 SDL_ConvertPixels(rect->w, rect->h, 1837 texture->format, pixels, pitch, 1838 native->format, native_pixels, native_pitch); 1839 SDL_UnlockTexture(native); 1840 } 1841 1842 void 1843 SDL_UnlockTexture(SDL_Texture * texture) 1844 { 1845 CHECK_TEXTURE_MAGIC(texture, ); 1846 1847 if (texture->access != SDL_TEXTUREACCESS_STREAMING) { 1848 return; 1849 } 1850 #if SDL_HAVE_YUV 1851 if (texture->yuv) { 1852 SDL_UnlockTextureYUV(texture); 1853 } else 1854 #endif 1855 if (texture->native) { 1856 SDL_UnlockTextureNative(texture); 1857 } else { 1858 SDL_Renderer *renderer = texture->renderer; 1859 renderer->UnlockTexture(renderer, texture); 1860 } 1861 1862 SDL_FreeSurface(texture->locked_surface); 1863 texture->locked_surface = NULL; 1864 } 1865 1866 SDL_bool 1867 SDL_RenderTargetSupported(SDL_Renderer *renderer) 1868 { 1869 if (!renderer || !renderer->SetRenderTarget) { 1870 return SDL_FALSE; 1871 } 1872 return (renderer->info.flags & SDL_RENDERER_TARGETTEXTURE) != 0; 1873 } 1874 1875 int 1876 SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) 1877 { 1878 if (!SDL_RenderTargetSupported(renderer)) { 1879 return SDL_Unsupported(); 1880 } 1881 1882 /* texture == NULL is valid and means reset the target to the window */ 1883 if (texture) { 1884 CHECK_TEXTURE_MAGIC(texture, -1); 1885 if (renderer != texture->renderer) { 1886 return SDL_SetError("Texture was not created with this renderer"); 1887 } 1888 if (texture->access != SDL_TEXTUREACCESS_TARGET) { 1889 return SDL_SetError("Texture not created with SDL_TEXTUREACCESS_TARGET"); 1890 } 1891 if (texture->native) { 1892 /* Always render to the native texture */ 1893 texture = texture->native; 1894 } 1895 } 1896 1897 if (texture == renderer->target) { 1898 /* Nothing to do! */ 1899 return 0; 1900 } 1901 1902 FlushRenderCommands(renderer); /* time to send everything to the GPU! */ 1903 1904 SDL_LockMutex(renderer->target_mutex); 1905 1906 if (texture && !renderer->target) { 1907 /* Make a backup of the viewport */ 1908 renderer->viewport_backup = renderer->viewport; 1909 renderer->clip_rect_backup = renderer->clip_rect; 1910 renderer->clipping_enabled_backup = renderer->clipping_enabled; 1911 renderer->scale_backup = renderer->scale; 1912 renderer->logical_w_backup = renderer->logical_w; 1913 renderer->logical_h_backup = renderer->logical_h; 1914 } 1915 renderer->target = texture; 1916 1917 if (renderer->SetRenderTarget(renderer, texture) < 0) { 1918 SDL_UnlockMutex(renderer->target_mutex); 1919 return -1; 1920 } 1921 1922 if (texture) { 1923 renderer->viewport.x = 0; 1924 renderer->viewport.y = 0; 1925 renderer->viewport.w = texture->w; 1926 renderer->viewport.h = texture->h; 1927 SDL_zero(renderer->clip_rect); 1928 renderer->clipping_enabled = SDL_FALSE; 1929 renderer->scale.x = 1.0f; 1930 renderer->scale.y = 1.0f; 1931 renderer->logical_w = texture->w; 1932 renderer->logical_h = texture->h; 1933 } else { 1934 renderer->viewport = renderer->viewport_backup; 1935 renderer->clip_rect = renderer->clip_rect_backup; 1936 renderer->clipping_enabled = renderer->clipping_enabled_backup; 1937 renderer->scale = renderer->scale_backup; 1938 renderer->logical_w = renderer->logical_w_backup; 1939 renderer->logical_h = renderer->logical_h_backup; 1940 } 1941 1942 SDL_UnlockMutex(renderer->target_mutex); 1943 1944 if (QueueCmdSetViewport(renderer) < 0) { 1945 return -1; 1946 } 1947 if (QueueCmdSetClipRect(renderer) < 0) { 1948 return -1; 1949 } 1950 1951 /* All set! */ 1952 return FlushRenderCommandsIfNotBatching(renderer); 1953 } 1954 1955 SDL_Texture * 1956 SDL_GetRenderTarget(SDL_Renderer *renderer) 1957 { 1958 return renderer->target; 1959 } 1960 1961 static int 1962 UpdateLogicalSize(SDL_Renderer *renderer) 1963 { 1964 int w = 1, h = 1; 1965 float want_aspect; 1966 float real_aspect; 1967 float scale; 1968 SDL_Rect viewport; 1969 /* 0 is for letterbox, 1 is for overscan */ 1970 int scale_policy = 0; 1971 const char *hint; 1972 1973 if (!renderer->logical_w || !renderer->logical_h) { 1974 return 0; 1975 } 1976 if (SDL_GetRendererOutputSize(renderer, &w, &h) < 0) { 1977 return -1; 1978 } 1979 1980 hint = SDL_GetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE); 1981 if (hint && (*hint == '1' || SDL_strcasecmp(hint, "overscan") == 0)) { 1982 #if SDL_VIDEO_RENDER_D3D 1983 SDL_bool overscan_supported = SDL_TRUE; 1984 /* Unfortunately, Direct3D 9 doesn't support negative viewport numbers 1985 which the overscan implementation relies on. 1986 */ 1987 if (SDL_strcasecmp(SDL_GetCurrentVideoDriver(), "direct3d") == 0) { 1988 overscan_supported = SDL_FALSE; 1989 } 1990 if (overscan_supported) { 1991 scale_policy = 1; 1992 } 1993 #else 1994 scale_policy = 1; 1995 #endif 1996 } 1997 1998 want_aspect = (float)renderer->logical_w / renderer->logical_h; 1999 real_aspect = (float)w / h; 2000 2001 /* Clear the scale because we're setting viewport in output coordinates */ 2002 SDL_RenderSetScale(renderer, 1.0f, 1.0f); 2003 2004 if (renderer->integer_scale) { 2005 if (want_aspect > real_aspect) { 2006 scale = (float)(w / renderer->logical_w); 2007 } else { 2008 scale = (float)(h / renderer->logical_h); 2009 } 2010 viewport.w = (int)SDL_ceil(renderer->logical_w * scale); 2011 viewport.x = (w - viewport.w) / 2; 2012 viewport.h = (int)SDL_ceil(renderer->logical_h * scale); 2013 viewport.y = (h - viewport.h) / 2; 2014 2015 SDL_RenderSetViewport(renderer, &viewport); 2016 } else if (SDL_fabs(want_aspect-real_aspect) < 0.0001) { 2017 /* The aspect ratios are the same, just scale appropriately */ 2018 scale = (float)w / renderer->logical_w; 2019 SDL_RenderSetViewport(renderer, NULL); 2020 } else if (want_aspect > real_aspect) { 2021 if (scale_policy == 1) { 2022 /* We want a wider aspect ratio than is available - 2023 zoom so logical height matches the real height 2024 and the width will grow off the screen 2025 */ 2026 scale = (float)h / renderer->logical_h; 2027 viewport.y = 0; 2028 viewport.h = h; 2029 viewport.w = (int)SDL_ceil(renderer->logical_w * scale); 2030 viewport.x = (w - viewport.w) / 2; 2031 SDL_RenderSetViewport(renderer, &viewport); 2032 } else { 2033 /* We want a wider aspect ratio than is available - letterbox it */ 2034 scale = (float)w / renderer->logical_w; 2035 viewport.x = 0; 2036 viewport.w = w; 2037 viewport.h = (int)SDL_ceil(renderer->logical_h * scale); 2038 viewport.y = (h - viewport.h) / 2; 2039 SDL_RenderSetViewport(renderer, &viewport); 2040 } 2041 } else { 2042 if (scale_policy == 1) { 2043 /* We want a narrower aspect ratio than is available - 2044 zoom so logical width matches the real width 2045 and the height will grow off the screen 2046 */ 2047 scale = (float)w / renderer->logical_w; 2048 viewport.x = 0; 2049 viewport.w = w; 2050 viewport.h = (int)SDL_ceil(renderer->logical_h * scale); 2051 viewport.y = (h - viewport.h) / 2; 2052 SDL_RenderSetViewport(renderer, &viewport); 2053 } else { 2054 /* We want a narrower aspect ratio than is available - use side-bars */ 2055 scale = (float)h / renderer->logical_h; 2056 viewport.y = 0; 2057 viewport.h = h; 2058 viewport.w = (int)SDL_ceil(renderer->logical_w * scale); 2059 viewport.x = (w - viewport.w) / 2; 2060 SDL_RenderSetViewport(renderer, &viewport); 2061 } 2062 } 2063 2064 /* Set the new scale */ 2065 SDL_RenderSetScale(renderer, scale, scale); 2066 2067 return 0; 2068 } 2069 2070 int 2071 SDL_RenderSetLogicalSize(SDL_Renderer * renderer, int w, int h) 2072 { 2073 CHECK_RENDERER_MAGIC(renderer, -1); 2074 2075 if (!w || !h) { 2076 /* Clear any previous logical resolution */ 2077 renderer->logical_w = 0; 2078 renderer->logical_h = 0; 2079 SDL_RenderSetViewport(renderer, NULL); 2080 SDL_RenderSetScale(renderer, 1.0f, 1.0f); 2081 return 0; 2082 } 2083 2084 renderer->logical_w = w; 2085 renderer->logical_h = h; 2086 2087 return UpdateLogicalSize(renderer); 2088 } 2089 2090 void 2091 SDL_RenderGetLogicalSize(SDL_Renderer * renderer, int *w, int *h) 2092 { 2093 CHECK_RENDERER_MAGIC(renderer, ); 2094 2095 if (w) { 2096 *w = renderer->logical_w; 2097 } 2098 if (h) { 2099 *h = renderer->logical_h; 2100 } 2101 } 2102 2103 int 2104 SDL_RenderSetIntegerScale(SDL_Renderer * renderer, SDL_bool enable) 2105 { 2106 CHECK_RENDERER_MAGIC(renderer, -1); 2107 2108 renderer->integer_scale = enable; 2109 2110 return UpdateLogicalSize(renderer); 2111 } 2112 2113 SDL_bool 2114 SDLCALL SDL_RenderGetIntegerScale(SDL_Renderer * renderer) 2115 { 2116 CHECK_RENDERER_MAGIC(renderer, SDL_FALSE); 2117 2118 return renderer->integer_scale; 2119 } 2120 2121 int 2122 SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect) 2123 { 2124 int retval; 2125 CHECK_RENDERER_MAGIC(renderer, -1); 2126 2127 if (rect) { 2128 renderer->viewport.x = (int)SDL_floor(rect->x * renderer->scale.x); 2129 renderer->viewport.y = (int)SDL_floor(rect->y * renderer->scale.y); 2130 renderer->viewport.w = (int)SDL_ceil(rect->w * renderer->scale.x); 2131 renderer->viewport.h = (int)SDL_ceil(rect->h * renderer->scale.y); 2132 } else { 2133 renderer->viewport.x = 0; 2134 renderer->viewport.y = 0; 2135 if (SDL_GetRendererOutputSize(renderer, &renderer->viewport.w, &renderer->viewport.h) < 0) { 2136 return -1; 2137 } 2138 } 2139 retval = QueueCmdSetViewport(renderer); 2140 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2141 } 2142 2143 void 2144 SDL_RenderGetViewport(SDL_Renderer * renderer, SDL_Rect * rect) 2145 { 2146 CHECK_RENDERER_MAGIC(renderer, ); 2147 2148 if (rect) { 2149 rect->x = (int)(renderer->viewport.x / renderer->scale.x); 2150 rect->y = (int)(renderer->viewport.y / renderer->scale.y); 2151 rect->w = (int)(renderer->viewport.w / renderer->scale.x); 2152 rect->h = (int)(renderer->viewport.h / renderer->scale.y); 2153 } 2154 } 2155 2156 int 2157 SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect) 2158 { 2159 int retval; 2160 CHECK_RENDERER_MAGIC(renderer, -1) 2161 2162 if (rect) { 2163 renderer->clipping_enabled = SDL_TRUE; 2164 renderer->clip_rect.x = (int)SDL_floor(rect->x * renderer->scale.x); 2165 renderer->clip_rect.y = (int)SDL_floor(rect->y * renderer->scale.y); 2166 renderer->clip_rect.w = (int)SDL_ceil(rect->w * renderer->scale.x); 2167 renderer->clip_rect.h = (int)SDL_ceil(rect->h * renderer->scale.y); 2168 } else { 2169 renderer->clipping_enabled = SDL_FALSE; 2170 SDL_zero(renderer->clip_rect); 2171 } 2172 2173 retval = QueueCmdSetClipRect(renderer); 2174 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2175 } 2176 2177 void 2178 SDL_RenderGetClipRect(SDL_Renderer * renderer, SDL_Rect * rect) 2179 { 2180 CHECK_RENDERER_MAGIC(renderer, ) 2181 2182 if (rect) { 2183 rect->x = (int)(renderer->clip_rect.x / renderer->scale.x); 2184 rect->y = (int)(renderer->clip_rect.y / renderer->scale.y); 2185 rect->w = (int)(renderer->clip_rect.w / renderer->scale.x); 2186 rect->h = (int)(renderer->clip_rect.h / renderer->scale.y); 2187 } 2188 } 2189 2190 SDL_bool 2191 SDL_RenderIsClipEnabled(SDL_Renderer * renderer) 2192 { 2193 CHECK_RENDERER_MAGIC(renderer, SDL_FALSE) 2194 return renderer->clipping_enabled; 2195 } 2196 2197 int 2198 SDL_RenderSetScale(SDL_Renderer * renderer, float scaleX, float scaleY) 2199 { 2200 CHECK_RENDERER_MAGIC(renderer, -1); 2201 2202 renderer->scale.x = scaleX; 2203 renderer->scale.y = scaleY; 2204 return 0; 2205 } 2206 2207 void 2208 SDL_RenderGetScale(SDL_Renderer * renderer, float *scaleX, float *scaleY) 2209 { 2210 CHECK_RENDERER_MAGIC(renderer, ); 2211 2212 if (scaleX) { 2213 *scaleX = renderer->scale.x; 2214 } 2215 if (scaleY) { 2216 *scaleY = renderer->scale.y; 2217 } 2218 } 2219 2220 int 2221 SDL_SetRenderDrawColor(SDL_Renderer * renderer, 2222 Uint8 r, Uint8 g, Uint8 b, Uint8 a) 2223 { 2224 CHECK_RENDERER_MAGIC(renderer, -1); 2225 2226 renderer->r = r; 2227 renderer->g = g; 2228 renderer->b = b; 2229 renderer->a = a; 2230 return 0; 2231 } 2232 2233 int 2234 SDL_GetRenderDrawColor(SDL_Renderer * renderer, 2235 Uint8 * r, Uint8 * g, Uint8 * b, Uint8 * a) 2236 { 2237 CHECK_RENDERER_MAGIC(renderer, -1); 2238 2239 if (r) { 2240 *r = renderer->r; 2241 } 2242 if (g) { 2243 *g = renderer->g; 2244 } 2245 if (b) { 2246 *b = renderer->b; 2247 } 2248 if (a) { 2249 *a = renderer->a; 2250 } 2251 return 0; 2252 } 2253 2254 int 2255 SDL_SetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode) 2256 { 2257 CHECK_RENDERER_MAGIC(renderer, -1); 2258 2259 if (!IsSupportedBlendMode(renderer, blendMode)) { 2260 return SDL_Unsupported(); 2261 } 2262 renderer->blendMode = blendMode; 2263 return 0; 2264 } 2265 2266 int 2267 SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode *blendMode) 2268 { 2269 CHECK_RENDERER_MAGIC(renderer, -1); 2270 2271 *blendMode = renderer->blendMode; 2272 return 0; 2273 } 2274 2275 int 2276 SDL_RenderClear(SDL_Renderer * renderer) 2277 { 2278 int retval; 2279 CHECK_RENDERER_MAGIC(renderer, -1); 2280 retval = QueueCmdClear(renderer); 2281 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2282 } 2283 2284 2285 /* !!! FIXME: delete all the duplicate code for the integer versions in 2.1, 2286 !!! FIXME: making the floating point versions the only available APIs. */ 2287 2288 int 2289 SDL_RenderDrawPoint(SDL_Renderer * renderer, int x, int y) 2290 { 2291 SDL_FPoint fpoint; 2292 fpoint.x = (float) x; 2293 fpoint.y = (float) y; 2294 return SDL_RenderDrawPointsF(renderer, &fpoint, 1); 2295 } 2296 2297 int 2298 SDL_RenderDrawPointF(SDL_Renderer * renderer, float x, float y) 2299 { 2300 SDL_FPoint fpoint; 2301 fpoint.x = x; 2302 fpoint.y = y; 2303 return SDL_RenderDrawPointsF(renderer, &fpoint, 1); 2304 } 2305 2306 static int 2307 RenderDrawPointsWithRects(SDL_Renderer * renderer, 2308 const SDL_Point * points, const int count) 2309 { 2310 int retval = -1; 2311 SDL_bool isstack; 2312 SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack); 2313 int i; 2314 2315 if (!frects) { 2316 return SDL_OutOfMemory(); 2317 } 2318 2319 for (i = 0; i < count; ++i) { 2320 frects[i].x = points[i].x * renderer->scale.x; 2321 frects[i].y = points[i].y * renderer->scale.y; 2322 frects[i].w = renderer->scale.x; 2323 frects[i].h = renderer->scale.y; 2324 } 2325 2326 retval = QueueCmdFillRects(renderer, frects, count); 2327 2328 SDL_small_free(frects, isstack); 2329 2330 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2331 } 2332 2333 int 2334 SDL_RenderDrawPoints(SDL_Renderer * renderer, 2335 const SDL_Point * points, int count) 2336 { 2337 SDL_FPoint *fpoints; 2338 int i; 2339 int retval; 2340 SDL_bool isstack; 2341 2342 CHECK_RENDERER_MAGIC(renderer, -1); 2343 2344 if (!points) { 2345 return SDL_SetError("SDL_RenderDrawPoints(): Passed NULL points"); 2346 } 2347 if (count < 1) { 2348 return 0; 2349 } 2350 2351 /* Don't draw while we're hidden */ 2352 if (renderer->hidden) { 2353 return 0; 2354 } 2355 2356 if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) { 2357 return RenderDrawPointsWithRects(renderer, points, count); 2358 } 2359 2360 fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack); 2361 if (!fpoints) { 2362 return SDL_OutOfMemory(); 2363 } 2364 for (i = 0; i < count; ++i) { 2365 fpoints[i].x = points[i].x * renderer->scale.x; 2366 fpoints[i].y = points[i].y * renderer->scale.y; 2367 } 2368 2369 retval = QueueCmdDrawPoints(renderer, fpoints, count); 2370 2371 SDL_small_free(fpoints, isstack); 2372 2373 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2374 } 2375 2376 static int 2377 RenderDrawPointsWithRectsF(SDL_Renderer * renderer, 2378 const SDL_FPoint * fpoints, const int count) 2379 { 2380 int retval = -1; 2381 SDL_bool isstack; 2382 SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack); 2383 int i; 2384 2385 if (!frects) { 2386 return SDL_OutOfMemory(); 2387 } 2388 2389 for (i = 0; i < count; ++i) { 2390 frects[i].x = fpoints[i].x * renderer->scale.x; 2391 frects[i].y = fpoints[i].y * renderer->scale.y; 2392 frects[i].w = renderer->scale.x; 2393 frects[i].h = renderer->scale.y; 2394 } 2395 2396 retval = QueueCmdFillRects(renderer, frects, count); 2397 2398 SDL_small_free(frects, isstack); 2399 2400 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2401 } 2402 2403 int 2404 SDL_RenderDrawPointsF(SDL_Renderer * renderer, 2405 const SDL_FPoint * points, int count) 2406 { 2407 SDL_FPoint *fpoints; 2408 int i; 2409 int retval; 2410 SDL_bool isstack; 2411 2412 CHECK_RENDERER_MAGIC(renderer, -1); 2413 2414 if (!points) { 2415 return SDL_SetError("SDL_RenderDrawFPoints(): Passed NULL points"); 2416 } 2417 if (count < 1) { 2418 return 0; 2419 } 2420 2421 /* Don't draw while we're hidden */ 2422 if (renderer->hidden) { 2423 return 0; 2424 } 2425 2426 if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) { 2427 return RenderDrawPointsWithRectsF(renderer, points, count); 2428 } 2429 2430 fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack); 2431 if (!fpoints) { 2432 return SDL_OutOfMemory(); 2433 } 2434 for (i = 0; i < count; ++i) { 2435 fpoints[i].x = points[i].x * renderer->scale.x; 2436 fpoints[i].y = points[i].y * renderer->scale.y; 2437 } 2438 2439 retval = QueueCmdDrawPoints(renderer, fpoints, count); 2440 2441 SDL_small_free(fpoints, isstack); 2442 2443 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2444 } 2445 2446 int 2447 SDL_RenderDrawLine(SDL_Renderer * renderer, int x1, int y1, int x2, int y2) 2448 { 2449 SDL_FPoint points[2]; 2450 points[0].x = (float) x1; 2451 points[0].y = (float) y1; 2452 points[1].x = (float) x2; 2453 points[1].y = (float) y2; 2454 return SDL_RenderDrawLinesF(renderer, points, 2); 2455 } 2456 2457 int 2458 SDL_RenderDrawLineF(SDL_Renderer * renderer, float x1, float y1, float x2, float y2) 2459 { 2460 SDL_FPoint points[2]; 2461 points[0].x = x1; 2462 points[0].y = y1; 2463 points[1].x = x2; 2464 points[1].y = y2; 2465 return SDL_RenderDrawLinesF(renderer, points, 2); 2466 } 2467 2468 static int 2469 RenderDrawLinesWithRects(SDL_Renderer * renderer, 2470 const SDL_Point * points, const int count) 2471 { 2472 SDL_FRect *frect; 2473 SDL_FRect *frects; 2474 SDL_FPoint fpoints[2]; 2475 int i, nrects = 0; 2476 int retval = 0; 2477 SDL_bool isstack; 2478 2479 frects = SDL_small_alloc(SDL_FRect, count-1, &isstack); 2480 if (!frects) { 2481 return SDL_OutOfMemory(); 2482 } 2483 2484 for (i = 0; i < count-1; ++i) { 2485 if (points[i].x == points[i+1].x) { 2486 const int minY = SDL_min(points[i].y, points[i+1].y); 2487 const int maxY = SDL_max(points[i].y, points[i+1].y); 2488 2489 frect = &frects[nrects++]; 2490 frect->x = points[i].x * renderer->scale.x; 2491 frect->y = minY * renderer->scale.y; 2492 frect->w = renderer->scale.x; 2493 frect->h = (maxY - minY + 1) * renderer->scale.y; 2494 } else if (points[i].y == points[i+1].y) { 2495 const int minX = SDL_min(points[i].x, points[i+1].x); 2496 const int maxX = SDL_max(points[i].x, points[i+1].x); 2497 2498 frect = &frects[nrects++]; 2499 frect->x = minX * renderer->scale.x; 2500 frect->y = points[i].y * renderer->scale.y; 2501 frect->w = (maxX - minX + 1) * renderer->scale.x; 2502 frect->h = renderer->scale.y; 2503 } else { 2504 /* FIXME: We can't use a rect for this line... */ 2505 fpoints[0].x = points[i].x * renderer->scale.x; 2506 fpoints[0].y = points[i].y * renderer->scale.y; 2507 fpoints[1].x = points[i+1].x * renderer->scale.x; 2508 fpoints[1].y = points[i+1].y * renderer->scale.y; 2509 retval += QueueCmdDrawLines(renderer, fpoints, 2); 2510 } 2511 } 2512 2513 retval += QueueCmdFillRects(renderer, frects, nrects); 2514 2515 SDL_small_free(frects, isstack); 2516 2517 if (retval < 0) { 2518 retval = -1; 2519 } 2520 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2521 } 2522 2523 static int 2524 RenderDrawLinesWithRectsF(SDL_Renderer * renderer, 2525 const SDL_FPoint * points, const int count) 2526 { 2527 SDL_FRect *frect; 2528 SDL_FRect *frects; 2529 SDL_FPoint fpoints[2]; 2530 int i, nrects = 0; 2531 int retval = 0; 2532 SDL_bool isstack; 2533 2534 frects = SDL_small_alloc(SDL_FRect, count-1, &isstack); 2535 if (!frects) { 2536 return SDL_OutOfMemory(); 2537 } 2538 2539 for (i = 0; i < count-1; ++i) { 2540 if (points[i].x == points[i+1].x) { 2541 const int minY = (int)SDL_min(points[i].y, points[i+1].y); 2542 const int maxY = (int)SDL_max(points[i].y, points[i+1].y); 2543 2544 frect = &frects[nrects++]; 2545 frect->x = points[i].x * renderer->scale.x; 2546 frect->y = minY * renderer->scale.y; 2547 frect->w = renderer->scale.x; 2548 frect->h = (maxY - minY + 1) * renderer->scale.y; 2549 } else if (points[i].y == points[i+1].y) { 2550 const int minX = (int)SDL_min(points[i].x, points[i+1].x); 2551 const int maxX = (int)SDL_max(points[i].x, points[i+1].x); 2552 2553 frect = &frects[nrects++]; 2554 frect->x = minX * renderer->scale.x; 2555 frect->y = points[i].y * renderer->scale.y; 2556 frect->w = (maxX - minX + 1) * renderer->scale.x; 2557 frect->h = renderer->scale.y; 2558 } else { 2559 /* FIXME: We can't use a rect for this line... */ 2560 fpoints[0].x = points[i].x * renderer->scale.x; 2561 fpoints[0].y = points[i].y * renderer->scale.y; 2562 fpoints[1].x = points[i+1].x * renderer->scale.x; 2563 fpoints[1].y = points[i+1].y * renderer->scale.y; 2564 retval += QueueCmdDrawLines(renderer, fpoints, 2); 2565 } 2566 } 2567 2568 retval += QueueCmdFillRects(renderer, frects, nrects); 2569 2570 SDL_small_free(frects, isstack); 2571 2572 if (retval < 0) { 2573 retval = -1; 2574 } 2575 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2576 } 2577 2578 int 2579 SDL_RenderDrawLines(SDL_Renderer * renderer, 2580 const SDL_Point * points, int count) 2581 { 2582 SDL_FPoint *fpoints; 2583 int i; 2584 int retval; 2585 SDL_bool isstack; 2586 2587 CHECK_RENDERER_MAGIC(renderer, -1); 2588 2589 if (!points) { 2590 return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points"); 2591 } 2592 if (count < 2) { 2593 return 0; 2594 } 2595 2596 /* Don't draw while we're hidden */ 2597 if (renderer->hidden) { 2598 return 0; 2599 } 2600 2601 if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) { 2602 return RenderDrawLinesWithRects(renderer, points, count); 2603 } 2604 2605 fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack); 2606 if (!fpoints) { 2607 return SDL_OutOfMemory(); 2608 } 2609 for (i = 0; i < count; ++i) { 2610 fpoints[i].x = points[i].x * renderer->scale.x; 2611 fpoints[i].y = points[i].y * renderer->scale.y; 2612 } 2613 2614 retval = QueueCmdDrawLines(renderer, fpoints, count); 2615 2616 SDL_small_free(fpoints, isstack); 2617 2618 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2619 } 2620 2621 int 2622 SDL_RenderDrawLinesF(SDL_Renderer * renderer, 2623 const SDL_FPoint * points, int count) 2624 { 2625 SDL_FPoint *fpoints; 2626 int i; 2627 int retval; 2628 SDL_bool isstack; 2629 2630 CHECK_RENDERER_MAGIC(renderer, -1); 2631 2632 if (!points) { 2633 return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points"); 2634 } 2635 if (count < 2) { 2636 return 0; 2637 } 2638 2639 /* Don't draw while we're hidden */ 2640 if (renderer->hidden) { 2641 return 0; 2642 } 2643 2644 if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) { 2645 return RenderDrawLinesWithRectsF(renderer, points, count); 2646 } 2647 2648 fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack); 2649 if (!fpoints) { 2650 return SDL_OutOfMemory(); 2651 } 2652 for (i = 0; i < count; ++i) { 2653 fpoints[i].x = points[i].x * renderer->scale.x; 2654 fpoints[i].y = points[i].y * renderer->scale.y; 2655 } 2656 2657 retval = QueueCmdDrawLines(renderer, fpoints, count); 2658 2659 SDL_small_free(fpoints, isstack); 2660 2661 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2662 } 2663 2664 int 2665 SDL_RenderDrawRect(SDL_Renderer * renderer, const SDL_Rect * rect) 2666 { 2667 SDL_FRect frect; 2668 SDL_FRect *prect = NULL; 2669 2670 if (rect) { 2671 frect.x = (float) rect->x; 2672 frect.y = (float) rect->y; 2673 frect.w = (float) rect->w; 2674 frect.h = (float) rect->h; 2675 prect = &frect; 2676 } 2677 2678 return SDL_RenderDrawRectF(renderer, prect); 2679 } 2680 2681 int 2682 SDL_RenderDrawRectF(SDL_Renderer * renderer, const SDL_FRect * rect) 2683 { 2684 SDL_FRect frect; 2685 SDL_FPoint points[5]; 2686 2687 CHECK_RENDERER_MAGIC(renderer, -1); 2688 2689 /* If 'rect' == NULL, then outline the whole surface */ 2690 if (!rect) { 2691 SDL_Rect r; 2692 SDL_RenderGetViewport(renderer, &r); 2693 frect.x = 0.0f; 2694 frect.y = 0.0f; 2695 frect.w = (float) r.w; 2696 frect.h = (float) r.h; 2697 rect = &frect; 2698 } 2699 2700 points[0].x = rect->x; 2701 points[0].y = rect->y; 2702 points[1].x = rect->x+rect->w-1; 2703 points[1].y = rect->y; 2704 points[2].x = rect->x+rect->w-1; 2705 points[2].y = rect->y+rect->h-1; 2706 points[3].x = rect->x; 2707 points[3].y = rect->y+rect->h-1; 2708 points[4].x = rect->x; 2709 points[4].y = rect->y; 2710 return SDL_RenderDrawLinesF(renderer, points, 5); 2711 } 2712 2713 int 2714 SDL_RenderDrawRects(SDL_Renderer * renderer, 2715 const SDL_Rect * rects, int count) 2716 { 2717 int i; 2718 2719 CHECK_RENDERER_MAGIC(renderer, -1); 2720 2721 if (!rects) { 2722 return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects"); 2723 } 2724 if (count < 1) { 2725 return 0; 2726 } 2727 2728 /* Don't draw while we're hidden */ 2729 if (renderer->hidden) { 2730 return 0; 2731 } 2732 2733 for (i = 0; i < count; ++i) { 2734 if (SDL_RenderDrawRect(renderer, &rects[i]) < 0) { 2735 return -1; 2736 } 2737 } 2738 return 0; 2739 } 2740 2741 int 2742 SDL_RenderDrawRectsF(SDL_Renderer * renderer, 2743 const SDL_FRect * rects, int count) 2744 { 2745 int i; 2746 2747 CHECK_RENDERER_MAGIC(renderer, -1); 2748 2749 if (!rects) { 2750 return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects"); 2751 } 2752 if (count < 1) { 2753 return 0; 2754 } 2755 2756 /* Don't draw while we're hidden */ 2757 if (renderer->hidden) { 2758 return 0; 2759 } 2760 2761 for (i = 0; i < count; ++i) { 2762 if (SDL_RenderDrawRectF(renderer, &rects[i]) < 0) { 2763 return -1; 2764 } 2765 } 2766 return 0; 2767 } 2768 2769 int 2770 SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect) 2771 { 2772 SDL_FRect frect; 2773 2774 CHECK_RENDERER_MAGIC(renderer, -1); 2775 2776 /* If 'rect' == NULL, then outline the whole surface */ 2777 if (rect) { 2778 frect.x = (float) rect->x; 2779 frect.y = (float) rect->y; 2780 frect.w = (float) rect->w; 2781 frect.h = (float) rect->h; 2782 } else { 2783 SDL_Rect r; 2784 SDL_zero(r); 2785 SDL_RenderGetViewport(renderer, &r); 2786 frect.x = 0.0f; 2787 frect.y = 0.0f; 2788 frect.w = (float) r.w; 2789 frect.h = (float) r.h; 2790 } 2791 return SDL_RenderFillRectsF(renderer, &frect, 1); 2792 } 2793 2794 int 2795 SDL_RenderFillRectF(SDL_Renderer * renderer, const SDL_FRect * rect) 2796 { 2797 SDL_FRect frect; 2798 2799 CHECK_RENDERER_MAGIC(renderer, -1); 2800 2801 /* If 'rect' == NULL, then outline the whole surface */ 2802 if (!rect) { 2803 SDL_Rect r; 2804 SDL_zero(r); 2805 SDL_RenderGetViewport(renderer, &r); 2806 frect.x = 0.0f; 2807 frect.y = 0.0f; 2808 frect.w = (float) r.w; 2809 frect.h = (float) r.h; 2810 rect = &frect; 2811 } 2812 return SDL_RenderFillRectsF(renderer, rect, 1); 2813 } 2814 2815 int 2816 SDL_RenderFillRects(SDL_Renderer * renderer, 2817 const SDL_Rect * rects, int count) 2818 { 2819 SDL_FRect *frects; 2820 int i; 2821 int retval; 2822 SDL_bool isstack; 2823 2824 CHECK_RENDERER_MAGIC(renderer, -1); 2825 2826 if (!rects) { 2827 return SDL_SetError("SDL_RenderFillRects(): Passed NULL rects"); 2828 } 2829 if (count < 1) { 2830 return 0; 2831 } 2832 2833 /* Don't draw while we're hidden */ 2834 if (renderer->hidden) { 2835 return 0; 2836 } 2837 2838 frects = SDL_small_alloc(SDL_FRect, count, &isstack); 2839 if (!frects) { 2840 return SDL_OutOfMemory(); 2841 } 2842 for (i = 0; i < count; ++i) { 2843 frects[i].x = rects[i].x * renderer->scale.x; 2844 frects[i].y = rects[i].y * renderer->scale.y; 2845 frects[i].w = rects[i].w * renderer->scale.x; 2846 frects[i].h = rects[i].h * renderer->scale.y; 2847 } 2848 2849 retval = QueueCmdFillRects(renderer, frects, count); 2850 2851 SDL_small_free(frects, isstack); 2852 2853 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2854 } 2855 2856 int 2857 SDL_RenderFillRectsF(SDL_Renderer * renderer, 2858 const SDL_FRect * rects, int count) 2859 { 2860 SDL_FRect *frects; 2861 int i; 2862 int retval; 2863 SDL_bool isstack; 2864 2865 CHECK_RENDERER_MAGIC(renderer, -1); 2866 2867 if (!rects) { 2868 return SDL_SetError("SDL_RenderFillFRects(): Passed NULL rects"); 2869 } 2870 if (count < 1) { 2871 return 0; 2872 } 2873 2874 /* Don't draw while we're hidden */ 2875 if (renderer->hidden) { 2876 return 0; 2877 } 2878 2879 frects = SDL_small_alloc(SDL_FRect, count, &isstack); 2880 if (!frects) { 2881 return SDL_OutOfMemory(); 2882 } 2883 for (i = 0; i < count; ++i) { 2884 frects[i].x = rects[i].x * renderer->scale.x; 2885 frects[i].y = rects[i].y * renderer->scale.y; 2886 frects[i].w = rects[i].w * renderer->scale.x; 2887 frects[i].h = rects[i].h * renderer->scale.y; 2888 } 2889 2890 retval = QueueCmdFillRects(renderer, frects, count); 2891 2892 SDL_small_free(frects, isstack); 2893 2894 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 2895 } 2896 2897 /* !!! FIXME: move this to a public API if we want to do float versions of all of these later */ 2898 SDL_FORCE_INLINE SDL_bool SDL_FRectEmpty(const SDL_FRect *r) 2899 { 2900 return ((!r) || (r->w <= 0.0f) || (r->h <= 0.0f)) ? SDL_TRUE : SDL_FALSE; 2901 } 2902 2903 /* !!! FIXME: move this to a public API if we want to do float versions of all of these later */ 2904 static SDL_bool 2905 SDL_HasIntersectionF(const SDL_FRect * A, const SDL_FRect * B) 2906 { 2907 float Amin, Amax, Bmin, Bmax; 2908 2909 if (!A) { 2910 SDL_InvalidParamError("A"); 2911 return SDL_FALSE; 2912 } 2913 2914 if (!B) { 2915 SDL_InvalidParamError("B"); 2916 return SDL_FALSE; 2917 } 2918 2919 /* Special cases for empty rects */ 2920 if (SDL_FRectEmpty(A) || SDL_FRectEmpty(B)) { 2921 return SDL_FALSE; 2922 } 2923 2924 /* Horizontal intersection */ 2925 Amin = A->x; 2926 Amax = Amin + A->w; 2927 Bmin = B->x; 2928 Bmax = Bmin + B->w; 2929 if (Bmin > Amin) 2930 Amin = Bmin; 2931 if (Bmax < Amax) 2932 Amax = Bmax; 2933 if (Amax <= Amin) 2934 return SDL_FALSE; 2935 2936 /* Vertical intersection */ 2937 Amin = A->y; 2938 Amax = Amin + A->h; 2939 Bmin = B->y; 2940 Bmax = Bmin + B->h; 2941 if (Bmin > Amin) 2942 Amin = Bmin; 2943 if (Bmax < Amax) 2944 Amax = Bmax; 2945 if (Amax <= Amin) 2946 return SDL_FALSE; 2947 2948 return SDL_TRUE; 2949 } 2950 2951 int 2952 SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, 2953 const SDL_Rect * srcrect, const SDL_Rect * dstrect) 2954 { 2955 SDL_FRect dstfrect; 2956 SDL_FRect *pdstfrect = NULL; 2957 if (dstrect) { 2958 dstfrect.x = (float) dstrect->x; 2959 dstfrect.y = (float) dstrect->y; 2960 dstfrect.w = (float) dstrect->w; 2961 dstfrect.h = (float) dstrect->h; 2962 pdstfrect = &dstfrect; 2963 } 2964 return SDL_RenderCopyF(renderer, texture, srcrect, pdstfrect); 2965 } 2966 2967 int 2968 SDL_RenderCopyF(SDL_Renderer * renderer, SDL_Texture * texture, 2969 const SDL_Rect * srcrect, const SDL_FRect * dstrect) 2970 { 2971 SDL_Rect real_srcrect; 2972 SDL_FRect real_dstrect; 2973 SDL_Rect r; 2974 int retval; 2975 2976 CHECK_RENDERER_MAGIC(renderer, -1); 2977 CHECK_TEXTURE_MAGIC(texture, -1); 2978 2979 if (renderer != texture->renderer) { 2980 return SDL_SetError("Texture was not created with this renderer"); 2981 } 2982 2983 /* Don't draw while we're hidden */ 2984 if (renderer->hidden) { 2985 return 0; 2986 } 2987 2988 real_srcrect.x = 0; 2989 real_srcrect.y = 0; 2990 real_srcrect.w = texture->w; 2991 real_srcrect.h = texture->h; 2992 if (srcrect) { 2993 if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) { 2994 return 0; 2995 } 2996 } 2997 2998 SDL_zero(r); 2999 SDL_RenderGetViewport(renderer, &r); 3000 real_dstrect.x = 0.0f; 3001 real_dstrect.y = 0.0f; 3002 real_dstrect.w = (float) r.w; 3003 real_dstrect.h = (float) r.h; 3004 if (dstrect) { 3005 if (!SDL_HasIntersectionF(dstrect, &real_dstrect)) { 3006 return 0; 3007 } 3008 real_dstrect = *dstrect; 3009 } 3010 3011 if (texture->native) { 3012 texture = texture->native; 3013 } 3014 3015 real_dstrect.x *= renderer->scale.x; 3016 real_dstrect.y *= renderer->scale.y; 3017 real_dstrect.w *= renderer->scale.x; 3018 real_dstrect.h *= renderer->scale.y; 3019 3020 texture->last_command_generation = renderer->render_command_generation; 3021 3022 retval = QueueCmdCopy(renderer, texture, &real_srcrect, &real_dstrect); 3023 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 3024 } 3025 3026 int 3027 SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, 3028 const SDL_Rect * srcrect, const SDL_Rect * dstrect, 3029 const double angle, const SDL_Point *center, const SDL_RendererFlip flip) 3030 { 3031 SDL_FRect dstfrect; 3032 SDL_FRect *pdstfrect = NULL; 3033 SDL_FPoint fcenter; 3034 SDL_FPoint *pfcenter = NULL; 3035 3036 if (dstrect) { 3037 dstfrect.x = (float) dstrect->x; 3038 dstfrect.y = (float) dstrect->y; 3039 dstfrect.w = (float) dstrect->w; 3040 dstfrect.h = (float) dstrect->h; 3041 pdstfrect = &dstfrect; 3042 } 3043 3044 if (center) { 3045 fcenter.x = (float) center->x; 3046 fcenter.y = (float) center->y; 3047 pfcenter = &fcenter; 3048 } 3049 3050 return SDL_RenderCopyExF(renderer, texture, srcrect, pdstfrect, angle, pfcenter, flip); 3051 } 3052 3053 int 3054 SDL_RenderCopyExF(SDL_Renderer * renderer, SDL_Texture * texture, 3055 const SDL_Rect * srcrect, const SDL_FRect * dstrect, 3056 const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) 3057 { 3058 SDL_Rect real_srcrect; 3059 SDL_FRect real_dstrect; 3060 SDL_FPoint real_center; 3061 int retval; 3062 3063 if (flip == SDL_FLIP_NONE && (int)(angle/360) == angle/360) { /* fast path when we don't need rotation or flipping */ 3064 return SDL_RenderCopyF(renderer, texture, srcrect, dstrect); 3065 } 3066 3067 CHECK_RENDERER_MAGIC(renderer, -1); 3068 CHECK_TEXTURE_MAGIC(texture, -1); 3069 3070 if (renderer != texture->renderer) { 3071 return SDL_SetError("Texture was not created with this renderer"); 3072 } 3073 if (!renderer->QueueCopyEx) { 3074 return SDL_SetError("Renderer does not support RenderCopyEx"); 3075 } 3076 3077 /* Don't draw while we're hidden */ 3078 if (renderer->hidden) { 3079 return 0; 3080 } 3081 3082 real_srcrect.x = 0; 3083 real_srcrect.y = 0; 3084 real_srcrect.w = texture->w; 3085 real_srcrect.h = texture->h; 3086 if (srcrect) { 3087 if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) { 3088 return 0; 3089 } 3090 } 3091 3092 /* We don't intersect the dstrect with the viewport as RenderCopy does because of potential rotation clipping issues... TODO: should we? */ 3093 if (dstrect) { 3094 real_dstrect = *dstrect; 3095 } else { 3096 SDL_Rect r; 3097 SDL_zero(r); 3098 SDL_RenderGetViewport(renderer, &r); 3099 real_dstrect.x = 0.0f; 3100 real_dstrect.y = 0.0f; 3101 real_dstrect.w = (float) r.w; 3102 real_dstrect.h = (float) r.h; 3103 } 3104 3105 if (texture->native) { 3106 texture = texture->native; 3107 } 3108 3109 if (center) { 3110 real_center = *center; 3111 } else { 3112 real_center.x = real_dstrect.w / 2.0f; 3113 real_center.y = real_dstrect.h / 2.0f; 3114 } 3115 3116 real_dstrect.x *= renderer->scale.x; 3117 real_dstrect.y *= renderer->scale.y; 3118 real_dstrect.w *= renderer->scale.x; 3119 real_dstrect.h *= renderer->scale.y; 3120 3121 real_center.x *= renderer->scale.x; 3122 real_center.y *= renderer->scale.y; 3123 3124 texture->last_command_generation = renderer->render_command_generation; 3125 3126 retval = QueueCmdCopyEx(renderer, texture, &real_srcrect, &real_dstrect, angle, &real_center, flip); 3127 return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); 3128 } 3129 3130 int 3131 SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, 3132 Uint32 format, void * pixels, int pitch) 3133 { 3134 SDL_Rect real_rect; 3135 3136 CHECK_RENDERER_MAGIC(renderer, -1); 3137 3138 if (!renderer->RenderReadPixels) { 3139 return SDL_Unsupported(); 3140 } 3141 3142 FlushRenderCommands(renderer); /* we need to render before we read the results. */ 3143 3144 if (!format) { 3145 format = SDL_GetWindowPixelFormat(renderer->window); 3146 } 3147 3148 real_rect.x = renderer->viewport.x; 3149 real_rect.y = renderer->viewport.y; 3150 real_rect.w = renderer->viewport.w; 3151 real_rect.h = renderer->viewport.h; 3152 if (rect) { 3153 if (!SDL_IntersectRect(rect, &real_rect, &real_rect)) { 3154 return 0; 3155 } 3156 if (real_rect.y > rect->y) { 3157 pixels = (Uint8 *)pixels + pitch * (real_rect.y - rect->y); 3158 } 3159 if (real_rect.x > rect->x) { 3160 int bpp = SDL_BYTESPERPIXEL(format); 3161 pixels = (Uint8 *)pixels + bpp * (real_rect.x - rect->x); 3162 } 3163 } 3164 3165 return renderer->RenderReadPixels(renderer, &real_rect, 3166 format, pixels, pitch); 3167 } 3168 3169 void 3170 SDL_RenderPresent(SDL_Renderer * renderer) 3171 { 3172 CHECK_RENDERER_MAGIC(renderer, ); 3173 3174 FlushRenderCommands(renderer); /* time to send everything to the GPU! */ 3175 3176 /* Don't present while we're hidden */ 3177 if (renderer->hidden) { 3178 return; 3179 } 3180 renderer->RenderPresent(renderer); 3181 } 3182 3183 void 3184 SDL_DestroyTexture(SDL_Texture * texture) 3185 { 3186 SDL_Renderer *renderer; 3187 3188 CHECK_TEXTURE_MAGIC(texture, ); 3189 3190 renderer = texture->renderer; 3191 if (texture == renderer->target) { 3192 SDL_SetRenderTarget(renderer, NULL); /* implies command queue flush */ 3193 } else { 3194 FlushRenderCommandsIfTextureNeeded(texture); 3195 } 3196 3197 texture->magic = NULL; 3198 3199 if (texture->next) { 3200 texture->next->prev = texture->prev; 3201 } 3202 if (texture->prev) { 3203 texture->prev->next = texture->next; 3204 } else { 3205 renderer->textures = texture->next; 3206 } 3207 3208 if (texture->native) { 3209 SDL_DestroyTexture(texture->native); 3210 } 3211 #if SDL_HAVE_YUV 3212 if (texture->yuv) { 3213 SDL_SW_DestroyYUVTexture(texture->yuv); 3214 } 3215 #endif 3216 SDL_free(texture->pixels); 3217 3218 renderer->DestroyTexture(renderer, texture); 3219 3220 SDL_FreeSurface(texture->locked_surface); 3221 texture->locked_surface = NULL; 3222 3223 SDL_free(texture); 3224 } 3225 3226 void 3227 SDL_DestroyRenderer(SDL_Renderer * renderer) 3228 { 3229 SDL_RenderCommand *cmd; 3230 3231 CHECK_RENDERER_MAGIC(renderer, ); 3232 3233 SDL_DelEventWatch(SDL_RendererEventWatch, renderer); 3234 3235 if (renderer->render_commands_tail != NULL) { 3236 renderer->render_commands_tail->next = renderer->render_commands_pool; 3237 cmd = renderer->render_commands; 3238 } else { 3239 cmd = renderer->render_commands_pool; 3240 } 3241 3242 renderer->render_commands_pool = NULL; 3243 renderer->render_commands_tail = NULL; 3244 renderer->render_commands = NULL; 3245 3246 while (cmd != NULL) { 3247 SDL_RenderCommand *next = cmd->next; 3248 SDL_free(cmd); 3249 cmd = next; 3250 } 3251 3252 SDL_free(renderer->vertex_data); 3253 3254 /* Free existing textures for this renderer */ 3255 while (renderer->textures) { 3256 SDL_Texture *tex = renderer->textures; (void) tex; 3257 SDL_DestroyTexture(renderer->textures); 3258 SDL_assert(tex != renderer->textures); /* satisfy static analysis. */ 3259 } 3260 3261 if (renderer->window) { 3262 SDL_SetWindowData(renderer->window, SDL_WINDOWRENDERDATA, NULL); 3263 } 3264 3265 /* It's no longer magical... */ 3266 renderer->magic = NULL; 3267 3268 /* Free the target mutex */ 3269 SDL_DestroyMutex(renderer->target_mutex); 3270 renderer->target_mutex = NULL; 3271 3272 /* Free the renderer instance */ 3273 renderer->DestroyRenderer(renderer); 3274 } 3275 3276 int SDL_GL_BindTexture(SDL_Texture *texture, float *texw, float *texh) 3277 { 3278 SDL_Renderer *renderer; 3279 3280 CHECK_TEXTURE_MAGIC(texture, -1); 3281 renderer = texture->renderer; 3282 if (texture->native) { 3283 return SDL_GL_BindTexture(texture->native, texw, texh); 3284 } else if (renderer && renderer->GL_BindTexture) { 3285 FlushRenderCommandsIfTextureNeeded(texture); /* in case the app is going to mess with it. */ 3286 return renderer->GL_BindTexture(renderer, texture, texw, texh); 3287 } else { 3288 return SDL_Unsupported(); 3289 } 3290 } 3291 3292 int SDL_GL_UnbindTexture(SDL_Texture *texture) 3293 { 3294 SDL_Renderer *renderer; 3295 3296 CHECK_TEXTURE_MAGIC(texture, -1); 3297 renderer = texture->renderer; 3298 if (texture->native) { 3299 return SDL_GL_UnbindTexture(texture->native); 3300 } else if (renderer && renderer->GL_UnbindTexture) { 3301 FlushRenderCommandsIfTextureNeeded(texture); /* in case the app messed with it. */ 3302 return renderer->GL_UnbindTexture(renderer, texture); 3303 } 3304 3305 return SDL_Unsupported(); 3306 } 3307 3308 void * 3309 SDL_RenderGetMetalLayer(SDL_Renderer * renderer) 3310 { 3311 CHECK_RENDERER_MAGIC(renderer, NULL); 3312 3313 if (renderer->GetMetalLayer) { 3314 FlushRenderCommands(renderer); /* in case the app is going to mess with it. */ 3315 return renderer->GetMetalLayer(renderer); 3316 } 3317 return NULL; 3318 } 3319 3320 void * 3321 SDL_RenderGetMetalCommandEncoder(SDL_Renderer * renderer) 3322 { 3323 CHECK_RENDERER_MAGIC(renderer, NULL); 3324 3325 if (renderer->GetMetalCommandEncoder) { 3326 FlushRenderCommands(renderer); /* in case the app is going to mess with it. */ 3327 return renderer->GetMetalCommandEncoder(renderer); 3328 } 3329 return NULL; 3330 } 3331 3332 static SDL_BlendMode 3333 SDL_GetShortBlendMode(SDL_BlendMode blendMode) 3334 { 3335 if (blendMode == SDL_BLENDMODE_NONE_FULL) { 3336 return SDL_BLENDMODE_NONE; 3337 } 3338 if (blendMode == SDL_BLENDMODE_BLEND_FULL) { 3339 return SDL_BLENDMODE_BLEND; 3340 } 3341 if (blendMode == SDL_BLENDMODE_ADD_FULL) { 3342 return SDL_BLENDMODE_ADD; 3343 } 3344 if (blendMode == SDL_BLENDMODE_MOD_FULL) { 3345 return SDL_BLENDMODE_MOD; 3346 } 3347 if (blendMode == SDL_BLENDMODE_MUL_FULL) { 3348 return SDL_BLENDMODE_MUL; 3349 } 3350 return blendMode; 3351 } 3352 3353 static SDL_BlendMode 3354 SDL_GetLongBlendMode(SDL_BlendMode blendMode) 3355 { 3356 if (blendMode == SDL_BLENDMODE_NONE) { 3357 return SDL_BLENDMODE_NONE_FULL; 3358 } 3359 if (blendMode == SDL_BLENDMODE_BLEND) { 3360 return SDL_BLENDMODE_BLEND_FULL; 3361 } 3362 if (blendMode == SDL_BLENDMODE_ADD) { 3363 return SDL_BLENDMODE_ADD_FULL; 3364 } 3365 if (blendMode == SDL_BLENDMODE_MOD) { 3366 return SDL_BLENDMODE_MOD_FULL; 3367 } 3368 if (blendMode == SDL_BLENDMODE_MUL) { 3369 return SDL_BLENDMODE_MUL_FULL; 3370 } 3371 return blendMode; 3372 } 3373 3374 SDL_BlendMode 3375 SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor, 3376 SDL_BlendOperation colorOperation, 3377 SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor, 3378 SDL_BlendOperation alphaOperation) 3379 { 3380 SDL_BlendMode blendMode = SDL_COMPOSE_BLENDMODE(srcColorFactor, dstColorFactor, colorOperation, 3381 srcAlphaFactor, dstAlphaFactor, alphaOperation); 3382 return SDL_GetShortBlendMode(blendMode); 3383 } 3384 3385 SDL_BlendFactor 3386 SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode) 3387 { 3388 blendMode = SDL_GetLongBlendMode(blendMode); 3389 return (SDL_BlendFactor)(((Uint32)blendMode >> 4) & 0xF); 3390 } 3391 3392 SDL_BlendFactor 3393 SDL_GetBlendModeDstColorFactor(SDL_BlendMode blendMode) 3394 { 3395 blendMode = SDL_GetLongBlendMode(blendMode); 3396 return (SDL_BlendFactor)(((Uint32)blendMode >> 8) & 0xF); 3397 } 3398 3399 SDL_BlendOperation 3400 SDL_GetBlendModeColorOperation(SDL_BlendMode blendMode) 3401 { 3402 blendMode = SDL_GetLongBlendMode(blendMode); 3403 return (SDL_BlendOperation)(((Uint32)blendMode >> 0) & 0xF); 3404 } 3405 3406 SDL_BlendFactor 3407 SDL_GetBlendModeSrcAlphaFactor(SDL_BlendMode blendMode) 3408 { 3409 blendMode = SDL_GetLongBlendMode(blendMode); 3410 return (SDL_BlendFactor)(((Uint32)blendMode >> 20) & 0xF); 3411 } 3412 3413 SDL_BlendFactor 3414 SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode) 3415 { 3416 blendMode = SDL_GetLongBlendMode(blendMode); 3417 return (SDL_BlendFactor)(((Uint32)blendMode >> 24) & 0xF); 3418 } 3419 3420 SDL_BlendOperation 3421 SDL_GetBlendModeAlphaOperation(SDL_BlendMode blendMode) 3422 { 3423 blendMode = SDL_GetLongBlendMode(blendMode); 3424 return (SDL_BlendOperation)(((Uint32)blendMode >> 16) & 0xF); 3425 } 3426 3427 /* vi: set ts=4 sw=4 expandtab: */