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