Atlas - SDL_rotate.c

Home / ext / SDL / src / video Lines: 1 | Size: 22112 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 3SDL_rotate.c: rotates 32bit or 8bit surfaces 4 5Shamelessly stolen from SDL_gfx by Andreas Schiffler. Original copyright follows: 6 7Copyright (C) 2001-2011 Andreas Schiffler 8 9This software is provided 'as-is', without any express or implied 10warranty. In no event will the authors be held liable for any damages 11arising from the use of this software. 12 13Permission is granted to anyone to use this software for any purpose, 14including commercial applications, and to alter it and redistribute it 15freely, subject to the following restrictions: 16 17 1. The origin of this software must not be misrepresented; you must not 18 claim that you wrote the original software. If you use this software 19 in a product, an acknowledgment in the product documentation would be 20 appreciated but is not required. 21 22 2. Altered source versions must be plainly marked as such, and must not be 23 misrepresented as being the original software. 24 25 3. This notice may not be removed or altered from any source 26 distribution. 27 28Andreas Schiffler -- aschiffler at ferzkopp dot net 29 30*/ 31#include "SDL_internal.h" 32 33#include "SDL_surface_c.h" 34#include "SDL_rotate.h" 35 36// ---- Internally used structures 37 38/** 39A 32 bit RGBA pixel. 40*/ 41typedef struct tColorRGBA 42{ 43 Uint8 r; 44 Uint8 g; 45 Uint8 b; 46 Uint8 a; 47} tColorRGBA; 48 49/** 50A 8bit Y/palette pixel. 51*/ 52typedef struct tColorY 53{ 54 Uint8 y; 55} tColorY; 56 57/** 58Number of guard rows added to destination surfaces. 59 60This is a simple but effective workaround for observed issues. 61These rows allocate extra memory and are then hidden from the surface. 62Rows are added to the end of destination surfaces when they are allocated. 63This catches any potential overflows which seem to happen with 64just the right src image dimensions and scale/rotation and can lead 65to a situation where the program can segfault. 66*/ 67#define GUARD_ROWS (2) 68 69/** 70Returns colorkey info for a surface 71*/ 72static Uint32 get_colorkey(SDL_Surface *src) 73{ 74 Uint32 key = 0; 75 if (SDL_SurfaceHasColorKey(src)) { 76 SDL_GetSurfaceColorKey(src, &key); 77 } 78 return key; 79} 80 81// rotate (sx, sy) by (angle, center) into (dx, dy) 82static void rotate(double sx, double sy, double sinangle, double cosangle, const SDL_FPoint *center, double *dx, double *dy) 83{ 84 sx -= center->x; 85 sy -= center->y; 86 87 *dx = cosangle * sx - sinangle * sy; 88 *dy = sinangle * sx + cosangle * sy; 89 90 *dx += center->x; 91 *dy += center->y; 92} 93 94/** 95Internal target surface sizing function for rotations with trig result return. 96 97\param width The source surface width. 98\param height The source surface height. 99\param angle The angle to rotate in degrees. 100\param center The center of ratation 101\param rect_dest Bounding box of rotated rectangle 102\param cangle The sine of the angle 103\param sangle The cosine of the angle 104 105*/ 106void SDLgfx_rotozoomSurfaceSizeTrig(int width, int height, double angle, const SDL_FPoint *center, 107 SDL_Rect *rect_dest, double *cangle, double *sangle) 108{ 109 int minx, maxx, miny, maxy; 110 double radangle; 111 double x0, x1, x2, x3; 112 double y0, y1, y2, y3; 113 double sinangle; 114 double cosangle; 115 116 radangle = angle * (SDL_PI_D / 180.0); 117 sinangle = SDL_sin(radangle); 118 cosangle = SDL_cos(radangle); 119 120 /* 121 * Determine destination width and height by rotating a source box, at pixel center 122 */ 123 rotate(0.5, 0.5, sinangle, cosangle, center, &x0, &y0); 124 rotate(width - 0.5, 0.5, sinangle, cosangle, center, &x1, &y1); 125 rotate(0.5, height - 0.5, sinangle, cosangle, center, &x2, &y2); 126 rotate(width - 0.5, height - 0.5, sinangle, cosangle, center, &x3, &y3); 127 128 minx = (int)SDL_floor(SDL_min(SDL_min(x0, x1), SDL_min(x2, x3))); 129 maxx = (int)SDL_ceil(SDL_max(SDL_max(x0, x1), SDL_max(x2, x3))); 130 131 miny = (int)SDL_floor(SDL_min(SDL_min(y0, y1), SDL_min(y2, y3))); 132 maxy = (int)SDL_ceil(SDL_max(SDL_max(y0, y1), SDL_max(y2, y3))); 133 134 rect_dest->w = maxx - minx; 135 rect_dest->h = maxy - miny; 136 rect_dest->x = minx; 137 rect_dest->y = miny; 138 139 // reverse the angle because our rotations are clockwise 140 *sangle = -sinangle; 141 *cangle = cosangle; 142 143 { 144 // The trig code below gets the wrong size (due to FP inaccuracy?) when angle is a multiple of 90 degrees 145 int angle90 = (int)(angle / 90); 146 if (angle90 == angle / 90) { // if the angle is a multiple of 90 degrees 147 angle90 %= 4; 148 if (angle90 < 0) { 149 angle90 += 4; // 0:0 deg, 1:90 deg, 2:180 deg, 3:270 deg 150 } 151 152 if (angle90 & 1) { 153 rect_dest->w = height; 154 rect_dest->h = width; 155 *cangle = 0; 156 *sangle = angle90 == 1 ? -1 : 1; // reversed because our rotations are clockwise 157 } else { 158 rect_dest->w = width; 159 rect_dest->h = height; 160 *cangle = angle90 == 0 ? 1 : -1; 161 *sangle = 0; 162 } 163 } 164 } 165} 166 167// Computes source pointer X/Y increments for a rotation that's a multiple of 90 degrees. 168static void computeSourceIncrements90(SDL_Surface *src, int bpp, int angle, int flipx, int flipy, 169 int *sincx, int *sincy, int *signx, int *signy) 170{ 171 int pitch = flipy ? -src->pitch : src->pitch; 172 if (flipx) { 173 bpp = -bpp; 174 } 175 switch (angle) { // 0:0 deg, 1:90 deg, 2:180 deg, 3:270 deg 176 case 0: 177 *sincx = bpp; 178 *sincy = pitch - src->w * *sincx; 179 *signx = *signy = 1; 180 break; 181 case 1: 182 *sincx = -pitch; 183 *sincy = bpp - *sincx * src->h; 184 *signx = 1; 185 *signy = -1; 186 break; 187 case 2: 188 *sincx = -bpp; 189 *sincy = -src->w * *sincx - pitch; 190 *signx = *signy = -1; 191 break; 192 case 3: 193 default: 194 *sincx = pitch; 195 *sincy = -*sincx * src->h - bpp; 196 *signx = -1; 197 *signy = 1; 198 break; 199 } 200 if (flipx) { 201 *signx = -*signx; 202 } 203 if (flipy) { 204 *signy = -*signy; 205 } 206} 207 208// Performs a relatively fast rotation/flip when the angle is a multiple of 90 degrees. 209#define TRANSFORM_SURFACE_90(pixelType) \ 210 int dy, dincy = dst->pitch - dst->w * sizeof(pixelType), sincx, sincy, signx, signy; \ 211 Uint8 *sp = (Uint8 *)src->pixels, *dp = (Uint8 *)dst->pixels, *de; \ 212 \ 213 computeSourceIncrements90(src, sizeof(pixelType), angle, flipx, flipy, &sincx, &sincy, &signx, &signy); \ 214 if (signx < 0) \ 215 sp += (src->w - 1) * sizeof(pixelType); \ 216 if (signy < 0) \ 217 sp += (src->h - 1) * src->pitch; \ 218 \ 219 for (dy = 0; dy < dst->h; sp += sincy, dp += dincy, dy++) { \ 220 if (sincx == sizeof(pixelType)) { /* if advancing src and dest equally, use SDL_memcpy */ \ 221 SDL_memcpy(dp, sp, dst->w * sizeof(pixelType)); \ 222 sp += dst->w * sizeof(pixelType); \ 223 dp += dst->w * sizeof(pixelType); \ 224 } else { \ 225 for (de = dp + dst->w * sizeof(pixelType); dp != de; sp += sincx, dp += sizeof(pixelType)) { \ 226 *(pixelType *)dp = *(pixelType *)sp; \ 227 } \ 228 } \ 229 } 230 231static void transformSurfaceRGBA90(SDL_Surface *src, SDL_Surface *dst, int angle, int flipx, int flipy) 232{ 233 TRANSFORM_SURFACE_90(tColorRGBA); 234} 235 236static void transformSurfaceY90(SDL_Surface *src, SDL_Surface *dst, int angle, int flipx, int flipy) 237{ 238 TRANSFORM_SURFACE_90(tColorY); 239} 240 241#undef TRANSFORM_SURFACE_90 242 243/** 244Internal 32 bit rotozoomer with optional anti-aliasing. 245 246Rotates and zooms 32 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control 247parameters by scanning the destination surface and applying optionally anti-aliasing 248by bilinear interpolation. 249Assumes src and dst surfaces are of 32 bit depth. 250Assumes dst surface was allocated with the correct dimensions. 251 252\param src Source surface. 253\param dst Destination surface. 254\param isin Integer version of sine of angle. 255\param icos Integer version of cosine of angle. 256\param flipx Flag indicating horizontal mirroring should be applied. 257\param flipy Flag indicating vertical mirroring should be applied. 258\param smooth Flag indicating anti-aliasing should be used. 259\param rect_dest destination coordinates 260\param center true center. 261*/ 262static void transformSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst, int isin, int icos, 263 int flipx, int flipy, int smooth, 264 const SDL_Rect *rect_dest, 265 const SDL_FPoint *center) 266{ 267 int sw, sh; 268 int cx, cy; 269 tColorRGBA c00, c01, c10, c11, cswap; 270 tColorRGBA *pc, *sp; 271 int gap; 272 const int fp_half = (1 << 15); 273 274 /* 275 * Variable setup 276 */ 277 sw = src->w - 1; 278 sh = src->h - 1; 279 pc = (tColorRGBA *)dst->pixels; 280 gap = dst->pitch - dst->w * 4; 281 cx = (int)(center->x * 65536.0); 282 cy = (int)(center->y * 65536.0); 283 284 /* 285 * Switch between interpolating and non-interpolating code 286 */ 287 if (smooth) { 288 int y; 289 for (y = 0; y < dst->h; y++) { 290 int x; 291 double src_x = ((double)rect_dest->x + 0 + 0.5 - center->x); 292 double src_y = ((double)rect_dest->y + y + 0.5 - center->y); 293 int sdx = (int)((icos * src_x - isin * src_y) + cx - fp_half); 294 int sdy = (int)((isin * src_x + icos * src_y) + cy - fp_half); 295 for (x = 0; x < dst->w; x++) { 296 int dx = (sdx >> 16); 297 int dy = (sdy >> 16); 298 if (flipx) { 299 dx = sw - dx; 300 } 301 if (flipy) { 302 dy = sh - dy; 303 } 304 if ((dx > -1) && (dy > -1) && (dx < (src->w - 1)) && (dy < (src->h - 1))) { 305 int ex, ey; 306 int t1, t2; 307 sp = (tColorRGBA *)((Uint8 *)src->pixels + src->pitch * dy) + dx; 308 c00 = *sp; 309 sp += 1; 310 c01 = *sp; 311 sp += (src->pitch / 4); 312 c11 = *sp; 313 sp -= 1; 314 c10 = *sp; 315 if (flipx) { 316 cswap = c00; 317 c00 = c01; 318 c01 = cswap; 319 cswap = c10; 320 c10 = c11; 321 c11 = cswap; 322 } 323 if (flipy) { 324 cswap = c00; 325 c00 = c10; 326 c10 = cswap; 327 cswap = c01; 328 c01 = c11; 329 c11 = cswap; 330 } 331 /* 332 * Interpolate colors 333 */ 334 ex = (sdx & 0xffff); 335 ey = (sdy & 0xffff); 336 t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff; 337 t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff; 338 pc->r = (Uint8)((((t2 - t1) * ey) >> 16) + t1); 339 t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff; 340 t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff; 341 pc->g = (Uint8)((((t2 - t1) * ey) >> 16) + t1); 342 t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff; 343 t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff; 344 pc->b = (Uint8)((((t2 - t1) * ey) >> 16) + t1); 345 t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff; 346 t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff; 347 pc->a = (Uint8)((((t2 - t1) * ey) >> 16) + t1); 348 } 349 sdx += icos; 350 sdy += isin; 351 pc++; 352 } 353 pc = (tColorRGBA *)((Uint8 *)pc + gap); 354 } 355 } else { 356 int y; 357 for (y = 0; y < dst->h; y++) { 358 int x; 359 double src_x = ((double)rect_dest->x + 0 + 0.5 - center->x); 360 double src_y = ((double)rect_dest->y + y + 0.5 - center->y); 361 int sdx = (int)((icos * src_x - isin * src_y) + cx - fp_half); 362 int sdy = (int)((isin * src_x + icos * src_y) + cy - fp_half); 363 for (x = 0; x < dst->w; x++) { 364 int dx = (sdx >> 16); 365 int dy = (sdy >> 16); 366 if ((unsigned)dx < (unsigned)src->w && (unsigned)dy < (unsigned)src->h) { 367 if (flipx) { 368 dx = sw - dx; 369 } 370 if (flipy) { 371 dy = sh - dy; 372 } 373 *pc = *((tColorRGBA *)((Uint8 *)src->pixels + src->pitch * dy) + dx); 374 } 375 sdx += icos; 376 sdy += isin; 377 pc++; 378 } 379 pc = (tColorRGBA *)((Uint8 *)pc + gap); 380 } 381 } 382} 383 384/** 385 386Rotates and zooms 8 bit palette/Y 'src' surface to 'dst' surface without smoothing. 387 388Rotates and zooms 8 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control 389parameters by scanning the destination surface. 390Assumes src and dst surfaces are of 8 bit depth. 391Assumes dst surface was allocated with the correct dimensions. 392 393\param src Source surface. 394\param dst Destination surface. 395\param isin Integer version of sine of angle. 396\param icos Integer version of cosine of angle. 397\param flipx Flag indicating horizontal mirroring should be applied. 398\param flipy Flag indicating vertical mirroring should be applied. 399\param rect_dest destination coordinates 400\param center true center. 401*/ 402static void transformSurfaceY(SDL_Surface *src, SDL_Surface *dst, int isin, int icos, int flipx, int flipy, 403 const SDL_Rect *rect_dest, 404 const SDL_FPoint *center) 405{ 406 int sw, sh; 407 int cx, cy; 408 tColorY *pc; 409 int gap; 410 const int fp_half = (1 << 15); 411 int y; 412 413 /* 414 * Variable setup 415 */ 416 sw = src->w - 1; 417 sh = src->h - 1; 418 pc = (tColorY *)dst->pixels; 419 gap = dst->pitch - dst->w; 420 cx = (int)(center->x * 65536.0); 421 cy = (int)(center->y * 65536.0); 422 423 /* 424 * Clear surface to colorkey 425 */ 426 SDL_memset(pc, (int)(get_colorkey(src) & 0xff), (size_t)dst->pitch * dst->h); 427 /* 428 * Iterate through destination surface 429 */ 430 for (y = 0; y < dst->h; y++) { 431 int x; 432 double src_x = ((double)rect_dest->x + 0 + 0.5 - center->x); 433 double src_y = ((double)rect_dest->y + y + 0.5 - center->y); 434 int sdx = (int)((icos * src_x - isin * src_y) + cx - fp_half); 435 int sdy = (int)((isin * src_x + icos * src_y) + cy - fp_half); 436 for (x = 0; x < dst->w; x++) { 437 int dx = (sdx >> 16); 438 int dy = (sdy >> 16); 439 if ((unsigned)dx < (unsigned)src->w && (unsigned)dy < (unsigned)src->h) { 440 if (flipx) { 441 dx = sw - dx; 442 } 443 if (flipy) { 444 dy = sh - dy; 445 } 446 *pc = *((tColorY *)src->pixels + src->pitch * dy + dx); 447 } 448 sdx += icos; 449 sdy += isin; 450 pc++; 451 } 452 pc += gap; 453 } 454} 455 456/** 457Rotates and zooms a surface with different horizontal and vertival scaling factors and optional anti-aliasing. 458 459Rotates a 32-bit or 8-bit 'src' surface to newly created 'dst' surface. 460'angle' is the rotation in degrees, 'center' the rotation center. If 'smooth' is set 461then the destination 32-bit surface is anti-aliased. 8-bit surfaces must have a colorkey. 32-bit 462surfaces must have a 8888 layout with red, green, blue and alpha masks (any ordering goes). 463The blend mode of the 'src' surface has some effects on generation of the 'dst' surface: The NONE 464mode will set the BLEND mode on the 'dst' surface. The MOD mode either generates a white 'dst' 465surface and sets the colorkey or fills the it with the colorkey before copying the pixels. 466When using the NONE and MOD modes, color and alpha modulation must be applied before using this function. 467 468\param src The surface to rotozoom. 469\param angle The angle to rotate in degrees. 470\param smooth Antialiasing flag; set to SMOOTHING_ON to enable. 471\param flipx Set to 1 to flip the image horizontally 472\param flipy Set to 1 to flip the image vertically 473\param rect_dest The destination rect bounding box 474\param cangle The angle cosine 475\param sangle The angle sine 476\param center The true coordinate of the center of rotation 477\return The new rotated surface. 478 479*/ 480 481SDL_Surface *SDLgfx_rotateSurface(SDL_Surface *src, double angle, int smooth, int flipx, int flipy, 482 const SDL_Rect *rect_dest, double cangle, double sangle, const SDL_FPoint *center) 483{ 484 SDL_Surface *rz_dst; 485 int is8bit, angle90; 486 SDL_BlendMode blendmode; 487 Uint32 colorkey = 0; 488 bool colorKeyAvailable = false; 489 double sangleinv, cangleinv; 490 491 // Sanity check 492 if (!SDL_SurfaceValid(src)) { 493 return NULL; 494 } 495 496 if (SDL_SurfaceHasColorKey(src)) { 497 if (SDL_GetSurfaceColorKey(src, &colorkey)) { 498 colorKeyAvailable = true; 499 } 500 } 501 // This function requires a 32-bit surface or 8-bit surface with a colorkey 502 is8bit = (src->format == SDL_PIXELFORMAT_INDEX8) && colorKeyAvailable; 503 if (!is8bit && 504 !(SDL_BITSPERPIXEL(src->format) == 32 && SDL_PIXELLAYOUT(src->format) == SDL_PACKEDLAYOUT_8888)) { 505 return NULL; 506 } 507 508 // Calculate target factors from sine/cosine and zoom 509 sangleinv = sangle * 65536.0; 510 cangleinv = cangle * 65536.0; 511 512 // Alloc space to completely contain the rotated surface 513 rz_dst = NULL; 514 if (is8bit) { 515 // Target surface is 8 bit 516 rz_dst = SDL_CreateSurface(rect_dest->w, rect_dest->h + GUARD_ROWS, src->format); 517 if (rz_dst) { 518 SDL_SetSurfacePalette(rz_dst, src->palette); 519 } 520 } else { 521 // Target surface is 32 bit with source RGBA ordering 522 rz_dst = SDL_CreateSurface(rect_dest->w, rect_dest->h + GUARD_ROWS, src->format); 523 } 524 525 // Check target 526 if (!rz_dst) { 527 return NULL; 528 } 529 530 // Adjust for guard rows 531 rz_dst->h = rect_dest->h; 532 533 SDL_GetSurfaceBlendMode(src, &blendmode); 534 535 if (colorKeyAvailable) { 536 // If available, the colorkey will be used to discard the pixels that are outside of the rotated area. 537 SDL_SetSurfaceColorKey(rz_dst, true, colorkey); 538 SDL_FillSurfaceRect(rz_dst, NULL, colorkey); 539 } else if (blendmode == SDL_BLENDMODE_NONE) { 540 blendmode = SDL_BLENDMODE_BLEND; 541 } else if (blendmode == SDL_BLENDMODE_MOD || blendmode == SDL_BLENDMODE_MUL) { 542 /* Without a colorkey, the target texture has to be white for the MOD and MUL blend mode so 543 * that the pixels outside the rotated area don't affect the destination surface. 544 */ 545 colorkey = SDL_MapSurfaceRGBA(rz_dst, 255, 255, 255, 0); 546 SDL_FillSurfaceRect(rz_dst, NULL, colorkey); 547 /* Setting a white colorkey for the destination surface makes the final blit discard 548 * all pixels outside of the rotated area. This doesn't interfere with anything because 549 * white pixels are already a no-op and the MOD blend mode does not interact with alpha. 550 */ 551 SDL_SetSurfaceColorKey(rz_dst, true, colorkey); 552 } 553 554 SDL_SetSurfaceBlendMode(rz_dst, blendmode); 555 556 // Lock source surface 557 if (SDL_MUSTLOCK(src)) { 558 if (!SDL_LockSurface(src)) { 559 SDL_DestroySurface(rz_dst); 560 return NULL; 561 } 562 } 563 564 /* check if the rotation is a multiple of 90 degrees so we can take a fast path and also somewhat reduce 565 * the off-by-one problem in transformSurfaceRGBA that expresses itself when the rotation is near 566 * multiples of 90 degrees. 567 */ 568 angle90 = (int)(angle / 90); 569 if (angle90 == angle / 90) { 570 angle90 %= 4; 571 if (angle90 < 0) { 572 angle90 += 4; // 0:0 deg, 1:90 deg, 2:180 deg, 3:270 deg 573 } 574 575 } else { 576 angle90 = -1; 577 } 578 579 if (is8bit) { 580 // Call the 8-bit transformation routine to do the rotation 581 if (angle90 >= 0) { 582 transformSurfaceY90(src, rz_dst, angle90, flipx, flipy); 583 } else { 584 transformSurfaceY(src, rz_dst, (int)sangleinv, (int)cangleinv, 585 flipx, flipy, rect_dest, center); 586 } 587 } else { 588 // Call the 32-bit transformation routine to do the rotation 589 if (angle90 >= 0) { 590 transformSurfaceRGBA90(src, rz_dst, angle90, flipx, flipy); 591 } else { 592 transformSurfaceRGBA(src, rz_dst, (int)sangleinv, (int)cangleinv, 593 flipx, flipy, smooth, rect_dest, center); 594 } 595 } 596 597 // Unlock source surface 598 if (SDL_MUSTLOCK(src)) { 599 SDL_UnlockSurface(src); 600 } 601 602 // Return rotated surface 603 return rz_dst; 604} 605 606
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.