Simple Directmedia Layer
at main 1202 lines 44 kB view raw
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_VIDEO_RENDER_SW 24 25#include "../SDL_sysrender.h" 26#include "SDL_render_sw_c.h" 27 28#include "SDL_draw.h" 29#include "SDL_blendfillrect.h" 30#include "SDL_blendline.h" 31#include "SDL_blendpoint.h" 32#include "SDL_drawline.h" 33#include "SDL_drawpoint.h" 34#include "SDL_rotate.h" 35#include "SDL_triangle.h" 36#include "../../video/SDL_pixels_c.h" 37 38// SDL surface based renderer implementation 39 40typedef struct 41{ 42 const SDL_Rect *viewport; 43 const SDL_Rect *cliprect; 44 bool surface_cliprect_dirty; 45 SDL_Color color; 46} SW_DrawStateCache; 47 48typedef struct 49{ 50 SDL_Surface *surface; 51 SDL_Surface *window; 52} SW_RenderData; 53 54static SDL_Surface *SW_ActivateRenderer(SDL_Renderer *renderer) 55{ 56 SW_RenderData *data = (SW_RenderData *)renderer->internal; 57 58 if (!data->surface) { 59 data->surface = data->window; 60 } 61 if (!data->surface) { 62 SDL_Surface *surface = SDL_GetWindowSurface(renderer->window); 63 if (surface) { 64 data->surface = data->window = surface; 65 } 66 } 67 return data->surface; 68} 69 70static void SW_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event) 71{ 72 SW_RenderData *data = (SW_RenderData *)renderer->internal; 73 74 if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) { 75 data->surface = NULL; 76 data->window = NULL; 77 } 78} 79 80static bool SW_GetOutputSize(SDL_Renderer *renderer, int *w, int *h) 81{ 82 SW_RenderData *data = (SW_RenderData *)renderer->internal; 83 84 if (data->surface) { 85 if (w) { 86 *w = data->surface->w; 87 } 88 if (h) { 89 *h = data->surface->h; 90 } 91 return true; 92 } 93 94 if (renderer->window) { 95 SDL_GetWindowSizeInPixels(renderer->window, w, h); 96 return true; 97 } 98 99 return SDL_SetError("Software renderer doesn't have an output surface"); 100} 101 102static bool SW_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) 103{ 104 SDL_Surface *surface = SDL_CreateSurface(texture->w, texture->h, texture->format); 105 Uint8 r, g, b, a; 106 107 if (!SDL_SurfaceValid(surface)) { 108 return SDL_SetError("Cannot create surface"); 109 } 110 texture->internal = surface; 111 r = (Uint8)SDL_roundf(SDL_clamp(texture->color.r, 0.0f, 1.0f) * 255.0f); 112 g = (Uint8)SDL_roundf(SDL_clamp(texture->color.g, 0.0f, 1.0f) * 255.0f); 113 b = (Uint8)SDL_roundf(SDL_clamp(texture->color.b, 0.0f, 1.0f) * 255.0f); 114 a = (Uint8)SDL_roundf(SDL_clamp(texture->color.a, 0.0f, 1.0f) * 255.0f); 115 SDL_SetSurfaceColorMod(surface, r, g, b); 116 SDL_SetSurfaceAlphaMod(surface, a); 117 SDL_SetSurfaceBlendMode(surface, texture->blendMode); 118 119 /* Only RLE encode textures without an alpha channel since the RLE coder 120 * discards the color values of pixels with an alpha value of zero. 121 */ 122 if (texture->access == SDL_TEXTUREACCESS_STATIC && !SDL_ISPIXELFORMAT_ALPHA(surface->format)) { 123 SDL_SetSurfaceRLE(surface, 1); 124 } 125 126 return true; 127} 128 129static bool SW_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, 130 const SDL_Rect *rect, const void *pixels, int pitch) 131{ 132 SDL_Surface *surface = (SDL_Surface *)texture->internal; 133 Uint8 *src, *dst; 134 int row; 135 size_t length; 136 137 if (SDL_MUSTLOCK(surface)) { 138 if (!SDL_LockSurface(surface)) { 139 return false; 140 } 141 } 142 src = (Uint8 *)pixels; 143 dst = (Uint8 *)surface->pixels + 144 rect->y * surface->pitch + 145 rect->x * surface->fmt->bytes_per_pixel; 146 length = (size_t)rect->w * surface->fmt->bytes_per_pixel; 147 for (row = 0; row < rect->h; ++row) { 148 SDL_memcpy(dst, src, length); 149 src += pitch; 150 dst += surface->pitch; 151 } 152 if (SDL_MUSTLOCK(surface)) { 153 SDL_UnlockSurface(surface); 154 } 155 return true; 156} 157 158static bool SW_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, 159 const SDL_Rect *rect, void **pixels, int *pitch) 160{ 161 SDL_Surface *surface = (SDL_Surface *)texture->internal; 162 163 *pixels = 164 (void *)((Uint8 *)surface->pixels + rect->y * surface->pitch + 165 rect->x * surface->fmt->bytes_per_pixel); 166 *pitch = surface->pitch; 167 return true; 168} 169 170static void SW_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) 171{ 172} 173 174static void SW_SetTextureScaleMode(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ScaleMode scaleMode) 175{ 176} 177 178static bool SW_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) 179{ 180 SW_RenderData *data = (SW_RenderData *)renderer->internal; 181 182 if (texture) { 183 data->surface = (SDL_Surface *)texture->internal; 184 } else { 185 data->surface = data->window; 186 } 187 return true; 188} 189 190static bool SW_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd) 191{ 192 return true; // nothing to do in this backend. 193} 194 195static bool SW_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count) 196{ 197 SDL_Point *verts = (SDL_Point *)SDL_AllocateRenderVertices(renderer, count * sizeof(SDL_Point), 0, &cmd->data.draw.first); 198 int i; 199 200 if (!verts) { 201 return false; 202 } 203 204 cmd->data.draw.count = count; 205 206 for (i = 0; i < count; i++, verts++, points++) { 207 verts->x = (int)points->x; 208 verts->y = (int)points->y; 209 } 210 211 return true; 212} 213 214static bool SW_QueueFillRects(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FRect *rects, int count) 215{ 216 SDL_Rect *verts = (SDL_Rect *)SDL_AllocateRenderVertices(renderer, count * sizeof(SDL_Rect), 0, &cmd->data.draw.first); 217 int i; 218 219 if (!verts) { 220 return false; 221 } 222 223 cmd->data.draw.count = count; 224 225 for (i = 0; i < count; i++, verts++, rects++) { 226 verts->x = (int)rects->x; 227 verts->y = (int)rects->y; 228 verts->w = SDL_max((int)rects->w, 1); 229 verts->h = SDL_max((int)rects->h, 1); 230 } 231 232 return true; 233} 234 235static bool SW_QueueCopy(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, 236 const SDL_FRect *srcrect, const SDL_FRect *dstrect) 237{ 238 SDL_Rect *verts = (SDL_Rect *)SDL_AllocateRenderVertices(renderer, 2 * sizeof(SDL_Rect), 0, &cmd->data.draw.first); 239 240 if (!verts) { 241 return false; 242 } 243 244 cmd->data.draw.count = 1; 245 246 verts->x = (int)srcrect->x; 247 verts->y = (int)srcrect->y; 248 verts->w = (int)srcrect->w; 249 verts->h = (int)srcrect->h; 250 verts++; 251 252 verts->x = (int)dstrect->x; 253 verts->y = (int)dstrect->y; 254 verts->w = (int)dstrect->w; 255 verts->h = (int)dstrect->h; 256 257 return true; 258} 259 260typedef struct CopyExData 261{ 262 SDL_Rect srcrect; 263 SDL_Rect dstrect; 264 double angle; 265 SDL_FPoint center; 266 SDL_FlipMode flip; 267 float scale_x; 268 float scale_y; 269} CopyExData; 270 271static bool SW_QueueCopyEx(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, 272 const SDL_FRect *srcrect, const SDL_FRect *dstrect, 273 const double angle, const SDL_FPoint *center, const SDL_FlipMode flip, float scale_x, float scale_y) 274{ 275 CopyExData *verts = (CopyExData *)SDL_AllocateRenderVertices(renderer, sizeof(CopyExData), 0, &cmd->data.draw.first); 276 277 if (!verts) { 278 return false; 279 } 280 281 cmd->data.draw.count = 1; 282 283 verts->srcrect.x = (int)srcrect->x; 284 verts->srcrect.y = (int)srcrect->y; 285 verts->srcrect.w = (int)srcrect->w; 286 verts->srcrect.h = (int)srcrect->h; 287 verts->dstrect.x = (int)dstrect->x; 288 verts->dstrect.y = (int)dstrect->y; 289 verts->dstrect.w = (int)dstrect->w; 290 verts->dstrect.h = (int)dstrect->h; 291 verts->angle = angle; 292 SDL_copyp(&verts->center, center); 293 verts->flip = flip; 294 verts->scale_x = scale_x; 295 verts->scale_y = scale_y; 296 297 return true; 298} 299 300static bool Blit_to_Screen(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *surface, SDL_Rect *dstrect, 301 float scale_x, float scale_y, SDL_ScaleMode scaleMode) 302{ 303 bool result; 304 // Renderer scaling, if needed 305 if (scale_x != 1.0f || scale_y != 1.0f) { 306 SDL_Rect r; 307 r.x = (int)((float)dstrect->x * scale_x); 308 r.y = (int)((float)dstrect->y * scale_y); 309 r.w = (int)((float)dstrect->w * scale_x); 310 r.h = (int)((float)dstrect->h * scale_y); 311 result = SDL_BlitSurfaceScaled(src, srcrect, surface, &r, scaleMode); 312 } else { 313 result = SDL_BlitSurface(src, srcrect, surface, dstrect); 314 } 315 return result; 316} 317 318static bool SW_RenderCopyEx(SDL_Renderer *renderer, SDL_Surface *surface, SDL_Texture *texture, 319 const SDL_Rect *srcrect, const SDL_Rect *final_rect, 320 const double angle, const SDL_FPoint *center, const SDL_FlipMode flip, float scale_x, float scale_y) 321{ 322 SDL_Surface *src = (SDL_Surface *)texture->internal; 323 SDL_Rect tmp_rect; 324 SDL_Surface *src_clone, *src_rotated, *src_scaled; 325 SDL_Surface *mask = NULL, *mask_rotated = NULL; 326 bool result = true; 327 SDL_BlendMode blendmode; 328 Uint8 alphaMod, rMod, gMod, bMod; 329 int applyModulation = false; 330 int blitRequired = false; 331 int isOpaque = false; 332 333 if (!SDL_SurfaceValid(surface)) { 334 return false; 335 } 336 337 tmp_rect.x = 0; 338 tmp_rect.y = 0; 339 tmp_rect.w = final_rect->w; 340 tmp_rect.h = final_rect->h; 341 342 /* It is possible to encounter an RLE encoded surface here and locking it is 343 * necessary because this code is going to access the pixel buffer directly. 344 */ 345 if (SDL_MUSTLOCK(src)) { 346 if (!SDL_LockSurface(src)) { 347 return false; 348 } 349 } 350 351 /* Clone the source surface but use its pixel buffer directly. 352 * The original source surface must be treated as read-only. 353 */ 354 src_clone = SDL_CreateSurfaceFrom(src->w, src->h, src->format, src->pixels, src->pitch); 355 if (!src_clone) { 356 if (SDL_MUSTLOCK(src)) { 357 SDL_UnlockSurface(src); 358 } 359 return false; 360 } 361 362 SDL_GetSurfaceBlendMode(src, &blendmode); 363 SDL_GetSurfaceAlphaMod(src, &alphaMod); 364 SDL_GetSurfaceColorMod(src, &rMod, &gMod, &bMod); 365 366 // SDLgfx_rotateSurface only accepts 32-bit surfaces with a 8888 layout. Everything else has to be converted. 367 if (src->fmt->bits_per_pixel != 32 || SDL_PIXELLAYOUT(src->format) != SDL_PACKEDLAYOUT_8888 || !SDL_ISPIXELFORMAT_ALPHA(src->format)) { 368 blitRequired = true; 369 } 370 371 // If scaling and cropping is necessary, it has to be taken care of before the rotation. 372 if (!(srcrect->w == final_rect->w && srcrect->h == final_rect->h && srcrect->x == 0 && srcrect->y == 0)) { 373 blitRequired = true; 374 } 375 376 // srcrect is not selecting the whole src surface, so cropping is needed 377 if (!(srcrect->w == src->w && srcrect->h == src->h && srcrect->x == 0 && srcrect->y == 0)) { 378 blitRequired = true; 379 } 380 381 // The color and alpha modulation has to be applied before the rotation when using the NONE, MOD or MUL blend modes. 382 if ((blendmode == SDL_BLENDMODE_NONE || blendmode == SDL_BLENDMODE_MOD || blendmode == SDL_BLENDMODE_MUL) && (alphaMod & rMod & gMod & bMod) != 255) { 383 applyModulation = true; 384 SDL_SetSurfaceAlphaMod(src_clone, alphaMod); 385 SDL_SetSurfaceColorMod(src_clone, rMod, gMod, bMod); 386 } 387 388 // Opaque surfaces are much easier to handle with the NONE blend mode. 389 if (blendmode == SDL_BLENDMODE_NONE && !SDL_ISPIXELFORMAT_ALPHA(src->format) && alphaMod == 255) { 390 isOpaque = true; 391 } 392 393 /* The NONE blend mode requires a mask for non-opaque surfaces. This mask will be used 394 * to clear the pixels in the destination surface. The other steps are explained below. 395 */ 396 if (blendmode == SDL_BLENDMODE_NONE && !isOpaque) { 397 mask = SDL_CreateSurface(final_rect->w, final_rect->h, SDL_PIXELFORMAT_ARGB8888); 398 if (!mask) { 399 result = false; 400 } else { 401 SDL_SetSurfaceBlendMode(mask, SDL_BLENDMODE_MOD); 402 } 403 } 404 405 /* Create a new surface should there be a format mismatch or if scaling, cropping, 406 * or modulation is required. It's possible to use the source surface directly otherwise. 407 */ 408 if (result && (blitRequired || applyModulation)) { 409 SDL_Rect scale_rect = tmp_rect; 410 src_scaled = SDL_CreateSurface(final_rect->w, final_rect->h, SDL_PIXELFORMAT_ARGB8888); 411 if (!src_scaled) { 412 result = false; 413 } else { 414 SDL_SetSurfaceBlendMode(src_clone, SDL_BLENDMODE_NONE); 415 result = SDL_BlitSurfaceScaled(src_clone, srcrect, src_scaled, &scale_rect, texture->scaleMode); 416 SDL_DestroySurface(src_clone); 417 src_clone = src_scaled; 418 src_scaled = NULL; 419 } 420 } 421 422 // SDLgfx_rotateSurface is going to make decisions depending on the blend mode. 423 SDL_SetSurfaceBlendMode(src_clone, blendmode); 424 425 if (result) { 426 SDL_Rect rect_dest; 427 double cangle, sangle; 428 429 SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, center, 430 &rect_dest, &cangle, &sangle); 431 src_rotated = SDLgfx_rotateSurface(src_clone, angle, 432 (texture->scaleMode == SDL_SCALEMODE_NEAREST) ? 0 : 1, flip & SDL_FLIP_HORIZONTAL, flip & SDL_FLIP_VERTICAL, 433 &rect_dest, cangle, sangle, center); 434 if (!src_rotated) { 435 result = false; 436 } 437 if (result && mask) { 438 // The mask needed for the NONE blend mode gets rotated with the same parameters. 439 mask_rotated = SDLgfx_rotateSurface(mask, angle, 440 false, 0, 0, 441 &rect_dest, cangle, sangle, center); 442 if (!mask_rotated) { 443 result = false; 444 } 445 } 446 if (result) { 447 448 tmp_rect.x = final_rect->x + rect_dest.x; 449 tmp_rect.y = final_rect->y + rect_dest.y; 450 tmp_rect.w = rect_dest.w; 451 tmp_rect.h = rect_dest.h; 452 453 /* The NONE blend mode needs some special care with non-opaque surfaces. 454 * Other blend modes or opaque surfaces can be blitted directly. 455 */ 456 if (blendmode != SDL_BLENDMODE_NONE || isOpaque) { 457 if (applyModulation == false) { 458 // If the modulation wasn't already applied, make it happen now. 459 SDL_SetSurfaceAlphaMod(src_rotated, alphaMod); 460 SDL_SetSurfaceColorMod(src_rotated, rMod, gMod, bMod); 461 } 462 // Renderer scaling, if needed 463 result = Blit_to_Screen(src_rotated, NULL, surface, &tmp_rect, scale_x, scale_y, texture->scaleMode); 464 } else { 465 /* The NONE blend mode requires three steps to get the pixels onto the destination surface. 466 * First, the area where the rotated pixels will be blitted to get set to zero. 467 * This is accomplished by simply blitting a mask with the NONE blend mode. 468 * The colorkey set by the rotate function will discard the correct pixels. 469 */ 470 SDL_Rect mask_rect = tmp_rect; 471 SDL_SetSurfaceBlendMode(mask_rotated, SDL_BLENDMODE_NONE); 472 // Renderer scaling, if needed 473 result = Blit_to_Screen(mask_rotated, NULL, surface, &mask_rect, scale_x, scale_y, texture->scaleMode); 474 if (result) { 475 /* The next step copies the alpha value. This is done with the BLEND blend mode and 476 * by modulating the source colors with 0. Since the destination is all zeros, this 477 * will effectively set the destination alpha to the source alpha. 478 */ 479 SDL_SetSurfaceColorMod(src_rotated, 0, 0, 0); 480 mask_rect = tmp_rect; 481 // Renderer scaling, if needed 482 result = Blit_to_Screen(src_rotated, NULL, surface, &mask_rect, scale_x, scale_y, texture->scaleMode); 483 if (result) { 484 /* The last step gets the color values in place. The ADD blend mode simply adds them to 485 * the destination (where the color values are all zero). However, because the ADD blend 486 * mode modulates the colors with the alpha channel, a surface without an alpha mask needs 487 * to be created. This makes all source pixels opaque and the colors get copied correctly. 488 */ 489 SDL_Surface *src_rotated_rgb = SDL_CreateSurfaceFrom(src_rotated->w, src_rotated->h, src_rotated->format, src_rotated->pixels, src_rotated->pitch); 490 if (!src_rotated_rgb) { 491 result = false; 492 } else { 493 SDL_SetSurfaceBlendMode(src_rotated_rgb, SDL_BLENDMODE_ADD); 494 // Renderer scaling, if needed 495 result = Blit_to_Screen(src_rotated_rgb, NULL, surface, &tmp_rect, scale_x, scale_y, texture->scaleMode); 496 SDL_DestroySurface(src_rotated_rgb); 497 } 498 } 499 } 500 SDL_DestroySurface(mask_rotated); 501 } 502 if (src_rotated) { 503 SDL_DestroySurface(src_rotated); 504 } 505 } 506 } 507 508 if (SDL_MUSTLOCK(src)) { 509 SDL_UnlockSurface(src); 510 } 511 if (mask) { 512 SDL_DestroySurface(mask); 513 } 514 if (src_clone) { 515 SDL_DestroySurface(src_clone); 516 } 517 return result; 518} 519 520typedef struct GeometryFillData 521{ 522 SDL_Point dst; 523 SDL_Color color; 524} GeometryFillData; 525 526typedef struct GeometryCopyData 527{ 528 SDL_Point src; 529 SDL_Point dst; 530 SDL_Color color; 531} GeometryCopyData; 532 533static bool SW_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, 534 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride, 535 int num_vertices, const void *indices, int num_indices, int size_indices, 536 float scale_x, float scale_y) 537{ 538 int i; 539 int count = indices ? num_indices : num_vertices; 540 void *verts; 541 size_t sz = texture ? sizeof(GeometryCopyData) : sizeof(GeometryFillData); 542 const float color_scale = cmd->data.draw.color_scale; 543 544 verts = SDL_AllocateRenderVertices(renderer, count * sz, 0, &cmd->data.draw.first); 545 if (!verts) { 546 return false; 547 } 548 549 cmd->data.draw.count = count; 550 size_indices = indices ? size_indices : 0; 551 552 if (texture) { 553 GeometryCopyData *ptr = (GeometryCopyData *)verts; 554 for (i = 0; i < count; i++) { 555 int j; 556 float *xy_; 557 SDL_FColor col_; 558 float *uv_; 559 if (size_indices == 4) { 560 j = ((const Uint32 *)indices)[i]; 561 } else if (size_indices == 2) { 562 j = ((const Uint16 *)indices)[i]; 563 } else if (size_indices == 1) { 564 j = ((const Uint8 *)indices)[i]; 565 } else { 566 j = i; 567 } 568 569 xy_ = (float *)((char *)xy + j * xy_stride); 570 col_ = *(SDL_FColor *)((char *)color + j * color_stride); 571 572 uv_ = (float *)((char *)uv + j * uv_stride); 573 574 ptr->src.x = (int)(uv_[0] * texture->w); 575 ptr->src.y = (int)(uv_[1] * texture->h); 576 577 ptr->dst.x = (int)(xy_[0] * scale_x); 578 ptr->dst.y = (int)(xy_[1] * scale_y); 579 trianglepoint_2_fixedpoint(&ptr->dst); 580 581 ptr->color.r = (Uint8)SDL_roundf(SDL_clamp(col_.r * color_scale, 0.0f, 1.0f) * 255.0f); 582 ptr->color.g = (Uint8)SDL_roundf(SDL_clamp(col_.g * color_scale, 0.0f, 1.0f) * 255.0f); 583 ptr->color.b = (Uint8)SDL_roundf(SDL_clamp(col_.b * color_scale, 0.0f, 1.0f) * 255.0f); 584 ptr->color.a = (Uint8)SDL_roundf(SDL_clamp(col_.a, 0.0f, 1.0f) * 255.0f); 585 586 ptr++; 587 } 588 } else { 589 GeometryFillData *ptr = (GeometryFillData *)verts; 590 591 for (i = 0; i < count; i++) { 592 int j; 593 float *xy_; 594 SDL_FColor col_; 595 if (size_indices == 4) { 596 j = ((const Uint32 *)indices)[i]; 597 } else if (size_indices == 2) { 598 j = ((const Uint16 *)indices)[i]; 599 } else if (size_indices == 1) { 600 j = ((const Uint8 *)indices)[i]; 601 } else { 602 j = i; 603 } 604 605 xy_ = (float *)((char *)xy + j * xy_stride); 606 col_ = *(SDL_FColor *)((char *)color + j * color_stride); 607 608 ptr->dst.x = (int)(xy_[0] * scale_x); 609 ptr->dst.y = (int)(xy_[1] * scale_y); 610 trianglepoint_2_fixedpoint(&ptr->dst); 611 612 ptr->color.r = (Uint8)SDL_roundf(SDL_clamp(col_.r * color_scale, 0.0f, 1.0f) * 255.0f); 613 ptr->color.g = (Uint8)SDL_roundf(SDL_clamp(col_.g * color_scale, 0.0f, 1.0f) * 255.0f); 614 ptr->color.b = (Uint8)SDL_roundf(SDL_clamp(col_.b * color_scale, 0.0f, 1.0f) * 255.0f); 615 ptr->color.a = (Uint8)SDL_roundf(SDL_clamp(col_.a, 0.0f, 1.0f) * 255.0f); 616 617 ptr++; 618 } 619 } 620 return true; 621} 622 623static void PrepTextureForCopy(const SDL_RenderCommand *cmd, SW_DrawStateCache *drawstate) 624{ 625 const Uint8 r = drawstate->color.r; 626 const Uint8 g = drawstate->color.g; 627 const Uint8 b = drawstate->color.b; 628 const Uint8 a = drawstate->color.a; 629 const SDL_BlendMode blend = cmd->data.draw.blend; 630 SDL_Texture *texture = cmd->data.draw.texture; 631 SDL_Surface *surface = (SDL_Surface *)texture->internal; 632 const bool colormod = ((r & g & b) != 0xFF); 633 const bool alphamod = (a != 0xFF); 634 const bool blending = ((blend == SDL_BLENDMODE_ADD) || (blend == SDL_BLENDMODE_MOD) || (blend == SDL_BLENDMODE_MUL)); 635 636 if (colormod || alphamod || blending) { 637 SDL_SetSurfaceRLE(surface, 0); 638 } 639 640 // !!! FIXME: we can probably avoid some of these calls. 641 SDL_SetSurfaceColorMod(surface, r, g, b); 642 SDL_SetSurfaceAlphaMod(surface, a); 643 SDL_SetSurfaceBlendMode(surface, blend); 644} 645 646static void SetDrawState(SDL_Surface *surface, SW_DrawStateCache *drawstate) 647{ 648 if (drawstate->surface_cliprect_dirty) { 649 const SDL_Rect *viewport = drawstate->viewport; 650 const SDL_Rect *cliprect = drawstate->cliprect; 651 SDL_assert_release(viewport != NULL); // the higher level should have forced a SDL_RENDERCMD_SETVIEWPORT 652 653 if (cliprect && viewport) { 654 SDL_Rect clip_rect; 655 clip_rect.x = cliprect->x + viewport->x; 656 clip_rect.y = cliprect->y + viewport->y; 657 clip_rect.w = cliprect->w; 658 clip_rect.h = cliprect->h; 659 SDL_GetRectIntersection(viewport, &clip_rect, &clip_rect); 660 SDL_SetSurfaceClipRect(surface, &clip_rect); 661 } else { 662 SDL_SetSurfaceClipRect(surface, drawstate->viewport); 663 } 664 drawstate->surface_cliprect_dirty = false; 665 } 666} 667 668static void SW_InvalidateCachedState(SDL_Renderer *renderer) 669{ 670 // SW_DrawStateCache only lives during SW_RunCommandQueue, so nothing to do here! 671} 672 673 674static bool SW_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) 675{ 676 SDL_Surface *surface = SW_ActivateRenderer(renderer); 677 SW_DrawStateCache drawstate; 678 679 if (!SDL_SurfaceValid(surface)) { 680 return false; 681 } 682 683 drawstate.viewport = NULL; 684 drawstate.cliprect = NULL; 685 drawstate.surface_cliprect_dirty = true; 686 drawstate.color.r = 0; 687 drawstate.color.g = 0; 688 drawstate.color.b = 0; 689 drawstate.color.a = 0; 690 691 while (cmd) { 692 switch (cmd->command) { 693 case SDL_RENDERCMD_SETDRAWCOLOR: 694 { 695 drawstate.color.r = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.r * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f); 696 drawstate.color.g = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.g * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f); 697 drawstate.color.b = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.b * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f); 698 drawstate.color.a = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.a, 0.0f, 1.0f) * 255.0f); 699 break; 700 } 701 702 case SDL_RENDERCMD_SETVIEWPORT: 703 { 704 drawstate.viewport = &cmd->data.viewport.rect; 705 drawstate.surface_cliprect_dirty = true; 706 break; 707 } 708 709 case SDL_RENDERCMD_SETCLIPRECT: 710 { 711 drawstate.cliprect = cmd->data.cliprect.enabled ? &cmd->data.cliprect.rect : NULL; 712 drawstate.surface_cliprect_dirty = true; 713 break; 714 } 715 716 case SDL_RENDERCMD_CLEAR: 717 { 718 const Uint8 r = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.r * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f); 719 const Uint8 g = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.g * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f); 720 const Uint8 b = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.b * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f); 721 const Uint8 a = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.a, 0.0f, 1.0f) * 255.0f); 722 // By definition the clear ignores the clip rect 723 SDL_SetSurfaceClipRect(surface, NULL); 724 SDL_FillSurfaceRect(surface, NULL, SDL_MapSurfaceRGBA(surface, r, g, b, a)); 725 drawstate.surface_cliprect_dirty = true; 726 break; 727 } 728 729 case SDL_RENDERCMD_DRAW_POINTS: 730 { 731 const Uint8 r = drawstate.color.r; 732 const Uint8 g = drawstate.color.g; 733 const Uint8 b = drawstate.color.b; 734 const Uint8 a = drawstate.color.a; 735 const int count = (int)cmd->data.draw.count; 736 SDL_Point *verts = (SDL_Point *)(((Uint8 *)vertices) + cmd->data.draw.first); 737 const SDL_BlendMode blend = cmd->data.draw.blend; 738 SetDrawState(surface, &drawstate); 739 740 // Apply viewport 741 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) { 742 int i; 743 for (i = 0; i < count; i++) { 744 verts[i].x += drawstate.viewport->x; 745 verts[i].y += drawstate.viewport->y; 746 } 747 } 748 749 if (blend == SDL_BLENDMODE_NONE) { 750 SDL_DrawPoints(surface, verts, count, SDL_MapSurfaceRGBA(surface, r, g, b, a)); 751 } else { 752 SDL_BlendPoints(surface, verts, count, blend, r, g, b, a); 753 } 754 break; 755 } 756 757 case SDL_RENDERCMD_DRAW_LINES: 758 { 759 const Uint8 r = drawstate.color.r; 760 const Uint8 g = drawstate.color.g; 761 const Uint8 b = drawstate.color.b; 762 const Uint8 a = drawstate.color.a; 763 const int count = (int)cmd->data.draw.count; 764 SDL_Point *verts = (SDL_Point *)(((Uint8 *)vertices) + cmd->data.draw.first); 765 const SDL_BlendMode blend = cmd->data.draw.blend; 766 SetDrawState(surface, &drawstate); 767 768 // Apply viewport 769 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) { 770 int i; 771 for (i = 0; i < count; i++) { 772 verts[i].x += drawstate.viewport->x; 773 verts[i].y += drawstate.viewport->y; 774 } 775 } 776 777 if (blend == SDL_BLENDMODE_NONE) { 778 SDL_DrawLines(surface, verts, count, SDL_MapSurfaceRGBA(surface, r, g, b, a)); 779 } else { 780 SDL_BlendLines(surface, verts, count, blend, r, g, b, a); 781 } 782 break; 783 } 784 785 case SDL_RENDERCMD_FILL_RECTS: 786 { 787 const Uint8 r = drawstate.color.r; 788 const Uint8 g = drawstate.color.g; 789 const Uint8 b = drawstate.color.b; 790 const Uint8 a = drawstate.color.a; 791 const int count = (int)cmd->data.draw.count; 792 SDL_Rect *verts = (SDL_Rect *)(((Uint8 *)vertices) + cmd->data.draw.first); 793 const SDL_BlendMode blend = cmd->data.draw.blend; 794 SetDrawState(surface, &drawstate); 795 796 // Apply viewport 797 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) { 798 int i; 799 for (i = 0; i < count; i++) { 800 verts[i].x += drawstate.viewport->x; 801 verts[i].y += drawstate.viewport->y; 802 } 803 } 804 805 if (blend == SDL_BLENDMODE_NONE) { 806 SDL_FillSurfaceRects(surface, verts, count, SDL_MapSurfaceRGBA(surface, r, g, b, a)); 807 } else { 808 SDL_BlendFillRects(surface, verts, count, blend, r, g, b, a); 809 } 810 break; 811 } 812 813 case SDL_RENDERCMD_COPY: 814 { 815 SDL_Rect *verts = (SDL_Rect *)(((Uint8 *)vertices) + cmd->data.draw.first); 816 const SDL_Rect *srcrect = verts; 817 SDL_Rect *dstrect = verts + 1; 818 SDL_Texture *texture = cmd->data.draw.texture; 819 SDL_Surface *src = (SDL_Surface *)texture->internal; 820 821 SetDrawState(surface, &drawstate); 822 823 PrepTextureForCopy(cmd, &drawstate); 824 825 // Apply viewport 826 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) { 827 dstrect->x += drawstate.viewport->x; 828 dstrect->y += drawstate.viewport->y; 829 } 830 831 if (srcrect->w == dstrect->w && srcrect->h == dstrect->h) { 832 SDL_BlitSurface(src, srcrect, surface, dstrect); 833 } else { 834 /* If scaling is ever done, permanently disable RLE (which doesn't support scaling) 835 * to avoid potentially frequent RLE encoding/decoding. 836 */ 837 SDL_SetSurfaceRLE(surface, 0); 838 839 // Prevent to do scaling + clipping on viewport boundaries as it may lose proportion 840 if (dstrect->x < 0 || dstrect->y < 0 || dstrect->x + dstrect->w > surface->w || dstrect->y + dstrect->h > surface->h) { 841 SDL_Surface *tmp = SDL_CreateSurface(dstrect->w, dstrect->h, src->format); 842 // Scale to an intermediate surface, then blit 843 if (tmp) { 844 SDL_Rect r; 845 SDL_BlendMode blendmode; 846 Uint8 alphaMod, rMod, gMod, bMod; 847 848 SDL_GetSurfaceBlendMode(src, &blendmode); 849 SDL_GetSurfaceAlphaMod(src, &alphaMod); 850 SDL_GetSurfaceColorMod(src, &rMod, &gMod, &bMod); 851 852 r.x = 0; 853 r.y = 0; 854 r.w = dstrect->w; 855 r.h = dstrect->h; 856 857 SDL_SetSurfaceBlendMode(src, SDL_BLENDMODE_NONE); 858 SDL_SetSurfaceColorMod(src, 255, 255, 255); 859 SDL_SetSurfaceAlphaMod(src, 255); 860 861 SDL_BlitSurfaceScaled(src, srcrect, tmp, &r, texture->scaleMode); 862 863 SDL_SetSurfaceColorMod(tmp, rMod, gMod, bMod); 864 SDL_SetSurfaceAlphaMod(tmp, alphaMod); 865 SDL_SetSurfaceBlendMode(tmp, blendmode); 866 867 SDL_BlitSurface(tmp, NULL, surface, dstrect); 868 SDL_DestroySurface(tmp); 869 // No need to set back r/g/b/a/blendmode to 'src' since it's done in PrepTextureForCopy() 870 } 871 } else { 872 SDL_BlitSurfaceScaled(src, srcrect, surface, dstrect, texture->scaleMode); 873 } 874 } 875 break; 876 } 877 878 case SDL_RENDERCMD_COPY_EX: 879 { 880 CopyExData *copydata = (CopyExData *)(((Uint8 *)vertices) + cmd->data.draw.first); 881 SetDrawState(surface, &drawstate); 882 PrepTextureForCopy(cmd, &drawstate); 883 884 // Apply viewport 885 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) { 886 copydata->dstrect.x += drawstate.viewport->x; 887 copydata->dstrect.y += drawstate.viewport->y; 888 } 889 890 SW_RenderCopyEx(renderer, surface, cmd->data.draw.texture, &copydata->srcrect, 891 &copydata->dstrect, copydata->angle, &copydata->center, copydata->flip, 892 copydata->scale_x, copydata->scale_y); 893 break; 894 } 895 896 case SDL_RENDERCMD_GEOMETRY: 897 { 898 int i; 899 SDL_Rect *verts = (SDL_Rect *)(((Uint8 *)vertices) + cmd->data.draw.first); 900 const int count = (int)cmd->data.draw.count; 901 SDL_Texture *texture = cmd->data.draw.texture; 902 const SDL_BlendMode blend = cmd->data.draw.blend; 903 904 SetDrawState(surface, &drawstate); 905 906 if (texture) { 907 SDL_Surface *src = (SDL_Surface *)texture->internal; 908 909 GeometryCopyData *ptr = (GeometryCopyData *)verts; 910 911 PrepTextureForCopy(cmd, &drawstate); 912 913 // Apply viewport 914 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) { 915 SDL_Point vp; 916 vp.x = drawstate.viewport->x; 917 vp.y = drawstate.viewport->y; 918 trianglepoint_2_fixedpoint(&vp); 919 for (i = 0; i < count; i++) { 920 ptr[i].dst.x += vp.x; 921 ptr[i].dst.y += vp.y; 922 } 923 } 924 925 for (i = 0; i < count; i += 3, ptr += 3) { 926 SDL_SW_BlitTriangle( 927 src, 928 &(ptr[0].src), &(ptr[1].src), &(ptr[2].src), 929 surface, 930 &(ptr[0].dst), &(ptr[1].dst), &(ptr[2].dst), 931 ptr[0].color, ptr[1].color, ptr[2].color, 932 cmd->data.draw.texture_address_mode); 933 } 934 } else { 935 GeometryFillData *ptr = (GeometryFillData *)verts; 936 937 // Apply viewport 938 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) { 939 SDL_Point vp; 940 vp.x = drawstate.viewport->x; 941 vp.y = drawstate.viewport->y; 942 trianglepoint_2_fixedpoint(&vp); 943 for (i = 0; i < count; i++) { 944 ptr[i].dst.x += vp.x; 945 ptr[i].dst.y += vp.y; 946 } 947 } 948 949 for (i = 0; i < count; i += 3, ptr += 3) { 950 SDL_SW_FillTriangle(surface, &(ptr[0].dst), &(ptr[1].dst), &(ptr[2].dst), blend, ptr[0].color, ptr[1].color, ptr[2].color); 951 } 952 } 953 break; 954 } 955 956 case SDL_RENDERCMD_NO_OP: 957 break; 958 } 959 960 cmd = cmd->next; 961 } 962 963 return true; 964} 965 966static SDL_Surface *SW_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect) 967{ 968 SDL_Surface *surface = SW_ActivateRenderer(renderer); 969 void *pixels; 970 971 if (!SDL_SurfaceValid(surface)) { 972 return NULL; 973 } 974 975 /* NOTE: The rect is already adjusted according to the viewport by 976 * SDL_RenderReadPixels. 977 */ 978 979 if (rect->x < 0 || rect->x + rect->w > surface->w || 980 rect->y < 0 || rect->y + rect->h > surface->h) { 981 SDL_SetError("Tried to read outside of surface bounds"); 982 return NULL; 983 } 984 985 pixels = (void *)((Uint8 *)surface->pixels + 986 rect->y * surface->pitch + 987 rect->x * surface->fmt->bytes_per_pixel); 988 989 return SDL_DuplicatePixels(rect->w, rect->h, surface->format, SDL_COLORSPACE_SRGB, pixels, surface->pitch); 990} 991 992static bool SW_RenderPresent(SDL_Renderer *renderer) 993{ 994 SDL_Window *window = renderer->window; 995 996 if (!window) { 997 return false; 998 } 999 return SDL_UpdateWindowSurface(window); 1000} 1001 1002static void SW_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) 1003{ 1004 SDL_Surface *surface = (SDL_Surface *)texture->internal; 1005 1006 SDL_DestroySurface(surface); 1007} 1008 1009static void SW_DestroyRenderer(SDL_Renderer *renderer) 1010{ 1011 SDL_Window *window = renderer->window; 1012 SW_RenderData *data = (SW_RenderData *)renderer->internal; 1013 1014 if (window) { 1015 SDL_DestroyWindowSurface(window); 1016 } 1017 SDL_free(data); 1018} 1019 1020static void SW_SelectBestFormats(SDL_Renderer *renderer, SDL_PixelFormat format) 1021{ 1022 // Prefer the format used by the framebuffer by default. 1023 SDL_AddSupportedTextureFormat(renderer, format); 1024 1025 switch (format) { 1026 case SDL_PIXELFORMAT_XRGB4444: 1027 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB4444); 1028 break; 1029 case SDL_PIXELFORMAT_XBGR4444: 1030 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR4444); 1031 break; 1032 case SDL_PIXELFORMAT_ARGB4444: 1033 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB4444); 1034 break; 1035 case SDL_PIXELFORMAT_ABGR4444: 1036 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XBGR4444); 1037 break; 1038 1039 case SDL_PIXELFORMAT_XRGB1555: 1040 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB1555); 1041 break; 1042 case SDL_PIXELFORMAT_XBGR1555: 1043 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR1555); 1044 break; 1045 case SDL_PIXELFORMAT_ARGB1555: 1046 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB1555); 1047 break; 1048 case SDL_PIXELFORMAT_ABGR1555: 1049 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XBGR1555); 1050 break; 1051 1052 case SDL_PIXELFORMAT_XRGB8888: 1053 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888); 1054 break; 1055 case SDL_PIXELFORMAT_RGBX8888: 1056 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA8888); 1057 break; 1058 case SDL_PIXELFORMAT_XBGR8888: 1059 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR8888); 1060 break; 1061 case SDL_PIXELFORMAT_BGRX8888: 1062 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRA8888); 1063 break; 1064 case SDL_PIXELFORMAT_ARGB8888: 1065 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB8888); 1066 break; 1067 case SDL_PIXELFORMAT_RGBA8888: 1068 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBX8888); 1069 break; 1070 case SDL_PIXELFORMAT_ABGR8888: 1071 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XBGR8888); 1072 break; 1073 case SDL_PIXELFORMAT_BGRA8888: 1074 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRX8888); 1075 break; 1076 default: 1077 break; 1078 } 1079 1080 /* Ensure that we always have a SDL_PACKEDLAYOUT_8888 format. Having a matching component order increases the 1081 * chances of getting a fast path for blitting. 1082 */ 1083 if (SDL_ISPIXELFORMAT_PACKED(format)) { 1084 if (SDL_PIXELLAYOUT(format) != SDL_PACKEDLAYOUT_8888) { 1085 switch (SDL_PIXELORDER(format)) { 1086 case SDL_PACKEDORDER_BGRX: 1087 case SDL_PACKEDORDER_BGRA: 1088 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRX8888); 1089 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRA8888); 1090 break; 1091 case SDL_PACKEDORDER_RGBX: 1092 case SDL_PACKEDORDER_RGBA: 1093 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBX8888); 1094 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA8888); 1095 break; 1096 case SDL_PACKEDORDER_XBGR: 1097 case SDL_PACKEDORDER_ABGR: 1098 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XBGR8888); 1099 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR8888); 1100 break; 1101 case SDL_PACKEDORDER_XRGB: 1102 case SDL_PACKEDORDER_ARGB: 1103 default: 1104 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB8888); 1105 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888); 1106 break; 1107 } 1108 } 1109 } else { 1110 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB8888); 1111 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888); 1112 } 1113} 1114 1115bool SW_CreateRendererForSurface(SDL_Renderer *renderer, SDL_Surface *surface, SDL_PropertiesID create_props) 1116{ 1117 SW_RenderData *data; 1118 1119 if (!SDL_SurfaceValid(surface)) { 1120 return SDL_InvalidParamError("surface"); 1121 } 1122 1123 renderer->software = true; 1124 1125 data = (SW_RenderData *)SDL_calloc(1, sizeof(*data)); 1126 if (!data) { 1127 return false; 1128 } 1129 data->surface = surface; 1130 data->window = surface; 1131 1132 renderer->WindowEvent = SW_WindowEvent; 1133 renderer->GetOutputSize = SW_GetOutputSize; 1134 renderer->CreateTexture = SW_CreateTexture; 1135 renderer->UpdateTexture = SW_UpdateTexture; 1136 renderer->LockTexture = SW_LockTexture; 1137 renderer->UnlockTexture = SW_UnlockTexture; 1138 renderer->SetTextureScaleMode = SW_SetTextureScaleMode; 1139 renderer->SetRenderTarget = SW_SetRenderTarget; 1140 renderer->QueueSetViewport = SW_QueueNoOp; 1141 renderer->QueueSetDrawColor = SW_QueueNoOp; 1142 renderer->QueueDrawPoints = SW_QueueDrawPoints; 1143 renderer->QueueDrawLines = SW_QueueDrawPoints; // lines and points queue vertices the same way. 1144 renderer->QueueFillRects = SW_QueueFillRects; 1145 renderer->QueueCopy = SW_QueueCopy; 1146 renderer->QueueCopyEx = SW_QueueCopyEx; 1147 renderer->QueueGeometry = SW_QueueGeometry; 1148 renderer->InvalidateCachedState = SW_InvalidateCachedState; 1149 renderer->RunCommandQueue = SW_RunCommandQueue; 1150 renderer->RenderReadPixels = SW_RenderReadPixels; 1151 renderer->RenderPresent = SW_RenderPresent; 1152 renderer->DestroyTexture = SW_DestroyTexture; 1153 renderer->DestroyRenderer = SW_DestroyRenderer; 1154 renderer->internal = data; 1155 SW_InvalidateCachedState(renderer); 1156 1157 renderer->name = SW_RenderDriver.name; 1158 1159 SW_SelectBestFormats(renderer, surface->format); 1160 1161 SDL_SetupRendererColorspace(renderer, create_props); 1162 1163 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) { 1164 return SDL_SetError("Unsupported output colorspace"); 1165 } 1166 1167 return true; 1168} 1169 1170static bool SW_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props) 1171{ 1172 // Set the vsync hint based on our flags, if it's not already set 1173 const char *hint = SDL_GetHint(SDL_HINT_RENDER_VSYNC); 1174 const bool no_hint_set = (!hint || !*hint); 1175 1176 if (no_hint_set) { 1177 if (SDL_GetBooleanProperty(create_props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 0)) { 1178 SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1"); 1179 } else { 1180 SDL_SetHint(SDL_HINT_RENDER_VSYNC, "0"); 1181 } 1182 } 1183 1184 SDL_Surface *surface = SDL_GetWindowSurface(window); 1185 1186 // Reset the vsync hint if we set it above 1187 if (no_hint_set) { 1188 SDL_SetHint(SDL_HINT_RENDER_VSYNC, ""); 1189 } 1190 1191 if (!SDL_SurfaceValid(surface)) { 1192 return false; 1193 } 1194 1195 return SW_CreateRendererForSurface(renderer, surface, create_props); 1196} 1197 1198SDL_RenderDriver SW_RenderDriver = { 1199 SW_CreateRenderer, SDL_SOFTWARE_RENDERER 1200}; 1201 1202#endif // SDL_VIDEO_RENDER_SW