Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions Tests/benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,17 +135,28 @@ def test_box_blur(


@pytest.mark.benchmark(group="composition")
@pytest.mark.parametrize("alpha", ["opaque", "transparent", "mixed"])
@pytest.mark.parametrize("mode", ALPHA_MODES)
@pytest.mark.parametrize("size", SIZES, ids=_format_size)
def test_alpha_composition(
def test_alpha_composite(
bench: BenchmarkFixture,
mode: str,
size: tuple[int, int],
alpha: str,
) -> None:
im = make_pillow_image(mode, size)
second = im.copy()
bench.extra_info["label"] = ["Composition"]
bench(Image.alpha_composite, im, second)
im1 = make_pillow_image(mode, size)
im2 = make_pillow_image(mode, size, pattern_offset=1024)
if alpha == "opaque":
im2.putalpha(255)
elif alpha == "transparent":
im2.putalpha(0)
else: # "mixed"
width, height = size
gradient = bytes((x * 256) // width for x in range(width)) * height
im2.putalpha(Image.frombytes("L", size, gradient))
bench.extra_info["label"] = ["Composition", alpha]
result = bench(Image.alpha_composite, im1, im2)
assert result.size == im1.size


@pytest.mark.benchmark(group="convert")
Expand Down
16 changes: 10 additions & 6 deletions src/libImaging/AlphaComposite.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ typedef struct {
Imaging
ImagingAlphaComposite(Imaging imDst, Imaging imSrc) {
Imaging imOut;
int x, y;

/* Check arguments */
if (!imDst || !imSrc ||
Expand All @@ -40,12 +39,17 @@ ImagingAlphaComposite(Imaging imDst, Imaging imSrc) {
return NULL;
}

for (y = 0; y < imDst->ysize; y++) {
rgba8 *dst = (rgba8 *)imDst->image[y];
rgba8 *src = (rgba8 *)imSrc->image[y];
rgba8 *out = (rgba8 *)imOut->image[y];
// Invariant over the loop.
int xsize = imDst->xsize;
int ysize = imDst->ysize;

for (x = 0; x < imDst->xsize; x++) {
for (int y = 0; y < ysize; y++) {
// restrict safe: imDst/imSrc are read-only, imOut is a fresh allocation.
rgba8 *restrict dst = (rgba8 *)imDst->image[y];
rgba8 *restrict src = (rgba8 *)imSrc->image[y];
rgba8 *restrict out = (rgba8 *)imOut->image[y];

for (int x = 0; x < xsize; x++) {
if (src->a == 0) {
// Copy 4 bytes at once.
*out = *dst;
Expand Down
2 changes: 1 addition & 1 deletion src/libImaging/Imaging.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ ImagingConvert(Imaging im, ModeID mode, ImagingPalette palette, int dither);
extern Imaging
ImagingConvertInPlace(Imaging im, ModeID mode);
extern Imaging
ImagingConvertMatrix(Imaging im, ModeID mode, float m[]);
ImagingConvertMatrix(Imaging im, ModeID mode, const float m[12]);
extern Imaging
ImagingConvertTransparent(Imaging im, ModeID mode, int r, int g, int b);
extern Imaging
Expand Down
27 changes: 17 additions & 10 deletions src/libImaging/Matrix.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@
#define CLIPF(v) ((v <= 0.0) ? 0 : (v >= 255.0F) ? 255 : (UINT8)v)

Imaging
ImagingConvertMatrix(Imaging im, const ModeID mode, float m[]) {
ImagingConvertMatrix(Imaging im, const ModeID mode, const float m[12]) {
Imaging imOut;
int x, y;
ImagingSectionCookie cookie;

/* Assume there's enough data in the buffer */
Expand All @@ -34,12 +33,16 @@ ImagingConvertMatrix(Imaging im, const ModeID mode, float m[]) {
return NULL;
}

// Invariant over the loop.
int xsize = im->xsize, ysize = im->ysize;

ImagingSectionEnter(&cookie);
for (y = 0; y < im->ysize; y++) {
UINT8 *in = (UINT8 *)im->image[y];
UINT8 *out = (UINT8 *)imOut->image[y];
for (int y = 0; y < ysize; y++) {
// restrict safe: im is read-only, imOut is a fresh allocation.
UINT8 *restrict in = (UINT8 *)im->image[y];
UINT8 *restrict out = (UINT8 *)imOut->image[y];

for (x = 0; x < im->xsize; x++) {
for (int x = 0; x < xsize; x++) {
float v = m[0] * in[0] + m[1] * in[1] + m[2] * in[2] + m[3] + 0.5;
out[x] = CLIPF(v);
in += 4;
Expand All @@ -52,12 +55,16 @@ ImagingConvertMatrix(Imaging im, const ModeID mode, float m[]) {
return NULL;
}

for (y = 0; y < im->ysize; y++) {
UINT8 *in = (UINT8 *)im->image[y];
UINT8 *out = (UINT8 *)imOut->image[y];
// Invariant over the loop.
int xsize = im->xsize, ysize = im->ysize;

for (int y = 0; y < ysize; y++) {
// restrict safe: im is read-only, imOut is a fresh allocation.
UINT8 *restrict in = (UINT8 *)im->image[y];
UINT8 *restrict out = (UINT8 *)imOut->image[y];

ImagingSectionEnter(&cookie);
for (x = 0; x < im->xsize; x++) {
for (int x = 0; x < xsize; x++) {
float v0 = m[0] * in[0] + m[1] * in[1] + m[2] * in[2] + m[3] + 0.5;
float v1 = m[4] * in[0] + m[5] * in[1] + m[6] * in[2] + m[7] + 0.5;
float v2 = m[8] * in[0] + m[9] * in[1] + m[10] * in[2] + m[11] + 0.5;
Expand Down
12 changes: 9 additions & 3 deletions src/libImaging/Negative.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,15 @@ ImagingNegative(Imaging im) {
return NULL;
}

for (y = 0; y < im->ysize; y++) {
for (x = 0; x < im->linesize; x++) {
imOut->image[y][x] = ~im->image[y][x];
// ysize and linesize are loop-invariant.
int ysize = im->ysize, linesize = im->linesize;

for (y = 0; y < ysize; y++) {
// restrict safe: im is read-only, imOut is a fresh allocation.
UINT8 *restrict in = (UINT8 *)im->image[y];
UINT8 *restrict out = (UINT8 *)imOut->image[y];
for (x = 0; x < linesize; x++) {
out[x] = ~in[x];
}
}

Expand Down
67 changes: 24 additions & 43 deletions src/libImaging/Quant.c
Original file line number Diff line number Diff line change
Expand Up @@ -1696,11 +1696,14 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
return ImagingError_ModeError();
}

if (im->xsize > INT_MAX / im->ysize) {
// Hoisted here as these are invariant over the loops below.
int xsize = im->xsize, ysize = im->ysize;

if (xsize > INT_MAX / ysize) {
return ImagingError_MemoryError();
}
/* malloc check ok, using calloc for final overflow, x*y above */
p = calloc(im->xsize * im->ysize, sizeof(Pixel));
p = calloc(xsize * ysize, sizeof(Pixel));
if (!p) {
return ImagingError_MemoryError();
}
Expand All @@ -1716,9 +1719,10 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
/* FIXME: converting a "L" image to "P" with 256 colors
should be done by a simple copy... */

for (i = y = 0; y < im->ysize; y++) {
for (x = 0; x < im->xsize; x++, i++) {
p[i].c.r = p[i].c.g = p[i].c.b = im->image8[y][x];
for (i = y = 0; y < ysize; y++) {
UINT8 *in = im->image8[y];
for (x = 0; x < xsize; x++, i++) {
p[i].c.r = p[i].c.g = p[i].c.b = in[x];
p[i].c.a = 255;
}
}
Expand All @@ -1728,9 +1732,10 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {

pp = im->palette->palette;

for (i = y = 0; y < im->ysize; y++) {
for (x = 0; x < im->xsize; x++, i++) {
v = im->image8[y][x];
for (i = y = 0; y < ysize; y++) {
UINT8 *in = im->image8[y];
for (x = 0; x < xsize; x++, i++) {
v = in[x];
p[i].c.r = pp[v * 4 + 0];
p[i].c.g = pp[v * 4 + 1];
p[i].c.b = pp[v * 4 + 2];
Expand All @@ -1744,9 +1749,10 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
withAlpha = im->mode == IMAGING_MODE_RGBA;
int transparency = 0;
unsigned char r = 0, g = 0, b = 0;
for (i = y = 0; y < im->ysize; y++) {
for (x = 0; x < im->xsize; x++, i++) {
p[i].v = im->image32[y][x];
for (i = y = 0; y < ysize; y++) {
INT32 *in = im->image32[y];
for (x = 0; x < xsize; x++, i++) {
p[i].v = in[x];
if (withAlpha) {
if (p[i].c.a == 0) {
if (transparency == 0) {
Expand Down Expand Up @@ -1779,49 +1785,24 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
case 0:
/* median cut */
result = quantize(
p,
im->xsize * im->ysize,
colors,
&palette,
&paletteLength,
&newData,
kmeans
p, xsize * ysize, colors, &palette, &paletteLength, &newData, kmeans
);
break;
case 1:
/* maximum coverage */
result = quantize2(
p,
im->xsize * im->ysize,
colors,
&palette,
&paletteLength,
&newData,
kmeans
p, xsize * ysize, colors, &palette, &paletteLength, &newData, kmeans
);
break;
case 2:
result = quantize_octree(
p,
im->xsize * im->ysize,
colors,
&palette,
&paletteLength,
&newData,
withAlpha
p, xsize * ysize, colors, &palette, &paletteLength, &newData, withAlpha
);
break;
case 3:
#ifdef HAVE_LIBIMAGEQUANT
result = quantize_pngquant(
p,
im->xsize,
im->ysize,
colors,
&palette,
&paletteLength,
&newData,
withAlpha
p, xsize, ysize, colors, &palette, &paletteLength, &newData, withAlpha
);
#else
result = -1;
Expand All @@ -1836,16 +1817,16 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
ImagingSectionLeave(&cookie);

if (result > 0) {
imOut = ImagingNewDirty(IMAGING_MODE_P, im->xsize, im->ysize);
imOut = ImagingNewDirty(IMAGING_MODE_P, xsize, ysize);
if (!imOut) {
free(newData);
free(palette);
return NULL;
}
ImagingSectionEnter(&cookie);

for (i = y = 0; y < im->ysize; y++) {
for (x = 0; x < im->xsize; x++) {
for (i = y = 0; y < ysize; y++) {
for (x = 0; x < xsize; x++) {
imOut->image8[y][x] = (unsigned char)newData[i++];
}
}
Expand Down
Loading