/* pgrotozoom.cpp Copyright (C) A. Schiffler, July 2001 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA The code here within is based on the LGPL SDL_Rotozoom package by A. Schiffler . The only changes made are cosmetical (interface and naming) to fit the ParaGUI library. Last Update: $Author: braindead $ Update Date: $Date: 2002/11/01 10:43:36 $ Source File: $Source: /cvsroot/paragui/paragui/src/draw/rotozoom.cpp,v $ CVS/RCS Revision: $Revision: 1.3.6.2 $ Status: $State: Exp $ */ #include "paragui.h" #include "pgdraw.h" #include #include #include #define MAX(a,b) (((a) > (b)) ? (a) : (b)) /* ---- Structures */ using namespace std; typedef struct tColorRGBA { Uint8 r; Uint8 g; Uint8 b; Uint8 a; } tColorRGBA; typedef struct tColorY { Uint8 y; } tColorY; // 32bit Zoomer with optional anti-aliasing by bilinear interpolation. // Zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface. static int zoomSurfaceRGBA (SDL_Surface * src, SDL_Surface * dst, bool smooth) { int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep; tColorRGBA *c00, *c01, *c10, *c11; tColorRGBA *sp, *csp, *dp; int sgap, dgap, orderRGBA; /* Variable setup */ if (smooth) { /* For interpolation: assume source dimension is one pixel */ /* smaller to avoid overflow on right and bottom edge. */ sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w); sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h); } else { sx = (int) (65536.0 * (float) src->w / (float) dst->w); sy = (int) (65536.0 * (float) src->h / (float) dst->h); } /* Allocate memory for row increments */ if ((sax = (int *) malloc ((dst->w + 1) * sizeof (Uint32))) == NULL) { return (-1); } if ((say = (int *) malloc ((dst->h + 1) * sizeof (Uint32))) == NULL) { free (sax); return (-1); } /* Precalculate row increments */ csx = 0; csax = sax; for (x = 0; x <= dst->w; x++) { *csax = csx; csax++; csx &= 0xffff; csx += sx; } csy = 0; csay = say; for (y = 0; y <= dst->h; y++) { *csay = csy; csay++; csy &= 0xffff; csy += sy; } /* Pointer setup */ sp = csp = (tColorRGBA *) src->pixels; dp = (tColorRGBA *) dst->pixels; sgap = src->pitch - src->w * 4; dgap = dst->pitch - dst->w * 4; orderRGBA = (src->format->Rmask == 0x000000ff); /* Switch between interpolating and non-interpolating code */ if (smooth) { /* Interpolating Zoom */ /* Scan destination */ csay = say; for (y = 0; y < dst->h; y++) { /* Setup color source pointers */ c00 = csp; c01 = csp; c01++; c10 = (tColorRGBA *) ((Uint8 *) csp + src->pitch); c11 = c10; c11++; csax = sax; for (x = 0; x < dst->w; x++) { /* ABGR ordering */ /* Interpolate colors */ ex = (*csax & 0xffff); ey = (*csay & 0xffff); t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff; t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff; dp->r = (((t2 - t1) * ey) >> 16) + t1; t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff; t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff; dp->g = (((t2 - t1) * ey) >> 16) + t1; t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff; t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff; dp->b = (((t2 - t1) * ey) >> 16) + t1; t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff; t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff; dp->a = (((t2 - t1) * ey) >> 16) + t1; /* Advance source pointers */ csax++; sstep = (*csax >> 16); c00 += sstep; c01 += sstep; c10 += sstep; c11 += sstep; /* Advance destination pointer */ dp++; } /* Advance source pointer */ csay++; csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); /* Advance destination pointers */ dp = (tColorRGBA *) ((Uint8 *) dp + dgap); } } else { /* Non-Interpolating Zoom */ csay = say; for (y = 0; y < dst->h; y++) { sp = csp; csax = sax; for (x = 0; x < dst->w; x++) { /* Draw */ *dp = *sp; /* Advance source pointers */ csax++; sp += (*csax >> 16); /* Advance destination pointer */ dp++; } /* Advance source pointer */ csay++; csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); /* Advance destination pointers */ dp = (tColorRGBA *) ((Uint8 *) dp + dgap); } } /* Remove temp arrays */ free (sax); free (say); return (0); } // 8bit Zoomer without smoothing. // Zoomes 8bit palette/Y 'src' surface to 'dst' surface. static int zoomSurfaceY (SDL_Surface * src, SDL_Surface * dst) { Uint32 sx, sy, *sax, *say, *csax, *csay, csx, csy; Sint32 x, y; Uint8 *sp, *dp, *csp; int dgap; /* Variable setup */ sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w); sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h); /* Allocate memory for row increments */ if ((sax = (Uint32 *) malloc (dst->w * sizeof (Uint32))) == NULL) { return (-1); } if ((say = (Uint32 *) malloc (dst->h * sizeof (Uint32))) == NULL) { if (sax != NULL) { free (sax); } return (-1); } /* Precalculate row increments */ csx = 0; csax = sax; for (x = 0; x < dst->w; x++) { csx += sx; *csax = (csx >> 16); csx &= 0xffff; csax++; } csy = 0; csay = say; for (y = 0; y < dst->h; y++) { csy += sy; *csay = (csy >> 16); csy &= 0xffff; csay++; } csx = 0; csax = sax; for (x = 0; x < dst->w; x++) { csx += (*csax); csax++; } csy = 0; csay = say; for (y = 0; y < dst->h; y++) { csy += (*csay); csay++; } /* Pointer setup */ sp = csp = (Uint8 *) src->pixels; dp = (Uint8 *) dst->pixels; dgap = dst->pitch - dst->w; /* Draw */ csay = say; for (y = 0; y < dst->h; y++) { csax = sax; sp = csp; for (x = 0; x < dst->w; x++) { /* Draw */ *dp = *sp; /* Advance source pointers */ sp += (*csax); csax++; /* Advance destination pointer */ dp++; } /* Advance source pointer (for row) */ csp += ((*csay) * src->pitch); csay++; /* Advance destination pointers */ dp += dgap; } /* Remove temp arrays */ free (sax); free (say); return (0); } // 32bit Rotozoomer with optional anti-aliasing by bilinear interpolation. // Rotates and zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface. static void transformSurfaceRGBA (SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, bool smooth) { int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh; tColorRGBA c00, c01, c10, c11; tColorRGBA *pc, *sp; int gap, orderRGBA; /* Variable setup */ xd = ((src->w - dst->w) << 15); yd = ((src->h - dst->h) << 15); ax = (cx << 16) - (icos * cx); ay = (cy << 16) - (isin * cx); sw = src->w - 1; sh = src->h - 1; pc = reinterpret_cast(dst->pixels); gap = dst->pitch - dst->w * 4; orderRGBA = (src->format->Rmask == 0x000000ff); /* Switch between interpolating and non-interpolating code */ if (smooth) { for (y = 0; y < dst->h; y++) { dy = cy - y; sdx = (ax + (isin * dy)) + xd; sdy = (ay - (icos * dy)) + yd; for (x = 0; x < dst->w; x++) { dx = (sdx >> 16); dy = (sdy >> 16); if ((dx >= -1) && (dy >= -1) && (dx < src->w) && (dy < src->h)) { if ((dx >= 0) && (dy >= 0) && (dx < sw) && (dy < sh)) { sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); sp += dx; c00 = *sp; sp += 1; c01 = *sp; sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); sp -= 1; c10 = *sp; sp += 1; c11 = *sp; } else if ((dx == sw) && (dy == sh)) { sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); sp += dx; c00 = *sp; c01 = *pc; c10 = *pc; c11 = *pc; } else if ((dx == -1) && (dy == -1)) { sp = (tColorRGBA *) (src->pixels); c00 = *pc; c01 = *pc; c10 = *pc; c11 = *sp; } else if ((dx == -1) && (dy == sh)) { sp = (tColorRGBA *) (src->pixels); sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); c00 = *pc; c01 = *sp; c10 = *pc; c11 = *pc; } else if ((dx == sw) && (dy == -1)) { sp = (tColorRGBA *) (src->pixels); sp += dx; c00 = *pc; c01 = *pc; c10 = *sp; c11 = *pc; } else if (dx == -1) { sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); c00 = *pc; c01 = *sp; c10 = *pc; sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); c11 = *sp; } else if (dy == -1) { sp = (tColorRGBA *) (src->pixels); sp += dx; c00 = *pc; c01 = *pc; c10 = *sp; sp += 1; c11 = *sp; } else if (dx == sw) { sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); sp += dx; c00 = *sp; c01 = *pc; sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); c10 = *sp; c11 = *pc; } else if (dy == sh) { sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); sp += dx; c00 = *sp; sp += 1; c01 = *sp; c10 = *pc; c11 = *pc; } /* Interpolate colors */ ex = (sdx & 0xffff); ey = (sdy & 0xffff); t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff; t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff; pc->r = (((t2 - t1) * ey) >> 16) + t1; t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff; t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff; pc->g = (((t2 - t1) * ey) >> 16) + t1; t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff; t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff; pc->b = (((t2 - t1) * ey) >> 16) + t1; t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff; t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff; pc->a = (((t2 - t1) * ey) >> 16) + t1; } sdx += icos; sdy += isin; pc++; } pc = (tColorRGBA *) ((Uint8 *) pc + gap); } } else { for (y = 0; y < dst->h; y++) { dy = cy - y; sdx = (ax + (isin * dy)) + xd; sdy = (ay - (icos * dy)) + yd; for (x = 0; x < dst->w; x++) { dx = (short) (sdx >> 16); dy = (short) (sdy >> 16); if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) { sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); sp += dx; *pc = *sp; } sdx += icos; sdy += isin; pc++; } pc = (tColorRGBA *) ((Uint8 *) pc + gap); } } } // 8bit Rotozoomer without smoothing // Rotates and zoomes 8bit palette/Y 'src' surface to 'dst' surface. static void transformSurfaceY (SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos) { int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh; tColorY *pc, *sp; int gap; /* Variable setup */ xd = ((src->w - dst->w) << 15); yd = ((src->h - dst->h) << 15); ax = (cx << 16) - (icos * cx); ay = (cy << 16) - (isin * cx); sw = src->w - 1; sh = src->h - 1; pc = reinterpret_cast(dst->pixels); gap = dst->pitch - dst->w; /* Clear surface to colorkey */ memset (pc, (unsigned char) (src->format->colorkey & 0xff), dst->pitch * dst->h); /* Iterate through destination surface */ for (y = 0; y < dst->h; y++) { dy = cy - y; sdx = (ax + (isin * dy)) + xd; sdy = (ay - (icos * dy)) + yd; for (x = 0; x < dst->w; x++) { dx = (short) (sdx >> 16); dy = (short) (sdy >> 16); if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) { sp = (tColorY *) (src->pixels); sp += (src->pitch * dy + dx); *pc = *sp; } sdx += icos; sdy += isin; pc++; } pc += gap; } } // Rotates and zoomes a 32bit or 8bit 'src' surface to newly created // 'dst' surface. 'angle' is the rotation in degrees. 'zoom' a // scaling factor. If 'smooth' is 1 then the destination 32bit surface // is anti-aliased. If the surface is not 8bit or 32bit RGBA/ABGR it // will be converted into a 32bit RGBA format on the fly. #define VALUE_LIMIT 0.001 SDL_Surface* PG_Draw::RotoScaleSurface(SDL_Surface *src, double angle, double zoom, bool smooth) { SDL_Surface *rz_src; SDL_Surface *rz_dst; double zoominv; double radangle, sanglezoom, canglezoom, sanglezoominv, canglezoominv; int dstwidthhalf, dstwidth, dstheighthalf, dstheight; double x,y,cx,cy,sx,sy; int is32bit; int i,src_converted; /* Sanity check */ if (src==NULL) return(NULL); /* Determine if source surface is 32bit or 8bit */ is32bit=(src->format->BitsPerPixel==32); if ( (is32bit) || (src->format->BitsPerPixel==8)) { /* Use source surface 'as is' */ rz_src=src; src_converted=0; } else { /* New source surface is 32bit with a defined RGBA ordering */ rz_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); SDL_BlitSurface(src,NULL,rz_src,NULL); src_converted=1; is32bit=1; } /* Sanity check zoom factor */ if (zoomVALUE_LIMIT) { /* Angle!=0: full rotozoom */ /* ----------------------- */ /* Calculate target factors from sin/cos and zoom */ radangle=angle*(M_PI/180.0); sanglezoom=sanglezoominv=sin(radangle); canglezoom=canglezoominv=cos(radangle); sanglezoom *= zoom; canglezoom *= zoom; sanglezoominv *= zoominv; canglezoominv *= zoominv; /* Determine destination width and height by rotating a centered source box */ x=rz_src->w/2; y=rz_src->h/2; cx=canglezoom*x; cy=canglezoom*y; sx=sanglezoom*x; sy=sanglezoom*y; dstwidthhalf =MAX((int)ceil(MAX(MAX(MAX(fabs(cx+sy),fabs(cx-sy)),fabs(-cx+sy)),fabs(-cx-sy))),1); dstheighthalf=MAX((int)ceil(MAX(MAX(MAX(fabs(sx+cy),fabs(sx-cy)),fabs(-sx+cy)),fabs(-sx-cy))),1); dstwidth=2*dstwidthhalf; dstheight=2*dstheighthalf; /* Alloc space to completely contain the rotated surface */ rz_dst=NULL; if (is32bit) { /* Target surface is 32bit with source RGBA/ABGR ordering */ rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, rz_src->format->Rmask, rz_src->format->Gmask, rz_src->format->Bmask, rz_src->format->Amask); } else { /* Target surface is 8bit */ rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); } /* Lock source surface */ SDL_LockSurface(rz_src); /* Check which kind of surface we have */ if (is32bit) { /* Call the 32bit transformation routine to do the rotation (using alpha) */ transformSurfaceRGBA(rz_src,rz_dst,dstwidthhalf,dstheighthalf, (int)(sanglezoominv), (int)(canglezoominv), smooth); /* Turn on source-alpha support */ SDL_SetAlpha(rz_dst, SDL_SRCALPHA , 255); } else { /* Copy palette and colorkey info */ for (i=0; iformat->palette->ncolors; i++) { rz_dst->format->palette->colors[i]=rz_src->format->palette->colors[i]; } rz_dst->format->palette->ncolors=rz_src->format->palette->ncolors; /* Call the 8bit transformation routine to do the rotation */ transformSurfaceY(rz_src,rz_dst,dstwidthhalf,dstheighthalf, (int)(sanglezoominv), (int)(canglezoominv)); SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey); } /* Unlock source surface */ SDL_UnlockSurface(rz_src); } else { /* Angle=0: Just a zoom */ /* -------------------- */ /* Calculate target size and set rect */ dstwidth=(int)((double)rz_src->w*zoom); dstheight=(int)((double)rz_src->h*zoom); if (dstwidth<1) { dstwidth=1; } if (dstheight<1) { dstheight=1; } /* Alloc space to completely contain the zoomed surface */ rz_dst=NULL; if (is32bit) { /* Target surface is 32bit with source RGBA/ABGR ordering */ rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, rz_src->format->Rmask, rz_src->format->Gmask, rz_src->format->Bmask, rz_src->format->Amask); } else { /* Target surface is 8bit */ rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); } /* Lock source surface */ SDL_LockSurface(rz_src); /* Check which kind of surface we have */ if (is32bit) { /* Call the 32bit transformation routine to do the zooming (using alpha) */ zoomSurfaceRGBA(rz_src,rz_dst,smooth); /* Turn on source-alpha support */ SDL_SetAlpha(rz_dst, SDL_SRCALPHA , 255); } else { /* Copy palette and colorkey info */ for (i=0; iformat->palette->ncolors; i++) { rz_dst->format->palette->colors[i]=rz_src->format->palette->colors[i]; } rz_dst->format->palette->ncolors=rz_src->format->palette->ncolors; /* Call the 8bit transformation routine to do the zooming */ zoomSurfaceY(rz_src,rz_dst); SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey); } /* Unlock source surface */ SDL_UnlockSurface(rz_src); } /* Cleanup temp surface */ if (src_converted) { SDL_FreeSurface(rz_src); } /* Return destination surface */ return(rz_dst); } // Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' // surface. 'zoomx' and 'zoomy' are scaling factors for width and // height. If 'smooth' is 1 then the destination 32bit surface is // anti-aliased. If the surface is not 8bit or 32bit RGBA/ABGR it will // be converted into a 32bit RGBA format on the fly. #define VALUE_LIMIT 0.001 SDL_Surface* PG_Draw::ScaleSurface(SDL_Surface *src, double zoomx, double zoomy, bool smooth) { SDL_Surface *rz_src; SDL_Surface *rz_dst; int dstwidth, dstheight; int is32bit; int i,src_converted; /* Sanity check */ if (src==NULL) return(NULL); /* Determine if source surface is 32bit or 8bit */ is32bit=(src->format->BitsPerPixel==32); if ( (is32bit) || (src->format->BitsPerPixel==8)) { /* Use source surface 'as is' */ rz_src=src; src_converted=0; } else { /* New source surface is 32bit with a defined RGBA ordering */ rz_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); SDL_BlitSurface(src,NULL,rz_src,NULL); src_converted=1; is32bit=1; } /* Sanity check zoom factors */ if (zoomxw*zoomx); dstheight=(int)((double)rz_src->h*zoomy); if (dstwidth<1) { dstwidth=1; } if (dstheight<1) { dstheight=1; } /* Alloc space to completely contain the zoomed surface */ rz_dst=NULL; if (is32bit) { /* Target surface is 32bit with source RGBA/ABGR ordering */ rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, rz_src->format->Rmask, rz_src->format->Gmask, rz_src->format->Bmask, rz_src->format->Amask); } else { /* Target surface is 8bit */ rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); } /* Lock source surface */ SDL_LockSurface(rz_src); /* Check which kind of surface we have */ if (is32bit) { /* Call the 32bit transformation routine to do the zooming (using alpha) */ zoomSurfaceRGBA(rz_src,rz_dst,smooth); /* Turn on source-alpha support */ SDL_SetAlpha(rz_dst, SDL_SRCALPHA , 255); } else { /* Copy palette and colorkey info */ for (i=0; iformat->palette->ncolors; i++) { rz_dst->format->palette->colors[i]=rz_src->format->palette->colors[i]; } rz_dst->format->palette->ncolors=rz_src->format->palette->ncolors; /* Call the 8bit transformation routine to do the zooming */ zoomSurfaceY(rz_src,rz_dst); SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey); } /* Unlock source surface */ SDL_UnlockSurface(rz_src); /* Cleanup temp surface */ if (src_converted) { SDL_FreeSurface(rz_src); } /* Return destination surface */ return(rz_dst); } void PG_Draw::BlitScale(SDL_Surface *src, SDL_Surface *dst, bool smooth) { SDL_Surface *tmp = ScaleSurface(src, static_cast(dst->w) / src->w, static_cast(dst->h) / src->h, smooth); SDL_BlitSurface(tmp, 0, dst, 0); SDL_FreeSurface(tmp); } /* * Local Variables: * c-basic-offset: 8 * End: */