resize.C

00001 /*
00002  *                Filtered filter_Image Rescaling
00003  *
00004  *                  by Dale Schumacher
00005  *
00006  */
00007 
00008 /*
00009         Additional changes by Ray Gardener, Daylon Graphics Ltd.
00010         December 4, 1999
00011 
00012         Extreme modification to this to make it usable with SDL_Surfaces -Dave Olsen 1/2006
00013         and compatible with c++ compilers.... namely VC++2005 Express edition.
00014     It's a major hack-job. If anyone cleans this up, please let me know!
00015     I'm sure it can be made more efficient. (It's lots faster in release than in debug)
00016 
00017         Summary:
00018 
00019                 - Horizontal filter contributions are calculated on the fly,
00020                   as each column is mapped from src to dst image. This lets
00021                   us omit having to allocate a temporary full horizontal stretch
00022                   of the src image.
00023 
00024                 - If none of the src pixels within a sampling region differ,
00025                   then the output pixel is forced to equal (any of) the source pixel.
00026                   This ensures that filters do not corrupt areas of constant color.
00027 
00028                 - Filter weight contribution results, after summing, are
00029                   rounded to the nearest pixel color value instead of
00030                   being casted to Pixel (usually an int or char). Otherwise,
00031                   artifacting occurs.
00032 
00033                 - All memory allocations checked for failure; zoom() returns
00034                   error code. filter_new_image() returns NULL if unable to allocate
00035                   pixel storage, even if filter_Image struct can be allocated.
00036                   Some assertions added.
00037 */
00038 
00039 
00040 // "Public Domain 1991 by Dale Schumacher. Mods by Ray Gardener";
00041 // further mods by ME! (David Olsen)
00042 // and even more by Kevin Baragona, to make it valid C
00043 // and a few more to make it valid C89, and return NULL when needed (David Olsen)
00044 
00045 
00046 //It would be fantastic if someone would eventually modify these routines to make use
00047 //of native SDL image and pixel formats during the resize process... but, whatever.
00048 
00049 #include <stdlib.h>
00050 #include <math.h>
00051 #include <SDL/SDL.h>
00052 
00053 /* clamp the input to the specified range */
00054 #define CLAMP(v,l,h)    ((v)<(l) ? (l) : (v) > (h) ? (h) : v)
00055 #ifndef M_PI
00056 #define M_PI    3.14159265359
00057 #endif
00058 
00059 typedef        Uint8 Pixel;
00060 typedef struct
00061 {
00062         int        xsize;                /* horizontal size of the image in Pixels */
00063         int        ysize;                /* vertical size of the image in Pixels */
00064         Pixel *        data;        /* pointer to first scanline of image */
00065         int        span;                /* byte offset between two scanlines */
00066 } filter_Image;
00067 typedef struct
00068 {
00069         int        pixel;
00070         double        weight;
00071 } CONTRIB;
00072 typedef struct
00073 {
00074         int        n;                /* number of contributors */
00075         CONTRIB        *p;                /* pointer to list of contributions */
00076 } CLIST;
00077 
00078 SDL_Surface* SDL_ResizeFactor(SDL_Surface *image, float scalefactor,    int filter);
00079 SDL_Surface* SDL_ResizeXY(SDL_Surface *image, int new_w, int new_h, int filter);
00080 
00081 static SDL_Surface *filter_resizexy(SDL_Surface* source,int new_w, int new_h, int filter);
00082 
00083 static Uint32 filter_GetPixel(SDL_Surface *surface, int x, int y)
00084 {
00085     int bpp = surface->format->BytesPerPixel;
00086     /* Here p is the address to the pixel we want to retrieve */
00087     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
00088 
00089     switch(bpp)
00090     {
00091         case 1: return *p;
00092         case 2: return *(Uint16 *)p;
00093         case 3: if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
00094                     return p[0] << 16 | p[1] << 8 | p[2];
00095                 else
00096                     return p[0] | p[1] << 8 | p[2] << 16;
00097         case 4: return *(Uint32 *)p;
00098         default: return 0;       /* shouldn't happen, but avoids warnings */
00099     }
00100 }
00101 
00102 static void filter_PutPixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
00103 {
00104     int bpp = surface->format->BytesPerPixel;
00105     /* Here p is the address to the pixel we want to set */
00106     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
00107 
00108     switch(bpp)
00109     {
00110     case 1: *p = pixel;
00111             break;
00112     case 2: *(Uint16 *)p = pixel;
00113             break;
00114     case 3: if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
00115             {
00116                 p[0] = (pixel >> 16) & 0xff;
00117                 p[1] = (pixel >> 8) & 0xff;
00118                 p[2] = pixel & 0xff;
00119             }
00120             else
00121             {
00122                 p[0] = pixel & 0xff;
00123                 p[1] = (pixel >> 8) & 0xff;
00124                 p[2] = (pixel >> 16) & 0xff;
00125             }
00126             break;
00127     case 4: *(Uint32 *)p = pixel;
00128             break;
00129     }
00130 }
00131 
00132 #ifdef __cplusplus
00133 SDL_Surface* SDL_Resize(SDL_Surface *image, float scalefactor, int filter)
00134 {
00135     return SDL_ResizeFactor(image, scalefactor, filter);
00136 }
00137 #endif
00138 SDL_Surface* SDL_ResizeFactor(SDL_Surface *image, float scalefactor, int filter)
00139 {
00140     int neww, newh;
00141     SDL_Surface * r;
00142     if (!image) return NULL; //invalid image passed in.
00143     if (scalefactor > 100.0f) scalefactor = 100.0f; //let's be reasonable...
00144     neww = (int)((float)image->w*scalefactor);
00145     newh = (int)((float)image->h*scalefactor);
00146     if (neww<1) neww = 1;
00147     if (newh<1) newh = 1;
00148     r = SDL_ResizeXY(image, neww, newh, filter);
00149     return r;
00150 }
00151 
00152 #ifdef __cplusplus
00153 SDL_Surface* SDL_Resize(SDL_Surface *image, int new_w, int new_h, int filter)
00154 {
00155     return SDL_ResizeXY(image, new_w, new_h, filter);
00156 }
00157 #endif
00158 SDL_Surface* SDL_ResizeXY(SDL_Surface *image, int new_w, int new_h, int filter)
00159 {
00160     SDL_Surface *dest = NULL;
00161     Uint8 alpha, r, g, b;
00162     char usealpha;
00163     int cx;
00164     if (!image) return NULL; //invalid image passed in
00165 
00166     if ((new_w != image->w) || (new_h != image->h))
00167         dest = filter_resizexy(image, new_w, new_h, filter);
00168         else
00169     {
00170         SDL_FreeSurface(dest);
00171         dest = image;
00172     }
00173 
00174     //check for alpha content of the image... like for buttons...
00175 
00176     if SDL_MUSTLOCK(dest) SDL_LockSurface(dest);
00177     alpha = 0; r = 0; g = 0; b = 0;
00178     usealpha = 0;
00179     cx = 0;
00180     for (; cx < dest->w; cx++)
00181     { //check the whole image for any occurance of alpha
00182         int cy = 0;
00183         for (; cy < dest->h; cy++)
00184         {
00185             SDL_GetRGBA(filter_GetPixel(dest, cx, cy), dest->format, &r, &g, &b, &alpha);
00186             if (alpha != SDL_ALPHA_OPAQUE) {usealpha = 1; cx=dest->w; break;}
00187         }
00188     }
00189     if SDL_MUSTLOCK(dest) SDL_UnlockSurface(dest);
00190 
00191     if (!usealpha) // no alpha component
00192     {
00193         image = SDL_DisplayFormat(dest);
00194         SDL_SetAlpha(image, SDL_RLEACCEL, 0);
00195     }
00196     else // it does have alpha
00197     {
00198         image = SDL_DisplayFormatAlpha(dest);
00199         SDL_SetAlpha(image, SDL_RLEACCEL | SDL_SRCALPHA, 0);
00200     }
00201     SDL_FreeSurface(dest);
00202     return image;
00203 }
00204 
00205 static Pixel filter_get_pixel2(SDL_Surface *image, int x, int y, int which)
00206 {
00207     static Uint8 r=0, g=0, b=0, a=0;
00208     static int xx=-1, yy=-1;
00209     Pixel p = 0;
00210 
00211     if((x < 0) || (x >= image->w) || (y < 0) || (y >= image->h))
00212         return(0);
00213 
00214     if ((xx!=x) || (yy!=y))
00215     {
00216         Uint32 fullpixel;
00217         xx = x; yy = y; //this way it only calls the Getpixel,RGBA once per pixel...
00218         fullpixel = filter_GetPixel(image,x,y);
00219         SDL_GetRGBA(fullpixel,image->format,&r,&g,&b,&a);
00220     }
00221 
00222     switch (which)
00223     {
00224         case 0 : p = r; break;
00225         case 1 : p = g; break;
00226         case 2 : p = b; break;
00227         case 3 : p = a; break;
00228         default: p = r; break; //not really needed...
00229     }
00230     return(p);
00231 }
00232 
00233 static char filter_put_pixel2(SDL_Surface *image, int x, int y, Uint8 c[4])
00234 {
00235     if((x < 0) || (x >= image->w) || (y < 0) || (y >= image->h))
00236         return 0;
00237     filter_PutPixel(image,x,y,SDL_MapRGBA(image->format,c[0],c[1],c[2],c[3]));
00238     return 1;
00239 }
00240 
00241 static double filter_hermite_interp(double t)
00242 {
00243         /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */
00244         if(t < 0.0) t = -t;
00245         if(t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0);
00246         return(0.0);
00247 }
00248 
00249 static double filter_box_interp(double t)
00250 {
00251         if((t > -0.5) && (t <= 0.5)) return(1.0);
00252         return(0.0);
00253 }
00254 
00255 static double filter_triangle_interp(double t)
00256 {
00257         if(t < 0.0) t = -t;
00258         if(t < 1.0) return(1.0 - t);
00259         return(0.0);
00260 }
00261 
00262 static double filter_bell_interp(double t)                /* box (*) box (*) box */
00263 {
00264         if(t < 0) t = -t;
00265         if(t < .5) return(.75 - (t * t));
00266         if(t < 1.5) {
00267                 t = (t - 1.5);
00268                 return(.5 * (t * t));
00269         }
00270         return(0.0);
00271 }
00272 
00273 static double filter_B_spline_interp(double t)        /* box (*) box (*) box (*) box */
00274 {
00275         double tt;
00276 
00277         if(t < 0) t = -t;
00278         if(t < 1) {
00279                 tt = t * t;
00280                 return((.5 * tt * t) - tt + (2.0 / 3.0));
00281         } else if(t < 2) {
00282                 t = 2 - t;
00283                 return((1.0 / 6.0) * (t * t * t));
00284         }
00285         return(0.0);
00286 }
00287 
00288 static double filter_sinc(double x)
00289 {
00290         x *= M_PI;
00291         if(x != 0) return(sin(x) / x);
00292         return(1.0);
00293 }
00294 
00295 static double filter_Lanczos3_interp(double t)
00296 {
00297         if(t < 0) t = -t;
00298         if(t < 3.0) return(filter_sinc(t) * filter_sinc(t/3.0));
00299         return(0.0);
00300 }
00301 
00302 static double filter_Mitchell_interp(double t)
00303 {
00304         static double B = (1.0 / 3.0);
00305         static double C = (1.0 / 3.0);
00306         double tt;
00307 
00308         tt = t * t;
00309         if(t < 0) t = -t;
00310         if(t < 1.0) {
00311                 t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt))
00312                    + ((-18.0 + 12.0 * B + 6.0 * C) * tt)
00313                    + (6.0 - 2 * B));
00314                 return(t / 6.0);
00315         } else if(t < 2.0) {
00316                 t = (((-1.0 * B - 6.0 * C) * (t * tt))
00317                    + ((6.0 * B + 30.0 * C) * tt)
00318                    + ((-12.0 * B - 48.0 * C) * t)
00319                    + (8.0 * B + 24 * C));
00320                 return(t / 6.0);
00321         }
00322         return(0.0);
00323 }
00324 
00325 static int filter_roundcloser(double d)
00326 {
00327         /* Untested potential one-liner, but smacks of call overhead */
00328         /* return fabs(ceil(d)-d) <= 0.5 ? ceil(d) : floor(d); */
00329 
00330         /* Untested potential optimized ceil() usage */
00331 /*        double cd = ceil(d);
00332         int ncd = (int)cd;
00333         if(fabs(cd - d) > 0.5)
00334                 ncd--;
00335         return ncd;
00336 */
00337 
00338         /* Version that uses no function calls at all. */
00339         int n = (int) d;
00340         double diff = d - (double)n;
00341         if(diff < 0)
00342                 diff = -diff;
00343         if(diff >= 0.5)
00344         {
00345                 if(d < 0)
00346                         n--;
00347                 else
00348                         n++;
00349         }
00350         return n;
00351 } /* filter_roundcloser */
00352 
00353 static int filter_calc_x_contrib(CLIST *contribX, double xscale, double fwidth,
00354                                         int dstwidth, int srcwidth, double (*filterf)(double), int i)
00355 {
00356         double width;
00357         double fscale;
00358         double center, left, right;
00359         double weight;
00360         int j, k, n;
00361 
00362         if(xscale < 1.0)
00363         {
00364                 /* Shrinking image */
00365                 width = fwidth / xscale;
00366                 fscale = 1.0 / xscale;
00367 
00368                 contribX->n = 0;
00369                 contribX->p = (CONTRIB *)calloc((int) (width * 2 + 1),
00370                                 sizeof(CONTRIB));
00371                 if(contribX->p == NULL)
00372                         return -1;
00373 
00374                 center = (double) i / xscale;
00375                 left = ceil(center - width);
00376                 right = floor(center + width);
00377                 for(j = (int)left; j <= right; ++j)
00378                 {
00379                         weight = center - (double) j;
00380                         weight = ((*filterf)(weight / fscale)) / fscale;
00381                         if(j < 0)
00382                                 n = -j;
00383                         else if(j >= srcwidth)
00384                                 n = (srcwidth - j) + srcwidth - 1;
00385                         else
00386                                 n = j;
00387 
00388                         k = contribX->n++;
00389                         contribX->p[k].pixel = n;
00390                         contribX->p[k].weight = weight;
00391                 }
00392 
00393         }
00394         else
00395         {
00396                 /* Expanding image */
00397                 contribX->n = 0;
00398                 contribX->p = (CONTRIB *)calloc((int) (fwidth * 2 + 1),
00399                                 sizeof(CONTRIB));
00400                 if(contribX->p == NULL)
00401                         return -1;
00402                 center = (double) i / xscale;
00403                 left = ceil(center - fwidth);
00404                 right = floor(center + fwidth);
00405 
00406                 for(j = (int)left; j <= right; ++j)
00407                 {
00408                         weight = center - (double) j;
00409                         weight = (*filterf)(weight);
00410                         if(j < 0) {
00411                                 n = -j;
00412                         } else if(j >= srcwidth) {
00413                                 n = (srcwidth - j) + srcwidth - 1;
00414                         } else {
00415                                 n = j;
00416                         }
00417                         k = contribX->n++;
00418                         contribX->p[k].pixel = n;
00419                         contribX->p[k].weight = weight;
00420                 }
00421         }
00422         return 0;
00423 } /* filter_calc_x_contrib */
00424 
00425 static int filter_zoom2(SDL_Surface *dst, SDL_Surface *src, double (*filterf)(double), double fwidth)
00426 {
00427         Pixel* tmp;
00428         double xscale, yscale;                /* zoom scale factors */
00429         int xx;
00430         int i, j, k;                        /* loop variables */
00431         int n;                                /* pixel number */
00432         double center, left, right;        /* filter calculation variables */
00433         double width, fscale, weight;        /* filter calculation variables */
00434         Uint8 weightedcolor[4]; //reconstruct the pixel out of these!
00435         Pixel pel, pel2;
00436         int bPelDelta;
00437         CLIST        *contribY;                /* array of contribution lists */
00438         CLIST        contribX;
00439         int                nRet = -1;
00440 
00441         /* create intermediate column to hold horizontal dst column zoom */
00442         tmp = (Pixel*)malloc(src->h * sizeof(Pixel) * 4);
00443         if(tmp == NULL)
00444                 return 0;
00445 
00446         xscale = (double) dst->w / (double) src->w;
00447 
00448         /* Build y weights */
00449         /* pre-calculate filter contributions for a column */
00450         contribY = (CLIST *)calloc(dst->h, sizeof(CLIST));
00451         if(contribY == NULL)
00452         {
00453                 free(tmp);
00454                 return -1;
00455         }
00456 
00457         yscale = (double) dst->h / (double) src->h;
00458 
00459         if(yscale < 1.0)
00460         {
00461                 width = fwidth / yscale;
00462                 fscale = 1.0 / yscale;
00463                 for(i = 0; i < dst->h; ++i)
00464                 {
00465                         contribY[i].n = 0;
00466                         contribY[i].p = (CONTRIB *)calloc((int) (width * 2 + 1),
00467                                         sizeof(CONTRIB));
00468                         if(contribY[i].p == NULL)
00469                         {
00470                                 free(tmp);
00471                                 free(contribY);
00472                                 return -1;
00473                         }
00474                         center = (double) i / yscale;
00475                         left = ceil(center - width);
00476                         right = floor(center + width);
00477                         for(j = (int)left; j <= right; ++j) {
00478                                 weight = center - (double) j;
00479                                 weight = (*filterf)(weight / fscale) / fscale;
00480                                 if(j < 0) {
00481                                         n = -j;
00482                                 } else if(j >= src->h) {
00483                                         n = (src->h - j) + src->h - 1;
00484                                 } else {
00485                                         n = j;
00486                                 }
00487                                 k = contribY[i].n++;
00488                                 contribY[i].p[k].pixel = n;
00489                                 contribY[i].p[k].weight = weight;
00490                         }
00491                 }
00492         } else {
00493                 for(i = 0; i < dst->h; ++i) {
00494                         contribY[i].n = 0;
00495                         contribY[i].p = (CONTRIB *)calloc((int) (fwidth * 2 + 1),
00496                                         sizeof(CONTRIB));
00497                         if(contribY[i].p == NULL)
00498                         {
00499                                 free(tmp);
00500                                 free(contribY);
00501                                 return -1;
00502                         }
00503                         center = (double) i / yscale;
00504                         left = ceil(center - fwidth);
00505                         right = floor(center + fwidth);
00506                         for(j = (int)left; j <= right; ++j) {
00507                                 weight = center - (double) j;
00508                                 weight = (*filterf)(weight);
00509                                 if(j < 0) {
00510                                         n = -j;
00511                                 } else if(j >= src->h) {
00512                                         n = (src->h - j) + src->h - 1;
00513                                 } else {
00514                                         n = j;
00515                                 }
00516                                 k = contribY[i].n++;
00517                                 contribY[i].p[k].pixel = n;
00518                                 contribY[i].p[k].weight = weight;
00519                         }
00520                 }
00521         }
00522 
00523 
00524 
00525         for(xx = 0; xx < dst->w; xx++)
00526         {
00527                 if(0 != filter_calc_x_contrib(&contribX, xscale, fwidth,
00528                                                                 dst->w, src->w, filterf, xx))
00529                 {
00530                         goto __zoom_cleanup;
00531                 }
00532                 /* Apply horz filter to make dst column in tmp. */
00533                 for(k = 0; k < src->h; ++k)
00534                 {
00535                   //mine!!!!
00536                   int w=0;
00537                   for (; w<4; w++) {
00538                         weight = 0.0;
00539                         bPelDelta = 0;
00540                         pel = filter_get_pixel2(src, contribX.p[0].pixel, k, w);
00541                         for(j = 0; j < contribX.n; ++j)
00542                         {
00543                                 pel2 = filter_get_pixel2(src, contribX.p[j].pixel, k, w);
00544                                 if(pel2 != pel)
00545                                         bPelDelta = 1;
00546                                 weight += pel2 * contribX.p[j].weight;
00547                         }
00548                         weight = bPelDelta ? filter_roundcloser(weight) : pel;
00549 
00550                         tmp[k+w*src->h] = (Pixel)CLAMP(weight, 0, 255); // keep it Uint8
00551                   } //cycle through each color...
00552                 } /* next row in temp column */
00553 
00554                 free(contribX.p);
00555 
00556 
00557                 /* The temp column has been built. Now stretch it
00558                  vertically into dst column. */
00559                 for(i = 0; i < dst->h; ++i)
00560                 {
00561                   int w=0;
00562                   for (; w<4; w++) {
00563                         weight = 0.0;
00564                         bPelDelta = 0;
00565                         pel = tmp[contribY[i].p[0].pixel+w*src->h];
00566 
00567                         for(j = 0; j < contribY[i].n; ++j)
00568                         {
00569                                 pel2 = tmp[contribY[i].p[j].pixel+w*src->h];
00570                                 if(pel2 != pel)
00571                                         bPelDelta = 1;
00572                                 weight += pel2 * contribY[i].p[j].weight;
00573                         }
00574                         weight = bPelDelta ? filter_roundcloser(weight) : pel;
00575                         weightedcolor[w] = (Uint8)CLAMP(weight, 0, 255); //get all 4 "colors" this way
00576               }
00577                   filter_put_pixel2(dst, xx, i, weightedcolor ); //keep it Uint8
00578                 } /* next dst row */
00579 
00580         } /* next dst column */
00581         nRet = 0; /* success */
00582 
00583 __zoom_cleanup:
00584         free(tmp);
00585 
00586         /* free the memory allocated for vertical filter weights */
00587         for(i = 0; i < dst->h; ++i)
00588                 free(contribY[i].p);
00589         free(contribY);
00590 
00591         return nRet;
00592 } /* zoom */
00593 
00594 static SDL_Surface *filter_resizexy(SDL_Surface* source,int new_w, int new_h, int filter)
00595 {
00596     //f and s need to be complementary... one as filter, one as support.
00597         double (*f)(double) ; //function pointer
00598         double s; //support
00599     SDL_Surface *temp, *dest;
00600 
00601         const double box_support = 0.5,
00602                         triangle_support = 1.0,
00603                         bell_support     = 1.5,
00604                         B_spline_support = 2.0,
00605                         hermite_support  = 1.0,
00606                         Mitchell_support = 2.0,
00607             Lanczos3_support = 3.0;
00608 
00609         switch (filter)
00610     {
00611         case 1 : f=filter_box_interp;       s=box_support;      break;
00612         case 2 : f=filter_triangle_interp;  s=triangle_support; break;
00613         case 3 : f=filter_bell_interp;      s=bell_support;     break;
00614                 case 4 : f=filter_hermite_interp;        s=hermite_support;        break;
00615                 case 5 : f=filter_B_spline_interp;        s=B_spline_support;        break;
00616                 case 6 : f=filter_Mitchell_interp;        s=Mitchell_support;        break;
00617         case 7 : f=filter_Lanczos3_interp;        s=Lanczos3_support;        break;
00618                 default: f=filter_Lanczos3_interp;        s=Lanczos3_support;        break;
00619         }
00620 
00621     //Make new surface and send it in to the real filter
00622         temp = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
00623                                 new_w, new_h, 32,0,0,0,0) ,
00624         dest = SDL_DisplayFormatAlpha(temp);
00625 
00626         SDL_FreeSurface(temp);
00627 
00628         if SDL_MUSTLOCK(source) SDL_LockSurface(source);
00629         if SDL_MUSTLOCK(dest)   SDL_LockSurface(dest);
00630 
00631         filter_zoom2(dest, source, f, s );
00632 
00633         if SDL_MUSTLOCK(dest)   SDL_UnlockSurface(dest);
00634         if SDL_MUSTLOCK(source) SDL_UnlockSurface(source);
00635 
00636         SDL_FreeSurface(source);
00637         //should be all cleaned up!
00638 
00639         return dest;
00640 }
Generated on Sun May 8 08:40:39 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3