Pillow/libImaging/Resample.c

553 lines
17 KiB
C
Raw Normal View History

2010-07-31 06:52:47 +04:00
#include "Imaging.h"
#include <math.h>
2016-05-09 14:37:50 +03:00
#define ROUND_UP(f) ((int) ((f) >= 0.0 ? (f) + 0.5F : (f) - 0.5F))
2010-07-31 06:52:47 +04:00
struct filter {
double (*filter)(double x);
double support;
2010-07-31 06:52:47 +04:00
};
static inline double sinc_filter(double x)
2010-07-31 06:52:47 +04:00
{
if (x == 0.0)
return 1.0;
x = x * M_PI;
return sin(x) / x;
}
static inline double lanczos_filter(double x)
2010-07-31 06:52:47 +04:00
{
/* truncated sinc */
2010-07-31 06:52:47 +04:00
if (-3.0 <= x && x < 3.0)
return sinc_filter(x) * sinc_filter(x/3);
return 0.0;
}
static inline double bilinear_filter(double x)
2010-07-31 06:52:47 +04:00
{
if (x < 0.0)
x = -x;
if (x < 1.0)
return 1.0-x;
return 0.0;
}
static inline double bicubic_filter(double x)
2010-07-31 06:52:47 +04:00
{
2016-02-10 11:37:16 +03:00
/* https://en.wikipedia.org/wiki/Bicubic_interpolation#Bicubic_convolution_algorithm */
2014-10-25 12:39:03 +04:00
#define a -0.5
2010-07-31 06:52:47 +04:00
if (x < 0.0)
x = -x;
if (x < 1.0)
2014-10-24 12:46:51 +04:00
return ((a + 2.0) * x - (a + 3.0)) * x*x + 1;
2010-07-31 06:52:47 +04:00
if (x < 2.0)
2014-10-24 12:46:51 +04:00
return (((x - 5) * x + 8) * x - 4) * a;
2010-07-31 06:52:47 +04:00
return 0.0;
#undef a
}
2016-05-03 13:23:16 +03:00
static struct filter LANCZOS = { lanczos_filter, 3.0 };
static struct filter BILINEAR = { bilinear_filter, 1.0 };
2010-07-31 06:52:47 +04:00
static struct filter BICUBIC = { bicubic_filter, 2.0 };
2014-10-25 06:04:04 +04:00
2016-05-03 13:23:16 +03:00
/* 8 bits for result. Filter can have negative areas.
In one cases the sum of the coefficients will be negative,
in the other it will be more than 1.0. That is why we need
two extra bits for overflow and int type. */
#define PRECISION_BITS (32 - 8 - 2)
static inline UINT8 clip8(int in)
2014-10-25 06:04:04 +04:00
{
2016-05-03 13:23:16 +03:00
if (in >= (1 << PRECISION_BITS << 8))
2014-10-25 06:04:04 +04:00
return 255;
2016-05-03 13:23:16 +03:00
if (in <= 0)
2014-10-25 06:04:04 +04:00
return 0;
2016-05-03 13:23:16 +03:00
return (UINT8) (in >> PRECISION_BITS);
}
2016-05-05 13:15:54 +03:00
int
ImagingPrecompute(int inSize, int outSize, struct filter *filterp,
int **xboundsp, double **kkp) {
double support, scale, filterscale;
double center, ww, ss;
int xx, x, kmax, xmin, xmax;
int *xbounds;
double *kk, *k;
/* prepare for horizontal stretch */
filterscale = scale = (double) inSize / outSize;
2016-05-05 13:15:54 +03:00
if (filterscale < 1.0) {
filterscale = 1.0;
}
/* determine support size (length of resampling filter) */
support = filterp->support * filterscale;
/* maximum number of coofs */
kmax = (int) ceil(support) * 2 + 1;
// check for overflow
2016-05-26 02:28:35 +03:00
if (outSize > INT_MAX / (kmax * sizeof(double)))
return 0;
2016-05-05 13:15:54 +03:00
// sizeof(double) should be greater than 0 as well
2016-05-26 02:28:35 +03:00
if (outSize > INT_MAX / (2 * sizeof(double)))
return 0;
2016-05-05 13:15:54 +03:00
/* coefficient buffer */
2016-05-26 02:28:35 +03:00
kk = calloc(outSize * kmax, sizeof(double));
2016-05-05 13:15:54 +03:00
if ( ! kk)
return 0;
2016-05-05 13:15:54 +03:00
xbounds = malloc(outSize * 2 * sizeof(int));
2016-05-05 13:15:54 +03:00
if ( ! xbounds) {
free(kk);
return 0;
2016-05-05 13:15:54 +03:00
}
for (xx = 0; xx < outSize; xx++) {
center = (xx + 0.5) * scale;
ww = 0.0;
ss = 1.0 / filterscale;
xmin = (int) floor(center - support);
if (xmin < 0)
xmin = 0;
xmax = (int) ceil(center + support);
if (xmax > inSize)
xmax = inSize;
xmax -= xmin;
2016-05-05 13:15:54 +03:00
k = &kk[xx * kmax];
for (x = 0; x < xmax; x++) {
double w = filterp->filter((x + xmin - center + 0.5) * ss);
k[x] = w;
2016-05-05 13:15:54 +03:00
ww += w;
}
for (x = 0; x < xmax; x++) {
2016-05-05 13:15:54 +03:00
if (ww != 0.0)
k[x] /= ww;
}
xbounds[xx * 2 + 0] = xmin;
xbounds[xx * 2 + 1] = xmax;
}
*xboundsp = xbounds;
*kkp = kk;
return kmax;
}
2010-07-31 06:52:47 +04:00
Imaging
2016-05-05 12:53:22 +03:00
ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp)
2010-07-31 06:52:47 +04:00
{
ImagingSectionCookie cookie;
2014-10-26 00:11:39 +04:00
Imaging imOut;
2016-05-03 13:23:16 +03:00
int ss0, ss1, ss2, ss3;
2014-10-26 01:20:24 +04:00
int xx, yy, x, kmax, xmin, xmax;
2014-10-25 05:57:08 +04:00
int *xbounds;
2016-05-03 13:23:16 +03:00
int *k, *kk;
double *prekk;
2010-07-31 06:52:47 +04:00
2014-10-25 05:37:33 +04:00
kmax = ImagingPrecompute(imIn->xsize, xsize, filterp, &xbounds, &prekk);
if ( ! kmax) {
return (Imaging) ImagingError_MemoryError();
}
kk = malloc(xsize * kmax * sizeof(int));
if ( ! kk) {
free(xbounds);
free(prekk);
2010-07-31 06:52:47 +04:00
return (Imaging) ImagingError_MemoryError();
2014-10-25 05:37:33 +04:00
}
for (x = 0; x < xsize * kmax; x++) {
kk[x] = (int) (0.5 + prekk[x] * (1 << PRECISION_BITS));
2014-10-25 05:37:33 +04:00
}
2010-07-31 06:52:47 +04:00
free(prekk);
2014-10-26 00:11:39 +04:00
imOut = ImagingNew(imIn->mode, xsize, imIn->ysize);
if ( ! imOut) {
free(kk);
free(xbounds);
return NULL;
}
2010-07-31 06:52:47 +04:00
ImagingSectionEnter(&cookie);
2016-05-05 18:27:33 +03:00
if (imIn->image8) {
for (yy = 0; yy < imOut->ysize; yy++) {
2014-10-26 00:11:39 +04:00
for (xx = 0; xx < xsize; xx++) {
2014-10-25 05:50:54 +04:00
xmin = xbounds[xx * 2 + 0];
xmax = xbounds[xx * 2 + 1];
2014-10-25 05:41:38 +04:00
k = &kk[xx * kmax];
2016-05-03 14:22:51 +03:00
ss0 = 1 << (PRECISION_BITS -1);
for (x = 0; x < xmax; x++)
ss0 += ((UINT8) imIn->image8[yy][x + xmin]) * k[x];
2016-05-03 13:23:16 +03:00
imOut->image8[yy][xx] = clip8(ss0);
2010-07-31 06:52:47 +04:00
}
2016-05-05 18:27:33 +03:00
}
} else if (imIn->type == IMAGING_TYPE_UINT8) {
if (imIn->bands == 2) {
for (yy = 0; yy < imOut->ysize; yy++) {
2016-05-05 12:53:22 +03:00
for (xx = 0; xx < xsize; xx++) {
xmin = xbounds[xx * 2 + 0];
xmax = xbounds[xx * 2 + 1];
k = &kk[xx * kmax];
ss0 = ss3 = 1 << (PRECISION_BITS -1);
for (x = 0; x < xmax; x++) {
ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x];
ss3 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 3]) * k[x];
}
2016-05-05 12:53:22 +03:00
imOut->image[yy][xx*4 + 0] = clip8(ss0);
imOut->image[yy][xx*4 + 3] = clip8(ss3);
2016-05-05 12:53:22 +03:00
}
2016-05-05 18:27:33 +03:00
}
} else if (imIn->bands == 3) {
for (yy = 0; yy < imOut->ysize; yy++) {
2016-05-05 12:53:22 +03:00
for (xx = 0; xx < xsize; xx++) {
xmin = xbounds[xx * 2 + 0];
xmax = xbounds[xx * 2 + 1];
k = &kk[xx * kmax];
ss0 = ss1 = ss2 = 1 << (PRECISION_BITS -1);
for (x = 0; x < xmax; x++) {
ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x];
ss1 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 1]) * k[x];
ss2 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 2]) * k[x];
}
2016-05-05 12:53:22 +03:00
imOut->image[yy][xx*4 + 0] = clip8(ss0);
imOut->image[yy][xx*4 + 1] = clip8(ss1);
imOut->image[yy][xx*4 + 2] = clip8(ss2);
}
2016-05-05 18:27:33 +03:00
}
} else {
for (yy = 0; yy < imOut->ysize; yy++) {
2016-05-05 12:53:22 +03:00
for (xx = 0; xx < xsize; xx++) {
xmin = xbounds[xx * 2 + 0];
xmax = xbounds[xx * 2 + 1];
k = &kk[xx * kmax];
ss0 = ss1 = ss2 = ss3 = 1 << (PRECISION_BITS -1);
for (x = 0; x < xmax; x++) {
ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x];
ss1 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 1]) * k[x];
ss2 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 2]) * k[x];
ss3 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 3]) * k[x];
2010-07-31 06:52:47 +04:00
}
2016-05-05 12:53:22 +03:00
imOut->image[yy][xx*4 + 0] = clip8(ss0);
imOut->image[yy][xx*4 + 1] = clip8(ss1);
imOut->image[yy][xx*4 + 2] = clip8(ss2);
imOut->image[yy][xx*4 + 3] = clip8(ss3);
2010-07-31 06:52:47 +04:00
}
2016-05-05 12:53:22 +03:00
}
}
}
2016-05-05 12:53:22 +03:00
ImagingSectionLeave(&cookie);
free(kk);
free(xbounds);
return imOut;
}
2016-05-27 06:52:19 +03:00
Imaging
ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp)
{
ImagingSectionCookie cookie;
Imaging imOut;
int ss0, ss1, ss2, ss3;
int xx, yy, y, kmax, ymin, ymax;
int *xbounds;
int *k, *kk;
double *prekk;
kmax = ImagingPrecompute(imIn->ysize, ysize, filterp, &xbounds, &prekk);
if ( ! kmax) {
return (Imaging) ImagingError_MemoryError();
}
kk = malloc(ysize * kmax * sizeof(int));
if ( ! kk) {
free(xbounds);
free(prekk);
return (Imaging) ImagingError_MemoryError();
}
for (y = 0; y < ysize * kmax; y++) {
kk[y] = (int) (0.5 + prekk[y] * (1 << PRECISION_BITS));
}
free(prekk);
imOut = ImagingNew(imIn->mode, imIn->xsize, ysize);
if ( ! imOut) {
free(kk);
free(xbounds);
return NULL;
}
ImagingSectionEnter(&cookie);
if (imIn->image8) {
for (yy = 0; yy < ysize; yy++) {
k = &kk[yy * kmax];
ymin = xbounds[yy * 2 + 0];
ymax = xbounds[yy * 2 + 1];
for (xx = 0; xx < imOut->xsize; xx++) {
ss0 = 1 << (PRECISION_BITS -1);
for (y = 0; y < ymax; y++)
ss0 += ((UINT8) imIn->image8[y + ymin][xx]) * k[y];
imOut->image8[yy][xx] = clip8(ss0);
}
}
2016-05-27 06:52:19 +03:00
} else if (imIn->type == IMAGING_TYPE_UINT8) {
if (imIn->bands == 2) {
for (yy = 0; yy < ysize; yy++) {
k = &kk[yy * kmax];
ymin = xbounds[yy * 2 + 0];
ymax = xbounds[yy * 2 + 1];
for (xx = 0; xx < imOut->xsize; xx++) {
ss0 = ss3 = 1 << (PRECISION_BITS -1);
for (y = 0; y < ymax; y++) {
ss0 += ((UINT8) imIn->image[y + ymin][xx*4 + 0]) * k[y];
ss3 += ((UINT8) imIn->image[y + ymin][xx*4 + 3]) * k[y];
}
imOut->image[yy][xx*4 + 0] = clip8(ss0);
imOut->image[yy][xx*4 + 3] = clip8(ss3);
}
}
2016-05-27 06:52:19 +03:00
} else if (imIn->bands == 3) {
for (yy = 0; yy < ysize; yy++) {
k = &kk[yy * kmax];
ymin = xbounds[yy * 2 + 0];
ymax = xbounds[yy * 2 + 1];
for (xx = 0; xx < imOut->xsize; xx++) {
ss0 = ss1 = ss2 = 1 << (PRECISION_BITS -1);
for (y = 0; y < ymax; y++) {
ss0 += ((UINT8) imIn->image[y + ymin][xx*4 + 0]) * k[y];
ss1 += ((UINT8) imIn->image[y + ymin][xx*4 + 1]) * k[y];
ss2 += ((UINT8) imIn->image[y + ymin][xx*4 + 2]) * k[y];
}
imOut->image[yy][xx*4 + 0] = clip8(ss0);
imOut->image[yy][xx*4 + 1] = clip8(ss1);
imOut->image[yy][xx*4 + 2] = clip8(ss2);
}
}
} else {
for (yy = 0; yy < ysize; yy++) {
k = &kk[yy * kmax];
ymin = xbounds[yy * 2 + 0];
ymax = xbounds[yy * 2 + 1];
for (xx = 0; xx < imOut->xsize; xx++) {
ss0 = ss1 = ss2 = ss3 = 1 << (PRECISION_BITS -1);
for (y = 0; y < ymax; y++) {
ss0 += ((UINT8) imIn->image[y + ymin][xx*4 + 0]) * k[y];
ss1 += ((UINT8) imIn->image[y + ymin][xx*4 + 1]) * k[y];
ss2 += ((UINT8) imIn->image[y + ymin][xx*4 + 2]) * k[y];
ss3 += ((UINT8) imIn->image[y + ymin][xx*4 + 3]) * k[y];
}
imOut->image[yy][xx*4 + 0] = clip8(ss0);
imOut->image[yy][xx*4 + 1] = clip8(ss1);
imOut->image[yy][xx*4 + 2] = clip8(ss2);
imOut->image[yy][xx*4 + 3] = clip8(ss3);
}
}
2016-05-27 06:52:19 +03:00
}
}
ImagingSectionLeave(&cookie);
free(kk);
free(xbounds);
return imOut;
}
2016-05-05 12:53:22 +03:00
Imaging
ImagingResampleHorizontal_32bpc(Imaging imIn, int xsize, struct filter *filterp)
{
ImagingSectionCookie cookie;
Imaging imOut;
double ss;
2016-05-05 12:53:22 +03:00
int xx, yy, x, kmax, xmin, xmax;
int *xbounds;
double *k, *kk;
kmax = ImagingPrecompute(imIn->xsize, xsize, filterp, &xbounds, &kk);
if ( ! kmax) {
2016-05-05 12:53:22 +03:00
return (Imaging) ImagingError_MemoryError();
}
imOut = ImagingNew(imIn->mode, xsize, imIn->ysize);
if ( ! imOut) {
free(kk);
free(xbounds);
return NULL;
}
ImagingSectionEnter(&cookie);
2016-05-05 18:11:20 +03:00
switch(imIn->type) {
case IMAGING_TYPE_INT32:
for (yy = 0; yy < imOut->ysize; yy++) {
2014-10-26 00:11:39 +04:00
for (xx = 0; xx < xsize; xx++) {
2014-10-25 05:50:54 +04:00
xmin = xbounds[xx * 2 + 0];
xmax = xbounds[xx * 2 + 1];
2014-10-25 05:41:38 +04:00
k = &kk[xx * kmax];
2010-07-31 06:52:47 +04:00
ss = 0.0;
for (x = 0; x < xmax; x++)
ss += IMAGING_PIXEL_I(imIn, x + xmin, yy) * k[x];
2016-05-09 14:37:50 +03:00
IMAGING_PIXEL_I(imOut, xx, yy) = ROUND_UP(ss);
2010-07-31 06:52:47 +04:00
}
2016-05-05 18:11:20 +03:00
}
break;
2016-05-05 18:11:20 +03:00
case IMAGING_TYPE_FLOAT32:
for (yy = 0; yy < imOut->ysize; yy++) {
2014-10-26 00:11:39 +04:00
for (xx = 0; xx < xsize; xx++) {
2014-10-25 05:50:54 +04:00
xmin = xbounds[xx * 2 + 0];
xmax = xbounds[xx * 2 + 1];
2014-10-25 05:41:38 +04:00
k = &kk[xx * kmax];
ss = 0.0;
for (x = 0; x < xmax; x++)
ss += IMAGING_PIXEL_F(imIn, x + xmin, yy) * k[x];
2014-10-25 05:50:54 +04:00
IMAGING_PIXEL_F(imOut, xx, yy) = ss;
2010-07-31 06:52:47 +04:00
}
2016-05-05 18:11:20 +03:00
}
break;
2010-07-31 06:52:47 +04:00
}
2010-07-31 06:52:47 +04:00
ImagingSectionLeave(&cookie);
2014-10-25 05:37:33 +04:00
free(kk);
free(xbounds);
return imOut;
}
Imaging
ImagingResampleVertical_32bpc(Imaging imIn, int ysize, struct filter *filterp)
{
ImagingSectionCookie cookie;
Imaging imOut;
double ss;
int xx, yy, y, kmax, ymin, ymax;
int *xbounds;
double *k, *kk;
kmax = ImagingPrecompute(imIn->ysize, ysize, filterp, &xbounds, &kk);
if ( ! kmax) {
return (Imaging) ImagingError_MemoryError();
}
imOut = ImagingNew(imIn->mode, imIn->xsize, ysize);
if ( ! imOut) {
free(kk);
free(xbounds);
return NULL;
}
ImagingSectionEnter(&cookie);
switch(imIn->type) {
case IMAGING_TYPE_INT32:
for (yy = 0; yy < ysize; yy++) {
ymin = xbounds[yy * 2 + 0];
ymax = xbounds[yy * 2 + 1];
k = &kk[yy * kmax];
for (xx = 0; xx < imOut->xsize; xx++) {
ss = 0.0;
for (y = 0; y < ymax; y++)
ss += IMAGING_PIXEL_I(imIn, xx, y + ymin) * k[y];
IMAGING_PIXEL_I(imOut, xx, yy) = ROUND_UP(ss);
}
}
break;
case IMAGING_TYPE_FLOAT32:
for (yy = 0; yy < ysize; yy++) {
ymin = xbounds[yy * 2 + 0];
ymax = xbounds[yy * 2 + 1];
k = &kk[yy * kmax];
for (xx = 0; xx < imOut->xsize; xx++) {
ss = 0.0;
for (y = 0; y < ymax; y++)
ss += IMAGING_PIXEL_F(imIn, xx, y + ymin) * k[y];
IMAGING_PIXEL_F(imOut, xx, yy) = ss;
}
}
break;
}
ImagingSectionLeave(&cookie);
free(kk);
2014-10-25 05:37:33 +04:00
free(xbounds);
2010-07-31 06:52:47 +04:00
return imOut;
}
2014-10-26 00:11:39 +04:00
Imaging
ImagingResample(Imaging imIn, int xsize, int ysize, int filter)
{
2016-05-27 07:24:22 +03:00
Imaging imTemp;
2014-10-26 00:11:39 +04:00
Imaging imOut;
struct filter *filterp;
2016-05-05 12:53:22 +03:00
Imaging (*ResampleHorizontal)(Imaging imIn, int xsize, struct filter *filterp);
2016-05-27 07:24:22 +03:00
Imaging (*ResampleVertical)(Imaging imIn, int xsize, struct filter *filterp);
if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "1") == 0)
return (Imaging) ImagingError_ModeError();
if (imIn->type == IMAGING_TYPE_SPECIAL) {
return (Imaging) ImagingError_ModeError();
} else if (imIn->image8) {
2016-05-05 12:53:22 +03:00
ResampleHorizontal = ImagingResampleHorizontal_8bpc;
2016-05-27 07:24:22 +03:00
ResampleVertical = ImagingResampleVertical_8bpc;
2016-05-05 12:53:22 +03:00
} else {
switch(imIn->type) {
case IMAGING_TYPE_UINT8:
ResampleHorizontal = ImagingResampleHorizontal_8bpc;
2016-05-27 07:24:22 +03:00
ResampleVertical = ImagingResampleVertical_8bpc;
2016-05-05 12:53:22 +03:00
break;
case IMAGING_TYPE_INT32:
case IMAGING_TYPE_FLOAT32:
ResampleHorizontal = ImagingResampleHorizontal_32bpc;
2016-05-27 07:24:22 +03:00
ResampleVertical = ImagingResampleVertical_32bpc;
2016-05-05 12:53:22 +03:00
break;
default:
return (Imaging) ImagingError_ModeError();
}
}
/* check filter */
switch (filter) {
case IMAGING_TRANSFORM_LANCZOS:
filterp = &LANCZOS;
break;
case IMAGING_TRANSFORM_BILINEAR:
filterp = &BILINEAR;
break;
case IMAGING_TRANSFORM_BICUBIC:
filterp = &BICUBIC;
break;
default:
return (Imaging) ImagingError_ValueError(
"unsupported resampling filter"
);
}
2014-10-26 00:11:39 +04:00
/* two-pass resize, first pass */
2016-05-27 07:24:22 +03:00
imTemp = ResampleHorizontal(imIn, xsize, filterp);
if ( ! imTemp)
return NULL;
/* second pass */
2016-05-27 07:24:22 +03:00
imOut = ResampleVertical(imTemp, ysize, filterp);
ImagingDelete(imTemp);
2014-10-26 00:11:39 +04:00
if ( ! imOut)
return NULL;
return imOut;
}