568 lines
12 KiB
C
568 lines
12 KiB
C
/*
|
|
* zoom.c
|
|
* This file is part of Bean Counters Classic
|
|
*
|
|
* Copyright (C) 2018 - Félix Arreola Rodríguez
|
|
*
|
|
* Bean Counters Classic is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Bean Counters Classic is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Bean Counters Classic; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/*
|
|
* Código tomado de la SDL_gfx
|
|
*/
|
|
|
|
/*
|
|
|
|
SDL_rotozoom.c: rotozoomer, zoomer and shrinker for 32bit or 8bit surfaces
|
|
|
|
Copyright (C) 2001-2012 Andreas Schiffler
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source
|
|
distribution.
|
|
|
|
Andreas Schiffler -- aschiffler at ferzkopp dot net
|
|
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <SDL.h>
|
|
|
|
typedef struct tColorRGBA {
|
|
Uint8 r;
|
|
Uint8 g;
|
|
Uint8 b;
|
|
Uint8 a;
|
|
} tColorRGBA;
|
|
|
|
#define GUARD_ROWS (2)
|
|
#define VALUE_LIMIT 0.001
|
|
|
|
Uint32 _colorkey(SDL_Surface *src) {
|
|
Uint32 key = 0;
|
|
#if (SDL_MINOR_VERSION == 3)
|
|
SDL_GetColorKey(src, &key);
|
|
#else
|
|
if (src)
|
|
{
|
|
key = src->format->colorkey;
|
|
}
|
|
#endif
|
|
return key;
|
|
}
|
|
|
|
int _zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst, int flipx, int flipy) {
|
|
int x, y;
|
|
Uint32 *sax, *say, *csax, *csay;
|
|
int csx, csy;
|
|
Uint8 *sp, *dp, *csp;
|
|
int dgap;
|
|
|
|
/*
|
|
* Allocate memory for row increments
|
|
*/
|
|
if ((sax = (Uint32 *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) {
|
|
return (-1);
|
|
}
|
|
if ((say = (Uint32 *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) {
|
|
free(sax);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Pointer setup
|
|
*/
|
|
sp = csp = (Uint8 *) src->pixels;
|
|
dp = (Uint8 *) dst->pixels;
|
|
dgap = dst->pitch - dst->w;
|
|
|
|
if (flipx) csp += (src->w-1);
|
|
if (flipy) csp = ( (Uint8*)csp + src->pitch*(src->h-1) );
|
|
|
|
/*
|
|
* Precalculate row increments
|
|
*/
|
|
csx = 0;
|
|
csax = sax;
|
|
for (x = 0; x < dst->w; x++) {
|
|
csx += src->w;
|
|
*csax = 0;
|
|
while (csx >= dst->w) {
|
|
csx -= dst->w;
|
|
(*csax)++;
|
|
}
|
|
(*csax) = (*csax) * (flipx ? -1 : 1);
|
|
csax++;
|
|
}
|
|
csy = 0;
|
|
csay = say;
|
|
for (y = 0; y < dst->h; y++) {
|
|
csy += src->h;
|
|
*csay = 0;
|
|
while (csy >= dst->h) {
|
|
csy -= dst->h;
|
|
(*csay)++;
|
|
}
|
|
(*csay) = (*csay) * (flipy ? -1 : 1);
|
|
csay++;
|
|
}
|
|
|
|
/*
|
|
* Draw
|
|
*/
|
|
csay = say;
|
|
for (y = 0; y < dst->h; y++) {
|
|
csax = sax;
|
|
sp = csp;
|
|
for (x = 0; x < dst->w; x++) {
|
|
/*
|
|
* Draw
|
|
*/
|
|
*dp = *sp;
|
|
/*
|
|
* Advance source pointers
|
|
*/
|
|
sp += (*csax);
|
|
csax++;
|
|
/*
|
|
* Advance destination pointer
|
|
*/
|
|
dp++;
|
|
}
|
|
/*
|
|
* Advance source pointer (for row)
|
|
*/
|
|
csp += ((*csay) * src->pitch);
|
|
csay++;
|
|
|
|
/*
|
|
* Advance destination pointers
|
|
*/
|
|
dp += dgap;
|
|
}
|
|
|
|
/*
|
|
* Remove temp arrays
|
|
*/
|
|
free(sax);
|
|
free(say);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int _zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int flipx, int flipy, int smooth) {
|
|
int x, y, sx, sy, ssx, ssy, *sax, *say, *csax, *csay, *salast, csx, csy, ex, ey, cx, cy, sstep, sstepx, sstepy;
|
|
tColorRGBA *c00, *c01, *c10, *c11;
|
|
tColorRGBA *sp, *csp, *dp;
|
|
int spixelgap, spixelw, spixelh, dgap, t1, t2;
|
|
|
|
/*
|
|
* Allocate memory for row/column increments
|
|
*/
|
|
if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) {
|
|
return (-1);
|
|
}
|
|
if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) {
|
|
free(sax);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Precalculate row increments
|
|
*/
|
|
spixelw = (src->w - 1);
|
|
spixelh = (src->h - 1);
|
|
if (smooth) {
|
|
sx = (int) (65536.0 * (float) spixelw / (float) (dst->w - 1));
|
|
sy = (int) (65536.0 * (float) spixelh / (float) (dst->h - 1));
|
|
} else {
|
|
sx = (int) (65536.0 * (float) (src->w) / (float) (dst->w));
|
|
sy = (int) (65536.0 * (float) (src->h) / (float) (dst->h));
|
|
}
|
|
|
|
/* Maximum scaled source size */
|
|
ssx = (src->w << 16) - 1;
|
|
ssy = (src->h << 16) - 1;
|
|
|
|
/* Precalculate horizontal row increments */
|
|
csx = 0;
|
|
csax = sax;
|
|
for (x = 0; x <= dst->w; x++) {
|
|
*csax = csx;
|
|
csax++;
|
|
csx += sx;
|
|
|
|
/* Guard from overflows */
|
|
if (csx > ssx) {
|
|
csx = ssx;
|
|
}
|
|
}
|
|
|
|
/* Precalculate vertical row increments */
|
|
csy = 0;
|
|
csay = say;
|
|
for (y = 0; y <= dst->h; y++) {
|
|
*csay = csy;
|
|
csay++;
|
|
csy += sy;
|
|
|
|
/* Guard from overflows */
|
|
if (csy > ssy) {
|
|
csy = ssy;
|
|
}
|
|
}
|
|
|
|
sp = (tColorRGBA *) src->pixels;
|
|
dp = (tColorRGBA *) dst->pixels;
|
|
dgap = dst->pitch - dst->w * 4;
|
|
spixelgap = src->pitch/4;
|
|
|
|
if (flipx) sp += spixelw;
|
|
if (flipy) sp += (spixelgap * spixelh);
|
|
|
|
/*
|
|
* Switch between interpolating and non-interpolating code
|
|
*/
|
|
if (smooth) {
|
|
|
|
/*
|
|
* Interpolating Zoom
|
|
*/
|
|
csay = say;
|
|
for (y = 0; y < dst->h; y++) {
|
|
csp = sp;
|
|
csax = sax;
|
|
for (x = 0; x < dst->w; x++) {
|
|
/*
|
|
* Setup color source pointers
|
|
*/
|
|
ex = (*csax & 0xffff);
|
|
ey = (*csay & 0xffff);
|
|
cx = (*csax >> 16);
|
|
cy = (*csay >> 16);
|
|
sstepx = cx < spixelw;
|
|
sstepy = cy < spixelh;
|
|
c00 = sp;
|
|
c01 = sp;
|
|
c10 = sp;
|
|
if (sstepy) {
|
|
if (flipy) {
|
|
c10 -= spixelgap;
|
|
} else {
|
|
c10 += spixelgap;
|
|
}
|
|
}
|
|
c11 = c10;
|
|
if (sstepx) {
|
|
if (flipx) {
|
|
c01--;
|
|
c11--;
|
|
} else {
|
|
c01++;
|
|
c11++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Draw and interpolate colors
|
|
*/
|
|
t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
|
|
t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
|
|
dp->r = (((t2 - t1) * ey) >> 16) + t1;
|
|
t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
|
|
t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
|
|
dp->g = (((t2 - t1) * ey) >> 16) + t1;
|
|
t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
|
|
t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
|
|
dp->b = (((t2 - t1) * ey) >> 16) + t1;
|
|
t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
|
|
t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
|
|
dp->a = (((t2 - t1) * ey) >> 16) + t1;
|
|
/*
|
|
* Advance source pointer x
|
|
*/
|
|
salast = csax;
|
|
csax++;
|
|
sstep = (*csax >> 16) - (*salast >> 16);
|
|
if (flipx) {
|
|
sp -= sstep;
|
|
} else {
|
|
sp += sstep;
|
|
}
|
|
|
|
/*
|
|
* Advance destination pointer x
|
|
*/
|
|
dp++;
|
|
}
|
|
/*
|
|
* Advance source pointer y
|
|
*/
|
|
salast = csay;
|
|
csay++;
|
|
sstep = (*csay >> 16) - (*salast >> 16);
|
|
sstep *= spixelgap;
|
|
if (flipy) {
|
|
sp = csp - sstep;
|
|
} else {
|
|
sp = csp + sstep;
|
|
}
|
|
|
|
/*
|
|
* Advance destination pointer y
|
|
*/
|
|
dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
|
|
}
|
|
} else {
|
|
/*
|
|
* Non-Interpolating Zoom
|
|
*/
|
|
csay = say;
|
|
for (y = 0; y < dst->h; y++) {
|
|
csp = sp;
|
|
csax = sax;
|
|
for (x = 0; x < dst->w; x++) {
|
|
/*
|
|
* Draw
|
|
*/
|
|
*dp = *sp;
|
|
|
|
/*
|
|
* Advance source pointer x
|
|
*/
|
|
salast = csax;
|
|
csax++;
|
|
sstep = (*csax >> 16) - (*salast >> 16);
|
|
if (flipx) sstep = -sstep;
|
|
sp += sstep;
|
|
|
|
/*
|
|
* Advance destination pointer x
|
|
*/
|
|
dp++;
|
|
}
|
|
/*
|
|
* Advance source pointer y
|
|
*/
|
|
salast = csay;
|
|
csay++;
|
|
sstep = (*csay >> 16) - (*salast >> 16);
|
|
sstep *= spixelgap;
|
|
if (flipy) sstep = -sstep;
|
|
sp = csp + sstep;
|
|
|
|
/*
|
|
* Advance destination pointer y
|
|
*/
|
|
dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Remove temp arrays
|
|
*/
|
|
free(sax);
|
|
free(say);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight) {
|
|
/*
|
|
* Make zoom factors positive
|
|
*/
|
|
int flipx, flipy;
|
|
flipx = (zoomx<0.0);
|
|
if (flipx) zoomx = -zoomx;
|
|
flipy = (zoomy<0.0);
|
|
if (flipy) zoomy = -zoomy;
|
|
|
|
/*
|
|
* Sanity check zoom factors
|
|
*/
|
|
if (zoomx < VALUE_LIMIT) {
|
|
zoomx = VALUE_LIMIT;
|
|
}
|
|
if (zoomy < VALUE_LIMIT) {
|
|
zoomy = VALUE_LIMIT;
|
|
}
|
|
|
|
/*
|
|
* Calculate target size
|
|
*/
|
|
*dstwidth = (int) floor(((double) width * zoomx) + 0.5);
|
|
*dstheight = (int) floor(((double) height * zoomy) + 0.5);
|
|
if (*dstwidth < 1) {
|
|
*dstwidth = 1;
|
|
}
|
|
if (*dstheight < 1) {
|
|
*dstheight = 1;
|
|
}
|
|
}
|
|
|
|
SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth) {
|
|
SDL_Surface *rz_src;
|
|
SDL_Surface *rz_dst;
|
|
int dstwidth, dstheight;
|
|
int is32bit;
|
|
int i, src_converted;
|
|
int flipx, flipy;
|
|
|
|
/*
|
|
* Sanity check
|
|
*/
|
|
if (src == NULL) return (NULL);
|
|
|
|
/*
|
|
* Determine if source surface is 32bit or 8bit
|
|
*/
|
|
is32bit = (src->format->BitsPerPixel == 32);
|
|
if ((is32bit) || (src->format->BitsPerPixel == 8)) {
|
|
/*
|
|
* Use source surface 'as is'
|
|
*/
|
|
rz_src = src;
|
|
src_converted = 0;
|
|
} else {
|
|
/*
|
|
* New source surface is 32bit with a defined RGBA ordering
|
|
*/
|
|
rz_src =
|
|
SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
|
|
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
|
|
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
|
|
#else
|
|
0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff
|
|
#endif
|
|
);
|
|
if (rz_src == NULL) {
|
|
return NULL;
|
|
}
|
|
SDL_BlitSurface(src, NULL, rz_src, NULL);
|
|
src_converted = 1;
|
|
is32bit = 1;
|
|
}
|
|
|
|
flipx = (zoomx<0.0);
|
|
if (flipx) zoomx = -zoomx;
|
|
flipy = (zoomy<0.0);
|
|
if (flipy) zoomy = -zoomy;
|
|
|
|
/* Get size if target */
|
|
zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight);
|
|
|
|
/*
|
|
* Alloc space to completely contain the zoomed surface
|
|
*/
|
|
rz_dst = NULL;
|
|
if (is32bit) {
|
|
/*
|
|
* Target surface is 32bit with source RGBA/ABGR ordering
|
|
*/
|
|
rz_dst =
|
|
SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 32,
|
|
rz_src->format->Rmask, rz_src->format->Gmask,
|
|
rz_src->format->Bmask, rz_src->format->Amask);
|
|
} else {
|
|
/*
|
|
* Target surface is 8bit
|
|
*/
|
|
rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
|
|
}
|
|
|
|
/* Check target */
|
|
if (rz_dst == NULL) {
|
|
/*
|
|
* Cleanup temp surface
|
|
*/
|
|
if (src_converted) {
|
|
SDL_FreeSurface(rz_src);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Adjust for guard rows */
|
|
rz_dst->h = dstheight;
|
|
|
|
/*
|
|
* Lock source surface
|
|
*/
|
|
if (SDL_MUSTLOCK(rz_src)) {
|
|
SDL_LockSurface(rz_src);
|
|
}
|
|
|
|
/*
|
|
* Check which kind of surface we have
|
|
*/
|
|
if (is32bit) {
|
|
/*
|
|
* Call the 32bit transformation routine to do the zooming (using alpha)
|
|
*/
|
|
_zoomSurfaceRGBA(rz_src, rz_dst, flipx, flipy, smooth);
|
|
/*
|
|
* Turn on source-alpha support
|
|
*/
|
|
SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
|
|
} else {
|
|
/*
|
|
* Copy palette and colorkey info
|
|
*/
|
|
for (i = 0; i < rz_src->format->palette->ncolors; i++) {
|
|
rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
|
|
}
|
|
rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
|
|
/*
|
|
* Call the 8bit transformation routine to do the zooming
|
|
*/
|
|
_zoomSurfaceY(rz_src, rz_dst, flipx, flipy);
|
|
SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
|
|
}
|
|
/*
|
|
* Unlock source surface
|
|
*/
|
|
if (SDL_MUSTLOCK(rz_src)) {
|
|
SDL_UnlockSurface(rz_src);
|
|
}
|
|
|
|
/*
|
|
* Cleanup temp surface
|
|
*/
|
|
if (src_converted) {
|
|
SDL_FreeSurface(rz_src);
|
|
}
|
|
|
|
/*
|
|
* Return destination surface
|
|
*/
|
|
return (rz_dst);
|
|
}
|