8 //code adapted by David Olsen from Lanczos filtering article on wikipedia.org
11 #define M_PI 3.14159265359
14 static inline double Lanczos(double x
, int Radius
)
16 if (x
== 0.0) return 1.0;
17 if (x
<= -Radius
|| x
>= Radius
) return 0.0;
18 double tmp
= x
* M_PI
;
19 return Radius
* std::sin(tmp
) * std::sin(tmp
/ Radius
) / (tmp
* tmp
);
22 static inline void Resample(SDL_Surface
* src
, SDL_Surface
* dst
, int filter
)
24 const double blur
= 1.0;
25 double factor
= dst
->w
/ (double)src
->w
;
26 double scale
= std::min(factor
, 1.0) / blur
;
27 int FilterRadius
= filter
;
30 if (filter
> 3) //automatically determine fastest filter setting
33 if (scale
< 0.67) FilterRadius
= 2;
34 if (scale
<= 0.5) FilterRadius
= 1;
36 double support
= FilterRadius
/ scale
;
38 std::vector
<double> contribution_x(std::min((size_t)src
->w
, 5+(size_t)(2*support
)));
39 /* 5 = room for rounding up in calculations of start, stop and support */
41 Uint32
** temp
= new Uint32
* [src
->h
]; //array of source->height * dest->width
42 for (int i
= 0 ; i
< src
->h
; i
++)
43 temp
[i
] = new Uint32
[dst
->w
];
45 if (support
<= 0.5) { support
= 0.5 + 1E-12; scale
= 1.0; }
47 for (int x
= 0; x
< dst
->w
; ++x
)
49 double center
= (x
+ 0.5) / factor
;
50 size_t start
= (size_t)std::max(center
- support
+ 0.5, (double)0);
51 size_t stop
= (size_t)std::min(center
+ support
+ 0.5, (double)src
->w
);
53 size_t nmax
= stop
- start
;
54 double s
= start
- center
+ 0.5;
55 double point
[4] = {0,0,0,0};
59 for (int y
= 0; y
< src
->h
; y
++)
61 for (size_t n
= 0; n
< nmax
; ++n
)
64 { //only come up with the contribution list once per column.
65 contribution_x
[n
] = Lanczos (s
* scale
, FilterRadius
);
66 density
+= contribution_x
[n
];
69 //it MUST be a 32-bit surface for following code to work correctly
70 Uint8
* p
= (Uint8
*)src
->pixels
+ y
* src
->pitch
+ (start
+n
) * 4;
71 for (int c
= 0; c
< 4; c
++)
72 point
[c
] += p
[c
] * contribution_x
[n
];
74 /* Normalize. Truncate to Uint8 values. Place in temp array*/
75 Uint8
* p
= (Uint8
*)&temp
[y
][x
];
76 for (size_t c
= 0; c
< 4; c
++)
78 if (density
!= 0.0 && density
!= 1.0)
85 diff
= point
[c
] - (double)v
;
91 point
[c
] = 0; //reset value for next loop
96 factor
= dst
->h
/ (double)src
->h
;
97 scale
= std::min(factor
, 1.0) / blur
;
98 if (filter
> 3) //automatically determine fastest filter setting
101 if (scale
< 0.67) FilterRadius
= 2;
102 if (scale
<= 0.5) FilterRadius
= 1;
104 support
= FilterRadius
/ scale
;
106 std::vector
<double> contribution_y(std::min((size_t)src
->h
, 5+(size_t)(2*support
)));
108 if (support
<= 0.5) { support
= 0.5 + 1E-12; scale
= 1.0; }
110 for (int y
= 0; y
<dst
->h
; ++y
)
112 double center
= (y
+ 0.5) / factor
;
113 size_t start
= (size_t)std::max(center
- support
+ 0.5, (double)0);
114 size_t stop
= (size_t)std::min(center
+ support
+ 0.5, (double)src
->h
);
115 double density
= 0.0;
116 size_t nmax
= stop
-start
;
117 double s
= start
- center
+0.5;
118 double point
[4] = {0,0,0,0};
122 for (int x
=0; x
<dst
->w
; x
++)
124 for (size_t n
=0; n
<nmax
; ++n
)
128 contribution_y
[n
] = Lanczos(s
* scale
, FilterRadius
);
129 density
+= contribution_y
[n
];
132 Uint8
* p
= (Uint8
*)&temp
[start
+n
][x
];
133 for (int c
= 0; c
< 4; c
++)
134 point
[c
] += p
[c
] * contribution_y
[n
];
136 //destination must also be a 32 bit surface for this to work!
137 Uint8
* p
= (Uint8
*)dst
->pixels
+ y
* dst
->pitch
+ x
* 4;
138 for (size_t c
= 0; c
< 4; c
++)
140 if (density
!= 0.0 && density
!= 1.0)
146 v
= (Uint8
) point
[c
];
147 diff
= point
[c
] - (double)v
;
158 //free the temp array, so we don't leak any memory
159 for (int i
= 0 ; i
< src
->h
; i
++)
164 static inline Uint32
get_pixel(SDL_Surface
*surface
, int x
, int y
)
166 int bpp
= surface
->format
->BytesPerPixel
;
167 /* Here p is the address to the pixel we want to retrieve */
168 Uint8
*p
= (Uint8
*)surface
->pixels
+ y
* surface
->pitch
+ x
* bpp
;
173 case 2: return *(Uint16
*)p
;
174 case 3: if(SDL_BYTEORDER
== SDL_BIG_ENDIAN
)
175 return p
[0] << 16 | p
[1] << 8 | p
[2];
177 return p
[0] | p
[1] << 8 | p
[2] << 16;
178 case 4: return *(Uint32
*)p
;
179 default: return 0; /* shouldn't happen, but avoids warnings */
183 static inline bool has_alpha(SDL_Surface
* src
)
186 bool is_alpha
= false;
188 if SDL_MUSTLOCK(src
) SDL_LockSurface(src
);
190 for (int x
= 0; x
< src
->w
; x
++)
191 for (int y
= 0; y
< src
->h
; y
++)
193 SDL_GetRGBA(get_pixel(src
, x
, y
), src
->format
, &r
, &g
, &b
, &a
);
194 if (a
!= SDL_ALPHA_OPAQUE
)
202 if SDL_MUSTLOCK(src
) SDL_UnlockSurface(src
);
207 SDL_Surface
* SDL_Resize(SDL_Surface
*src
, float factor
, bool free
, int filter
)
209 if (factor
> 100.0f
) factor
= 100.0f
; //let's be reasonable...
210 int new_w
= (int)(src
->w
* factor
),
211 new_h
= (int)(src
->h
* factor
);
212 if (new_w
< 1) new_w
= 1;
213 if (new_h
< 1) new_h
= 1;
215 return SDL_Resize(src
, new_w
, new_h
, free
, filter
);
218 SDL_Surface
* SDL_Resize(SDL_Surface
*src
, int new_w
, int new_h
, bool free
, int filter
)
221 bool is_alpha
= has_alpha(src
);
223 if (src
->w
== new_w
&& src
->h
== new_h
)
225 //No change in size. Return an optimized image.
228 dst
= SDL_DisplayFormatAlpha(src
);
229 SDL_SetAlpha(src
, SDL_SRCALPHA
, 0);
232 dst
= SDL_DisplayFormat(src
);
235 SDL_FreeSurface(src
);
239 Uint32 rmask
= 0x000000ff,
243 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
250 dst
= SDL_CreateRGBSurface(0, new_w
, new_h
, 32, rmask
, gmask
, bmask
, amask
);
251 SDL_Surface
* temp
= SDL_ConvertSurface(src
,dst
->format
,0);
253 SDL_FreeSurface(src
);
256 Resample(src
,dst
,filter
);
258 SDL_FreeSurface(temp
);
261 temp
= SDL_DisplayFormatAlpha(dst
);
262 SDL_SetAlpha(temp
, SDL_SRCALPHA
, 0);
265 temp
= SDL_DisplayFormat(dst
);
267 SDL_FreeSurface(dst
);