From ea76b6988ccafaa6a4d4ed90f2489d0e49e1f180 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Sat, 5 Sep 2015 09:32:30 -0400 Subject: Imported Upstream version 0.40.24 --- g_src/resize++.cpp | 269 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100755 g_src/resize++.cpp (limited to 'g_src/resize++.cpp') 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 +#include + +#include + +#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 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 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; yh; ++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; xw; x++) + { + for (size_t n=0; npixels + 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; +} -- cgit v1.2.3