Atlas - SDL_rotate.c
Home / ext / SDL2 / src / render / software Lines: 1 | Size: 20124 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#if defined(__WIN32__) 34#include "../../core/windows/SDL_windows.h" 35#endif 36 37#include <stdlib.h> 38#include <string.h> 39 40#include "SDL.h" 41#include "SDL_rotate.h" 42 43/* ---- Internally used structures */ 44 45/* ! 46\brief A 32 bit RGBA pixel. 47*/ 48typedef struct tColorRGBA { 49 Uint8 r; 50 Uint8 g; 51 Uint8 b; 52 Uint8 a; 53} tColorRGBA; 54 55/* ! 56\brief A 8bit Y/palette pixel. 57*/ 58typedef struct tColorY { 59 Uint8 y; 60} tColorY; 61 62/* ! 63\brief Returns maximum of two numbers a and b. 64*/ 65#define MAX(a,b) (((a) > (b)) ? (a) : (b)) 66 67/* ! 68\brief Number of guard rows added to destination surfaces. 69 70This is a simple but effective workaround for observed issues. 71These rows allocate extra memory and are then hidden from the surface. 72Rows are added to the end of destination surfaces when they are allocated. 73This catches any potential overflows which seem to happen with 74just the right src image dimensions and scale/rotation and can lead 75to a situation where the program can segfault. 76*/ 77#define GUARD_ROWS (2) 78 79/* ! 80\brief Returns colorkey info for a surface 81*/ 82static Uint32 83_colorkey(SDL_Surface *src) 84{ 85 Uint32 key = 0; 86 SDL_GetColorKey(src, &key); 87 return key; 88} 89 90 91/* ! 92\brief Internal target surface sizing function for rotations with trig result return. 93 94\param width The source surface width. 95\param height The source surface height. 96\param angle The angle to rotate in degrees. 97\param dstwidth The calculated width of the destination surface. 98\param dstheight The calculated height of the destination surface. 99\param cangle The sine of the angle 100\param sangle The cosine of the angle 101 102*/ 103void 104SDLgfx_rotozoomSurfaceSizeTrig(int width, int height, double angle, 105 int *dstwidth, int *dstheight, 106 double *cangle, double *sangle) 107{ 108 /* The trig code below gets the wrong size (due to FP inaccuracy?) when angle is a multiple of 90 degrees */ 109 int angle90 = (int)(angle/90); 110 if(angle90 == angle/90) { /* if the angle is a multiple of 90 degrees */ 111 angle90 %= 4; 112 if(angle90 < 0) angle90 += 4; /* 0:0 deg, 1:90 deg, 2:180 deg, 3:270 deg */ 113 if(angle90 & 1) { 114 *dstwidth = height; 115 *dstheight = width; 116 *cangle = 0; 117 *sangle = angle90 == 1 ? -1 : 1; /* reversed because our rotations are clockwise */ 118 } else { 119 *dstwidth = width; 120 *dstheight = height; 121 *cangle = angle90 == 0 ? 1 : -1; 122 *sangle = 0; 123 } 124 } else { 125 double x, y, cx, cy, sx, sy; 126 double radangle; 127 int dstwidthhalf, dstheighthalf; 128 /* 129 * Determine destination width and height by rotating a centered source box 130 */ 131 radangle = angle * (M_PI / -180.0); /* reverse the angle because our rotations are clockwise */ 132 *sangle = SDL_sin(radangle); 133 *cangle = SDL_cos(radangle); 134 x = (double)(width / 2); 135 y = (double)(height / 2); 136 cx = *cangle * x; 137 cy = *cangle * y; 138 sx = *sangle * x; 139 sy = *sangle * y; 140 141 dstwidthhalf = MAX((int) 142 SDL_ceil(MAX(MAX(MAX(SDL_fabs(cx + sy), SDL_fabs(cx - sy)), SDL_fabs(-cx + sy)), SDL_fabs(-cx - sy))), 1); 143 dstheighthalf = MAX((int) 144 SDL_ceil(MAX(MAX(MAX(SDL_fabs(sx + cy), SDL_fabs(sx - cy)), SDL_fabs(-sx + cy)), SDL_fabs(-sx - cy))), 1); 145 *dstwidth = 2 * dstwidthhalf; 146 *dstheight = 2 * dstheighthalf; 147 } 148} 149 150/* Computes source pointer X/Y increments for a rotation that's a multiple of 90 degrees. */ 151static void 152computeSourceIncrements90(SDL_Surface * src, int bpp, int angle, int flipx, int flipy, 153 int *sincx, int *sincy, int *signx, int *signy) 154{ 155 int pitch = flipy ? -src->pitch : src->pitch; 156 if (flipx) { 157 bpp = -bpp; 158 } 159 switch (angle) { /* 0:0 deg, 1:90 deg, 2:180 deg, 3:270 deg */ 160 case 0: *sincx = bpp; *sincy = pitch - src->w * *sincx; *signx = *signy = 1; break; 161 case 1: *sincx = -pitch; *sincy = bpp - *sincx * src->h; *signx = 1; *signy = -1; break; 162 case 2: *sincx = -bpp; *sincy = -src->w * *sincx - pitch; *signx = *signy = -1; break; 163 case 3: default: *sincx = pitch; *sincy = -*sincx * src->h - bpp; *signx = -1; *signy = 1; break; 164 } 165 if (flipx) { 166 *signx = -*signx; 167 } 168 if (flipy) { 169 *signy = -*signy; 170 } 171} 172 173/* Performs a relatively fast rotation/flip when the angle is a multiple of 90 degrees. */ 174#define TRANSFORM_SURFACE_90(pixelType) \ 175 int dy, dincy = dst->pitch - dst->w*sizeof(pixelType), sincx, sincy, signx, signy; \ 176 Uint8 *sp = (Uint8*)src->pixels, *dp = (Uint8*)dst->pixels, *de; \ 177 \ 178 computeSourceIncrements90(src, sizeof(pixelType), angle, flipx, flipy, &sincx, &sincy, &signx, &signy); \ 179 if (signx < 0) sp += (src->w-1)*sizeof(pixelType); \ 180 if (signy < 0) sp += (src->h-1)*src->pitch; \ 181 \ 182 for (dy = 0; dy < dst->h; sp += sincy, dp += dincy, dy++) { \ 183 if (sincx == sizeof(pixelType)) { /* if advancing src and dest equally, use memcpy */ \ 184 SDL_memcpy(dp, sp, dst->w*sizeof(pixelType)); \ 185 sp += dst->w*sizeof(pixelType); \ 186 dp += dst->w*sizeof(pixelType); \ 187 } else { \ 188 for (de = dp + dst->w*sizeof(pixelType); dp != de; sp += sincx, dp += sizeof(pixelType)) { \ 189 *(pixelType*)dp = *(pixelType*)sp; \ 190 } \ 191 } \ 192 } 193 194static void 195transformSurfaceRGBA90(SDL_Surface * src, SDL_Surface * dst, int angle, int flipx, int flipy) 196{ 197 TRANSFORM_SURFACE_90(tColorRGBA); 198} 199 200static void 201transformSurfaceY90(SDL_Surface * src, SDL_Surface * dst, int angle, int flipx, int flipy) 202{ 203 TRANSFORM_SURFACE_90(tColorY); 204} 205 206#undef TRANSFORM_SURFACE_90 207 208/* ! 209\brief Internal 32 bit rotozoomer with optional anti-aliasing. 210 211Rotates and zooms 32 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control 212parameters by scanning the destination surface and applying optionally anti-aliasing 213by bilinear interpolation. 214Assumes src and dst surfaces are of 32 bit depth. 215Assumes dst surface was allocated with the correct dimensions. 216 217\param src Source surface. 218\param dst Destination surface. 219\param cx Horizontal center coordinate. 220\param cy Vertical center coordinate. 221\param isin Integer version of sine of angle. 222\param icos Integer version of cosine of angle. 223\param flipx Flag indicating horizontal mirroring should be applied. 224\param flipy Flag indicating vertical mirroring should be applied. 225\param smooth Flag indicating anti-aliasing should be used. 226*/ 227static void 228_transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy, int smooth) 229{ 230 int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh; 231 tColorRGBA c00, c01, c10, c11, cswap; 232 tColorRGBA *pc, *sp; 233 int gap; 234 235 /* 236 * Variable setup 237 */ 238 xd = ((src->w - dst->w) << 15); 239 yd = ((src->h - dst->h) << 15); 240 ax = (cx << 16) - (icos * cx); 241 ay = (cy << 16) - (isin * cx); 242 sw = src->w - 1; 243 sh = src->h - 1; 244 pc = (tColorRGBA*) dst->pixels; 245 gap = dst->pitch - dst->w * 4; 246 247 /* 248 * Switch between interpolating and non-interpolating code 249 */ 250 if (smooth) { 251 for (y = 0; y < dst->h; y++) { 252 dy = cy - y; 253 sdx = (ax + (isin * dy)) + xd; 254 sdy = (ay - (icos * dy)) + yd; 255 for (x = 0; x < dst->w; x++) { 256 dx = (sdx >> 16); 257 dy = (sdy >> 16); 258 if (flipx) dx = sw - dx; 259 if (flipy) dy = sh - dy; 260 if ((dx > -1) && (dy > -1) && (dx < (src->w-1)) && (dy < (src->h-1))) { 261 sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy) + dx; 262 c00 = *sp; 263 sp += 1; 264 c01 = *sp; 265 sp += (src->pitch/4); 266 c11 = *sp; 267 sp -= 1; 268 c10 = *sp; 269 if (flipx) { 270 cswap = c00; c00=c01; c01=cswap; 271 cswap = c10; c10=c11; c11=cswap; 272 } 273 if (flipy) { 274 cswap = c00; c00=c10; c10=cswap; 275 cswap = c01; c01=c11; c11=cswap; 276 } 277 /* 278 * Interpolate colors 279 */ 280 ex = (sdx & 0xffff); 281 ey = (sdy & 0xffff); 282 t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff; 283 t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff; 284 pc->r = (((t2 - t1) * ey) >> 16) + t1; 285 t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff; 286 t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff; 287 pc->g = (((t2 - t1) * ey) >> 16) + t1; 288 t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff; 289 t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff; 290 pc->b = (((t2 - t1) * ey) >> 16) + t1; 291 t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff; 292 t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff; 293 pc->a = (((t2 - t1) * ey) >> 16) + t1; 294 } 295 sdx += icos; 296 sdy += isin; 297 pc++; 298 } 299 pc = (tColorRGBA *) ((Uint8 *) pc + gap); 300 } 301 } else { 302 for (y = 0; y < dst->h; y++) { 303 dy = cy - y; 304 sdx = (ax + (isin * dy)) + xd; 305 sdy = (ay - (icos * dy)) + yd; 306 for (x = 0; x < dst->w; x++) { 307 dx = (sdx >> 16); 308 dy = (sdy >> 16); 309 if ((unsigned)dx < (unsigned)src->w && (unsigned)dy < (unsigned)src->h) { 310 if(flipx) dx = sw - dx; 311 if(flipy) dy = sh - dy; 312 *pc = *((tColorRGBA *)((Uint8 *)src->pixels + src->pitch * dy) + dx); 313 } 314 sdx += icos; 315 sdy += isin; 316 pc++; 317 } 318 pc = (tColorRGBA *) ((Uint8 *) pc + gap); 319 } 320 } 321} 322 323/* ! 324 325\brief Rotates and zooms 8 bit palette/Y 'src' surface to 'dst' surface without smoothing. 326 327Rotates and zooms 8 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control 328parameters by scanning the destination surface. 329Assumes src and dst surfaces are of 8 bit depth. 330Assumes dst surface was allocated with the correct dimensions. 331 332\param src Source surface. 333\param dst Destination surface. 334\param cx Horizontal center coordinate. 335\param cy Vertical center coordinate. 336\param isin Integer version of sine of angle. 337\param icos Integer version of cosine of angle. 338\param flipx Flag indicating horizontal mirroring should be applied. 339\param flipy Flag indicating vertical mirroring should be applied. 340*/ 341static void 342transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy) 343{ 344 int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay; 345 tColorY *pc; 346 int gap; 347 348 /* 349 * Variable setup 350 */ 351 xd = ((src->w - dst->w) << 15); 352 yd = ((src->h - dst->h) << 15); 353 ax = (cx << 16) - (icos * cx); 354 ay = (cy << 16) - (isin * cx); 355 pc = (tColorY*) dst->pixels; 356 gap = dst->pitch - dst->w; 357 /* 358 * Clear surface to colorkey 359 */ 360 SDL_memset(pc, (int)(_colorkey(src) & 0xff), dst->pitch * dst->h); 361 /* 362 * Iterate through destination surface 363 */ 364 for (y = 0; y < dst->h; y++) { 365 dy = cy - y; 366 sdx = (ax + (isin * dy)) + xd; 367 sdy = (ay - (icos * dy)) + yd; 368 for (x = 0; x < dst->w; x++) { 369 dx = (sdx >> 16); 370 dy = (sdy >> 16); 371 if ((unsigned)dx < (unsigned)src->w && (unsigned)dy < (unsigned)src->h) { 372 if (flipx) dx = (src->w-1)-dx; 373 if (flipy) dy = (src->h-1)-dy; 374 *pc = *((tColorY *)src->pixels + src->pitch * dy + dx); 375 } 376 sdx += icos; 377 sdy += isin; 378 pc++; 379 } 380 pc += gap; 381 } 382} 383 384 385/* ! 386\brief Rotates and zooms a surface with different horizontal and vertival scaling factors and optional anti-aliasing. 387 388Rotates a 32-bit or 8-bit 'src' surface to newly created 'dst' surface. 389'angle' is the rotation in degrees, 'centerx' and 'centery' the rotation center. If 'smooth' is set 390then the destination 32-bit surface is anti-aliased. 8-bit surfaces must have a colorkey. 32-bit 391surfaces must have a 8888 layout with red, green, blue and alpha masks (any ordering goes). 392The blend mode of the 'src' surface has some effects on generation of the 'dst' surface: The NONE 393mode will set the BLEND mode on the 'dst' surface. The MOD mode either generates a white 'dst' 394surface and sets the colorkey or fills the it with the colorkey before copying the pixels. 395When using the NONE and MOD modes, color and alpha modulation must be applied before using this function. 396 397\param src The surface to rotozoom. 398\param angle The angle to rotate in degrees. 399\param centerx The horizontal coordinate of the center of rotation 400\param zoomy The vertical coordinate of the center of rotation 401\param smooth Antialiasing flag; set to SMOOTHING_ON to enable. 402\param flipx Set to 1 to flip the image horizontally 403\param flipy Set to 1 to flip the image vertically 404\param dstwidth The destination surface width 405\param dstheight The destination surface height 406\param cangle The angle cosine 407\param sangle The angle sine 408\return The new rotated surface. 409 410*/ 411 412SDL_Surface * 413SDLgfx_rotateSurface(SDL_Surface * src, double angle, int centerx, int centery, int smooth, int flipx, int flipy, int dstwidth, int dstheight, double cangle, double sangle) 414{ 415 SDL_Surface *rz_dst; 416 int is8bit, angle90; 417 int i; 418 SDL_BlendMode blendmode; 419 Uint32 colorkey = 0; 420 int colorKeyAvailable = SDL_FALSE; 421 double sangleinv, cangleinv; 422 423 /* Sanity check */ 424 if (src == NULL) 425 return NULL; 426 427 if (SDL_GetColorKey(src, &colorkey) == 0) { 428 colorKeyAvailable = SDL_TRUE; 429 } 430 431 /* This function requires a 32-bit surface or 8-bit surface with a colorkey */ 432 is8bit = src->format->BitsPerPixel == 8 && colorKeyAvailable; 433 if (!(is8bit || (src->format->BitsPerPixel == 32 && src->format->Amask))) 434 return NULL; 435 436 /* Calculate target factors from sin/cos and zoom */ 437 sangleinv = sangle*65536.0; 438 cangleinv = cangle*65536.0; 439 440 /* Alloc space to completely contain the rotated surface */ 441 rz_dst = NULL; 442 if (is8bit) { 443 /* Target surface is 8 bit */ 444 rz_dst = SDL_CreateRGBSurface(0, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0); 445 if (rz_dst != NULL) { 446 for (i = 0; i < src->format->palette->ncolors; i++) { 447 rz_dst->format->palette->colors[i] = src->format->palette->colors[i]; 448 } 449 rz_dst->format->palette->ncolors = src->format->palette->ncolors; 450 } 451 } else { 452 /* Target surface is 32 bit with source RGBA ordering */ 453 rz_dst = SDL_CreateRGBSurface(0, dstwidth, dstheight + GUARD_ROWS, 32, 454 src->format->Rmask, src->format->Gmask, 455 src->format->Bmask, src->format->Amask); 456 } 457 458 /* Check target */ 459 if (rz_dst == NULL) 460 return NULL; 461 462 /* Adjust for guard rows */ 463 rz_dst->h = dstheight; 464 465 SDL_GetSurfaceBlendMode(src, &blendmode); 466 467 if (colorKeyAvailable == SDL_TRUE) { 468 /* If available, the colorkey will be used to discard the pixels that are outside of the rotated area. */ 469 SDL_SetColorKey(rz_dst, SDL_TRUE, colorkey); 470 SDL_FillRect(rz_dst, NULL, colorkey); 471 } else if (blendmode == SDL_BLENDMODE_NONE) { 472 blendmode = SDL_BLENDMODE_BLEND; 473 } else if (blendmode == SDL_BLENDMODE_MOD) { 474 /* Without a colorkey, the target texture has to be white for the MOD blend mode so 475 * that the pixels outside the rotated area don't affect the destination surface. 476 */ 477 colorkey = SDL_MapRGBA(rz_dst->format, 255, 255, 255, 0); 478 SDL_FillRect(rz_dst, NULL, colorkey); 479 /* Setting a white colorkey for the destination surface makes the final blit discard 480 * all pixels outside of the rotated area. This doesn't interfere with anything because 481 * white pixels are already a no-op and the MOD blend mode does not interact with alpha. 482 */ 483 SDL_SetColorKey(rz_dst, SDL_TRUE, colorkey); 484 } 485 486 SDL_SetSurfaceBlendMode(rz_dst, blendmode); 487 488 /* Lock source surface */ 489 if (SDL_MUSTLOCK(src)) { 490 SDL_LockSurface(src); 491 } 492 493 /* check if the rotation is a multiple of 90 degrees so we can take a fast path and also somewhat reduce 494 * the off-by-one problem in _transformSurfaceRGBA that expresses itself when the rotation is near 495 * multiples of 90 degrees. 496 */ 497 angle90 = (int)(angle/90); 498 if (angle90 == angle/90) { 499 angle90 %= 4; 500 if (angle90 < 0) angle90 += 4; /* 0:0 deg, 1:90 deg, 2:180 deg, 3:270 deg */ 501 } else { 502 angle90 = -1; 503 } 504 505 if (is8bit) { 506 /* Call the 8-bit transformation routine to do the rotation */ 507 if(angle90 >= 0) { 508 transformSurfaceY90(src, rz_dst, angle90, flipx, flipy); 509 } else { 510 transformSurfaceY(src, rz_dst, centerx, centery, (int)sangleinv, (int)cangleinv, 511 flipx, flipy); 512 } 513 } else { 514 /* Call the 32-bit transformation routine to do the rotation */ 515 if (angle90 >= 0) { 516 transformSurfaceRGBA90(src, rz_dst, angle90, flipx, flipy); 517 } else { 518 _transformSurfaceRGBA(src, rz_dst, centerx, centery, (int)sangleinv, (int)cangleinv, 519 flipx, flipy, smooth); 520 } 521 } 522 523 /* Unlock source surface */ 524 if (SDL_MUSTLOCK(src)) { 525 SDL_UnlockSurface(src); 526 } 527 528 /* Return rotated surface */ 529 return rz_dst; 530} 531[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.