From c19932757ccdbe0a95903bbb3e8ce0f06e5ac8b4 Mon Sep 17 00:00:00 2001 From: ybai08 <271969568+ybai08@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:46:08 -0500 Subject: [PATCH 1/4] Support I;16 modes in BoxBlur and GaussianBlur --- Tests/test_box_blur.py | 44 ++++++++++++++ Tests/test_image_filter.py | 27 +++++++++ docs/releasenotes/13.1.0.rst | 11 ++++ docs/releasenotes/index.rst | 1 + src/libImaging/BoxBlur.c | 110 ++++++++++++++++++++++++++++++++++- src/libImaging/UnsharpMask.c | 7 ++- 6 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 docs/releasenotes/13.1.0.rst diff --git a/Tests/test_box_blur.py b/Tests/test_box_blur.py index e10200c0158..65f2cc55df2 100644 --- a/Tests/test_box_blur.py +++ b/Tests/test_box_blur.py @@ -51,6 +51,8 @@ def assert_blur( rgba = Image.merge("RGBA", (im, im, im, im)) for band in box_blur(rgba, radius, passes).split(): assert_image(band, data, delta) + for mode in ("I;16", "I;16L", "I;16B", "I;16N"): + assert_image(box_blur(im.convert(mode), radius, passes), data, delta) def test_color_modes() -> None: @@ -61,6 +63,10 @@ def test_color_modes() -> None: box_blur(sample.convert("L")) box_blur(sample.convert("LA")) box_blur(sample.convert("LA").convert("La")) + box_blur(sample.convert("I;16")) + box_blur(sample.convert("I;16L")) + box_blur(sample.convert("I;16B")) + box_blur(sample.convert("I;16N")) with pytest.raises(ValueError): box_blur(sample.convert("I")) with pytest.raises(ValueError): @@ -73,6 +79,44 @@ def test_color_modes() -> None: box_blur(sample.convert("YCbCr")) +@pytest.mark.parametrize("mode", ("I;16", "I;16L", "I;16B", "I;16N")) +def test_i16_high_values(mode: str) -> None: + # Use the full 16-bit range, so that the high byte is exercised + values = [] + for v in sample.get_flattened_data(): + assert isinstance(v, int) + values.append(v * 257) + im = Image.new(mode, sample.size) + im.putdata(values) + + # With a radius of 1, each pixel becomes the mean of the 3 x 3 pixels + # around it, with the edge pixels repeated + w, h = sample.size + + def mean(x: int, y: int) -> float: + return ( + sum( + values[min(max(y + dy, 0), h - 1) * w + min(max(x + dx, 0), w - 1)] + for dx in (-1, 0, 1) + for dy in (-1, 0, 1) + ) + / 9 + ) + + blurred = box_blur(im, 1).get_flattened_data() + for y in range(h): + for x in range(w): + value = blurred[y * w + x] + assert isinstance(value, int) + assert abs(value - mean(x, y)) <= 1 + + +@pytest.mark.parametrize("mode", ("I;16", "I;16L", "I;16B", "I;16N")) +def test_i16_max_value(mode: str) -> None: + im = Image.new(mode, (7, 5), 65535) + assert box_blur(im, 600, 3).get_flattened_data() == (65535,) * 35 + + @pytest.mark.parametrize("size", ((0, 1), (1, 0))) def test_zero_dimension(size: tuple[int, int]) -> None: assert box_blur(Image.new("L", size)).size == size diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 5f838c092d3..fdaedf9bff8 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -265,6 +265,33 @@ def test_consistency_i16_high_byte(mode: str) -> None: assert im.getpixel((4, 4)) == 1000 +@pytest.mark.parametrize( + "filter_to_apply", + ( + ImageFilter.GaussianBlur, + ImageFilter.GaussianBlur(5), + ImageFilter.GaussianBlur((2, 5)), + ImageFilter.BoxBlur(5), + ImageFilter.BoxBlur((2, 5)), + ), +) +@pytest.mark.parametrize("mode", ("I;16", "I;16L", "I;16B", "I;16N")) +def test_blur_i16( + filter_to_apply: ImageFilter.Filter | type[ImageFilter.Filter], mode: str +) -> None: + im = hopper("L") + reference = im.filter(filter_to_apply) + result = im.convert(mode).filter(filter_to_apply) + assert result.mode == mode + assert_image_equal(result.convert("L"), reference) + + +@pytest.mark.parametrize("mode", ("I;16", "I;16L", "I;16B", "I;16N")) +def test_unsharp_mask_i16(mode: str) -> None: + with pytest.raises(ValueError, match="image has wrong mode"): + hopper(mode).filter(ImageFilter.UnsharpMask) + + @pytest.mark.parametrize("size", (-1, math.nan, math.inf, 2**31)) def test_invalid_box_blur_filter(size: int) -> None: for radius in (size, (size, size), (size, 1), (1, size)): diff --git a/docs/releasenotes/13.1.0.rst b/docs/releasenotes/13.1.0.rst new file mode 100644 index 00000000000..493dbe7db87 --- /dev/null +++ b/docs/releasenotes/13.1.0.rst @@ -0,0 +1,11 @@ +13.1.0 +------ + +Other changes +============= + +Blurring I;16 images +^^^^^^^^^^^^^^^^^^^^ + +:py:class:`~PIL.ImageFilter.GaussianBlur` and :py:class:`~PIL.ImageFilter.BoxBlur` +now support I;16, I;16L, I;16B and I;16N images. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index e45919afb07..bc0cb895d92 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -15,6 +15,7 @@ expected to be backported to earlier versions. :maxdepth: 2 versioning + 13.1.0 13.0.0 12.3.0 12.2.0 diff --git a/src/libImaging/BoxBlur.c b/src/libImaging/BoxBlur.c index 9c4005c3071..6f08a0c83f4 100644 --- a/src/libImaging/BoxBlur.c +++ b/src/libImaging/BoxBlur.c @@ -165,6 +165,82 @@ void static inline ImagingLineBoxBlur8( #undef SAVE } +void static inline ImagingLineBoxBlur16( + UINT8 *lineOut, + UINT8 *lineIn, + int lastx, + int radius, + int edgeA, + int edgeB, + UINT32 ww, + UINT32 fw, + int bigendian +) { + int x; + uint64_t acc; + uint64_t bulk; + UINT16 value; + +#define GET(x) \ + ((uint64_t)lineIn[(x) * 2 + (bigendian ? 1 : 0)] + \ + ((uint64_t)lineIn[(x) * 2 + (bigendian ? 0 : 1)] << 8)) + +#define MOVE_ACC(acc, subtract, add) acc += GET(add) - GET(subtract); + +#define ADD_FAR(bulk, acc, left, right) \ + bulk = (acc * ww) + (GET(left) + GET(right)) * fw; + +#define SAVE(x, bulk) \ + value = (UINT16)((bulk + (1 << 23)) >> 24); \ + lineOut[(x) * 2 + (bigendian ? 1 : 0)] = (UINT8)value; \ + lineOut[(x) * 2 + (bigendian ? 0 : 1)] = (UINT8)(value >> 8); + + acc = GET(0) * (radius + 1); + for (x = 0; x < edgeA - 1; x++) { + acc += GET(x); + } + acc += GET(lastx) * (radius - edgeA + 1); + + if (edgeA <= edgeB) { + for (x = 0; x < edgeA; x++) { + MOVE_ACC(acc, 0, x + radius); + ADD_FAR(bulk, acc, 0, x + radius + 1); + SAVE(x, bulk); + } + for (x = edgeA; x < edgeB; x++) { + MOVE_ACC(acc, x - radius - 1, x + radius); + ADD_FAR(bulk, acc, x - radius - 1, x + radius + 1); + SAVE(x, bulk); + } + for (x = edgeB; x <= lastx; x++) { + MOVE_ACC(acc, x - radius - 1, lastx); + ADD_FAR(bulk, acc, x - radius - 1, lastx); + SAVE(x, bulk); + } + } else { + for (x = 0; x < edgeB; x++) { + MOVE_ACC(acc, 0, x + radius); + ADD_FAR(bulk, acc, 0, x + radius + 1); + SAVE(x, bulk); + } + for (x = edgeB; x < edgeA; x++) { + MOVE_ACC(acc, 0, lastx); + ADD_FAR(bulk, acc, 0, lastx); + SAVE(x, bulk); + } + for (x = edgeA; x <= lastx; x++) { + MOVE_ACC(acc, x - radius - 1, lastx); + ADD_FAR(bulk, acc, x - radius - 1, lastx); + SAVE(x, bulk); + } + } + +#undef GET +#undef MOVE_ACC +#undef ADD_FAR +#undef SAVE +} + Imaging ImagingHorizontalBoxBlur(Imaging imOut, Imaging imIn, float floatRadius) { ImagingSectionCookie cookie; @@ -185,7 +261,34 @@ ImagingHorizontalBoxBlur(Imaging imOut, Imaging imIn, float floatRadius) { ImagingSectionEnter(&cookie); - if (imIn->image8) { + if (imIn->type == IMAGING_TYPE_I16) { + int bigendian = 0; + if ( + imIn->mode == IMAGING_MODE_I_16B +#ifdef WORDS_BIGENDIAN + || imIn->mode == IMAGING_MODE_I_16N +#endif + ) { + bigendian = 1; + } + for (y = 0; y < imIn->ysize; y++) { + ImagingLineBoxBlur16( + (imIn == imOut ? (UINT8 *)lineOut : imOut->image8[y]), + imIn->image8[y], + imIn->xsize - 1, + radius, + edgeA, + edgeB, + ww, + fw, + bigendian + ); + if (imIn == imOut) { + // Commit. + memcpy(imOut->image8[y], lineOut, imIn->xsize * 2); + } + } + } else if (imIn->image8) { for (y = 0; y < imIn->ysize; y++) { ImagingLineBoxBlur8( (imIn == imOut ? (UINT8 *)lineOut : imOut->image8[y]), @@ -254,14 +357,15 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int n) return ImagingError_Mismatch(); } - if (imIn->type != IMAGING_TYPE_UINT8) { + if (imIn->type != IMAGING_TYPE_UINT8 && imIn->type != IMAGING_TYPE_I16) { return ImagingError_ModeError(); } if (imIn->mode != IMAGING_MODE_RGB && imIn->mode != IMAGING_MODE_RGBA && imIn->mode != IMAGING_MODE_RGBa && imIn->mode != IMAGING_MODE_RGBX && imIn->mode != IMAGING_MODE_CMYK && imIn->mode != IMAGING_MODE_L && - imIn->mode != IMAGING_MODE_LA && imIn->mode != IMAGING_MODE_La) { + imIn->mode != IMAGING_MODE_LA && imIn->mode != IMAGING_MODE_La && + !isModeI16(imIn->mode)) { return ImagingError_ModeError(); } diff --git a/src/libImaging/UnsharpMask.c b/src/libImaging/UnsharpMask.c index e714749effd..66b223b8a95 100644 --- a/src/libImaging/UnsharpMask.c +++ b/src/libImaging/UnsharpMask.c @@ -35,8 +35,13 @@ ImagingUnsharpMask( UINT8 *lineIn8 = NULL; UINT8 *lineOut8 = NULL; + /* The sharpening below only handles 8-bit channels. */ + if (imIn->type != IMAGING_TYPE_UINT8) { + return ImagingError_ModeError(); + } + /* First, do a gaussian blur on the image, putting results in imOut - temporarily. All format checks are in gaussian blur. */ + temporarily. All other format checks are in gaussian blur. */ result = ImagingGaussianBlur(imOut, imIn, radius, radius, 3); if (!result) { return NULL; From eea10a27c37462033d9935d16332fa566e669819 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 20 Oct 2026 10:55:45 +1100 Subject: [PATCH 2/4] Use linesize --- src/libImaging/BoxBlur.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libImaging/BoxBlur.c b/src/libImaging/BoxBlur.c index 6f08a0c83f4..e7b69683971 100644 --- a/src/libImaging/BoxBlur.c +++ b/src/libImaging/BoxBlur.c @@ -285,7 +285,7 @@ ImagingHorizontalBoxBlur(Imaging imOut, Imaging imIn, float floatRadius) { ); if (imIn == imOut) { // Commit. - memcpy(imOut->image8[y], lineOut, imIn->xsize * 2); + memcpy(imOut->image8[y], lineOut, imIn->linesize); } } } else if (imIn->image8) { @@ -302,7 +302,7 @@ ImagingHorizontalBoxBlur(Imaging imOut, Imaging imIn, float floatRadius) { ); if (imIn == imOut) { // Commit. - memcpy(imOut->image8[y], lineOut, imIn->xsize); + memcpy(imOut->image8[y], lineOut, imIn->linesize); } } } else { @@ -319,7 +319,7 @@ ImagingHorizontalBoxBlur(Imaging imOut, Imaging imIn, float floatRadius) { ); if (imIn == imOut) { // Commit. - memcpy(imOut->image32[y], lineOut, imIn->xsize * 4); + memcpy(imOut->image32[y], lineOut, imIn->linesize); } } } From 29e094d846d3bc670f020de0ae774085cd6353ab Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 20 Oct 2026 11:39:17 +1100 Subject: [PATCH 3/4] Use release notes template --- docs/releasenotes/13.1.0.rst | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/docs/releasenotes/13.1.0.rst b/docs/releasenotes/13.1.0.rst index 493dbe7db87..140bcbe3115 100644 --- a/docs/releasenotes/13.1.0.rst +++ b/docs/releasenotes/13.1.0.rst @@ -1,6 +1,51 @@ 13.1.0 ------ +Security +======== + +TODO +^^^^ + +TODO + +:cve:`YYYY-XXXXX`: TODO +^^^^^^^^^^^^^^^^^^^^^^^ + +TODO + +Backwards incompatible changes +============================== + +TODO +^^^^ + +TODO + +Deprecations +============ + +TODO +^^^^ + +TODO + +API changes +=========== + +TODO +^^^^ + +TODO + +API additions +============= + +TODO +^^^^ + +TODO + Other changes ============= From 2c515a32342870907d77b05f527729292928130a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 20 Oct 2026 12:06:21 +1100 Subject: [PATCH 4/4] Simplified code --- Tests/test_box_blur.py | 41 ++------- Tests/test_image_filter.py | 2 + src/libImaging/BoxBlur.c | 167 +++++++++++++++++------------------ src/libImaging/UnsharpMask.c | 1 - 4 files changed, 89 insertions(+), 122 deletions(-) diff --git a/Tests/test_box_blur.py b/Tests/test_box_blur.py index 65f2cc55df2..10065d6bd69 100644 --- a/Tests/test_box_blur.py +++ b/Tests/test_box_blur.py @@ -51,8 +51,6 @@ def assert_blur( rgba = Image.merge("RGBA", (im, im, im, im)) for band in box_blur(rgba, radius, passes).split(): assert_image(band, data, delta) - for mode in ("I;16", "I;16L", "I;16B", "I;16N"): - assert_image(box_blur(im.convert(mode), radius, passes), data, delta) def test_color_modes() -> None: @@ -79,42 +77,13 @@ def test_color_modes() -> None: box_blur(sample.convert("YCbCr")) -@pytest.mark.parametrize("mode", ("I;16", "I;16L", "I;16B", "I;16N")) -def test_i16_high_values(mode: str) -> None: - # Use the full 16-bit range, so that the high byte is exercised - values = [] - for v in sample.get_flattened_data(): - assert isinstance(v, int) - values.append(v * 257) - im = Image.new(mode, sample.size) - im.putdata(values) - - # With a radius of 1, each pixel becomes the mean of the 3 x 3 pixels - # around it, with the edge pixels repeated - w, h = sample.size - - def mean(x: int, y: int) -> float: - return ( - sum( - values[min(max(y + dy, 0), h - 1) * w + min(max(x + dx, 0), w - 1)] - for dx in (-1, 0, 1) - for dy in (-1, 0, 1) - ) - / 9 - ) - - blurred = box_blur(im, 1).get_flattened_data() - for y in range(h): - for x in range(w): - value = blurred[y * w + x] - assert isinstance(value, int) - assert abs(value - mean(x, y)) <= 1 - - @pytest.mark.parametrize("mode", ("I;16", "I;16L", "I;16B", "I;16N")) def test_i16_max_value(mode: str) -> None: - im = Image.new(mode, (7, 5), 65535) - assert box_blur(im, 600, 3).get_flattened_data() == (65535,) * 35 + im = Image.new(mode, sample.size, 65535) + assert ( + box_blur(im, 600, 3).get_flattened_data() + == (65535,) * sample.width * sample.height + ) @pytest.mark.parametrize("size", ((0, 1), (1, 0))) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index fdaedf9bff8..ccf2d5f069b 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -269,8 +269,10 @@ def test_consistency_i16_high_byte(mode: str) -> None: "filter_to_apply", ( ImageFilter.GaussianBlur, + ImageFilter.GaussianBlur(0), ImageFilter.GaussianBlur(5), ImageFilter.GaussianBlur((2, 5)), + ImageFilter.BoxBlur(0), ImageFilter.BoxBlur(5), ImageFilter.BoxBlur((2, 5)), ), diff --git a/src/libImaging/BoxBlur.c b/src/libImaging/BoxBlur.c index e7b69683971..222b527c48a 100644 --- a/src/libImaging/BoxBlur.c +++ b/src/libImaging/BoxBlur.c @@ -2,9 +2,9 @@ typedef UINT8 pixel[4]; -void static inline ImagingLineBoxBlur32( - pixel *lineOut, - pixel *lineIn, +void static inline ImagingLineBoxBlur8( + UINT8 *lineOut, + UINT8 *lineIn, int lastx, int radius, int edgeA, @@ -13,64 +13,33 @@ void static inline ImagingLineBoxBlur32( UINT32 fw ) { int x; - UINT32 acc[4]; - UINT32 bulk[4]; + UINT32 acc; + UINT32 bulk; -#define MOVE_ACC(acc, subtract, add) \ - acc[0] += lineIn[add][0] - lineIn[subtract][0]; \ - acc[1] += lineIn[add][1] - lineIn[subtract][1]; \ - acc[2] += lineIn[add][2] - lineIn[subtract][2]; \ - acc[3] += lineIn[add][3] - lineIn[subtract][3]; +#define MOVE_ACC(acc, subtract, add) acc += lineIn[add] - lineIn[subtract]; -#define ADD_FAR(bulk, acc, left, right) \ - bulk[0] = (acc[0] * ww) + (lineIn[left][0] + lineIn[right][0]) * fw; \ - bulk[1] = (acc[1] * ww) + (lineIn[left][1] + lineIn[right][1]) * fw; \ - bulk[2] = (acc[2] * ww) + (lineIn[left][2] + lineIn[right][2]) * fw; \ - bulk[3] = (acc[3] * ww) + (lineIn[left][3] + lineIn[right][3]) * fw; +#define ADD_FAR(bulk, acc, left, right) \ + bulk = (acc * ww) + (lineIn[left] + lineIn[right]) * fw; -#define SAVE(x, bulk) \ - lineOut[x][0] = (UINT8)((bulk[0] + (1 << 23)) >> 24); \ - lineOut[x][1] = (UINT8)((bulk[1] + (1 << 23)) >> 24); \ - lineOut[x][2] = (UINT8)((bulk[2] + (1 << 23)) >> 24); \ - lineOut[x][3] = (UINT8)((bulk[3] + (1 << 23)) >> 24); +#define SAVE(x, bulk) lineOut[x] = (UINT8)((bulk + (1 << 23)) >> 24) - /* Compute acc for -1 pixel (outside of image): - From "-radius-1" to "-1" get first pixel, - then from "0" to "radius-1". */ - acc[0] = lineIn[0][0] * (radius + 1); - acc[1] = lineIn[0][1] * (radius + 1); - acc[2] = lineIn[0][2] * (radius + 1); - acc[3] = lineIn[0][3] * (radius + 1); - /* As radius can be bigger than xsize, iterate to edgeA -1. */ + acc = lineIn[0] * (radius + 1); for (x = 0; x < edgeA - 1; x++) { - acc[0] += lineIn[x][0]; - acc[1] += lineIn[x][1]; - acc[2] += lineIn[x][2]; - acc[3] += lineIn[x][3]; + acc += lineIn[x]; } - /* Then multiply remainder to last x. */ - acc[0] += lineIn[lastx][0] * (radius - edgeA + 1); - acc[1] += lineIn[lastx][1] * (radius - edgeA + 1); - acc[2] += lineIn[lastx][2] * (radius - edgeA + 1); - acc[3] += lineIn[lastx][3] * (radius - edgeA + 1); + acc += lineIn[lastx] * (radius - edgeA + 1); if (edgeA <= edgeB) { - /* Subtract pixel from left ("0"). - Add pixels from radius. */ for (x = 0; x < edgeA; x++) { MOVE_ACC(acc, 0, x + radius); ADD_FAR(bulk, acc, 0, x + radius + 1); SAVE(x, bulk); } - /* Subtract previous pixel from "-radius". - Add pixels from radius. */ for (x = edgeA; x < edgeB; x++) { MOVE_ACC(acc, x - radius - 1, x + radius); ADD_FAR(bulk, acc, x - radius - 1, x + radius + 1); SAVE(x, bulk); } - /* Subtract previous pixel from "-radius". - Add last pixel. */ for (x = edgeB; x <= lastx; x++) { MOVE_ACC(acc, x - radius - 1, lastx); ADD_FAR(bulk, acc, x - radius - 1, lastx); @@ -99,7 +68,7 @@ void static inline ImagingLineBoxBlur32( #undef SAVE } -void static inline ImagingLineBoxBlur8( +void static inline ImagingLineBoxBlur16( UINT8 *lineOut, UINT8 *lineIn, int lastx, @@ -107,24 +76,33 @@ void static inline ImagingLineBoxBlur8( int edgeA, int edgeB, UINT32 ww, - UINT32 fw + UINT32 fw, + int bigendian ) { int x; - UINT32 acc; - UINT32 bulk; + uint64_t acc; + uint64_t bulk; + UINT16 value; -#define MOVE_ACC(acc, subtract, add) acc += lineIn[add] - lineIn[subtract]; +#define GET(x) \ + ((uint64_t)lineIn[(x) * 2 + (bigendian ? 1 : 0)] + \ + ((uint64_t)lineIn[(x) * 2 + (bigendian ? 0 : 1)] << 8)) + +#define MOVE_ACC(acc, subtract, add) acc += GET(add) - GET(subtract); #define ADD_FAR(bulk, acc, left, right) \ - bulk = (acc * ww) + (lineIn[left] + lineIn[right]) * fw; + bulk = (acc * ww) + (GET(left) + GET(right)) * fw; -#define SAVE(x, bulk) lineOut[x] = (UINT8)((bulk + (1 << 23)) >> 24) +#define SAVE(x, bulk) \ + value = (UINT16)((bulk + (1 << 23)) >> 24); \ + lineOut[x * 2 + (bigendian ? 1 : 0)] = (UINT8)value; \ + lineOut[x * 2 + (bigendian ? 0 : 1)] = (UINT8)(value >> 8); - acc = lineIn[0] * (radius + 1); + acc = GET(0) * (radius + 1); for (x = 0; x < edgeA - 1; x++) { - acc += lineIn[x]; + acc += GET(x); } - acc += lineIn[lastx] * (radius - edgeA + 1); + acc += GET(lastx) * (radius - edgeA + 1); if (edgeA <= edgeB) { for (x = 0; x < edgeA; x++) { @@ -160,58 +138,81 @@ void static inline ImagingLineBoxBlur8( } } +#undef GET #undef MOVE_ACC #undef ADD_FAR #undef SAVE } -void static inline ImagingLineBoxBlur16( - UINT8 *lineOut, - UINT8 *lineIn, +void static inline ImagingLineBoxBlur32( + pixel *lineOut, + pixel *lineIn, int lastx, int radius, int edgeA, int edgeB, UINT32 ww, - UINT32 fw, - int bigendian + UINT32 fw ) { int x; - uint64_t acc; - uint64_t bulk; - UINT16 value; - -#define GET(x) \ - ((uint64_t)lineIn[(x) * 2 + (bigendian ? 1 : 0)] + \ - ((uint64_t)lineIn[(x) * 2 + (bigendian ? 0 : 1)] << 8)) + UINT32 acc[4]; + UINT32 bulk[4]; -#define MOVE_ACC(acc, subtract, add) acc += GET(add) - GET(subtract); +#define MOVE_ACC(acc, subtract, add) \ + acc[0] += lineIn[add][0] - lineIn[subtract][0]; \ + acc[1] += lineIn[add][1] - lineIn[subtract][1]; \ + acc[2] += lineIn[add][2] - lineIn[subtract][2]; \ + acc[3] += lineIn[add][3] - lineIn[subtract][3]; -#define ADD_FAR(bulk, acc, left, right) \ - bulk = (acc * ww) + (GET(left) + GET(right)) * fw; +#define ADD_FAR(bulk, acc, left, right) \ + bulk[0] = (acc[0] * ww) + (lineIn[left][0] + lineIn[right][0]) * fw; \ + bulk[1] = (acc[1] * ww) + (lineIn[left][1] + lineIn[right][1]) * fw; \ + bulk[2] = (acc[2] * ww) + (lineIn[left][2] + lineIn[right][2]) * fw; \ + bulk[3] = (acc[3] * ww) + (lineIn[left][3] + lineIn[right][3]) * fw; -#define SAVE(x, bulk) \ - value = (UINT16)((bulk + (1 << 23)) >> 24); \ - lineOut[(x) * 2 + (bigendian ? 1 : 0)] = (UINT8)value; \ - lineOut[(x) * 2 + (bigendian ? 0 : 1)] = (UINT8)(value >> 8); +#define SAVE(x, bulk) \ + lineOut[x][0] = (UINT8)((bulk[0] + (1 << 23)) >> 24); \ + lineOut[x][1] = (UINT8)((bulk[1] + (1 << 23)) >> 24); \ + lineOut[x][2] = (UINT8)((bulk[2] + (1 << 23)) >> 24); \ + lineOut[x][3] = (UINT8)((bulk[3] + (1 << 23)) >> 24); - acc = GET(0) * (radius + 1); + /* Compute acc for -1 pixel (outside of image): + From "-radius-1" to "-1" get first pixel, + then from "0" to "radius-1". */ + acc[0] = lineIn[0][0] * (radius + 1); + acc[1] = lineIn[0][1] * (radius + 1); + acc[2] = lineIn[0][2] * (radius + 1); + acc[3] = lineIn[0][3] * (radius + 1); + /* As radius can be bigger than xsize, iterate to edgeA -1. */ for (x = 0; x < edgeA - 1; x++) { - acc += GET(x); + acc[0] += lineIn[x][0]; + acc[1] += lineIn[x][1]; + acc[2] += lineIn[x][2]; + acc[3] += lineIn[x][3]; } - acc += GET(lastx) * (radius - edgeA + 1); + /* Then multiply remainder to last x. */ + acc[0] += lineIn[lastx][0] * (radius - edgeA + 1); + acc[1] += lineIn[lastx][1] * (radius - edgeA + 1); + acc[2] += lineIn[lastx][2] * (radius - edgeA + 1); + acc[3] += lineIn[lastx][3] * (radius - edgeA + 1); if (edgeA <= edgeB) { + /* Subtract pixel from left ("0"). + Add pixels from radius. */ for (x = 0; x < edgeA; x++) { MOVE_ACC(acc, 0, x + radius); ADD_FAR(bulk, acc, 0, x + radius + 1); SAVE(x, bulk); } + /* Subtract previous pixel from "-radius". + Add pixels from radius. */ for (x = edgeA; x < edgeB; x++) { MOVE_ACC(acc, x - radius - 1, x + radius); ADD_FAR(bulk, acc, x - radius - 1, x + radius + 1); SAVE(x, bulk); } + /* Subtract previous pixel from "-radius". + Add last pixel. */ for (x = edgeB; x <= lastx; x++) { MOVE_ACC(acc, x - radius - 1, lastx); ADD_FAR(bulk, acc, x - radius - 1, lastx); @@ -235,7 +236,6 @@ void static inline ImagingLineBoxBlur16( } } -#undef GET #undef MOVE_ACC #undef ADD_FAR #undef SAVE @@ -357,15 +357,12 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int n) return ImagingError_Mismatch(); } - if (imIn->type != IMAGING_TYPE_UINT8 && imIn->type != IMAGING_TYPE_I16) { - return ImagingError_ModeError(); - } - - if (imIn->mode != IMAGING_MODE_RGB && imIn->mode != IMAGING_MODE_RGBA && - imIn->mode != IMAGING_MODE_RGBa && imIn->mode != IMAGING_MODE_RGBX && - imIn->mode != IMAGING_MODE_CMYK && imIn->mode != IMAGING_MODE_L && - imIn->mode != IMAGING_MODE_LA && imIn->mode != IMAGING_MODE_La && - !isModeI16(imIn->mode)) { + if (!(imIn->type == IMAGING_TYPE_UINT8 && + (imIn->mode == IMAGING_MODE_RGB || imIn->mode == IMAGING_MODE_RGBA || + imIn->mode == IMAGING_MODE_RGBa || imIn->mode == IMAGING_MODE_RGBX || + imIn->mode == IMAGING_MODE_CMYK || imIn->mode == IMAGING_MODE_L || + imIn->mode == IMAGING_MODE_LA || imIn->mode == IMAGING_MODE_La)) && + imIn->type != IMAGING_TYPE_I16) { return ImagingError_ModeError(); } diff --git a/src/libImaging/UnsharpMask.c b/src/libImaging/UnsharpMask.c index 66b223b8a95..b56e01631ca 100644 --- a/src/libImaging/UnsharpMask.c +++ b/src/libImaging/UnsharpMask.c @@ -35,7 +35,6 @@ ImagingUnsharpMask( UINT8 *lineIn8 = NULL; UINT8 *lineOut8 = NULL; - /* The sharpening below only handles 8-bit channels. */ if (imIn->type != IMAGING_TYPE_UINT8) { return ImagingError_ModeError(); }