00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049 #include <stdlib.h>
00050 #include <math.h>
00051 #include <SDL/SDL.h>
00052
00053
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;
00063 int ysize;
00064 Pixel * data;
00065 int span;
00066 } filter_Image;
00067 typedef struct
00068 {
00069 int pixel;
00070 double weight;
00071 } CONTRIB;
00072 typedef struct
00073 {
00074 int n;
00075 CONTRIB *p;
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
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;
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
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;
00143 if (scalefactor > 100.0f) scalefactor = 100.0f;
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;
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
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 {
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)
00192 {
00193 image = SDL_DisplayFormat(dest);
00194 SDL_SetAlpha(image, SDL_RLEACCEL, 0);
00195 }
00196 else
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;
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;
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
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)
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)
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
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
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 }
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
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
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 }
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;
00429 int xx;
00430 int i, j, k;
00431 int n;
00432 double center, left, right;
00433 double width, fscale, weight;
00434 Uint8 weightedcolor[4];
00435 Pixel pel, pel2;
00436 int bPelDelta;
00437 CLIST *contribY;
00438 CLIST contribX;
00439 int nRet = -1;
00440
00441
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
00449
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
00533 for(k = 0; k < src->h; ++k)
00534 {
00535
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);
00551 }
00552 }
00553
00554 free(contribX.p);
00555
00556
00557
00558
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);
00576 }
00577 filter_put_pixel2(dst, xx, i, weightedcolor );
00578 }
00579
00580 }
00581 nRet = 0;
00582
00583 __zoom_cleanup:
00584 free(tmp);
00585
00586
00587 for(i = 0; i < dst->h; ++i)
00588 free(contribY[i].p);
00589 free(contribY);
00590
00591 return nRet;
00592 }
00593
00594 static SDL_Surface *filter_resizexy(SDL_Surface* source,int new_w, int new_h, int filter)
00595 {
00596
00597 double (*f)(double) ;
00598 double s;
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
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
00638
00639 return dest;
00640 }