diff options
author | Benjamin Barenblat <bbaren@mit.edu> | 2015-09-05 09:32:30 -0400 |
---|---|---|
committer | Benjamin Barenblat <bbaren@mit.edu> | 2015-09-05 09:32:30 -0400 |
commit | ea76b6988ccafaa6a4d4ed90f2489d0e49e1f180 (patch) | |
tree | b01dd19f5c53406d9d636b18bc49916bfdd6d3bc /g_src/resize++.cpp |
Imported Upstream version 0.40.24upstream/0.40.24upstream
Diffstat (limited to 'g_src/resize++.cpp')
-rwxr-xr-x | g_src/resize++.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/g_src/resize++.cpp b/g_src/resize++.cpp new file mode 100755 index 0000000..fd1b77c --- /dev/null +++ b/g_src/resize++.cpp @@ -0,0 +1,269 @@ +#include <cmath> +#include <vector> + +#include <SDL/SDL.h> + +#include "resize++.h" + +//code adapted by David Olsen from Lanczos filtering article on wikipedia.org + +#ifndef M_PI +#define M_PI 3.14159265359 +#endif + +static inline double Lanczos(double x, int Radius) +{ + if (x == 0.0) return 1.0; + if (x <= -Radius || x >= Radius) return 0.0; + double tmp = x * M_PI; + return Radius * std::sin(tmp) * std::sin(tmp / Radius) / (tmp * tmp); +} + +static inline void Resample(SDL_Surface * src, SDL_Surface * dst, int filter) +{ + const double blur = 1.0; + double factor = dst->w / (double)src->w; + double scale = std::min(factor, 1.0) / blur; + int FilterRadius = filter; + if (filter < 1 ) + FilterRadius = 1; + if (filter > 3) //automatically determine fastest filter setting + { + FilterRadius = 3; + if (scale < 0.67) FilterRadius = 2; + if (scale <= 0.5) FilterRadius = 1; + } + double support = FilterRadius / scale; + + std::vector<double> contribution_x(std::min((size_t)src->w, 5+(size_t)(2*support))); + /* 5 = room for rounding up in calculations of start, stop and support */ + + Uint32 ** temp = new Uint32 * [src->h]; //array of source->height * dest->width + for (int i = 0 ; i < src->h; i++) + temp[i] = new Uint32 [dst->w]; + + if (support <= 0.5) { support = 0.5 + 1E-12; scale = 1.0; } + + for (int x = 0; x < dst->w; ++x) + { + double center = (x + 0.5) / factor; + size_t start = (size_t)std::max(center - support + 0.5, (double)0); + size_t stop = (size_t)std::min(center + support + 0.5, (double)src->w); + double density = 0.0; + size_t nmax = stop - start; + double s = start - center + 0.5; + double point[4] = {0,0,0,0}; + Uint8 v; + double diff; + + for (int y = 0; y < src->h; y++) + { + for (size_t n = 0; n < nmax; ++n) + { + if (y == 0) + { //only come up with the contribution list once per column. + contribution_x[n] = Lanczos (s * scale, FilterRadius); + density += contribution_x[n]; + s++; + } + //it MUST be a 32-bit surface for following code to work correctly + Uint8 * p = (Uint8 *)src->pixels + y * src->pitch + (start+n) * 4; + for (int c = 0; c < 4; c++) + point[c] += p[c] * contribution_x[n]; + } + /* Normalize. Truncate to Uint8 values. Place in temp array*/ + Uint8 * p = (Uint8 *)&temp[y][x]; + for (size_t c = 0; c < 4; c++) + { + if (density != 0.0 && density != 1.0) + point[c] /= density; + if (point[c] < 0) + point[c] = 0; + if (point[c] > 255) + point[c] = 255; + v = (Uint8) point[c]; + diff = point[c] - (double)v; + if (diff < 0) + diff = -diff; + if (diff >= 0.5) + v++; + p[c] = v; + point[c] = 0; //reset value for next loop + } + } + } + + factor = dst->h / (double)src->h; + scale = std::min(factor, 1.0) / blur; + if (filter > 3) //automatically determine fastest filter setting + { + FilterRadius = 3; + if (scale < 0.67) FilterRadius = 2; + if (scale <= 0.5) FilterRadius = 1; + } + support = FilterRadius / scale; + + std::vector<double> contribution_y(std::min((size_t)src->h, 5+(size_t)(2*support))); + + if (support <= 0.5) { support = 0.5 + 1E-12; scale = 1.0; } + + for (int y = 0; y<dst->h; ++y) + { + double center = (y + 0.5) / factor; + size_t start = (size_t)std::max(center - support + 0.5, (double)0); + size_t stop = (size_t)std::min(center + support + 0.5, (double)src->h); + double density = 0.0; + size_t nmax = stop-start; + double s = start - center+0.5; + double point[4] = {0,0,0,0}; + Uint8 v; + double diff; + + for (int x=0; x<dst->w; x++) + { + for (size_t n=0; n<nmax; ++n) + { + if (x == 0) + { + contribution_y[n] = Lanczos(s * scale, FilterRadius); + density += contribution_y[n]; + s++; + } + Uint8 * p = (Uint8 *)&temp[start+n][x]; + for (int c = 0; c < 4; c++) + point[c] += p[c] * contribution_y[n]; + } + //destination must also be a 32 bit surface for this to work! + Uint8 * p = (Uint8 *)dst->pixels + y * dst->pitch + x * 4; + for (size_t c = 0; c < 4; c++) + { + if (density != 0.0 && density != 1.0) + point[c] /= density; + if (point[c] < 0) + point[c] = 0; + if (point[c] > 255) + point[c] = 255; + v = (Uint8) point[c]; + diff = point[c] - (double)v; + if (diff < 0) + diff = -diff; + if (diff >= 0.5) + v++; + p[c] = v; + point[c] = 0; + } + } + } + + //free the temp array, so we don't leak any memory + for (int i = 0 ; i < src->h; i++) + delete [] temp[i]; + delete [] temp; +} + +static inline Uint32 get_pixel(SDL_Surface *surface, int x, int y) +{ + int bpp = surface->format->BytesPerPixel; + /* Here p is the address to the pixel we want to retrieve */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; + + switch(bpp) + { + case 1: return *p; + case 2: return *(Uint16 *)p; + case 3: if(SDL_BYTEORDER == SDL_BIG_ENDIAN) + return p[0] << 16 | p[1] << 8 | p[2]; + else + return p[0] | p[1] << 8 | p[2] << 16; + case 4: return *(Uint32 *)p; + default: return 0; /* shouldn't happen, but avoids warnings */ + } +} + +static inline bool has_alpha(SDL_Surface * src) +{ + Uint8 r, g, b, a; + bool is_alpha = false; + + if SDL_MUSTLOCK(src) SDL_LockSurface(src); + + for (int x = 0; x < src->w; x++) + for (int y = 0; y < src->h; y++) + { + SDL_GetRGBA(get_pixel(src, x, y), src->format, &r, &g, &b, &a); + if (a != SDL_ALPHA_OPAQUE) + { + is_alpha = true; + x = src->w; + break; + } + } + + if SDL_MUSTLOCK(src) SDL_UnlockSurface(src); + + return is_alpha; +} + +SDL_Surface* SDL_Resize(SDL_Surface *src, float factor, bool free, int filter) +{ + if (factor > 100.0f) factor = 100.0f; //let's be reasonable... + int new_w = (int)(src->w * factor), + new_h = (int)(src->h * factor); + if (new_w < 1) new_w = 1; + if (new_h < 1) new_h = 1; + + return SDL_Resize(src, new_w, new_h, free, filter); +} + +SDL_Surface* SDL_Resize(SDL_Surface *src, int new_w, int new_h, bool free, int filter) +{ + SDL_Surface * dst; + bool is_alpha = has_alpha(src); + + if (src->w == new_w && src->h == new_h) + { + //No change in size. Return an optimized image. + if (is_alpha) + { + dst = SDL_DisplayFormatAlpha(src); + SDL_SetAlpha(src, SDL_SRCALPHA, 0); + } + else + dst = SDL_DisplayFormat(src); + + if (free) + SDL_FreeSurface(src); + return dst; + } + + Uint32 rmask = 0x000000ff, + gmask = 0x0000ff00, + bmask = 0x00ff0000, + amask = 0xff000000; + #if SDL_BYTEORDER == SDL_BIG_ENDIAN + rmask = 0xff000000; + gmask = 0x00ff0000; + bmask = 0x0000ff00; + amask = 0x000000ff; + #endif + + dst = SDL_CreateRGBSurface(0, new_w, new_h, 32, rmask, gmask, bmask, amask); + SDL_Surface * temp = SDL_ConvertSurface(src,dst->format,0); + if (free) + SDL_FreeSurface(src); + src = temp; + + Resample(src,dst,filter); + + SDL_FreeSurface(temp); + if (is_alpha) + { + temp = SDL_DisplayFormatAlpha(dst); + SDL_SetAlpha(temp, SDL_SRCALPHA, 0); + } + else + temp = SDL_DisplayFormat(dst); + + SDL_FreeSurface(dst); + return temp; +} |