diff --git a/PIL/Image.py b/PIL/Image.py index ed948e048..922fe36a8 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -150,6 +150,7 @@ FLIP_TOP_BOTTOM = 1 ROTATE_90 = 2 ROTATE_180 = 3 ROTATE_270 = 4 +TRANSPOSE = 5 # transforms AFFINE = 0 @@ -1920,13 +1921,13 @@ class Image: :param method: One of :py:attr:`PIL.Image.FLIP_LEFT_RIGHT`, :py:attr:`PIL.Image.FLIP_TOP_BOTTOM`, :py:attr:`PIL.Image.ROTATE_90`, - :py:attr:`PIL.Image.ROTATE_180`, or :py:attr:`PIL.Image.ROTATE_270`. + :py:attr:`PIL.Image.ROTATE_180`, :py:attr:`PIL.Image.ROTATE_270` or + :py:attr:`PIL.Image.TRANSPOSE`. :returns: Returns a flipped or rotated copy of this image. """ self.load() - im = self.im.transpose(method) - return self._new(im) + return self._new(self.im.transpose(method)) def effect_spread(self, distance): """ diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index 3e4257bc0..3183ceadd 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -1,30 +1,114 @@ from helper import unittest, PillowTestCase, hopper -from PIL import Image - -FLIP_LEFT_RIGHT = Image.FLIP_LEFT_RIGHT -FLIP_TOP_BOTTOM = Image.FLIP_TOP_BOTTOM -ROTATE_90 = Image.ROTATE_90 -ROTATE_180 = Image.ROTATE_180 -ROTATE_270 = Image.ROTATE_270 +from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, + ROTATE_270, TRANSPOSE) class TestImageTranspose(PillowTestCase): - def test_sanity(self): + hopper = { + 'L': hopper('L').crop((0, 0, 121, 127)).copy(), + 'RGB': hopper('RGB').crop((0, 0, 121, 127)).copy(), + } - im = hopper() + def test_flip_left_right(self): + def transpose(mode): + im = self.hopper[mode] + out = im.transpose(FLIP_LEFT_RIGHT) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size) - im.transpose(FLIP_LEFT_RIGHT) - im.transpose(FLIP_TOP_BOTTOM) + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((x-2, 1))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, y-2))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, y-2))) - im.transpose(ROTATE_90) - im.transpose(ROTATE_180) - im.transpose(ROTATE_270) + for mode in ("L", "RGB"): + transpose(mode) + + def test_flip_top_bottom(self): + def transpose(mode): + im = self.hopper[mode] + out = im.transpose(FLIP_TOP_BOTTOM) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size) + + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, y-2))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((x-2, y-2))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((x-2, 1))) + + for mode in ("L", "RGB"): + transpose(mode) + + def test_rotate_90(self): + def transpose(mode): + im = self.hopper[mode] + out = im.transpose(ROTATE_90) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size[::-1]) + + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, x-2))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((y-2, x-2))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((y-2, 1))) + + for mode in ("L", "RGB"): + transpose(mode) + + def test_rotate_180(self): + def transpose(mode): + im = self.hopper[mode] + out = im.transpose(ROTATE_180) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size) + + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((x-2, y-2))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, y-2))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, 1))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, 1))) + + for mode in ("L", "RGB"): + transpose(mode) + + def test_rotate_270(self): + def transpose(mode): + im = self.hopper[mode] + out = im.transpose(ROTATE_270) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size[::-1]) + + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((y-2, 1))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((y-2, x-2))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, x-2))) + + for mode in ("L", "RGB"): + transpose(mode) + + def test_transpose(self): + def transpose(mode): + im = self.hopper[mode] + out = im.transpose(TRANSPOSE) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size[::-1]) + + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, x-2))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((y-2, 1))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((y-2, x-2))) + + for mode in ("L", "RGB"): + transpose(mode) def test_roundtrip(self): - - im = hopper() + im = self.hopper['L'] def transpose(first, second): return im.transpose(first).transpose(second) @@ -33,12 +117,13 @@ class TestImageTranspose(PillowTestCase): im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT)) self.assert_image_equal( im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM)) - self.assert_image_equal(im, transpose(ROTATE_90, ROTATE_270)) self.assert_image_equal(im, transpose(ROTATE_180, ROTATE_180)) + self.assert_image_equal( + im.transpose(TRANSPOSE), transpose(ROTATE_90, FLIP_TOP_BOTTOM)) + self.assert_image_equal( + im.transpose(TRANSPOSE), transpose(ROTATE_270, FLIP_LEFT_RIGHT)) if __name__ == '__main__': unittest.main() - -# End of file diff --git a/_imaging.c b/_imaging.c index 1759d4c8d..4eb878cf7 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1750,6 +1750,7 @@ _transpose(ImagingObject* self, PyObject* args) break; case 2: /* rotate 90 */ case 4: /* rotate 270 */ + case 5: /* transpose */ imOut = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize); break; default: @@ -1774,6 +1775,9 @@ _transpose(ImagingObject* self, PyObject* args) case 4: (void) ImagingRotate270(imOut, imIn); break; + case 5: + (void) ImagingTranspose(imOut, imIn); + break; } return PyImagingNew(imOut); diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c index 0f59ee0d5..afd162dad 100644 --- a/libImaging/Geometry.c +++ b/libImaging/Geometry.c @@ -30,11 +30,18 @@ /* Undef if you don't need resampling filters */ #define WITH_FILTERS +/* For large images rotation is an inefficient operation in terms of CPU cache. + One row in the source image affects each column in destination. + Rotating in chunks that fit in the cache can speed up rotation + 8x on a modern CPU. A chunk size of 128 requires only 65k and is large enough + that the overhead from the extra loops are not apparent. */ +#define ROTATE_CHUNK 128 + #define COORD(v) ((v) < 0.0 ? -1 : ((int)(v))) #define FLOOR(v) ((v) < 0.0 ? ((int)floor(v)) : ((int)(v))) /* -------------------------------------------------------------------- */ -/* Transpose operations */ +/* Transpose operations */ Imaging ImagingFlipLeftRight(Imaging imOut, Imaging imIn) @@ -43,25 +50,25 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn) int x, y, xr; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) - return (Imaging) ImagingError_Mismatch(); + return (Imaging) ImagingError_Mismatch(); ImagingCopyInfo(imOut, imIn); -#define FLIP_HORIZ(image)\ +#define FLIP_HORIZ(image)\ for (y = 0; y < imIn->ysize; y++) {\ - xr = imIn->xsize-1;\ - for (x = 0; x < imIn->xsize; x++, xr--)\ - imOut->image[y][x] = imIn->image[y][xr];\ + xr = imIn->xsize-1;\ + for (x = 0; x < imIn->xsize; x++, xr--)\ + imOut->image[y][x] = imIn->image[y][xr];\ } ImagingSectionEnter(&cookie); if (imIn->image8) - FLIP_HORIZ(image8) + FLIP_HORIZ(image8) else - FLIP_HORIZ(image32) + FLIP_HORIZ(image32) ImagingSectionLeave(&cookie); @@ -76,9 +83,9 @@ ImagingFlipTopBottom(Imaging imOut, Imaging imIn) int y, yr; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) - return (Imaging) ImagingError_Mismatch(); + return (Imaging) ImagingError_Mismatch(); ImagingCopyInfo(imOut, imIn); @@ -86,7 +93,7 @@ ImagingFlipTopBottom(Imaging imOut, Imaging imIn) yr = imIn->ysize-1; 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); ImagingSectionLeave(&cookie); @@ -98,28 +105,35 @@ Imaging ImagingRotate90(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; - int x, y, xr; + int x, y, xx, yy, xr, xxsize, yysize; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) - return (Imaging) ImagingError_Mismatch(); + return (Imaging) ImagingError_Mismatch(); ImagingCopyInfo(imOut, imIn); -#define ROTATE_90(image)\ - for (y = 0; y < imIn->ysize; y++) {\ - xr = imIn->xsize-1;\ - for (x = 0; x < imIn->xsize; x++, xr--)\ - imOut->image[xr][y] = imIn->image[y][x];\ +#define ROTATE_90(image) \ + for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \ + for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \ + yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \ + xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \ + for (yy = y; yy < yysize; yy++) { \ + xr = imIn->xsize - 1 - x; \ + for (xx = x; xx < xxsize; xx++, xr--) { \ + imOut->image[xr][yy] = imIn->image[yy][xx]; \ + } \ + } \ + } \ } ImagingSectionEnter(&cookie); if (imIn->image8) - ROTATE_90(image8) + ROTATE_90(image8) else - ROTATE_90(image32) + ROTATE_90(image32) ImagingSectionLeave(&cookie); @@ -127,6 +141,43 @@ ImagingRotate90(Imaging imOut, Imaging imIn) } +Imaging +ImagingTranspose(Imaging imOut, Imaging imIn) +{ + ImagingSectionCookie cookie; + int x, y, xx, yy, xxsize, yysize; + + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + return (Imaging) ImagingError_ModeError(); + if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) + return (Imaging) ImagingError_Mismatch(); + +#define TRANSPOSE(image) \ + for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \ + for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \ + yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \ + xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \ + for (yy = y; yy < yysize; yy++) { \ + for (xx = x; xx < xxsize; xx++) { \ + imOut->image[xx][yy] = imIn->image[yy][xx]; \ + } \ + } \ + } \ + } + + ImagingCopyInfo(imOut, imIn); + + ImagingSectionEnter(&cookie); + if (imIn->image8) + TRANSPOSE(image8) + else + TRANSPOSE(image32) + ImagingSectionLeave(&cookie); + + return imOut; +} + + Imaging ImagingRotate180(Imaging imOut, Imaging imIn) { @@ -134,27 +185,27 @@ ImagingRotate180(Imaging imOut, Imaging imIn) int x, y, xr, yr; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) - return (Imaging) ImagingError_Mismatch(); + return (Imaging) ImagingError_Mismatch(); ImagingCopyInfo(imOut, imIn); yr = imIn->ysize-1; -#define ROTATE_180(image)\ +#define ROTATE_180(image)\ for (y = 0; y < imIn->ysize; y++, yr--) {\ - xr = imIn->xsize-1;\ - for (x = 0; x < imIn->xsize; x++, xr--)\ - imOut->image[y][x] = imIn->image[yr][xr];\ + xr = imIn->xsize-1;\ + for (x = 0; x < imIn->xsize; x++, xr--)\ + imOut->image[y][x] = imIn->image[yr][xr];\ } ImagingSectionEnter(&cookie); if (imIn->image8) - ROTATE_180(image8) + ROTATE_180(image8) else - ROTATE_180(image32) + ROTATE_180(image32) ImagingSectionLeave(&cookie); @@ -166,28 +217,35 @@ Imaging ImagingRotate270(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; - int x, y, yr; + int x, y, xx, yy, yr, xxsize, yysize; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) - return (Imaging) ImagingError_Mismatch(); + return (Imaging) ImagingError_Mismatch(); ImagingCopyInfo(imOut, imIn); - yr = imIn->ysize - 1; - -#define ROTATE_270(image)\ - for (y = 0; y < imIn->ysize; y++, yr--)\ - for (x = 0; x < imIn->xsize; x++)\ - imOut->image[x][y] = imIn->image[yr][x]; +#define ROTATE_270(image) \ + for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \ + for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \ + yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \ + xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \ + yr = imIn->ysize - 1 - y; \ + for (yy = y; yy < yysize; yy++, yr--) { \ + for (xx = x; xx < xxsize; xx++) { \ + imOut->image[xx][yr] = imIn->image[yy][xx]; \ + } \ + } \ + } \ + } ImagingSectionEnter(&cookie); if (imIn->image8) - ROTATE_270(image8) + ROTATE_270(image8) else - ROTATE_270(image32) + ROTATE_270(image32) ImagingSectionLeave(&cookie); @@ -196,7 +254,7 @@ ImagingRotate270(Imaging imOut, Imaging imIn) /* -------------------------------------------------------------------- */ -/* Transforms */ +/* Transforms */ /* transform primitives (ImagingTransformMap) */ @@ -597,7 +655,7 @@ ImagingTransform( double xx, yy; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); ImagingCopyInfo(imOut, imIn); @@ -613,15 +671,15 @@ ImagingTransform( y1 = imOut->ysize; for (y = y0; y < y1; y++) { - out = imOut->image[y] + x0*imOut->pixelsize; - for (x = x0; x < x1; x++) { - if (!transform(&xx, &yy, x-x0, y-y0, transform_data) || + out = imOut->image[y] + x0*imOut->pixelsize; + for (x = x0; x < x1; x++) { + if (!transform(&xx, &yy, x-x0, y-y0, transform_data) || !filter(out, imIn, xx, yy, filter_data)) { if (fill) memset(out, 0, imOut->pixelsize); } out += imOut->pixelsize; - } + } } ImagingSectionLeave(&cookie); @@ -644,7 +702,7 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn, int *xintab; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); ImagingCopyInfo(imOut, imIn); @@ -659,8 +717,8 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn, xintab = (int*) malloc(imOut->xsize * sizeof(int)); if (!xintab) { - ImagingDelete(imOut); - return (Imaging) ImagingError_MemoryError(); + ImagingDelete(imOut); + return (Imaging) ImagingError_MemoryError(); } xo = a[0]; @@ -671,29 +729,29 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn, /* Pretabulate horizontal pixel positions */ for (x = x0; x < x1; x++) { - xin = COORD(xo); - if (xin >= 0 && xin < (int) imIn->xsize) { - xmax = x+1; - if (x < xmin) - xmin = x; - xintab[x] = xin; - } - xo += a[1]; + xin = COORD(xo); + if (xin >= 0 && xin < (int) imIn->xsize) { + xmax = x+1; + if (x < xmin) + xmin = x; + xintab[x] = xin; + } + xo += a[1]; } -#define AFFINE_SCALE(pixel, image)\ +#define AFFINE_SCALE(pixel, image)\ for (y = y0; y < y1; y++) {\ - int yi = COORD(yo);\ - pixel *in, *out;\ - out = imOut->image[y];\ + int yi = COORD(yo);\ + pixel *in, *out;\ + out = imOut->image[y];\ if (fill && x1 > x0)\ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ - if (yi >= 0 && yi < imIn->ysize) {\ - in = imIn->image[yi];\ - for (x = xmin; x < xmax; x++)\ - out[x] = in[xintab[x]];\ - }\ - yo += a[5];\ + if (yi >= 0 && yi < imIn->ysize) {\ + in = imIn->image[yi];\ + for (x = xmin; x < xmax; x++)\ + out[x] = in[xintab[x]];\ + }\ + yo += a[5];\ } ImagingSectionEnter(&cookie); @@ -743,32 +801,32 @@ affine_fixed(Imaging imOut, Imaging imIn, a0 = FIX(a[0]); a1 = FIX(a[1]); a2 = FIX(a[2]); a3 = FIX(a[3]); a4 = FIX(a[4]); a5 = FIX(a[5]); -#define AFFINE_TRANSFORM_FIXED(pixel, image)\ +#define AFFINE_TRANSFORM_FIXED(pixel, image)\ for (y = y0; y < y1; y++) {\ - pixel *out;\ - xx = a0;\ - yy = a3;\ - out = imOut->image[y];\ + pixel *out;\ + xx = a0;\ + yy = a3;\ + out = imOut->image[y];\ if (fill && x1 > x0)\ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ for (x = x0; x < x1; x++, out++) {\ - xin = xx >> 16;\ - if (xin >= 0 && xin < xsize) {\ - yin = yy >> 16;\ - if (yin >= 0 && yin < ysize)\ + xin = xx >> 16;\ + if (xin >= 0 && xin < xsize) {\ + yin = yy >> 16;\ + if (yin >= 0 && yin < ysize)\ *out = imIn->image[yin][xin];\ }\ - xx += a1;\ - yy += a4;\ - }\ - a0 += a2;\ - a3 += a5;\ + xx += a1;\ + yy += a4;\ + }\ + a0 += a2;\ + a3 += a5;\ } if (imIn->image8) - AFFINE_TRANSFORM_FIXED(UINT8, image8) + AFFINE_TRANSFORM_FIXED(UINT8, image8) else - AFFINE_TRANSFORM_FIXED(INT32, image32) + AFFINE_TRANSFORM_FIXED(INT32, image32) return imOut; } @@ -801,11 +859,11 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn, } if (a[2] == 0 && a[4] == 0) - /* Scaling */ - return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill); + /* Scaling */ + return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill); if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (x0 < 0) x0 = 0; @@ -835,34 +893,34 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn, xo = a[0]; yo = a[3]; -#define AFFINE_TRANSFORM(pixel, image)\ +#define AFFINE_TRANSFORM(pixel, image)\ for (y = y0; y < y1; y++) {\ - pixel *out;\ - xx = xo;\ - yy = yo;\ - out = imOut->image[y];\ + pixel *out;\ + xx = xo;\ + yy = yo;\ + out = imOut->image[y];\ if (fill && x1 > x0)\ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ for (x = x0; x < x1; x++, out++) {\ - xin = COORD(xx);\ - if (xin >= 0 && xin < xsize) {\ - yin = COORD(yy);\ - if (yin >= 0 && yin < ysize)\ + xin = COORD(xx);\ + if (xin >= 0 && xin < xsize) {\ + yin = COORD(yy);\ + if (yin >= 0 && yin < ysize)\ *out = imIn->image[yin][xin];\ }\ - xx += a[1];\ - yy += a[4];\ - }\ - xo += a[2];\ - yo += a[5];\ + xx += a[1];\ + yy += a[4];\ + }\ + xo += a[2];\ + yo += a[5];\ } ImagingSectionEnter(&cookie); if (imIn->image8) - AFFINE_TRANSFORM(UINT8, image8) + AFFINE_TRANSFORM(UINT8, image8) else - AFFINE_TRANSFORM(INT32, image32) + AFFINE_TRANSFORM(INT32, image32) ImagingSectionLeave(&cookie); @@ -912,7 +970,7 @@ ImagingResize(Imaging imOut, Imaging imIn, int filterid) double a[6]; if (imOut->xsize == imIn->xsize && imOut->ysize == imIn->ysize) - return ImagingCopy2(imOut, imIn); + return ImagingCopy2(imOut, imIn); memset(a, 0, sizeof a); a[1] = (double) imIn->xsize / imOut->xsize; diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index d958387c9..f014bc0aa 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -292,6 +292,7 @@ extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn); extern Imaging ImagingStretch(Imaging imOut, Imaging imIn, int filter); +extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn); extern Imaging ImagingTransformPerspective( Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, double a[8], int filter, int fill);