From bdd292f2cd7cc512aaa1c10f139665d65ba7bba6 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sun, 21 Aug 2022 18:41:38 -0500 Subject: [PATCH] update Fill.c functions to handle more pixel sizes --- Tests/test_image.py | 118 ++++++++++++----- src/libImaging/Convert.c | 2 + src/libImaging/Fill.c | 276 +++++++++++++++++++++++++++++++-------- 3 files changed, 307 insertions(+), 89 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 6dc89918f..f28bfdfea 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -531,57 +531,103 @@ class TestImage: px = im.load() assert px[0, 0] == 5 - def test_linear_gradient_wrong_mode(self): - # Arrange - wrong_mode = "RGB" - - # Act / Assert + @pytest.mark.parametrize("wrong_mode", [ + "bad", + "I;16N", + # "LAB", + "BGR;15", + "BGR;16", + "BGR;24", + "BGR;32", + ]) + def test_linear_gradient_wrong_mode(self, wrong_mode): with pytest.raises(ValueError): Image.linear_gradient(wrong_mode) - def test_linear_gradient(self): - + @pytest.mark.parametrize("mode,pixel_a,pixel_b", [ + ("1", 0, 255), + ("P", 0, 255), + ("PA", (0, 255), (255, 255)), + ("L", 0, 255), + ("LA", (0, 255), (255, 255)), + ("La", (0, 255), (255, 255)), + ("F", 0, 255), + ("I", 0, 255), + ("I;16", 0, 255), + ("I;16L", 0, 255), + ("I;16B", 0, 255), + ("RGB", (0, 0, 0), (255, 255, 255)), + ("RGBX", (0, 0, 0, 255), (255, 255, 255, 255)), + ("RGBA", (0, 0, 0, 255), (255, 255, 255, 255)), + ("RGBa", (0, 0, 0, 255), (255, 255, 255, 255)), + ("CMYK", (0, 0, 0, 255), (0, 0, 0, 0)), + ("YCbCr", (0, 128, 128), (255, 128, 128)), + ("HSV", (0, 0, 0), (0, 0, 255)), + ]) + def test_linear_gradient(self, mode, pixel_a, pixel_b): # Arrange target_file = "Tests/images/linear_gradient.png" - for mode in ["L", "P", "I", "F"]: - # Act - im = Image.linear_gradient(mode) + # Act + im = Image.linear_gradient(mode) - # Assert - assert im.size == (256, 256) - assert im.mode == mode - assert im.getpixel((0, 0)) == 0 - assert im.getpixel((255, 255)) == 255 - with Image.open(target_file) as target: - target = target.convert(mode) - assert_image_equal(im, target) + # Assert + assert im.size == (256, 256) + assert im.mode == mode + assert im.getpixel((0, 0)) == pixel_a + assert im.getpixel((255, 255)) == pixel_b + with Image.open(target_file) as target: + target = target.convert(mode, dither=0) + assert_image_equal(im, target) - def test_radial_gradient_wrong_mode(self): - # Arrange - wrong_mode = "RGB" - - # Act / Assert + @pytest.mark.parametrize("wrong_mode", [ + "bad", + "I;16N", + # "LAB", + "BGR;15", + "BGR;16", + "BGR;24", + "BGR;32", + ]) + def test_radial_gradient_wrong_mode(self, wrong_mode): with pytest.raises(ValueError): Image.radial_gradient(wrong_mode) - def test_radial_gradient(self): - + @pytest.mark.parametrize("mode,pixel_a,pixel_b", [ + ("1", 255, 0), + ("P", 255, 0), + ("PA", (255, 255), (0, 255)), + ("L", 255, 0), + ("LA", (255, 255), (0, 255)), + ("La", (255, 255), (0, 255)), + ("F", 255, 0), + ("I", 255, 0), + ("I;16", 255, 0), + ("I;16L", 255, 0), + ("I;16B", 255, 0), + ("RGB", (255, 255, 255), (0, 0, 0)), + ("RGBX", (255, 255, 255, 255), (0, 0, 0, 255)), + ("RGBA", (255, 255, 255, 255), (0, 0, 0, 255)), + ("RGBa", (255, 255, 255, 255), (0, 0, 0, 255)), + ("CMYK", (0, 0, 0, 0), (0, 0, 0, 255)), + ("YCbCr", (255, 128, 128), (0, 128, 128)), + ("HSV", (0, 0, 255), (0, 0, 0)), + ]) + def test_radial_gradient(self, mode, pixel_a, pixel_b): # Arrange target_file = "Tests/images/radial_gradient.png" - for mode in ["L", "P", "I", "F"]: - # Act - im = Image.radial_gradient(mode) + # Act + im = Image.radial_gradient(mode) - # Assert - assert im.size == (256, 256) - assert im.mode == mode - assert im.getpixel((0, 0)) == 255 - assert im.getpixel((128, 128)) == 0 - with Image.open(target_file) as target: - target = target.convert(mode) - assert_image_equal(im, target) + # Assert + assert im.size == (256, 256) + assert im.mode == mode + assert im.getpixel((0, 0)) == pixel_a + assert im.getpixel((128, 128)) == pixel_b + with Image.open(target_file) as target: + target = target.convert(mode, dither=0) + assert_image_equal(im, target) def test_register_extensions(self): test_format = "a" diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index 5a8e1ac73..777c5fdd7 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -926,10 +926,12 @@ static struct { {"L", "1", l2bit}, {"L", "LA", l2la}, + {"L", "La", l2la}, {"L", "I", l2i}, {"L", "F", l2f}, {"L", "RGB", l2rgb}, {"L", "RGBA", l2rgb}, + {"L", "RGBa", l2rgb}, {"L", "RGBX", l2rgb}, {"L", "CMYK", l2cmyk}, {"L", "YCbCr", l2ycbcr}, diff --git a/src/libImaging/Fill.c b/src/libImaging/Fill.c index f72060228..188b84804 100644 --- a/src/libImaging/Fill.c +++ b/src/libImaging/Fill.c @@ -24,40 +24,45 @@ ImagingFill(Imaging im, const void *colour) { int x, y; ImagingSectionCookie cookie; - if (im->type == IMAGING_TYPE_SPECIAL) { - /* use generic API */ + // Check if every byte in the given color is the same. + // If they are, we can use memset. + int allSame = 1; + const UINT8 b = *(UINT8 *)colour; + for (x = 1; x < im->pixelsize; ++x) { + if (((UINT8 *)colour)[x] != b) { + allSame = 0; + break; + } + } + + ImagingSectionEnter(&cookie); + if (allSame) { + for (y = 0; y < im->ysize; ++y) { + memset(im->image[y], b, im->linesize); + } + } else if (im->type == IMAGING_TYPE_SPECIAL) { ImagingAccess access = ImagingAccessNew(im); if (access) { - for (y = 0; y < im->ysize; y++) { - for (x = 0; x < im->xsize; x++) { + for (y = 0; y < im->ysize; ++y) { + for (x = 0; x < im->xsize; ++x) { access->put_pixel(im, x, y, colour); } } ImagingAccessDelete(im, access); } else { - /* wipe the image */ - for (y = 0; y < im->ysize; y++) { + for (y = 0; y < im->ysize; ++y) { memset(im->image[y], 0, im->linesize); } } } else { - INT32 c = 0L; - ImagingSectionEnter(&cookie); - memcpy(&c, colour, im->pixelsize); - if (im->image32 && c != 0L) { - for (y = 0; y < im->ysize; y++) { - for (x = 0; x < im->xsize; x++) { - im->image32[y][x] = c; - } - } - } else { - unsigned char cc = (unsigned char)*(UINT8 *)colour; - for (y = 0; y < im->ysize; y++) { - memset(im->image[y], cc, im->linesize); + for (y = 0; y < im->ysize; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < im->xsize; ++x) { + memcpy(out + x * im->pixelsize, colour, im->pixelsize); } } - ImagingSectionLeave(&cookie); } + ImagingSectionLeave(&cookie); return im; } @@ -65,70 +70,235 @@ ImagingFill(Imaging im, const void *colour) { Imaging ImagingFillLinearGradient(const char *mode) { Imaging im; - int y; - if (strlen(mode) != 1) { - return (Imaging)ImagingError_ModeError(); + if (strcmp(mode, "P") == 0 || strcmp(mode, "PA") == 0) { + Imaging imTemp = ImagingFillLinearGradient("L"); + im = ImagingConvert(imTemp, mode, NULL, 0); + ImagingDelete(imTemp); + return im; } - im = ImagingNewDirty(mode, 256, 256); + const int size = 256; + im = ImagingNewDirty(mode, size, size); if (!im) { return NULL; } - if (im->image8) { - for (y = 0; y < 256; y++) { - memset(im->image8[y], (unsigned char)y, 256); - } - } else { - int x; - for (y = 0; y < 256; y++) { - for (x = 0; x < 256; x++) { - if (im->type == IMAGING_TYPE_FLOAT32) { - IMAGING_PIXEL_FLOAT32(im, x, y) = y; - } else { + int y, x; + switch (im->type) { + case IMAGING_TYPE_UINT8: + if (strcmp(mode, "1") == 0) { + for (y = 0; y < size; ++y) { + memset(im->image[y], y < 128 ? 0 : 255, size); + } + } else if (strcmp(mode, "LA") == 0 || strcmp(mode, "La") == 0) { + for (y = 0; y < size; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < size; ++x) { + *out++ = y; + *out++ = 255; + } + } + } else if (strcmp(mode, "RGBA") == 0 || strcmp(mode, "RGBa") == 0 || strcmp(mode, "RGBX") == 0) { + for (y = 0; y < size; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < size; ++x) { + memset(out + x * 4, y, 3); + out[x * 4 + 3] = 255; + } + } + } else if (strcmp(mode, "CMYK") == 0) { + for (y = 0; y < size; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < size; ++x) { + memset(out + x * 4, 0, 3); + out[x * 4 + 3] = ~(UINT8)y; + } + } + } else if (strcmp(mode, "YCbCr") == 0) { + for (y = 0; y < size; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < size; ++x) { + *out++ = y; + *out++ = 128; + *out++ = 128; + *out++ = 255; + } + } + } else if (strcmp(mode, "HSV") == 0) { + for (y = 0; y < size; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < size; ++x) { + *out++ = 0; + *out++ = 0; + *out++ = y; + *out++ = 255; + } + } + } else { + for (y = 0; y < size; ++y) { + memset(im->image[y], y, im->linesize); + } + } + break; + case IMAGING_TYPE_INT32: + for (y = 0; y < size; ++y) { + for (x = 0; x < size; ++x) { IMAGING_PIXEL_INT32(im, x, y) = y; } } + break; + case IMAGING_TYPE_FLOAT32: + for (y = 0; y < size; ++y) { + for (x = 0; x < size; ++x) { + IMAGING_PIXEL_FLOAT32(im, x, y) = y; + } + } + break; + case IMAGING_TYPE_SPECIAL: { + ImagingAccess access = ImagingAccessNew(im); + if (access) { + for (y = 0; y < im->ysize; ++y) { + for (x = 0; x < im->xsize; ++x) { + access->put_pixel(im, x, y, &y); + } + } + ImagingAccessDelete(im, access); + } else { + ImagingDelete(im); + return (Imaging)ImagingError_ModeError(); + } + break; } } return im; } +#define CALC_RADIAL_COLOUR(c, x, y) c = ((int)sqrt(\ +(double)((x - 128) * (x - 128) + (y - 128) * (y - 128)) * 2.0));\ +if (c >= 255) {c = 255;} Imaging ImagingFillRadialGradient(const char *mode) { Imaging im; - int x, y; - int d; - if (strlen(mode) != 1) { - return (Imaging)ImagingError_ModeError(); + if (strcmp(mode, "P") == 0 || strcmp(mode, "PA") == 0) { + Imaging imTemp = ImagingFillRadialGradient("L"); + im = ImagingConvert(imTemp, mode, NULL, 0); + ImagingDelete(imTemp); + return im; } - im = ImagingNewDirty(mode, 256, 256); + const int size = 256; + im = ImagingNewDirty(mode, size, size); if (!im) { return NULL; } - for (y = 0; y < 256; y++) { - for (x = 0; x < 256; x++) { - d = (int)sqrt( - (double)((x - 128) * (x - 128) + (y - 128) * (y - 128)) * 2.0); - if (d >= 255) { - d = 255; - } - if (im->image8) { - im->image8[y][x] = d; + int y, x, c; + switch (im->type) { + case IMAGING_TYPE_UINT8: + if (strcmp(mode, "1") == 0) { + for (y = 0; y < size; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < size; ++x) { + CALC_RADIAL_COLOUR(c, x, y) + out[x] = c < 128 ? 0 : 255; + } + } + } else if (strcmp(mode, "LA") == 0 || strcmp(mode, "La") == 0) { + for (y = 0; y < size; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < size; ++x) { + CALC_RADIAL_COLOUR(c, x, y) + *out++ = c; + *out++ = 255; + } + } + } else if (strcmp(mode, "RGBA") == 0 || strcmp(mode, "RGBa") == 0 || strcmp(mode, "RGBX") == 0) { + for (y = 0; y < size; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < size; ++x) { + CALC_RADIAL_COLOUR(c, x, y) + memset(out + x * 4, c, 3); + out[x * 4 + 3] = 255; + } + } + } else if (strcmp(mode, "CMYK") == 0) { + for (y = 0; y < size; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < size; ++x) { + CALC_RADIAL_COLOUR(c, x, y) + memset(out + x * 4, 0, 3); + out[x * 4 + 3] = ~(UINT8)c; + } + } + } else if (strcmp(mode, "YCbCr") == 0) { + for (y = 0; y < size; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < size; ++x) { + CALC_RADIAL_COLOUR(c, x, y) + *out++ = c; + *out++ = 128; + *out++ = 128; + *out++ = 255; + } + } + } else if (strcmp(mode, "HSV") == 0) { + for (y = 0; y < size; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < size; ++x) { + CALC_RADIAL_COLOUR(c, x, y) + *out++ = 0; + *out++ = 0; + *out++ = c; + *out++ = 255; + } + } } else { - if (im->type == IMAGING_TYPE_FLOAT32) { - IMAGING_PIXEL_FLOAT32(im, x, y) = d; - } else { - IMAGING_PIXEL_INT32(im, x, y) = d; + for (y = 0; y < size; ++y) { + UINT8 *out = (UINT8 *)im->image[y]; + for (x = 0; x < size; ++x) { + CALC_RADIAL_COLOUR(c, x, y) + memset(out + x * im->pixelsize, c, im->pixelsize); + } } } + break; + case IMAGING_TYPE_INT32: + for (y = 0; y < size; ++y) { + for (x = 0; x < size; ++x) { + CALC_RADIAL_COLOUR(c, x, y) + IMAGING_PIXEL_INT32(im, x, y) = c; + } + } + break; + case IMAGING_TYPE_FLOAT32: + for (y = 0; y < size; ++y) { + for (x = 0; x < size; ++x) { + CALC_RADIAL_COLOUR(c, x, y) + IMAGING_PIXEL_FLOAT32(im, x, y) = c; + } + } + break; + case IMAGING_TYPE_SPECIAL: { + ImagingAccess access = ImagingAccessNew(im); + if (access) { + for (y = 0; y < im->ysize; ++y) { + for (x = 0; x < im->xsize; ++x) { + CALC_RADIAL_COLOUR(c, x, y) + access->put_pixel(im, x, y, &c); + } + } + ImagingAccessDelete(im, access); + } else { + ImagingDelete(im); + return (Imaging)ImagingError_ModeError(); + } + break; } } return im; } +#undef CALC_RADIAL_COLOUR