Merge pull request #1941 from uploadcare/cleanup-transforms

Cleanup transforms
This commit is contained in:
wiredfool 2016-06-11 15:25:34 +01:00 committed by GitHub
commit ebd3c35de5
4 changed files with 175 additions and 328 deletions

View File

@ -30,6 +30,7 @@ from PIL import VERSION, PILLOW_VERSION, _plugins
import logging import logging
import warnings import warnings
import math
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -995,8 +996,7 @@ class Image(object):
im = self.im.convert("P", 1, palette.im) im = self.im.convert("P", 1, palette.im)
return self._makeself(im) return self._makeself(im)
im = self.im.quantize(colors, method, kmeans) return self._new(self.im.quantize(colors, method, kmeans))
return self._new(im)
def copy(self): def copy(self):
""" """
@ -1007,8 +1007,7 @@ class Image(object):
:returns: An :py:class:`~PIL.Image.Image` object. :returns: An :py:class:`~PIL.Image.Image` object.
""" """
self.load() self.load()
im = self.im.copy() return self._new(self.im.copy())
return self._new(im)
__copy__ = copy __copy__ = copy
@ -1571,20 +1570,31 @@ class Image(object):
:returns: An :py:class:`~PIL.Image.Image` object. :returns: An :py:class:`~PIL.Image.Image` object.
""" """
angle = angle % 360.0
# Fast paths regardless of filter
if angle == 0:
return self._new(self.im)
if angle == 180:
return self.transpose(ROTATE_180)
if angle == 90 and expand:
return self.transpose(ROTATE_90)
if angle == 270 and expand:
return self.transpose(ROTATE_270)
angle = - math.radians(angle)
matrix = [
round(math.cos(angle), 15), round(math.sin(angle), 15), 0.0,
round(-math.sin(angle), 15), round(math.cos(angle), 15), 0.0
]
def transform(x, y, matrix=matrix):
(a, b, c, d, e, f) = matrix
return a*x + b*y + c, d*x + e*y + f
w, h = self.size
if expand: if expand:
import math
angle = -angle * math.pi / 180
matrix = [
math.cos(angle), math.sin(angle), 0.0,
-math.sin(angle), math.cos(angle), 0.0
]
def transform(x, y, matrix=matrix):
(a, b, c, d, e, f) = matrix
return a*x + b*y + c, d*x + e*y + f
# calculate output size # calculate output size
w, h = self.size
xx = [] xx = []
yy = [] yy = []
for x, y in ((0, 0), (w, 0), (w, h), (0, h)): for x, y in ((0, 0), (w, 0), (w, h), (0, h)):
@ -1594,22 +1604,12 @@ class Image(object):
w = int(math.ceil(max(xx)) - math.floor(min(xx))) w = int(math.ceil(max(xx)) - math.floor(min(xx)))
h = int(math.ceil(max(yy)) - math.floor(min(yy))) h = int(math.ceil(max(yy)) - math.floor(min(yy)))
# adjust center # adjust center
x, y = transform(w / 2.0, h / 2.0) x, y = transform(w / 2.0, h / 2.0)
matrix[2] = self.size[0] / 2.0 - x matrix[2] = self.size[0] / 2.0 - x
matrix[5] = self.size[1] / 2.0 - y matrix[5] = self.size[1] / 2.0 - y
return self.transform((w, h), AFFINE, matrix, resample) return self.transform((w, h), AFFINE, matrix, resample)
if resample not in (NEAREST, BILINEAR, BICUBIC):
raise ValueError("unknown resampling filter")
self.load()
if self.mode in ("1", "P"):
resample = NEAREST
return self._new(self.im.rotate(angle, resample, expand))
def save(self, fp, format=None, **params): def save(self, fp, format=None, **params):
""" """
@ -1845,9 +1845,11 @@ class Image(object):
if isinstance(method, ImageTransformHandler): if isinstance(method, ImageTransformHandler):
return method.transform(size, self, resample=resample, fill=fill) return method.transform(size, self, resample=resample, fill=fill)
if hasattr(method, "getdata"): if hasattr(method, "getdata"):
# compatibility w. old-style transform objects # compatibility w. old-style transform objects
method, data = method.getdata() method, data = method.getdata()
if data is None: if data is None:
raise ValueError("missing method data") raise ValueError("missing method data")
@ -1863,28 +1865,23 @@ class Image(object):
def __transformer(self, box, image, method, data, def __transformer(self, box, image, method, data,
resample=NEAREST, fill=1): resample=NEAREST, fill=1):
w = box[2] - box[0]
# FIXME: this should be turned into a lazy operation (?) h = box[3] - box[1]
w = box[2]-box[0]
h = box[3]-box[1]
if method == AFFINE: if method == AFFINE:
# change argument order to match implementation data = data[0:6]
data = (data[2], data[0], data[1],
data[5], data[3], data[4])
elif method == EXTENT: elif method == EXTENT:
# convert extent to an affine transform # convert extent to an affine transform
x0, y0, x1, y1 = data x0, y0, x1, y1 = data
xs = float(x1 - x0) / w xs = float(x1 - x0) / w
ys = float(y1 - y0) / h ys = float(y1 - y0) / h
method = AFFINE method = AFFINE
data = (x0 + xs/2, xs, 0, y0 + ys/2, 0, ys) data = (xs, 0, x0 + xs/2, 0, ys, y0 + ys/2)
elif method == PERSPECTIVE: elif method == PERSPECTIVE:
# change argument order to match implementation data = data[0:8]
data = (data[2], data[0], data[1],
data[5], data[3], data[4],
data[6], data[7])
elif method == QUAD: elif method == QUAD:
# quadrilateral warp. data specifies the four corners # quadrilateral warp. data specifies the four corners
# given as NW, SW, SE, and NE. # given as NW, SW, SE, and NE.
@ -1899,6 +1896,7 @@ class Image(object):
(se[0]-sw[0]-ne[0]+x0)*As*At, (se[0]-sw[0]-ne[0]+x0)*As*At,
y0, (ne[1]-y0)*As, (sw[1]-y0)*At, y0, (ne[1]-y0)*As, (sw[1]-y0)*At,
(se[1]-sw[1]-ne[1]+y0)*As*At) (se[1]-sw[1]-ne[1]+y0)*As*At)
else: else:
raise ValueError("unknown transformation method") raise ValueError("unknown transformation method")
@ -1935,8 +1933,7 @@ class Image(object):
:param distance: Distance to spread pixels. :param distance: Distance to spread pixels.
""" """
self.load() self.load()
im = self.im.effect_spread(distance) return self._new(self.im.effect_spread(distance))
return self._new(im)
def toqimage(self): def toqimage(self):
"""Returns a QImage copy of this image""" """Returns a QImage copy of this image"""

View File

@ -1544,17 +1544,17 @@ _resize(ImagingObject* self, PyObject* args)
if (imIn->xsize == xsize && imIn->ysize == ysize) { if (imIn->xsize == xsize && imIn->ysize == ysize) {
imOut = ImagingCopy(imIn); imOut = ImagingCopy(imIn);
} }
else if ( ! filter) { else if (filter == IMAGING_TRANSFORM_NEAREST) {
double a[6]; double a[6];
memset(a, 0, sizeof a); memset(a, 0, sizeof a);
a[1] = (double) imIn->xsize / xsize; a[0] = (double) imIn->xsize / xsize;
a[5] = (double) imIn->ysize / ysize; a[4] = (double) imIn->ysize / ysize;
imOut = ImagingNew(imIn->mode, xsize, ysize); imOut = ImagingNew(imIn->mode, xsize, ysize);
imOut = ImagingTransformAffine( imOut = ImagingTransform(
imOut, imIn, imOut, imIn, IMAGING_TRANSFORM_AFFINE,
0, 0, xsize, ysize, 0, 0, xsize, ysize,
a, filter, 1); a, filter, 1);
} }
@ -1565,55 +1565,6 @@ _resize(ImagingObject* self, PyObject* args)
return PyImagingNew(imOut); return PyImagingNew(imOut);
} }
static PyObject*
_rotate(ImagingObject* self, PyObject* args)
{
Imaging imOut;
Imaging imIn;
double theta;
int filter = IMAGING_TRANSFORM_NEAREST;
int expand;
if (!PyArg_ParseTuple(args, "d|i|i", &theta, &filter, &expand))
return NULL;
imIn = self->image;
theta = fmod(theta, 360.0);
if (theta < 0.0)
theta += 360;
if (filter && imIn->type != IMAGING_TYPE_SPECIAL) {
/* Rotate with resampling filter */
imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
(void) ImagingRotate(imOut, imIn, theta, filter);
} else if ((theta == 90.0 || theta == 270.0)
&& (expand || imIn->xsize == imIn->ysize)) {
/* Use fast version */
imOut = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize);
if (imOut) {
if (theta == 90.0)
(void) ImagingRotate90(imOut, imIn);
else
(void) ImagingRotate270(imOut, imIn);
}
} else {
imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
if (imOut) {
if (theta == 0.0)
/* No rotation: simply copy the input image */
(void) ImagingCopy2(imOut, imIn);
else if (theta == 180.0)
/* Use fast version */
(void) ImagingRotate180(imOut, imIn);
else
/* Use ordinary version */
(void) ImagingRotate(imOut, imIn, theta, 0);
}
}
return PyImagingNew(imOut);
}
#define IS_RGB(mode)\ #define IS_RGB(mode)\
(!strcmp(mode, "RGB") || !strcmp(mode, "RGBA") || !strcmp(mode, "RGBX")) (!strcmp(mode, "RGB") || !strcmp(mode, "RGBA") || !strcmp(mode, "RGBX"))
@ -1662,7 +1613,6 @@ _transform2(ImagingObject* self, PyObject* args)
{ {
static const char* wrong_number = "wrong number of matrix entries"; static const char* wrong_number = "wrong number of matrix entries";
Imaging imIn;
Imaging imOut; Imaging imOut;
int n; int n;
double *a; double *a;
@ -1698,30 +1648,9 @@ _transform2(ImagingObject* self, PyObject* args)
if (!a) if (!a)
return NULL; return NULL;
imOut = self->image; imOut = ImagingTransform(
imIn = imagep->image; self->image, imagep->image, method,
x0, y0, x1, y1, a, filter, 1);
/* FIXME: move transform dispatcher into libImaging */
switch (method) {
case IMAGING_TRANSFORM_AFFINE:
imOut = ImagingTransformAffine(
imOut, imIn, x0, y0, x1, y1, a, filter, 1
);
break;
case IMAGING_TRANSFORM_PERSPECTIVE:
imOut = ImagingTransformPerspective(
imOut, imIn, x0, y0, x1, y1, a, filter, 1
);
break;
case IMAGING_TRANSFORM_QUAD:
imOut = ImagingTransformQuad(
imOut, imIn, x0, y0, x1, y1, a, filter, 1
);
break;
default:
(void) ImagingError_ValueError("bad transform method");
}
free(a); free(a);
@ -3048,7 +2977,6 @@ static struct PyMethodDef methods[] = {
// There were two methods for image resize before. // There were two methods for image resize before.
// Starting from Pillow 2.7.0 stretch is depreciated. // Starting from Pillow 2.7.0 stretch is depreciated.
{"stretch", (PyCFunction)_resize, 1}, {"stretch", (PyCFunction)_resize, 1},
{"rotate", (PyCFunction)_rotate, 1},
{"transpose", (PyCFunction)_transpose, 1}, {"transpose", (PyCFunction)_transpose, 1},
{"transform2", (PyCFunction)_transform2, 1}, {"transform2", (PyCFunction)_transform2, 1},

View File

@ -1,35 +1,5 @@
/*
* The Python Imaging Library
* $Id$
*
* the imaging geometry methods
*
* history:
* 1995-06-15 fl Created
* 1996-04-15 fl Changed origin
* 1996-05-18 fl Fixed rotate90/270 for rectangular images
* 1996-05-27 fl Added general purpose transform
* 1996-11-22 fl Don't crash when resizing from outside source image
* 1997-08-09 fl Fixed rounding error in resize
* 1998-09-21 fl Incorporated transformation patches (from Zircon #2)
* 1998-09-22 fl Added bounding box to transform engines
* 1999-02-03 fl Fixed bicubic filtering for RGB images
* 1999-02-16 fl Added fixed-point version of affine transform
* 2001-03-28 fl Fixed transform(EXTENT) for xoffset < 0
* 2003-03-10 fl Compiler tweaks
* 2004-09-19 fl Fixed bilinear/bicubic filtering of LA images
*
* Copyright (c) 1997-2003 by Secret Labs AB
* Copyright (c) 1995-1997 by Fredrik Lundh
*
* See the README file for information on usage and redistribution.
*/
#include "Imaging.h" #include "Imaging.h"
/* Undef if you don't need resampling filters */
#define WITH_FILTERS
/* For large images rotation is an inefficient operation in terms of CPU cache. /* For large images rotation is an inefficient operation in terms of CPU cache.
One row in the source image affects each column in destination. One row in the source image affects each column in destination.
Rotating in chunks that fit in the cache can speed up rotation Rotating in chunks that fit in the cache can speed up rotation
@ -72,6 +42,8 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn)
ImagingSectionLeave(&cookie); ImagingSectionLeave(&cookie);
#undef FLIP_HORIZ
return imOut; return imOut;
} }
@ -91,7 +63,7 @@ ImagingFlipTopBottom(Imaging imOut, Imaging imIn)
ImagingSectionEnter(&cookie); ImagingSectionEnter(&cookie);
yr = imIn->ysize-1; yr = imIn->ysize - 1;
for (y = 0; y < imIn->ysize; y++, yr--) for (y = 0; y < imIn->ysize; y++, yr--)
memcpy(imOut->image[yr], imIn->image[y], imIn->linesize); memcpy(imOut->image[yr], imIn->image[y], imIn->linesize);
@ -137,6 +109,8 @@ ImagingRotate90(Imaging imOut, Imaging imIn)
ImagingSectionLeave(&cookie); ImagingSectionLeave(&cookie);
#undef ROTATE_90
return imOut; return imOut;
} }
@ -152,6 +126,8 @@ ImagingTranspose(Imaging imOut, Imaging imIn)
if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize)
return (Imaging) ImagingError_Mismatch(); return (Imaging) ImagingError_Mismatch();
ImagingCopyInfo(imOut, imIn);
#define TRANSPOSE(image) \ #define TRANSPOSE(image) \
for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \ for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \ for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
@ -165,34 +141,21 @@ ImagingTranspose(Imaging imOut, Imaging imIn)
} \ } \
} }
ImagingCopyInfo(imOut, imIn);
ImagingSectionEnter(&cookie); ImagingSectionEnter(&cookie);
if (imIn->image8) if (imIn->image8)
TRANSPOSE(image8) TRANSPOSE(image8)
else else
TRANSPOSE(image32) TRANSPOSE(image32)
ImagingSectionLeave(&cookie); ImagingSectionLeave(&cookie);
#undef TRANSPOSE
return imOut; return imOut;
} }
Imaging
ImagingTransposeToNew(Imaging imIn)
{
Imaging imTemp = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize);
if ( ! imTemp)
return NULL;
if ( ! ImagingTranspose(imTemp, imIn)) {
ImagingDelete(imTemp);
return NULL;
}
return imTemp;
}
Imaging Imaging
ImagingRotate180(Imaging imOut, Imaging imIn) ImagingRotate180(Imaging imOut, Imaging imIn)
{ {
@ -206,8 +169,6 @@ ImagingRotate180(Imaging imOut, Imaging imIn)
ImagingCopyInfo(imOut, imIn); ImagingCopyInfo(imOut, imIn);
yr = imIn->ysize-1;
#define ROTATE_180(image)\ #define ROTATE_180(image)\
for (y = 0; y < imIn->ysize; y++, yr--) {\ for (y = 0; y < imIn->ysize; y++, yr--) {\
xr = imIn->xsize-1;\ xr = imIn->xsize-1;\
@ -217,6 +178,7 @@ ImagingRotate180(Imaging imOut, Imaging imIn)
ImagingSectionEnter(&cookie); ImagingSectionEnter(&cookie);
yr = imIn->ysize-1;
if (imIn->image8) if (imIn->image8)
ROTATE_180(image8) ROTATE_180(image8)
else else
@ -224,6 +186,8 @@ ImagingRotate180(Imaging imOut, Imaging imIn)
ImagingSectionLeave(&cookie); ImagingSectionLeave(&cookie);
#undef ROTATE_180
return imOut; return imOut;
} }
@ -264,6 +228,8 @@ ImagingRotate270(Imaging imOut, Imaging imIn)
ImagingSectionLeave(&cookie); ImagingSectionLeave(&cookie);
#undef ROTATE_270
return imOut; return imOut;
} }
@ -284,8 +250,8 @@ affine_transform(double* xin, double* yin, int x, int y, void* data)
double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a0 = a[0]; double a1 = a[1]; double a2 = a[2];
double a3 = a[3]; double a4 = a[4]; double a5 = a[5]; double a3 = a[3]; double a4 = a[4]; double a5 = a[5];
xin[0] = a0 + a1*x + a2*y; xin[0] = a0*x + a1*y + a2;
yin[0] = a3 + a4*x + a5*y; yin[0] = a3*x + a4*y + a5;
return 1; return 1;
} }
@ -298,8 +264,8 @@ perspective_transform(double* xin, double* yin, int x, int y, void* data)
double a3 = a[3]; double a4 = a[4]; double a5 = a[5]; double a3 = a[3]; double a4 = a[4]; double a5 = a[5];
double a6 = a[6]; double a7 = a[7]; double a6 = a[6]; double a7 = a[7];
xin[0] = (a0 + a1*x + a2*y) / (a6*x + a7*y + 1); xin[0] = (a0*x + a1*y + a2) / (a6*x + a7*y + 1);
yin[0] = (a3 + a4*x + a5*y) / (a6*x + a7*y + 1); yin[0] = (a3*x + a4*y + a5) / (a6*x + a7*y + 1);
return 1; return 1;
} }
@ -321,10 +287,8 @@ quad_transform(double* xin, double* yin, int x, int y, void* data)
/* transform filters (ImagingTransformFilter) */ /* transform filters (ImagingTransformFilter) */
#ifdef WITH_FILTERS
static int static int
nearest_filter8(void* out, Imaging im, double xin, double yin, void* data) nearest_filter8(void* out, Imaging im, double xin, double yin)
{ {
int x = COORD(xin); int x = COORD(xin);
int y = COORD(yin); int y = COORD(yin);
@ -335,7 +299,7 @@ nearest_filter8(void* out, Imaging im, double xin, double yin, void* data)
} }
static int static int
nearest_filter16(void* out, Imaging im, double xin, double yin, void* data) nearest_filter16(void* out, Imaging im, double xin, double yin)
{ {
int x = COORD(xin); int x = COORD(xin);
int y = COORD(yin); int y = COORD(yin);
@ -346,7 +310,7 @@ nearest_filter16(void* out, Imaging im, double xin, double yin, void* data)
} }
static int static int
nearest_filter32(void* out, Imaging im, double xin, double yin, void* data) nearest_filter32(void* out, Imaging im, double xin, double yin)
{ {
int x = COORD(xin); int x = COORD(xin);
int y = COORD(yin); int y = COORD(yin);
@ -391,7 +355,7 @@ nearest_filter32(void* out, Imaging im, double xin, double yin, void* data)
} }
static int static int
bilinear_filter8(void* out, Imaging im, double xin, double yin, void* data) bilinear_filter8(void* out, Imaging im, double xin, double yin)
{ {
BILINEAR_HEAD(UINT8); BILINEAR_HEAD(UINT8);
BILINEAR_BODY(UINT8, im->image8, 1, 0); BILINEAR_BODY(UINT8, im->image8, 1, 0);
@ -400,7 +364,7 @@ bilinear_filter8(void* out, Imaging im, double xin, double yin, void* data)
} }
static int static int
bilinear_filter32I(void* out, Imaging im, double xin, double yin, void* data) bilinear_filter32I(void* out, Imaging im, double xin, double yin)
{ {
BILINEAR_HEAD(INT32); BILINEAR_HEAD(INT32);
BILINEAR_BODY(INT32, im->image32, 1, 0); BILINEAR_BODY(INT32, im->image32, 1, 0);
@ -409,7 +373,7 @@ bilinear_filter32I(void* out, Imaging im, double xin, double yin, void* data)
} }
static int static int
bilinear_filter32F(void* out, Imaging im, double xin, double yin, void* data) bilinear_filter32F(void* out, Imaging im, double xin, double yin)
{ {
BILINEAR_HEAD(FLOAT32); BILINEAR_HEAD(FLOAT32);
BILINEAR_BODY(FLOAT32, im->image32, 1, 0); BILINEAR_BODY(FLOAT32, im->image32, 1, 0);
@ -418,7 +382,7 @@ bilinear_filter32F(void* out, Imaging im, double xin, double yin, void* data)
} }
static int static int
bilinear_filter32LA(void* out, Imaging im, double xin, double yin, void* data) bilinear_filter32LA(void* out, Imaging im, double xin, double yin)
{ {
BILINEAR_HEAD(UINT8); BILINEAR_HEAD(UINT8);
BILINEAR_BODY(UINT8, im->image, 4, 0); BILINEAR_BODY(UINT8, im->image, 4, 0);
@ -431,7 +395,7 @@ bilinear_filter32LA(void* out, Imaging im, double xin, double yin, void* data)
} }
static int static int
bilinear_filter32RGB(void* out, Imaging im, double xin, double yin, void* data) bilinear_filter32RGB(void* out, Imaging im, double xin, double yin)
{ {
int b; int b;
BILINEAR_HEAD(UINT8); BILINEAR_HEAD(UINT8);
@ -442,6 +406,10 @@ bilinear_filter32RGB(void* out, Imaging im, double xin, double yin, void* data)
return 1; return 1;
} }
#undef BILINEAR
#undef BILINEAR_HEAD
#undef BILINEAR_BODY
#define BICUBIC(v, v1, v2, v3, v4, d) {\ #define BICUBIC(v, v1, v2, v3, v4, d) {\
double p1 = v2;\ double p1 = v2;\
double p2 = -v1 + v3;\ double p2 = -v1 + v3;\
@ -494,7 +462,7 @@ bilinear_filter32RGB(void* out, Imaging im, double xin, double yin, void* data)
static int static int
bicubic_filter8(void* out, Imaging im, double xin, double yin, void* data) bicubic_filter8(void* out, Imaging im, double xin, double yin)
{ {
BICUBIC_HEAD(UINT8); BICUBIC_HEAD(UINT8);
BICUBIC_BODY(UINT8, im->image8, 1, 0); BICUBIC_BODY(UINT8, im->image8, 1, 0);
@ -508,7 +476,7 @@ bicubic_filter8(void* out, Imaging im, double xin, double yin, void* data)
} }
static int static int
bicubic_filter32I(void* out, Imaging im, double xin, double yin, void* data) bicubic_filter32I(void* out, Imaging im, double xin, double yin)
{ {
BICUBIC_HEAD(INT32); BICUBIC_HEAD(INT32);
BICUBIC_BODY(INT32, im->image32, 1, 0); BICUBIC_BODY(INT32, im->image32, 1, 0);
@ -517,7 +485,7 @@ bicubic_filter32I(void* out, Imaging im, double xin, double yin, void* data)
} }
static int static int
bicubic_filter32F(void* out, Imaging im, double xin, double yin, void* data) bicubic_filter32F(void* out, Imaging im, double xin, double yin)
{ {
BICUBIC_HEAD(FLOAT32); BICUBIC_HEAD(FLOAT32);
BICUBIC_BODY(FLOAT32, im->image32, 1, 0); BICUBIC_BODY(FLOAT32, im->image32, 1, 0);
@ -526,7 +494,7 @@ bicubic_filter32F(void* out, Imaging im, double xin, double yin, void* data)
} }
static int static int
bicubic_filter32LA(void* out, Imaging im, double xin, double yin, void* data) bicubic_filter32LA(void* out, Imaging im, double xin, double yin)
{ {
BICUBIC_HEAD(UINT8); BICUBIC_HEAD(UINT8);
BICUBIC_BODY(UINT8, im->image, 4, 0); BICUBIC_BODY(UINT8, im->image, 4, 0);
@ -554,7 +522,7 @@ bicubic_filter32LA(void* out, Imaging im, double xin, double yin, void* data)
} }
static int static int
bicubic_filter32RGB(void* out, Imaging im, double xin, double yin, void* data) bicubic_filter32RGB(void* out, Imaging im, double xin, double yin)
{ {
int b; int b;
BICUBIC_HEAD(UINT8); BICUBIC_HEAD(UINT8);
@ -570,6 +538,10 @@ bicubic_filter32RGB(void* out, Imaging im, double xin, double yin, void* data)
return 1; return 1;
} }
#undef BICUBIC
#undef BICUBIC_HEAD
#undef BICUBIC_BODY
static ImagingTransformFilter static ImagingTransformFilter
getfilter(Imaging im, int filterid) getfilter(Imaging im, int filterid)
{ {
@ -578,51 +550,51 @@ getfilter(Imaging im, int filterid)
if (im->image8) if (im->image8)
switch (im->type) { switch (im->type) {
case IMAGING_TYPE_UINT8: case IMAGING_TYPE_UINT8:
return (ImagingTransformFilter) nearest_filter8; return nearest_filter8;
case IMAGING_TYPE_SPECIAL: case IMAGING_TYPE_SPECIAL:
switch (im->pixelsize) { switch (im->pixelsize) {
case 1: case 1:
return (ImagingTransformFilter) nearest_filter8; return nearest_filter8;
case 2: case 2:
return (ImagingTransformFilter) nearest_filter16; return nearest_filter16;
case 4: case 4:
return (ImagingTransformFilter) nearest_filter32; return nearest_filter32;
} }
} }
else else
return (ImagingTransformFilter) nearest_filter32; return nearest_filter32;
break; break;
case IMAGING_TRANSFORM_BILINEAR: case IMAGING_TRANSFORM_BILINEAR:
if (im->image8) if (im->image8)
return (ImagingTransformFilter) bilinear_filter8; return bilinear_filter8;
else if (im->image32) { else if (im->image32) {
switch (im->type) { switch (im->type) {
case IMAGING_TYPE_UINT8: case IMAGING_TYPE_UINT8:
if (im->bands == 2) if (im->bands == 2)
return (ImagingTransformFilter) bilinear_filter32LA; return bilinear_filter32LA;
else else
return (ImagingTransformFilter) bilinear_filter32RGB; return bilinear_filter32RGB;
case IMAGING_TYPE_INT32: case IMAGING_TYPE_INT32:
return (ImagingTransformFilter) bilinear_filter32I; return bilinear_filter32I;
case IMAGING_TYPE_FLOAT32: case IMAGING_TYPE_FLOAT32:
return (ImagingTransformFilter) bilinear_filter32F; return bilinear_filter32F;
} }
} }
break; break;
case IMAGING_TRANSFORM_BICUBIC: case IMAGING_TRANSFORM_BICUBIC:
if (im->image8) if (im->image8)
return (ImagingTransformFilter) bicubic_filter8; return bicubic_filter8;
else if (im->image32) { else if (im->image32) {
switch (im->type) { switch (im->type) {
case IMAGING_TYPE_UINT8: case IMAGING_TYPE_UINT8:
if (im->bands == 2) if (im->bands == 2)
return (ImagingTransformFilter) bicubic_filter32LA; return bicubic_filter32LA;
else else
return (ImagingTransformFilter) bicubic_filter32RGB; return bicubic_filter32RGB;
case IMAGING_TYPE_INT32: case IMAGING_TYPE_INT32:
return (ImagingTransformFilter) bicubic_filter32I; return bicubic_filter32I;
case IMAGING_TYPE_FLOAT32: case IMAGING_TYPE_FLOAT32:
return (ImagingTransformFilter) bicubic_filter32F; return bicubic_filter32F;
} }
} }
break; break;
@ -631,18 +603,13 @@ getfilter(Imaging im, int filterid)
return NULL; return NULL;
} }
#else
#define getfilter(im, id) NULL
#endif
/* transformation engines */ /* transformation engines */
Imaging Imaging
ImagingTransform( ImagingGenericTransform(
Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
ImagingTransformMap transform, void* transform_data, ImagingTransformMap transform, void* transform_data,
ImagingTransformFilter filter, void* filter_data, int filterid, int fill)
int fill)
{ {
/* slow generic transformation. use ImagingTransformAffine or /* slow generic transformation. use ImagingTransformAffine or
ImagingScaleAffine where possible. */ ImagingScaleAffine where possible. */
@ -652,6 +619,10 @@ ImagingTransform(
char *out; char *out;
double xx, yy; double xx, yy;
ImagingTransformFilter filter = getfilter(imIn, filterid);
if (!filter)
return (Imaging) ImagingError_ValueError("bad filter number");
if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
return (Imaging) ImagingError_ModeError(); return (Imaging) ImagingError_ModeError();
@ -671,8 +642,8 @@ ImagingTransform(
for (y = y0; y < y1; y++) { for (y = y0; y < y1; y++) {
out = imOut->image[y] + x0*imOut->pixelsize; out = imOut->image[y] + x0*imOut->pixelsize;
for (x = x0; x < x1; x++) { for (x = x0; x < x1; x++) {
if (!transform(&xx, &yy, x-x0, y-y0, transform_data) || if ( ! transform(&xx, &yy, x-x0, y-y0, transform_data) ||
!filter(out, imIn, xx, yy, filter_data)) { ! filter(out, imIn, xx, yy)) {
if (fill) if (fill)
memset(out, 0, imOut->pixelsize); memset(out, 0, imOut->pixelsize);
} }
@ -719,8 +690,8 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn,
return (Imaging) ImagingError_MemoryError(); return (Imaging) ImagingError_MemoryError();
} }
xo = a[0]; xo = a[2];
yo = a[3]; yo = a[5];
xmin = x1; xmin = x1;
xmax = x0; xmax = x0;
@ -734,7 +705,7 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn,
xmin = x; xmin = x;
xintab[x] = xin; xintab[x] = xin;
} }
xo += a[1]; xo += a[0];
} }
#define AFFINE_SCALE(pixel, image)\ #define AFFINE_SCALE(pixel, image)\
@ -749,7 +720,7 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn,
for (x = xmin; x < xmax; x++)\ for (x = xmin; x < xmax; x++)\
out[x] = in[xintab[x]];\ out[x] = in[xintab[x]];\
}\ }\
yo += a[5];\ yo += a[4];\
} }
ImagingSectionEnter(&cookie); ImagingSectionEnter(&cookie);
@ -762,6 +733,8 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn,
ImagingSectionLeave(&cookie); ImagingSectionLeave(&cookie);
#undef AFFINE_SCALE
free(xintab); free(xintab);
return imOut; return imOut;
@ -770,8 +743,8 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn,
static inline int static inline int
check_fixed(double a[6], int x, int y) check_fixed(double a[6], int x, int y)
{ {
return (fabs(a[0] + x*a[1] + y*a[2]) < 32768.0 && return (fabs(x*a[0] + y*a[1] + a[2]) < 32768.0 &&
fabs(a[3] + x*a[4] + y*a[5]) < 32768.0); fabs(x*a[3] + y*a[4] + a[5]) < 32768.0);
} }
static inline Imaging static inline Imaging
@ -782,6 +755,7 @@ affine_fixed(Imaging imOut, Imaging imIn,
/* affine transform, nearest neighbour resampling, fixed point /* affine transform, nearest neighbour resampling, fixed point
arithmetics */ arithmetics */
ImagingSectionCookie cookie;
int x, y; int x, y;
int xin, yin; int xin, yin;
int xsize, ysize; int xsize, ysize;
@ -799,11 +773,13 @@ affine_fixed(Imaging imOut, Imaging imIn,
a0 = FIX(a[0]); a1 = FIX(a[1]); a2 = FIX(a[2]); a0 = FIX(a[0]); a1 = FIX(a[1]); a2 = FIX(a[2]);
a3 = FIX(a[3]); a4 = FIX(a[4]); a5 = FIX(a[5]); a3 = FIX(a[3]); a4 = FIX(a[4]); a5 = FIX(a[5]);
#undef FIX
#define AFFINE_TRANSFORM_FIXED(pixel, image)\ #define AFFINE_TRANSFORM_FIXED(pixel, image)\
for (y = y0; y < y1; y++) {\ for (y = y0; y < y1; y++) {\
pixel *out;\ pixel *out;\
xx = a0;\ xx = a2;\
yy = a3;\ yy = a5;\
out = imOut->image[y];\ out = imOut->image[y];\
if (fill && x1 > x0)\ if (fill && x1 > x0)\
memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\
@ -814,18 +790,24 @@ affine_fixed(Imaging imOut, Imaging imIn,
if (yin >= 0 && yin < ysize)\ if (yin >= 0 && yin < ysize)\
*out = imIn->image[yin][xin];\ *out = imIn->image[yin][xin];\
}\ }\
xx += a1;\ xx += a0;\
yy += a4;\ yy += a3;\
}\ }\
a0 += a2;\ a2 += a1;\
a3 += a5;\ a5 += a4;\
} }
ImagingSectionEnter(&cookie);
if (imIn->image8) if (imIn->image8)
AFFINE_TRANSFORM_FIXED(UINT8, image8) AFFINE_TRANSFORM_FIXED(UINT8, image8)
else else
AFFINE_TRANSFORM_FIXED(INT32, image32) AFFINE_TRANSFORM_FIXED(INT32, image32)
ImagingSectionLeave(&cookie);
#undef AFFINE_TRANSFORM_FIXED
return imOut; return imOut;
} }
@ -845,18 +827,14 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn,
double xo, yo; double xo, yo;
if (filterid || imIn->type == IMAGING_TYPE_SPECIAL) { if (filterid || imIn->type == IMAGING_TYPE_SPECIAL) {
/* Filtered transform */ return ImagingGenericTransform(
ImagingTransformFilter filter = getfilter(imIn, filterid);
if (!filter)
return (Imaging) ImagingError_ValueError("unknown filter");
return ImagingTransform(
imOut, imIn, imOut, imIn,
x0, y0, x1, y1, x0, y0, x1, y1,
affine_transform, a, affine_transform, a,
filter, NULL, fill); filterid, fill);
} }
if (a[2] == 0 && a[4] == 0) if (a[1] == 0 && a[3] == 0)
/* Scaling */ /* Scaling */
return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill); return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill);
@ -872,8 +850,6 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn,
if (y1 > imOut->ysize) if (y1 > imOut->ysize)
y1 = imOut->ysize; y1 = imOut->ysize;
ImagingCopyInfo(imOut, imIn);
/* translate all four corners to check if they are within the /* translate all four corners to check if they are within the
range that can be represented by the fixed point arithmetics */ range that can be represented by the fixed point arithmetics */
@ -885,11 +861,13 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn,
following code is used. maybe we should fall back on the slow following code is used. maybe we should fall back on the slow
generic transform engine in this case? */ generic transform engine in this case? */
ImagingCopyInfo(imOut, imIn);
xsize = (int) imIn->xsize; xsize = (int) imIn->xsize;
ysize = (int) imIn->ysize; ysize = (int) imIn->ysize;
xo = a[0]; xo = a[2];
yo = a[3]; yo = a[5];
#define AFFINE_TRANSFORM(pixel, image)\ #define AFFINE_TRANSFORM(pixel, image)\
for (y = y0; y < y1; y++) {\ for (y = y0; y < y1; y++) {\
@ -906,11 +884,11 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn,
if (yin >= 0 && yin < ysize)\ if (yin >= 0 && yin < ysize)\
*out = imIn->image[yin][xin];\ *out = imIn->image[yin][xin];\
}\ }\
xx += a[1];\ xx += a[0];\
yy += a[4];\ yy += a[3];\
}\ }\
xo += a[2];\ xo += a[1];\
yo += a[5];\ yo += a[4];\
} }
ImagingSectionEnter(&cookie); ImagingSectionEnter(&cookie);
@ -922,70 +900,35 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn,
ImagingSectionLeave(&cookie); ImagingSectionLeave(&cookie);
#undef AFFINE_TRANSFORM
return imOut; return imOut;
} }
Imaging Imaging
ImagingTransformPerspective(Imaging imOut, Imaging imIn, ImagingTransform(Imaging imOut, Imaging imIn, int method,
int x0, int y0, int x1, int y1, int x0, int y0, int x1, int y1,
double a[8], int filterid, int fill) double a[8], int filterid, int fill)
{ {
ImagingTransformFilter filter = getfilter(imIn, filterid); ImagingTransformMap transform;
if (!filter)
return (Imaging) ImagingError_ValueError("bad filter number");
return ImagingTransform( switch(method) {
case IMAGING_TRANSFORM_AFFINE:
return ImagingTransformAffine(
imOut, imIn, x0, y0, x1, y1, a, filterid, fill);
break;
case IMAGING_TRANSFORM_PERSPECTIVE:
transform = perspective_transform;
break;
case IMAGING_TRANSFORM_QUAD:
transform = quad_transform;
break;
default:
return (Imaging) ImagingError_ValueError("bad transform method");
}
return ImagingGenericTransform(
imOut, imIn, imOut, imIn,
x0, y0, x1, y1, x0, y0, x1, y1,
perspective_transform, a, transform, a, filterid, fill);
filter, NULL,
fill);
}
Imaging
ImagingTransformQuad(Imaging imOut, Imaging imIn,
int x0, int y0, int x1, int y1,
double a[8], int filterid, int fill)
{
ImagingTransformFilter filter = getfilter(imIn, filterid);
if (!filter)
return (Imaging) ImagingError_ValueError("bad filter number");
return ImagingTransform(
imOut, imIn,
x0, y0, x1, y1,
quad_transform, a,
filter, NULL,
fill);
}
/* -------------------------------------------------------------------- */
/* Convenience functions */
Imaging
ImagingRotate(Imaging imOut, Imaging imIn, double theta, int filterid)
{
int xsize, ysize;
double sintheta, costheta;
double a[6];
/* Setup an affine transform to rotate around the image center */
theta = -theta * M_PI / 180.0;
sintheta = sin(theta);
costheta = cos(theta);
xsize = imOut->xsize;
ysize = imOut->ysize;
a[0] = -costheta * xsize/2 - sintheta * ysize/2 + xsize/2;
a[1] = costheta;
a[2] = sintheta;
a[3] = sintheta * xsize/2 - costheta * ysize/2 + ysize/2;
a[4] = -sintheta;
a[5] = costheta;
return ImagingTransformAffine(
imOut, imIn,
0, 0, imOut->xsize, imOut->ysize,
a, filterid, 1);
} }

View File

@ -236,8 +236,7 @@ extern void ImagingError_Clear(void);
typedef int (*ImagingTransformMap)(double* X, double* Y, typedef int (*ImagingTransformMap)(double* X, double* Y,
int x, int y, void* data); int x, int y, void* data);
typedef int (*ImagingTransformFilter)(void* out, Imaging im, typedef int (*ImagingTransformFilter)(void* out, Imaging im,
double x, double y, double x, double y);
void* data);
/* Image Manipulation Methods */ /* Image Manipulation Methods */
/* -------------------------- */ /* -------------------------- */
@ -286,28 +285,14 @@ extern Imaging ImagingPointTransform(
Imaging imIn, double scale, double offset); Imaging imIn, double scale, double offset);
extern Imaging ImagingPutBand(Imaging im, Imaging imIn, int band); extern Imaging ImagingPutBand(Imaging im, Imaging imIn, int band);
extern Imaging ImagingRankFilter(Imaging im, int size, int rank); extern Imaging ImagingRankFilter(Imaging im, int size, int rank);
extern Imaging ImagingRotate(
Imaging imOut, Imaging imIn, double theta, int filter);
extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn);
extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn);
extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn);
extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter); extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter);
extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn); extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn);
extern Imaging ImagingTransposeToNew(Imaging imIn);
extern Imaging ImagingTransformPerspective(
Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
double a[8], int filter, int fill);
extern Imaging ImagingTransformAffine(
Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
double a[6], int filter, int fill);
extern Imaging ImagingTransformQuad(
Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
double a[8], int filter, int fill);
extern Imaging ImagingTransform( extern Imaging ImagingTransform(
Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, Imaging imOut, Imaging imIn, int method, int x0, int y0, int x1, int y1,
ImagingTransformMap transform, void* transform_data, double *a, int filter, int fill);
ImagingTransformFilter filter, void* filter_data,
int fill);
extern Imaging ImagingUnsharpMask( extern Imaging ImagingUnsharpMask(
Imaging imOut, Imaging im, float radius, int percent, int threshold); Imaging imOut, Imaging im, float radius, int percent, int threshold);
extern Imaging ImagingBoxBlur(Imaging imOut, Imaging imIn, float radius, int n); extern Imaging ImagingBoxBlur(Imaging imOut, Imaging imIn, float radius, int n);
@ -338,12 +323,6 @@ extern Imaging ImagingChopXor(Imaging imIn1, Imaging imIn2);
extern void ImagingCrack(Imaging im, int x0, int y0); extern void ImagingCrack(Imaging im, int x0, int y0);
/* Graphics */ /* Graphics */
struct ImagingAffineMatrixInstance {
float a[9];
};
typedef struct ImagingAffineMatrixInstance *ImagingAffineMatrix;
extern int ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1, extern int ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1,
float start, float end, const void* ink, int op); float start, float end, const void* ink, int op);
extern int ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap, extern int ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap,