diff --git a/PIL/Image.py b/PIL/Image.py index e49b1555d..a0ba25b8c 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -150,6 +150,7 @@ ROTATE_90 = 2 ROTATE_180 = 3 ROTATE_270 = 4 TRANSPOSE = 5 +TRANSVERSE = 6 # transforms AFFINE = 0 @@ -2173,8 +2174,8 @@ class Image(object): :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`, :py:attr:`PIL.Image.ROTATE_270` or - :py:attr:`PIL.Image.TRANSPOSE`. + :py:attr:`PIL.Image.ROTATE_180`, :py:attr:`PIL.Image.ROTATE_270`, + :py:attr:`PIL.Image.TRANSPOSE` or :py:attr:`PIL.Image.TRANSVERSE`. :returns: Returns a flipped or rotated copy of this image. """ diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index e13fc8605..a6b1191db 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -2,7 +2,7 @@ import helper from helper import unittest, PillowTestCase from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, - ROTATE_270, TRANSPOSE) + ROTATE_270, TRANSPOSE, TRANSVERSE) class TestImageTranspose(PillowTestCase): @@ -108,6 +108,22 @@ class TestImageTranspose(PillowTestCase): for mode in ("L", "RGB"): transpose(mode) + def test_tranverse(self): + def transpose(mode): + im = self.hopper[mode] + out = im.transpose(TRANSVERSE) + 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, x-2))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((y-2, 1))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, x-2))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, 1))) + + for mode in ("L", "RGB"): + transpose(mode) + def test_roundtrip(self): im = self.hopper['L'] @@ -124,6 +140,12 @@ class TestImageTranspose(PillowTestCase): im.transpose(TRANSPOSE), transpose(ROTATE_90, FLIP_TOP_BOTTOM)) self.assert_image_equal( im.transpose(TRANSPOSE), transpose(ROTATE_270, FLIP_LEFT_RIGHT)) + self.assert_image_equal( + im.transpose(TRANSVERSE), transpose(ROTATE_90, FLIP_LEFT_RIGHT)) + self.assert_image_equal( + im.transpose(TRANSVERSE), transpose(ROTATE_270, FLIP_TOP_BOTTOM)) + self.assert_image_equal( + im.transpose(TRANSVERSE), transpose(ROTATE_180, TRANSPOSE)) if __name__ == '__main__': diff --git a/_imaging.c b/_imaging.c index d0777c73a..84f82f943 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1678,6 +1678,7 @@ _transpose(ImagingObject* self, PyObject* args) case 2: /* rotate 90 */ case 4: /* rotate 270 */ case 5: /* transpose */ + case 6: /* transverse */ imOut = ImagingNewDirty(imIn->mode, imIn->ysize, imIn->xsize); break; default: @@ -1705,6 +1706,9 @@ _transpose(ImagingObject* self, PyObject* args) case 5: (void) ImagingTranspose(imOut, imIn); break; + case 6: + (void) ImagingTransverse(imOut, imIn); + break; } return PyImagingNew(imOut); diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c index 2b3b1d5ef..759056ae0 100644 --- a/libImaging/Geometry.c +++ b/libImaging/Geometry.c @@ -5,7 +5,8 @@ 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 ROTATE_CHUNK 512 +#define ROTATE_SMALL_CHUNK 8 #define COORD(v) ((v) < 0.0 ? -1 : ((int)(v))) #define FLOOR(v) ((v) < 0.0 ? ((int)floor(v)) : ((int)(v))) @@ -26,30 +27,27 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn) ImagingCopyInfo(imOut, imIn); +#define FLIP_LEFT_RIGHT(INT, image) \ + for (y = 0; y < imIn->ysize; y++) { \ + INT* in = imIn->image[y]; \ + INT* out = imOut->image[y]; \ + xr = imIn->xsize-1; \ + for (x = 0; x < imIn->xsize; x++, xr--) \ + out[xr] = in[x]; \ + } + ImagingSectionEnter(&cookie); if (imIn->image8) { - for (y = 0; y < imIn->ysize; y++) { - UINT8* in = (UINT8*) imIn->image8[y]; - UINT8* out = (UINT8*) imOut->image8[y]; - x = 0; - xr = imIn->xsize-1; - for (; x < imIn->xsize; x++, xr--) - out[xr] = in[x]; - } + FLIP_LEFT_RIGHT(UINT8, image8) } else { - for (y = 0; y < imIn->ysize; y++) { - UINT32* in = (UINT32*) imIn->image32[y]; - UINT32* out = (UINT32*) imOut->image32[y]; - x = 0; - xr = imIn->xsize-1; - for (; x < imIn->xsize; x++, xr--) - out[xr] = in[x]; - } + FLIP_LEFT_RIGHT(INT32, image32) } ImagingSectionLeave(&cookie); +#undef FLIP_LEFT_RIGHT + return imOut; } @@ -84,6 +82,7 @@ ImagingRotate90(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; int x, y, xx, yy, xr, xxsize, yysize; + int xxx, yyy, xxxsize, yyysize; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); @@ -92,15 +91,22 @@ ImagingRotate90(Imaging imOut, Imaging imIn) ImagingCopyInfo(imOut, imIn); -#define ROTATE_90(image) \ +#define ROTATE_90(INT, 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]; \ + for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \ + for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \ + yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \ + xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \ + for (yyy = yy; yyy < yyysize; yyy++) { \ + INT* in = imIn->image[yyy]; \ + xr = imIn->xsize - 1 - xx; \ + for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \ + imOut->image[xr][yyy] = in[xxx]; \ + } \ + } \ } \ } \ } \ @@ -109,9 +115,9 @@ ImagingRotate90(Imaging imOut, Imaging imIn) ImagingSectionEnter(&cookie); if (imIn->image8) - ROTATE_90(image8) + ROTATE_90(UINT8, image8) else - ROTATE_90(image32) + ROTATE_90(INT32, image32) ImagingSectionLeave(&cookie); @@ -126,6 +132,7 @@ ImagingTranspose(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; int x, y, xx, yy, xxsize, yysize; + int xxx, yyy, xxxsize, yyysize; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); @@ -134,14 +141,21 @@ ImagingTranspose(Imaging imOut, Imaging imIn) ImagingCopyInfo(imOut, imIn); -#define TRANSPOSE(image) \ +#define TRANSPOSE(INT, 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]; \ + for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \ + for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \ + yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \ + xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \ + for (yyy = yy; yyy < yyysize; yyy++) { \ + INT* in = imIn->image[yyy]; \ + for (xxx = xx; xxx < xxxsize; xxx++) { \ + imOut->image[xxx][yyy] = in[xxx]; \ + } \ + } \ } \ } \ } \ @@ -150,9 +164,9 @@ ImagingTranspose(Imaging imOut, Imaging imIn) ImagingSectionEnter(&cookie); if (imIn->image8) - TRANSPOSE(image8) + TRANSPOSE(UINT8, image8) else - TRANSPOSE(image32) + TRANSPOSE(INT32, image32) ImagingSectionLeave(&cookie); @@ -162,6 +176,57 @@ ImagingTranspose(Imaging imOut, Imaging imIn) } +Imaging +ImagingTransverse(Imaging imOut, Imaging imIn) +{ + ImagingSectionCookie cookie; + int x, y, xr, yr, xx, yy, xxsize, yysize; + int xxx, yyy, xxxsize, yyysize; + + 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(); + + ImagingCopyInfo(imOut, imIn); + +#define TRANSVERSE(INT, 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 += ROTATE_SMALL_CHUNK) { \ + for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \ + yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \ + xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \ + yr = imIn->ysize - 1 - yy; \ + for (yyy = yy; yyy < yyysize; yyy++, yr--) { \ + INT* in = imIn->image[yyy]; \ + xr = imIn->xsize - 1 - xx; \ + for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \ + imOut->image[xr][yr] = in[xxx]; \ + } \ + } \ + } \ + } \ + } \ + } + + ImagingSectionEnter(&cookie); + + if (imIn->image8) + TRANSVERSE(UINT8, image8) + else + TRANSVERSE(INT32, image32) + + ImagingSectionLeave(&cookie); + +#undef TRANSVERSE + + return imOut; +} + + Imaging ImagingRotate180(Imaging imOut, Imaging imIn) { @@ -175,20 +240,23 @@ ImagingRotate180(Imaging imOut, Imaging imIn) ImagingCopyInfo(imOut, imIn); -#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];\ +#define ROTATE_180(INT, image) \ + for (y = 0; y < imIn->ysize; y++, yr--) { \ + INT* in = imIn->image[y]; \ + INT* out = imOut->image[yr]; \ + xr = imIn->xsize-1; \ + for (x = 0; x < imIn->xsize; x++, xr--) \ + out[xr] = in[x]; \ } ImagingSectionEnter(&cookie); yr = imIn->ysize-1; - if (imIn->image8) - ROTATE_180(image8) - else - ROTATE_180(image32) + if (imIn->image8) { + ROTATE_180(UINT8, image8) + } else { + ROTATE_180(INT32, image32) + } ImagingSectionLeave(&cookie); @@ -203,6 +271,7 @@ ImagingRotate270(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; int x, y, xx, yy, yr, xxsize, yysize; + int xxx, yyy, xxxsize, yyysize; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); @@ -211,15 +280,22 @@ ImagingRotate270(Imaging imOut, Imaging imIn) ImagingCopyInfo(imOut, imIn); -#define ROTATE_270(image) \ +#define ROTATE_270(INT, 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]; \ + for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \ + for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \ + yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \ + xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \ + yr = imIn->ysize - 1 - yy; \ + for (yyy = yy; yyy < yyysize; yyy++, yr--) { \ + INT* in = imIn->image[yyy]; \ + for (xxx = xx; xxx < xxxsize; xxx++) { \ + imOut->image[xxx][yr] = in[xxx]; \ + } \ + } \ } \ } \ } \ @@ -228,9 +304,9 @@ ImagingRotate270(Imaging imOut, Imaging imIn) ImagingSectionEnter(&cookie); if (imIn->image8) - ROTATE_270(image8) + ROTATE_270(UINT8, image8) else - ROTATE_270(image32) + ROTATE_270(INT32, image32) ImagingSectionLeave(&cookie); diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index 60c5b4847..ac1ae6d34 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -289,8 +289,9 @@ extern Imaging ImagingRankFilter(Imaging im, int size, int rank); extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn); -extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]); extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn); +extern Imaging ImagingTransverse(Imaging imOut, Imaging imIn); +extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]); extern Imaging ImagingTransform( Imaging imOut, Imaging imIn, int method, int x0, int y0, int x1, int y1, double *a, int filter, int fill);