SDL_rotate.c (15718B)
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 Lower limit of absolute zoom factor or rotation degrees. 81*/ 82#define VALUE_LIMIT 0.001 83 84/* ! 85\brief Returns colorkey info for a surface 86*/ 87static Uint32 88_colorkey(SDL_Surface *src) 89{ 90 Uint32 key = 0; 91 SDL_GetColorKey(src, &key); 92 return key; 93} 94 95 96/* ! 97\brief Internal target surface sizing function for rotations with trig result return. 98 99\param width The source surface width. 100\param height The source surface height. 101\param angle The angle to rotate in degrees. 102\param dstwidth The calculated width of the destination surface. 103\param dstheight The calculated height of the destination surface. 104\param cangle The sine of the angle 105\param sangle The cosine of the angle 106 107*/ 108void 109SDLgfx_rotozoomSurfaceSizeTrig(int width, int height, double angle, 110 int *dstwidth, int *dstheight, 111 double *cangle, double *sangle) 112{ 113 double x, y, cx, cy, sx, sy; 114 double radangle; 115 int dstwidthhalf, dstheighthalf; 116 117 /* 118 * Determine destination width and height by rotating a centered source box 119 */ 120 radangle = angle * (M_PI / 180.0); 121 *sangle = SDL_sin(radangle); 122 *cangle = SDL_cos(radangle); 123 x = (double)(width / 2); 124 y = (double)(height / 2); 125 cx = *cangle * x; 126 cy = *cangle * y; 127 sx = *sangle * x; 128 sy = *sangle * y; 129 130 dstwidthhalf = MAX((int) 131 SDL_ceil(MAX(MAX(MAX(SDL_fabs(cx + sy), SDL_fabs(cx - sy)), SDL_fabs(-cx + sy)), SDL_fabs(-cx - sy))), 1); 132 dstheighthalf = MAX((int) 133 SDL_ceil(MAX(MAX(MAX(SDL_fabs(sx + cy), SDL_fabs(sx - cy)), SDL_fabs(-sx + cy)), SDL_fabs(-sx - cy))), 1); 134 *dstwidth = 2 * dstwidthhalf; 135 *dstheight = 2 * dstheighthalf; 136} 137 138 139/* ! 140\brief Internal 32 bit rotozoomer with optional anti-aliasing. 141 142Rotates and zooms 32 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control 143parameters by scanning the destination surface and applying optionally anti-aliasing 144by bilinear interpolation. 145Assumes src and dst surfaces are of 32 bit depth. 146Assumes dst surface was allocated with the correct dimensions. 147 148\param src Source surface. 149\param dst Destination surface. 150\param cx Horizontal center coordinate. 151\param cy Vertical center coordinate. 152\param isin Integer version of sine of angle. 153\param icos Integer version of cosine of angle. 154\param flipx Flag indicating horizontal mirroring should be applied. 155\param flipy Flag indicating vertical mirroring should be applied. 156\param smooth Flag indicating anti-aliasing should be used. 157*/ 158static void 159_transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy, int smooth) 160{ 161 int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh; 162 tColorRGBA c00, c01, c10, c11, cswap; 163 tColorRGBA *pc, *sp; 164 int gap; 165 166 /* 167 * Variable setup 168 */ 169 xd = ((src->w - dst->w) << 15); 170 yd = ((src->h - dst->h) << 15); 171 ax = (cx << 16) - (icos * cx); 172 ay = (cy << 16) - (isin * cx); 173 sw = src->w - 1; 174 sh = src->h - 1; 175 pc = (tColorRGBA*) dst->pixels; 176 gap = dst->pitch - dst->w * 4; 177 178 /* 179 * Switch between interpolating and non-interpolating code 180 */ 181 if (smooth) { 182 for (y = 0; y < dst->h; y++) { 183 dy = cy - y; 184 sdx = (ax + (isin * dy)) + xd; 185 sdy = (ay - (icos * dy)) + yd; 186 for (x = 0; x < dst->w; x++) { 187 dx = (sdx >> 16); 188 dy = (sdy >> 16); 189 if (flipx) dx = sw - dx; 190 if (flipy) dy = sh - dy; 191 if ((dx > -1) && (dy > -1) && (dx < (src->w-1)) && (dy < (src->h-1))) { 192 sp = (tColorRGBA *)src->pixels; 193 sp += ((src->pitch/4) * dy); 194 sp += dx; 195 c00 = *sp; 196 sp += 1; 197 c01 = *sp; 198 sp += (src->pitch/4); 199 c11 = *sp; 200 sp -= 1; 201 c10 = *sp; 202 if (flipx) { 203 cswap = c00; c00=c01; c01=cswap; 204 cswap = c10; c10=c11; c11=cswap; 205 } 206 if (flipy) { 207 cswap = c00; c00=c10; c10=cswap; 208 cswap = c01; c01=c11; c11=cswap; 209 } 210 /* 211 * Interpolate colors 212 */ 213 ex = (sdx & 0xffff); 214 ey = (sdy & 0xffff); 215 t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff; 216 t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff; 217 pc->r = (((t2 - t1) * ey) >> 16) + t1; 218 t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff; 219 t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff; 220 pc->g = (((t2 - t1) * ey) >> 16) + t1; 221 t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff; 222 t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff; 223 pc->b = (((t2 - t1) * ey) >> 16) + t1; 224 t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff; 225 t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff; 226 pc->a = (((t2 - t1) * ey) >> 16) + t1; 227 } 228 sdx += icos; 229 sdy += isin; 230 pc++; 231 } 232 pc = (tColorRGBA *) ((Uint8 *) pc + gap); 233 } 234 } else { 235 for (y = 0; y < dst->h; y++) { 236 dy = cy - y; 237 sdx = (ax + (isin * dy)) + xd; 238 sdy = (ay - (icos * dy)) + yd; 239 for (x = 0; x < dst->w; x++) { 240 dx = (short) (sdx >> 16); 241 dy = (short) (sdy >> 16); 242 if (flipx) dx = (src->w-1)-dx; 243 if (flipy) dy = (src->h-1)-dy; 244 if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) { 245 sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); 246 sp += dx; 247 *pc = *sp; 248 } 249 sdx += icos; 250 sdy += isin; 251 pc++; 252 } 253 pc = (tColorRGBA *) ((Uint8 *) pc + gap); 254 } 255 } 256} 257 258/* ! 259 260\brief Rotates and zooms 8 bit palette/Y 'src' surface to 'dst' surface without smoothing. 261 262Rotates and zooms 8 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control 263parameters by scanning the destination surface. 264Assumes src and dst surfaces are of 8 bit depth. 265Assumes dst surface was allocated with the correct dimensions. 266 267\param src Source surface. 268\param dst Destination surface. 269\param cx Horizontal center coordinate. 270\param cy Vertical center coordinate. 271\param isin Integer version of sine of angle. 272\param icos Integer version of cosine of angle. 273\param flipx Flag indicating horizontal mirroring should be applied. 274\param flipy Flag indicating vertical mirroring should be applied. 275*/ 276static void 277transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy) 278{ 279 int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay; 280 tColorY *pc, *sp; 281 int gap; 282 283 /* 284 * Variable setup 285 */ 286 xd = ((src->w - dst->w) << 15); 287 yd = ((src->h - dst->h) << 15); 288 ax = (cx << 16) - (icos * cx); 289 ay = (cy << 16) - (isin * cx); 290 pc = (tColorY*) dst->pixels; 291 gap = dst->pitch - dst->w; 292 /* 293 * Clear surface to colorkey 294 */ 295 SDL_memset(pc, (int)(_colorkey(src) & 0xff), dst->pitch * dst->h); 296 /* 297 * Iterate through destination surface 298 */ 299 for (y = 0; y < dst->h; y++) { 300 dy = cy - y; 301 sdx = (ax + (isin * dy)) + xd; 302 sdy = (ay - (icos * dy)) + yd; 303 for (x = 0; x < dst->w; x++) { 304 dx = (short) (sdx >> 16); 305 dy = (short) (sdy >> 16); 306 if (flipx) dx = (src->w-1)-dx; 307 if (flipy) dy = (src->h-1)-dy; 308 if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) { 309 sp = (tColorY *) (src->pixels); 310 sp += (src->pitch * dy + dx); 311 *pc = *sp; 312 } 313 sdx += icos; 314 sdy += isin; 315 pc++; 316 } 317 pc += gap; 318 } 319} 320 321 322/* ! 323\brief Rotates and zooms a surface with different horizontal and vertival scaling factors and optional anti-aliasing. 324 325Rotates a 32bit or 8bit 'src' surface to newly created 'dst' surface. 326'angle' is the rotation in degrees, 'centerx' and 'centery' the rotation center. If 'smooth' is set 327then the destination 32bit surface is anti-aliased. If the surface is not 8bit 328or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. 329 330\param src The surface to rotozoom. 331\param angle The angle to rotate in degrees. 332\param centerx The horizontal coordinate of the center of rotation 333\param zoomy The vertical coordinate of the center of rotation 334\param smooth Antialiasing flag; set to SMOOTHING_ON to enable. 335\param flipx Set to 1 to flip the image horizontally 336\param flipy Set to 1 to flip the image vertically 337\param dstwidth The destination surface width 338\param dstheight The destination surface height 339\param cangle The angle cosine 340\param sangle The angle sine 341\return The new rotated surface. 342 343*/ 344 345SDL_Surface * 346SDLgfx_rotateSurface(SDL_Surface * src, double angle, int centerx, int centery, int smooth, int flipx, int flipy, int dstwidth, int dstheight, double cangle, double sangle) 347{ 348 SDL_Surface *rz_src; 349 SDL_Surface *rz_dst; 350 int is32bit; 351 int i, src_converted; 352 Uint8 r,g,b; 353 Uint32 colorkey = 0; 354 int colorKeyAvailable = 0; 355 double sangleinv, cangleinv; 356 357 /* 358 * Sanity check 359 */ 360 if (src == NULL) 361 return (NULL); 362 363 if (src->flags & SDL_TRUE/* SDL_SRCCOLORKEY */) 364 { 365 colorkey = _colorkey(src); 366 SDL_GetRGB(colorkey, src->format, &r, &g, &b); 367 colorKeyAvailable = 1; 368 } 369 /* 370 * Determine if source surface is 32bit or 8bit 371 */ 372 is32bit = (src->format->BitsPerPixel == 32); 373 if ((is32bit) || (src->format->BitsPerPixel == 8)) { 374 /* 375 * Use source surface 'as is' 376 */ 377 rz_src = src; 378 src_converted = 0; 379 } else { 380 /* 381 * New source surface is 32bit with a defined RGBA ordering 382 */ 383 rz_src = 384 SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, 385#if SDL_BYTEORDER == SDL_LIL_ENDIAN 386 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 387#else 388 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff 389#endif 390 ); 391 if(colorKeyAvailable) 392 SDL_SetColorKey(src, 0, 0); 393 394 SDL_BlitSurface(src, NULL, rz_src, NULL); 395 396 if(colorKeyAvailable) 397 SDL_SetColorKey(src, SDL_TRUE /* SDL_SRCCOLORKEY */, colorkey); 398 src_converted = 1; 399 is32bit = 1; 400 } 401 402 403 /* Determine target size */ 404 /* _rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, &dstwidth, &dstheight, &cangle, &sangle); */ 405 406 /* 407 * Calculate target factors from sin/cos and zoom 408 */ 409 sangleinv = sangle*65536.0; 410 cangleinv = cangle*65536.0; 411 412 /* 413 * Alloc space to completely contain the rotated surface 414 */ 415 rz_dst = NULL; 416 if (is32bit) { 417 /* 418 * Target surface is 32bit with source RGBA/ABGR ordering 419 */ 420 rz_dst = 421 SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 32, 422 rz_src->format->Rmask, rz_src->format->Gmask, 423 rz_src->format->Bmask, rz_src->format->Amask); 424 } else { 425 /* 426 * Target surface is 8bit 427 */ 428 rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0); 429 } 430 431 /* Check target */ 432 if (rz_dst == NULL) 433 return NULL; 434 435 /* Adjust for guard rows */ 436 rz_dst->h = dstheight; 437 438 if (colorKeyAvailable == 1){ 439 colorkey = SDL_MapRGB(rz_dst->format, r, g, b); 440 441 SDL_FillRect(rz_dst, NULL, colorkey ); 442 } 443 444 /* 445 * Lock source surface 446 */ 447 if (SDL_MUSTLOCK(rz_src)) { 448 SDL_LockSurface(rz_src); 449 } 450 451 /* 452 * Check which kind of surface we have 453 */ 454 if (is32bit) { 455 /* 456 * Call the 32bit transformation routine to do the rotation (using alpha) 457 */ 458 _transformSurfaceRGBA(rz_src, rz_dst, centerx, centery, 459 (int) (sangleinv), (int) (cangleinv), 460 flipx, flipy, 461 smooth); 462 /* 463 * Turn on source-alpha support 464 */ 465 /* SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255); */ 466 SDL_SetColorKey(rz_dst, /* SDL_SRCCOLORKEY */ SDL_TRUE | SDL_RLEACCEL, _colorkey(rz_src)); 467 } else { 468 /* 469 * Copy palette and colorkey info 470 */ 471 for (i = 0; i < rz_src->format->palette->ncolors; i++) { 472 rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i]; 473 } 474 rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; 475 /* 476 * Call the 8bit transformation routine to do the rotation 477 */ 478 transformSurfaceY(rz_src, rz_dst, centerx, centery, 479 (int) (sangleinv), (int) (cangleinv), 480 flipx, flipy); 481 SDL_SetColorKey(rz_dst, /* SDL_SRCCOLORKEY */ SDL_TRUE | SDL_RLEACCEL, _colorkey(rz_src)); 482 } 483 /* 484 * Unlock source surface 485 */ 486 if (SDL_MUSTLOCK(rz_src)) { 487 SDL_UnlockSurface(rz_src); 488 } 489 490 /* 491 * Cleanup temp surface 492 */ 493 if (src_converted) { 494 SDL_FreeSurface(rz_src); 495 } 496 497 /* 498 * Return destination surface 499 */ 500 return (rz_dst); 501}