make it possible to override CXX from the command line
[rofl0r-df-libgraphics.git] / g_src / resize++.cpp
blobfd1b77cf979eece2c070ec88025e97d527cb221c
1 #include <cmath>
2 #include <vector>
4 #include <SDL/SDL.h>
6 #include "resize++.h"
8 //code adapted by David Olsen from Lanczos filtering article on wikipedia.org
10 #ifndef M_PI
11 #define M_PI 3.14159265359
12 #endif
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;
28 if (filter < 1 )
29 FilterRadius = 1;
30 if (filter > 3) //automatically determine fastest filter setting
32 FilterRadius = 3;
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);
52 double density = 0.0;
53 size_t nmax = stop - start;
54 double s = start - center + 0.5;
55 double point[4] = {0,0,0,0};
56 Uint8 v;
57 double diff;
59 for (int y = 0; y < src->h; y++)
61 for (size_t n = 0; n < nmax; ++n)
63 if (y == 0)
64 { //only come up with the contribution list once per column.
65 contribution_x[n] = Lanczos (s * scale, FilterRadius);
66 density += contribution_x[n];
67 s++;
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)
79 point[c] /= density;
80 if (point[c] < 0)
81 point[c] = 0;
82 if (point[c] > 255)
83 point[c] = 255;
84 v = (Uint8) point[c];
85 diff = point[c] - (double)v;
86 if (diff < 0)
87 diff = -diff;
88 if (diff >= 0.5)
89 v++;
90 p[c] = 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
100 FilterRadius = 3;
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};
119 Uint8 v;
120 double diff;
122 for (int x=0; x<dst->w; x++)
124 for (size_t n=0; n<nmax; ++n)
126 if (x == 0)
128 contribution_y[n] = Lanczos(s * scale, FilterRadius);
129 density += contribution_y[n];
130 s++;
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)
141 point[c] /= density;
142 if (point[c] < 0)
143 point[c] = 0;
144 if (point[c] > 255)
145 point[c] = 255;
146 v = (Uint8) point[c];
147 diff = point[c] - (double)v;
148 if (diff < 0)
149 diff = -diff;
150 if (diff >= 0.5)
151 v++;
152 p[c] = v;
153 point[c] = 0;
158 //free the temp array, so we don't leak any memory
159 for (int i = 0 ; i < src->h; i++)
160 delete [] temp[i];
161 delete [] temp;
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;
170 switch(bpp)
172 case 1: return *p;
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];
176 else
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)
185 Uint8 r, g, b, a;
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)
196 is_alpha = true;
197 x = src->w;
198 break;
202 if SDL_MUSTLOCK(src) SDL_UnlockSurface(src);
204 return is_alpha;
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)
220 SDL_Surface * dst;
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.
226 if (is_alpha)
228 dst = SDL_DisplayFormatAlpha(src);
229 SDL_SetAlpha(src, SDL_SRCALPHA, 0);
231 else
232 dst = SDL_DisplayFormat(src);
234 if (free)
235 SDL_FreeSurface(src);
236 return dst;
239 Uint32 rmask = 0x000000ff,
240 gmask = 0x0000ff00,
241 bmask = 0x00ff0000,
242 amask = 0xff000000;
243 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
244 rmask = 0xff000000;
245 gmask = 0x00ff0000;
246 bmask = 0x0000ff00;
247 amask = 0x000000ff;
248 #endif
250 dst = SDL_CreateRGBSurface(0, new_w, new_h, 32, rmask, gmask, bmask, amask);
251 SDL_Surface * temp = SDL_ConvertSurface(src,dst->format,0);
252 if (free)
253 SDL_FreeSurface(src);
254 src = temp;
256 Resample(src,dst,filter);
258 SDL_FreeSurface(temp);
259 if (is_alpha)
261 temp = SDL_DisplayFormatAlpha(dst);
262 SDL_SetAlpha(temp, SDL_SRCALPHA, 0);
264 else
265 temp = SDL_DisplayFormat(dst);
267 SDL_FreeSurface(dst);
268 return temp;