diff --git a/Tests/test_arrow.py b/Tests/test_arrow.py index f1ab5ab6d..fa39275c6 100644 --- a/Tests/test_arrow.py +++ b/Tests/test_arrow.py @@ -68,6 +68,26 @@ def test_to_array(mode: str, dtype: Any, mask: Any) -> None: assert_image_equal(img, reloaded) +@pytest.mark.parametrize( + "mode, dest_modes", + ( + ("L", ["I", "F", "LA", "RGB", "RGBA", "RGBX", "CMYK", "YCbCr", "HSV"]), + ("I", ["L", "F"]), # Technically I32 can work for any 4x8bit storage. + ("F", ["I", "L", "LA", "RGB", "RGBA", "RGBX", "CMYK", "YCbCr", "HSV"]), + ("LA", ["L", "F"]), + ("RGB", ["L", "F"]), + ("RGBA", ["L", "F"]), + ("RGBX", ["L", "F"]), + ("CMYK", ["L", "F"]), + ("YCbCr", ["L", "F"]), + ("HSV", ["L", "F"]), + ), +) +def test_invalid_array_type(mode: str, dest_modes: List[str]) -> None: + img = hopper(mode) + for dest_mode in dest_modes: + with pytest.raises(ValueError): + Image.fromarrow(img, dest_mode, img.size) def test_lifetime(): # valgrind shouldn't error out here. diff --git a/src/_imaging.c b/src/_imaging.c index 09d7096dc..727e8a494 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -290,6 +290,7 @@ _new_arrow(PyObject *self, PyObject *args) { if (!ret && array->release) { array->release(array); array->release = NULL; + return ImagingError_ValueError("Invalid arrow array mode or size mismatch"); } return ret; } diff --git a/src/libImaging/Storage.c b/src/libImaging/Storage.c index 31604706a..c49949d71 100644 --- a/src/libImaging/Storage.c +++ b/src/libImaging/Storage.c @@ -705,25 +705,36 @@ ImagingNewArrow( int64_t pixels = (int64_t)xsize * (int64_t)ysize; - if (((strcmp(schema->format, "i") == 0 && im->pixelsize == 4) || - (strcmp(schema->format, im->arrow_band_format) == 0 && im->bands == 1)) && - pixels == external_array->length) { + // fmt:off // don't reformat this + if (((strcmp(schema->format, "I") == 0 + && im->pixelsize == 4 + && im->bands >= 2) // INT32 into any INT32 Storage mode + || + (strcmp(schema->format, im->arrow_band_format) == 0 + && im->bands == 1)) // Single band match + && pixels == external_array->length) { // one arrow element per, and it matches a pixelsize*char if (ImagingAllocateArrow(im, external_array)) { return im; } } - if (strcmp(schema->format, "+w:4") == 0 && im->pixelsize == 4 && - schema->n_children > 0 && schema->children && - strcmp(schema->children[0]->format, "C") == 0 && - pixels == external_array->length && external_array->n_children == 1 && - external_array->children && 4 * pixels == external_array->children[0]->length) { + // linter: don't mess with the formatting here + if (strcmp(schema->format, "+w:4") == 0 // 4 up array + && im->pixelsize == 4 // storage as 32 bpc + && schema->n_children > 0 // make sure schema is well formed. + && schema->children // make sure schema is well formed + && strcmp(schema->children[0]->format, "C") == 0 // Expected format + && strcmp(im->arrow_band_format, "C") == 0 // Expected Format + && pixels == external_array->length // expected length + && external_array->n_children == 1 // array is well formed + && external_array->children // array is well formed + && 4 * pixels == external_array->children[0]->length) { // 4 up element of char into pixelsize == 4 if (ImagingAllocateArrow(im, external_array)) { return im; } } - + // fmt: on ImagingDelete(im); return NULL; }