SDL_render_sw.c (31761B)
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 #if SDL_VIDEO_RENDER_SW && !SDL_RENDER_DISABLED 24 25 #include "../SDL_sysrender.h" 26 #include "SDL_render_sw_c.h" 27 #include "SDL_hints.h" 28 29 #include "SDL_draw.h" 30 #include "SDL_blendfillrect.h" 31 #include "SDL_blendline.h" 32 #include "SDL_blendpoint.h" 33 #include "SDL_drawline.h" 34 #include "SDL_drawpoint.h" 35 #include "SDL_rotate.h" 36 37 /* SDL surface based renderer implementation */ 38 39 typedef struct 40 { 41 const SDL_Rect *viewport; 42 const SDL_Rect *cliprect; 43 SDL_bool surface_cliprect_dirty; 44 } SW_DrawStateCache; 45 46 typedef struct 47 { 48 SDL_Surface *surface; 49 SDL_Surface *window; 50 } SW_RenderData; 51 52 53 static SDL_Surface * 54 SW_ActivateRenderer(SDL_Renderer * renderer) 55 { 56 SW_RenderData *data = (SW_RenderData *) renderer->driverdata; 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 70 static void 71 SW_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) 72 { 73 SW_RenderData *data = (SW_RenderData *) renderer->driverdata; 74 75 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) { 76 data->surface = NULL; 77 data->window = NULL; 78 } 79 } 80 81 static int 82 SW_GetOutputSize(SDL_Renderer * renderer, int *w, int *h) 83 { 84 SW_RenderData *data = (SW_RenderData *) renderer->driverdata; 85 86 if (data->surface) { 87 if (w) { 88 *w = data->surface->w; 89 } 90 if (h) { 91 *h = data->surface->h; 92 } 93 return 0; 94 } 95 96 if (renderer->window) { 97 SDL_GetWindowSize(renderer->window, w, h); 98 return 0; 99 } 100 101 SDL_SetError("Software renderer doesn't have an output surface"); 102 return -1; 103 } 104 105 static int 106 SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) 107 { 108 int bpp; 109 Uint32 Rmask, Gmask, Bmask, Amask; 110 111 if (!SDL_PixelFormatEnumToMasks 112 (texture->format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) { 113 return SDL_SetError("Unknown texture format"); 114 } 115 116 texture->driverdata = 117 SDL_CreateRGBSurface(0, texture->w, texture->h, bpp, Rmask, Gmask, 118 Bmask, Amask); 119 SDL_SetSurfaceColorMod(texture->driverdata, texture->r, texture->g, 120 texture->b); 121 SDL_SetSurfaceAlphaMod(texture->driverdata, texture->a); 122 SDL_SetSurfaceBlendMode(texture->driverdata, texture->blendMode); 123 124 /* Only RLE encode textures without an alpha channel since the RLE coder 125 * discards the color values of pixels with an alpha value of zero. 126 */ 127 if (texture->access == SDL_TEXTUREACCESS_STATIC && !Amask) { 128 SDL_SetSurfaceRLE(texture->driverdata, 1); 129 } 130 131 if (!texture->driverdata) { 132 return -1; 133 } 134 return 0; 135 } 136 137 static int 138 SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, 139 const SDL_Rect * rect, const void *pixels, int pitch) 140 { 141 SDL_Surface *surface = (SDL_Surface *) texture->driverdata; 142 Uint8 *src, *dst; 143 int row; 144 size_t length; 145 146 if(SDL_MUSTLOCK(surface)) 147 SDL_LockSurface(surface); 148 src = (Uint8 *) pixels; 149 dst = (Uint8 *) surface->pixels + 150 rect->y * surface->pitch + 151 rect->x * surface->format->BytesPerPixel; 152 length = rect->w * surface->format->BytesPerPixel; 153 for (row = 0; row < rect->h; ++row) { 154 SDL_memcpy(dst, src, length); 155 src += pitch; 156 dst += surface->pitch; 157 } 158 if(SDL_MUSTLOCK(surface)) 159 SDL_UnlockSurface(surface); 160 return 0; 161 } 162 163 static int 164 SW_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, 165 const SDL_Rect * rect, void **pixels, int *pitch) 166 { 167 SDL_Surface *surface = (SDL_Surface *) texture->driverdata; 168 169 *pixels = 170 (void *) ((Uint8 *) surface->pixels + rect->y * surface->pitch + 171 rect->x * surface->format->BytesPerPixel); 172 *pitch = surface->pitch; 173 return 0; 174 } 175 176 static void 177 SW_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture) 178 { 179 } 180 181 static void 182 SW_SetTextureScaleMode(SDL_Renderer * renderer, SDL_Texture * texture, SDL_ScaleMode scaleMode) 183 { 184 } 185 186 static int 187 SW_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) 188 { 189 SW_RenderData *data = (SW_RenderData *) renderer->driverdata; 190 191 if (texture) { 192 data->surface = (SDL_Surface *) texture->driverdata; 193 } else { 194 data->surface = data->window; 195 } 196 return 0; 197 } 198 199 static int 200 SW_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) 201 { 202 return 0; /* nothing to do in this backend. */ 203 } 204 205 static int 206 SW_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) 207 { 208 SDL_Point *verts = (SDL_Point *) SDL_AllocateRenderVertices(renderer, count * sizeof (SDL_Point), 0, &cmd->data.draw.first); 209 int i; 210 211 if (!verts) { 212 return -1; 213 } 214 215 cmd->data.draw.count = count; 216 217 if (renderer->viewport.x || renderer->viewport.y) { 218 const int x = renderer->viewport.x; 219 const int y = renderer->viewport.y; 220 for (i = 0; i < count; i++, verts++, points++) { 221 verts->x = (int)(x + points->x); 222 verts->y = (int)(y + points->y); 223 } 224 } else { 225 for (i = 0; i < count; i++, verts++, points++) { 226 verts->x = (int)points->x; 227 verts->y = (int)points->y; 228 } 229 } 230 231 return 0; 232 } 233 234 static int 235 SW_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) 236 { 237 SDL_Rect *verts = (SDL_Rect *) SDL_AllocateRenderVertices(renderer, count * sizeof (SDL_Rect), 0, &cmd->data.draw.first); 238 int i; 239 240 if (!verts) { 241 return -1; 242 } 243 244 cmd->data.draw.count = count; 245 246 if (renderer->viewport.x || renderer->viewport.y) { 247 const int x = renderer->viewport.x; 248 const int y = renderer->viewport.y; 249 250 for (i = 0; i < count; i++, verts++, rects++) { 251 verts->x = (int)(x + rects->x); 252 verts->y = (int)(y + rects->y); 253 verts->w = SDL_max((int)rects->w, 1); 254 verts->h = SDL_max((int)rects->h, 1); 255 } 256 } else { 257 for (i = 0; i < count; i++, verts++, rects++) { 258 verts->x = (int)rects->x; 259 verts->y = (int)rects->y; 260 verts->w = SDL_max((int)rects->w, 1); 261 verts->h = SDL_max((int)rects->h, 1); 262 } 263 } 264 265 return 0; 266 } 267 268 static int 269 SW_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, 270 const SDL_Rect * srcrect, const SDL_FRect * dstrect) 271 { 272 SDL_Rect *verts = (SDL_Rect *) SDL_AllocateRenderVertices(renderer, 2 * sizeof (SDL_Rect), 0, &cmd->data.draw.first); 273 274 if (!verts) { 275 return -1; 276 } 277 278 cmd->data.draw.count = 1; 279 280 SDL_memcpy(verts, srcrect, sizeof (SDL_Rect)); 281 verts++; 282 283 if (renderer->viewport.x || renderer->viewport.y) { 284 verts->x = (int)(renderer->viewport.x + dstrect->x); 285 verts->y = (int)(renderer->viewport.y + dstrect->y); 286 } else { 287 verts->x = (int)dstrect->x; 288 verts->y = (int)dstrect->y; 289 } 290 verts->w = (int)dstrect->w; 291 verts->h = (int)dstrect->h; 292 293 return 0; 294 } 295 296 typedef struct CopyExData 297 { 298 SDL_Rect srcrect; 299 SDL_Rect dstrect; 300 double angle; 301 SDL_FPoint center; 302 SDL_RendererFlip flip; 303 } CopyExData; 304 305 static int 306 SW_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, 307 const SDL_Rect * srcrect, const SDL_FRect * dstrect, 308 const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) 309 { 310 CopyExData *verts = (CopyExData *) SDL_AllocateRenderVertices(renderer, sizeof (CopyExData), 0, &cmd->data.draw.first); 311 312 if (!verts) { 313 return -1; 314 } 315 316 cmd->data.draw.count = 1; 317 318 SDL_memcpy(&verts->srcrect, srcrect, sizeof (SDL_Rect)); 319 320 if (renderer->viewport.x || renderer->viewport.y) { 321 verts->dstrect.x = (int)(renderer->viewport.x + dstrect->x); 322 verts->dstrect.y = (int)(renderer->viewport.y + dstrect->y); 323 } else { 324 verts->dstrect.x = (int)dstrect->x; 325 verts->dstrect.y = (int)dstrect->y; 326 } 327 verts->dstrect.w = (int)dstrect->w; 328 verts->dstrect.h = (int)dstrect->h; 329 verts->angle = angle; 330 SDL_memcpy(&verts->center, center, sizeof (SDL_FPoint)); 331 verts->flip = flip; 332 333 return 0; 334 } 335 336 static int 337 SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Surface *surface, SDL_Texture * texture, 338 const SDL_Rect * srcrect, const SDL_Rect * final_rect, 339 const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip) 340 { 341 SDL_Surface *src = (SDL_Surface *) texture->driverdata; 342 SDL_Rect tmp_rect; 343 SDL_Surface *src_clone, *src_rotated, *src_scaled; 344 SDL_Surface *mask = NULL, *mask_rotated = NULL; 345 int retval = 0, dstwidth, dstheight, abscenterx, abscentery; 346 double cangle, sangle, px, py, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y; 347 SDL_BlendMode blendmode; 348 Uint8 alphaMod, rMod, gMod, bMod; 349 int applyModulation = SDL_FALSE; 350 int blitRequired = SDL_FALSE; 351 int isOpaque = SDL_FALSE; 352 353 if (!surface) { 354 return -1; 355 } 356 357 tmp_rect.x = 0; 358 tmp_rect.y = 0; 359 tmp_rect.w = final_rect->w; 360 tmp_rect.h = final_rect->h; 361 362 /* It is possible to encounter an RLE encoded surface here and locking it is 363 * necessary because this code is going to access the pixel buffer directly. 364 */ 365 if (SDL_MUSTLOCK(src)) { 366 SDL_LockSurface(src); 367 } 368 369 /* Clone the source surface but use its pixel buffer directly. 370 * The original source surface must be treated as read-only. 371 */ 372 src_clone = SDL_CreateRGBSurfaceFrom(src->pixels, src->w, src->h, src->format->BitsPerPixel, src->pitch, 373 src->format->Rmask, src->format->Gmask, 374 src->format->Bmask, src->format->Amask); 375 if (src_clone == NULL) { 376 if (SDL_MUSTLOCK(src)) { 377 SDL_UnlockSurface(src); 378 } 379 return -1; 380 } 381 382 SDL_GetSurfaceBlendMode(src, &blendmode); 383 SDL_GetSurfaceAlphaMod(src, &alphaMod); 384 SDL_GetSurfaceColorMod(src, &rMod, &gMod, &bMod); 385 386 /* SDLgfx_rotateSurface only accepts 32-bit surfaces with a 8888 layout. Everything else has to be converted. */ 387 if (src->format->BitsPerPixel != 32 || SDL_PIXELLAYOUT(src->format->format) != SDL_PACKEDLAYOUT_8888 || !src->format->Amask) { 388 blitRequired = SDL_TRUE; 389 } 390 391 /* If scaling and cropping is necessary, it has to be taken care of before the rotation. */ 392 if (!(srcrect->w == final_rect->w && srcrect->h == final_rect->h && srcrect->x == 0 && srcrect->y == 0)) { 393 blitRequired = SDL_TRUE; 394 } 395 396 /* srcrect is not selecting the whole src surface, so cropping is needed */ 397 if (!(srcrect->w == src->w && srcrect->h == src->h && srcrect->x == 0 && srcrect->y == 0)) { 398 blitRequired = SDL_TRUE; 399 } 400 401 /* The color and alpha modulation has to be applied before the rotation when using the NONE, MOD or MUL blend modes. */ 402 if ((blendmode == SDL_BLENDMODE_NONE || blendmode == SDL_BLENDMODE_MOD || blendmode == SDL_BLENDMODE_MUL) && (alphaMod & rMod & gMod & bMod) != 255) { 403 applyModulation = SDL_TRUE; 404 SDL_SetSurfaceAlphaMod(src_clone, alphaMod); 405 SDL_SetSurfaceColorMod(src_clone, rMod, gMod, bMod); 406 } 407 408 /* Opaque surfaces are much easier to handle with the NONE blend mode. */ 409 if (blendmode == SDL_BLENDMODE_NONE && !src->format->Amask && alphaMod == 255) { 410 isOpaque = SDL_TRUE; 411 } 412 413 /* The NONE blend mode requires a mask for non-opaque surfaces. This mask will be used 414 * to clear the pixels in the destination surface. The other steps are explained below. 415 */ 416 if (blendmode == SDL_BLENDMODE_NONE && !isOpaque) { 417 mask = SDL_CreateRGBSurface(0, final_rect->w, final_rect->h, 32, 418 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); 419 if (mask == NULL) { 420 retval = -1; 421 } else { 422 SDL_SetSurfaceBlendMode(mask, SDL_BLENDMODE_MOD); 423 } 424 } 425 426 /* Create a new surface should there be a format mismatch or if scaling, cropping, 427 * or modulation is required. It's possible to use the source surface directly otherwise. 428 */ 429 if (!retval && (blitRequired || applyModulation)) { 430 SDL_Rect scale_rect = tmp_rect; 431 src_scaled = SDL_CreateRGBSurface(0, final_rect->w, final_rect->h, 32, 432 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); 433 if (src_scaled == NULL) { 434 retval = -1; 435 } else { 436 SDL_SetSurfaceBlendMode(src_clone, SDL_BLENDMODE_NONE); 437 retval = SDL_BlitScaled(src_clone, srcrect, src_scaled, &scale_rect); 438 SDL_FreeSurface(src_clone); 439 src_clone = src_scaled; 440 src_scaled = NULL; 441 } 442 } 443 444 /* SDLgfx_rotateSurface is going to make decisions depending on the blend mode. */ 445 SDL_SetSurfaceBlendMode(src_clone, blendmode); 446 447 if (!retval) { 448 SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, &dstwidth, &dstheight, &cangle, &sangle); 449 src_rotated = SDLgfx_rotateSurface(src_clone, angle, dstwidth/2, dstheight/2, (texture->scaleMode == SDL_ScaleModeNearest) ? 0 : 1, flip & SDL_FLIP_HORIZONTAL, flip & SDL_FLIP_VERTICAL, dstwidth, dstheight, cangle, sangle); 450 if (src_rotated == NULL) { 451 retval = -1; 452 } 453 if (!retval && mask != NULL) { 454 /* The mask needed for the NONE blend mode gets rotated with the same parameters. */ 455 mask_rotated = SDLgfx_rotateSurface(mask, angle, dstwidth/2, dstheight/2, SDL_FALSE, 0, 0, dstwidth, dstheight, cangle, sangle); 456 if (mask_rotated == NULL) { 457 retval = -1; 458 } 459 } 460 if (!retval) { 461 /* Find out where the new origin is by rotating the four final_rect points around the center and then taking the extremes */ 462 abscenterx = final_rect->x + (int)center->x; 463 abscentery = final_rect->y + (int)center->y; 464 /* Compensate the angle inversion to match the behaviour of the other backends */ 465 sangle = -sangle; 466 467 /* Top Left */ 468 px = final_rect->x - abscenterx; 469 py = final_rect->y - abscentery; 470 p1x = px * cangle - py * sangle + abscenterx; 471 p1y = px * sangle + py * cangle + abscentery; 472 473 /* Top Right */ 474 px = final_rect->x + final_rect->w - abscenterx; 475 py = final_rect->y - abscentery; 476 p2x = px * cangle - py * sangle + abscenterx; 477 p2y = px * sangle + py * cangle + abscentery; 478 479 /* Bottom Left */ 480 px = final_rect->x - abscenterx; 481 py = final_rect->y + final_rect->h - abscentery; 482 p3x = px * cangle - py * sangle + abscenterx; 483 p3y = px * sangle + py * cangle + abscentery; 484 485 /* Bottom Right */ 486 px = final_rect->x + final_rect->w - abscenterx; 487 py = final_rect->y + final_rect->h - abscentery; 488 p4x = px * cangle - py * sangle + abscenterx; 489 p4y = px * sangle + py * cangle + abscentery; 490 491 tmp_rect.x = (int)MIN(MIN(p1x, p2x), MIN(p3x, p4x)); 492 tmp_rect.y = (int)MIN(MIN(p1y, p2y), MIN(p3y, p4y)); 493 tmp_rect.w = dstwidth; 494 tmp_rect.h = dstheight; 495 496 /* The NONE blend mode needs some special care with non-opaque surfaces. 497 * Other blend modes or opaque surfaces can be blitted directly. 498 */ 499 if (blendmode != SDL_BLENDMODE_NONE || isOpaque) { 500 if (applyModulation == SDL_FALSE) { 501 /* If the modulation wasn't already applied, make it happen now. */ 502 SDL_SetSurfaceAlphaMod(src_rotated, alphaMod); 503 SDL_SetSurfaceColorMod(src_rotated, rMod, gMod, bMod); 504 } 505 retval = SDL_BlitSurface(src_rotated, NULL, surface, &tmp_rect); 506 } else { 507 /* The NONE blend mode requires three steps to get the pixels onto the destination surface. 508 * First, the area where the rotated pixels will be blitted to get set to zero. 509 * This is accomplished by simply blitting a mask with the NONE blend mode. 510 * The colorkey set by the rotate function will discard the correct pixels. 511 */ 512 SDL_Rect mask_rect = tmp_rect; 513 SDL_SetSurfaceBlendMode(mask_rotated, SDL_BLENDMODE_NONE); 514 retval = SDL_BlitSurface(mask_rotated, NULL, surface, &mask_rect); 515 if (!retval) { 516 /* The next step copies the alpha value. This is done with the BLEND blend mode and 517 * by modulating the source colors with 0. Since the destination is all zeros, this 518 * will effectively set the destination alpha to the source alpha. 519 */ 520 SDL_SetSurfaceColorMod(src_rotated, 0, 0, 0); 521 mask_rect = tmp_rect; 522 retval = SDL_BlitSurface(src_rotated, NULL, surface, &mask_rect); 523 if (!retval) { 524 /* The last step gets the color values in place. The ADD blend mode simply adds them to 525 * the destination (where the color values are all zero). However, because the ADD blend 526 * mode modulates the colors with the alpha channel, a surface without an alpha mask needs 527 * to be created. This makes all source pixels opaque and the colors get copied correctly. 528 */ 529 SDL_Surface *src_rotated_rgb; 530 src_rotated_rgb = SDL_CreateRGBSurfaceFrom(src_rotated->pixels, src_rotated->w, src_rotated->h, 531 src_rotated->format->BitsPerPixel, src_rotated->pitch, 532 src_rotated->format->Rmask, src_rotated->format->Gmask, 533 src_rotated->format->Bmask, 0); 534 if (src_rotated_rgb == NULL) { 535 retval = -1; 536 } else { 537 SDL_SetSurfaceBlendMode(src_rotated_rgb, SDL_BLENDMODE_ADD); 538 retval = SDL_BlitSurface(src_rotated_rgb, NULL, surface, &tmp_rect); 539 SDL_FreeSurface(src_rotated_rgb); 540 } 541 } 542 } 543 SDL_FreeSurface(mask_rotated); 544 } 545 if (src_rotated != NULL) { 546 SDL_FreeSurface(src_rotated); 547 } 548 } 549 } 550 551 if (SDL_MUSTLOCK(src)) { 552 SDL_UnlockSurface(src); 553 } 554 if (mask != NULL) { 555 SDL_FreeSurface(mask); 556 } 557 if (src_clone != NULL) { 558 SDL_FreeSurface(src_clone); 559 } 560 return retval; 561 } 562 563 static void 564 PrepTextureForCopy(const SDL_RenderCommand *cmd) 565 { 566 const Uint8 r = cmd->data.draw.r; 567 const Uint8 g = cmd->data.draw.g; 568 const Uint8 b = cmd->data.draw.b; 569 const Uint8 a = cmd->data.draw.a; 570 const SDL_BlendMode blend = cmd->data.draw.blend; 571 SDL_Texture *texture = cmd->data.draw.texture; 572 SDL_Surface *surface = (SDL_Surface *) texture->driverdata; 573 const SDL_bool colormod = ((r & g & b) != 0xFF); 574 const SDL_bool alphamod = (a != 0xFF); 575 const SDL_bool blending = ((blend == SDL_BLENDMODE_ADD) || (blend == SDL_BLENDMODE_MOD) || (blend == SDL_BLENDMODE_MUL)); 576 577 if (colormod || alphamod || blending) { 578 SDL_SetSurfaceRLE(surface, 0); 579 } 580 581 /* !!! FIXME: we can probably avoid some of these calls. */ 582 SDL_SetSurfaceColorMod(surface, r, g, b); 583 SDL_SetSurfaceAlphaMod(surface, a); 584 SDL_SetSurfaceBlendMode(surface, blend); 585 } 586 587 static void 588 SetDrawState(SDL_Surface *surface, SW_DrawStateCache *drawstate) 589 { 590 if (drawstate->surface_cliprect_dirty) { 591 const SDL_Rect *viewport = drawstate->viewport; 592 const SDL_Rect *cliprect = drawstate->cliprect; 593 SDL_assert(viewport != NULL); /* the higher level should have forced a SDL_RENDERCMD_SETVIEWPORT */ 594 595 if (cliprect != NULL) { 596 SDL_Rect clip_rect; 597 clip_rect.x = cliprect->x + viewport->x; 598 clip_rect.y = cliprect->y + viewport->y; 599 clip_rect.w = cliprect->w; 600 clip_rect.h = cliprect->h; 601 SDL_IntersectRect(viewport, &clip_rect, &clip_rect); 602 SDL_SetClipRect(surface, &clip_rect); 603 } else { 604 SDL_SetClipRect(surface, drawstate->viewport); 605 } 606 drawstate->surface_cliprect_dirty = SDL_FALSE; 607 } 608 } 609 610 static int 611 SW_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) 612 { 613 SDL_Surface *surface = SW_ActivateRenderer(renderer); 614 SW_DrawStateCache drawstate; 615 616 if (!surface) { 617 return -1; 618 } 619 620 drawstate.viewport = NULL; 621 drawstate.cliprect = NULL; 622 drawstate.surface_cliprect_dirty = SDL_TRUE; 623 624 while (cmd) { 625 switch (cmd->command) { 626 case SDL_RENDERCMD_SETDRAWCOLOR: { 627 break; /* Not used in this backend. */ 628 } 629 630 case SDL_RENDERCMD_SETVIEWPORT: { 631 drawstate.viewport = &cmd->data.viewport.rect; 632 drawstate.surface_cliprect_dirty = SDL_TRUE; 633 break; 634 } 635 636 case SDL_RENDERCMD_SETCLIPRECT: { 637 drawstate.cliprect = cmd->data.cliprect.enabled ? &cmd->data.cliprect.rect : NULL; 638 drawstate.surface_cliprect_dirty = SDL_TRUE; 639 break; 640 } 641 642 case SDL_RENDERCMD_CLEAR: { 643 const Uint8 r = cmd->data.color.r; 644 const Uint8 g = cmd->data.color.g; 645 const Uint8 b = cmd->data.color.b; 646 const Uint8 a = cmd->data.color.a; 647 /* By definition the clear ignores the clip rect */ 648 SDL_SetClipRect(surface, NULL); 649 SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, r, g, b, a)); 650 drawstate.surface_cliprect_dirty = SDL_TRUE; 651 break; 652 } 653 654 case SDL_RENDERCMD_DRAW_POINTS: { 655 const Uint8 r = cmd->data.draw.r; 656 const Uint8 g = cmd->data.draw.g; 657 const Uint8 b = cmd->data.draw.b; 658 const Uint8 a = cmd->data.draw.a; 659 const int count = (int) cmd->data.draw.count; 660 const SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first); 661 const SDL_BlendMode blend = cmd->data.draw.blend; 662 SetDrawState(surface, &drawstate); 663 if (blend == SDL_BLENDMODE_NONE) { 664 SDL_DrawPoints(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a)); 665 } else { 666 SDL_BlendPoints(surface, verts, count, blend, r, g, b, a); 667 } 668 break; 669 } 670 671 case SDL_RENDERCMD_DRAW_LINES: { 672 const Uint8 r = cmd->data.draw.r; 673 const Uint8 g = cmd->data.draw.g; 674 const Uint8 b = cmd->data.draw.b; 675 const Uint8 a = cmd->data.draw.a; 676 const int count = (int) cmd->data.draw.count; 677 const SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first); 678 const SDL_BlendMode blend = cmd->data.draw.blend; 679 SetDrawState(surface, &drawstate); 680 if (blend == SDL_BLENDMODE_NONE) { 681 SDL_DrawLines(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a)); 682 } else { 683 SDL_BlendLines(surface, verts, count, blend, r, g, b, a); 684 } 685 break; 686 } 687 688 case SDL_RENDERCMD_FILL_RECTS: { 689 const Uint8 r = cmd->data.draw.r; 690 const Uint8 g = cmd->data.draw.g; 691 const Uint8 b = cmd->data.draw.b; 692 const Uint8 a = cmd->data.draw.a; 693 const int count = (int) cmd->data.draw.count; 694 const SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first); 695 const SDL_BlendMode blend = cmd->data.draw.blend; 696 SetDrawState(surface, &drawstate); 697 if (blend == SDL_BLENDMODE_NONE) { 698 SDL_FillRects(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a)); 699 } else { 700 SDL_BlendFillRects(surface, verts, count, blend, r, g, b, a); 701 } 702 break; 703 } 704 705 case SDL_RENDERCMD_COPY: { 706 SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first); 707 const SDL_Rect *srcrect = verts; 708 SDL_Rect *dstrect = verts + 1; 709 SDL_Texture *texture = cmd->data.draw.texture; 710 SDL_Surface *src = (SDL_Surface *) texture->driverdata; 711 712 SetDrawState(surface, &drawstate); 713 714 PrepTextureForCopy(cmd); 715 716 if ( srcrect->w == dstrect->w && srcrect->h == dstrect->h ) { 717 SDL_BlitSurface(src, srcrect, surface, dstrect); 718 } else { 719 /* If scaling is ever done, permanently disable RLE (which doesn't support scaling) 720 * to avoid potentially frequent RLE encoding/decoding. 721 */ 722 SDL_SetSurfaceRLE(surface, 0); 723 SDL_BlitScaled(src, srcrect, surface, dstrect); 724 } 725 break; 726 } 727 728 case SDL_RENDERCMD_COPY_EX: { 729 const CopyExData *copydata = (CopyExData *) (((Uint8 *) vertices) + cmd->data.draw.first); 730 SetDrawState(surface, &drawstate); 731 PrepTextureForCopy(cmd); 732 SW_RenderCopyEx(renderer, surface, cmd->data.draw.texture, ©data->srcrect, 733 ©data->dstrect, copydata->angle, ©data->center, copydata->flip); 734 break; 735 } 736 737 case SDL_RENDERCMD_NO_OP: 738 break; 739 } 740 741 cmd = cmd->next; 742 } 743 744 return 0; 745 } 746 747 static int 748 SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, 749 Uint32 format, void * pixels, int pitch) 750 { 751 SDL_Surface *surface = SW_ActivateRenderer(renderer); 752 Uint32 src_format; 753 void *src_pixels; 754 755 if (!surface) { 756 return -1; 757 } 758 759 /* NOTE: The rect is already adjusted according to the viewport by 760 * SDL_RenderReadPixels. 761 */ 762 763 if (rect->x < 0 || rect->x+rect->w > surface->w || 764 rect->y < 0 || rect->y+rect->h > surface->h) { 765 return SDL_SetError("Tried to read outside of surface bounds"); 766 } 767 768 src_format = surface->format->format; 769 src_pixels = (void*)((Uint8 *) surface->pixels + 770 rect->y * surface->pitch + 771 rect->x * surface->format->BytesPerPixel); 772 773 return SDL_ConvertPixels(rect->w, rect->h, 774 src_format, src_pixels, surface->pitch, 775 format, pixels, pitch); 776 } 777 778 static void 779 SW_RenderPresent(SDL_Renderer * renderer) 780 { 781 SDL_Window *window = renderer->window; 782 783 if (window) { 784 SDL_UpdateWindowSurface(window); 785 } 786 } 787 788 static void 789 SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture) 790 { 791 SDL_Surface *surface = (SDL_Surface *) texture->driverdata; 792 793 SDL_FreeSurface(surface); 794 } 795 796 static void 797 SW_DestroyRenderer(SDL_Renderer * renderer) 798 { 799 SW_RenderData *data = (SW_RenderData *) renderer->driverdata; 800 801 SDL_free(data); 802 SDL_free(renderer); 803 } 804 805 SDL_Renderer * 806 SW_CreateRendererForSurface(SDL_Surface * surface) 807 { 808 SDL_Renderer *renderer; 809 SW_RenderData *data; 810 811 if (!surface) { 812 SDL_SetError("Can't create renderer for NULL surface"); 813 return NULL; 814 } 815 816 renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); 817 if (!renderer) { 818 SDL_OutOfMemory(); 819 return NULL; 820 } 821 822 data = (SW_RenderData *) SDL_calloc(1, sizeof(*data)); 823 if (!data) { 824 SW_DestroyRenderer(renderer); 825 SDL_OutOfMemory(); 826 return NULL; 827 } 828 data->surface = surface; 829 data->window = surface; 830 831 renderer->WindowEvent = SW_WindowEvent; 832 renderer->GetOutputSize = SW_GetOutputSize; 833 renderer->CreateTexture = SW_CreateTexture; 834 renderer->UpdateTexture = SW_UpdateTexture; 835 renderer->LockTexture = SW_LockTexture; 836 renderer->UnlockTexture = SW_UnlockTexture; 837 renderer->SetTextureScaleMode = SW_SetTextureScaleMode; 838 renderer->SetRenderTarget = SW_SetRenderTarget; 839 renderer->QueueSetViewport = SW_QueueSetViewport; 840 renderer->QueueSetDrawColor = SW_QueueSetViewport; /* SetViewport and SetDrawColor are (currently) no-ops. */ 841 renderer->QueueDrawPoints = SW_QueueDrawPoints; 842 renderer->QueueDrawLines = SW_QueueDrawPoints; /* lines and points queue vertices the same way. */ 843 renderer->QueueFillRects = SW_QueueFillRects; 844 renderer->QueueCopy = SW_QueueCopy; 845 renderer->QueueCopyEx = SW_QueueCopyEx; 846 renderer->RunCommandQueue = SW_RunCommandQueue; 847 renderer->RenderReadPixels = SW_RenderReadPixels; 848 renderer->RenderPresent = SW_RenderPresent; 849 renderer->DestroyTexture = SW_DestroyTexture; 850 renderer->DestroyRenderer = SW_DestroyRenderer; 851 renderer->info = SW_RenderDriver.info; 852 renderer->driverdata = data; 853 854 SW_ActivateRenderer(renderer); 855 856 return renderer; 857 } 858 859 static SDL_Renderer * 860 SW_CreateRenderer(SDL_Window * window, Uint32 flags) 861 { 862 SDL_Surface *surface; 863 864 surface = SDL_GetWindowSurface(window); 865 if (!surface) { 866 return NULL; 867 } 868 return SW_CreateRendererForSurface(surface); 869 } 870 871 SDL_RenderDriver SW_RenderDriver = { 872 SW_CreateRenderer, 873 { 874 "software", 875 SDL_RENDERER_SOFTWARE | SDL_RENDERER_TARGETTEXTURE, 876 8, 877 { 878 SDL_PIXELFORMAT_ARGB8888, 879 SDL_PIXELFORMAT_ABGR8888, 880 SDL_PIXELFORMAT_RGBA8888, 881 SDL_PIXELFORMAT_BGRA8888, 882 SDL_PIXELFORMAT_RGB888, 883 SDL_PIXELFORMAT_BGR888, 884 SDL_PIXELFORMAT_RGB565, 885 SDL_PIXELFORMAT_RGB555 886 }, 887 0, 888 0} 889 }; 890 891 #endif /* SDL_VIDEO_RENDER_SW && !SDL_RENDER_DISABLED */ 892 893 /* vi: set ts=4 sw=4 expandtab: */