From d8e99cbc56a59a5f11132c64bf4378c9aa711fbf Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 1 Jul 2026 16:59:31 +0300 Subject: [PATCH 1/4] Document the internal image distinctness constraints added in the optimization PRs --- src/libImaging/AlphaComposite.c | 4 ++ src/libImaging/Bands.c | 25 +++++++++++++ src/libImaging/Blend.c | 6 +++ src/libImaging/Chops.c | 66 +++++++++++++++++++++++++++++++++ src/libImaging/ColorLUT.c | 6 +++ src/libImaging/Fill.c | 3 ++ src/libImaging/Filter.c | 10 +++++ src/libImaging/Histo.c | 5 +++ src/libImaging/Matrix.c | 6 +++ src/libImaging/Negative.c | 5 +++ src/libImaging/Quant.c | 6 +++ 11 files changed, 142 insertions(+) diff --git a/src/libImaging/AlphaComposite.c b/src/libImaging/AlphaComposite.c index d1a1e7ad34a..fff9d8537e1 100644 --- a/src/libImaging/AlphaComposite.c +++ b/src/libImaging/AlphaComposite.c @@ -19,6 +19,10 @@ typedef struct { UINT8 a; } rgba8; +/** + * Alpha-composite imSrc over imDst, returning a newly allocated result. + * Contract: imDst and imSrc are read-only and may alias each other. + */ Imaging ImagingAlphaComposite(Imaging imDst, Imaging imSrc) { Imaging imOut; diff --git a/src/libImaging/Bands.c b/src/libImaging/Bands.c index d1fa16addff..4c0652a9978 100644 --- a/src/libImaging/Bands.c +++ b/src/libImaging/Bands.c @@ -17,6 +17,10 @@ #include "Imaging.h" +/** + * Extract a single band from imIn into a newly allocated single-band image. + * Contract: imIn is read-only and the returned image is a distinct allocation. + */ Imaging ImagingGetBand(Imaging imIn, int band) { Imaging imOut; @@ -68,6 +72,14 @@ ImagingGetBand(Imaging imIn, int band) { return imOut; } +/** + * Split imIn into its component bands. + * The caller must provide an array of 4 Imaging pointers, + * which will be allocated and filled with the individual bands. + * The number of bands returned is the number of bands in imIn. + * + * Contract: imIn is read-only. + */ int ImagingSplit(Imaging imIn, Imaging bands[4]) { int i, j, x, y; @@ -173,6 +185,11 @@ ImagingSplit(Imaging imIn, Imaging bands[4]) { return imIn->bands; } +/** + * Insert single-band imIn into `band` of imOut, in place. + * + * Contract: imIn and imOut MUST be distinct images and not alias. + */ Imaging ImagingPutBand(Imaging imOut, Imaging imIn, int band) { int x, y; @@ -219,6 +236,9 @@ ImagingPutBand(Imaging imOut, Imaging imIn, int band) { return imOut; } +/** + * Fill a single band of imOut with a constant colour, in place. + */ Imaging ImagingFillBand(Imaging imOut, int band, int color) { int x, y; @@ -253,6 +273,11 @@ ImagingFillBand(Imaging imOut, int band, int color) { return imOut; } +/** + * Merge the caller-supplied bands[] into a newly allocated multi-band image. + * + * Contract: the bands[] inputs are read-only, and the output is new. + */ Imaging ImagingMerge(const ModeID mode, Imaging bands[4]) { int i, x, y; diff --git a/src/libImaging/Blend.c b/src/libImaging/Blend.c index 19662abf044..1589bf2bdc5 100644 --- a/src/libImaging/Blend.c +++ b/src/libImaging/Blend.c @@ -17,6 +17,12 @@ #include "Imaging.h" +/** + * Interpolate (or, for alpha outside [0, 1], extrapolate) between imIn1 and + * imIn2 by `alpha`, returning a newly allocated result. + * + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) { Imaging imOut; diff --git a/src/libImaging/Chops.c b/src/libImaging/Chops.c index 557c2f52f10..bd18a06ace0 100644 --- a/src/libImaging/Chops.c +++ b/src/libImaging/Chops.c @@ -84,66 +84,124 @@ create(Imaging im1, Imaging im2, const ModeID mode) { return ImagingNewDirty(im1->mode, xsize, ysize); } +/** + * Return a newly allocated image containing the lighter pixels of the two images. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopLighter(Imaging imIn1, Imaging imIn2) { CHOP((in1[x] > in2[x]) ? in1[x] : in2[x]); } +/** + * Return a newly allocated image containing the darker pixels of the two images. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopDarker(Imaging imIn1, Imaging imIn2) { CHOP((in1[x] < in2[x]) ? in1[x] : in2[x]); } +/** + * Return a newly allocated image containing the absolute per-pixel difference of the + * two images. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopDifference(Imaging imIn1, Imaging imIn2) { CHOP(abs((int)in1[x] - (int)in2[x])); } +/** + * Return a newly allocated image containing the per-pixel product (scaled to 0-255) of + * the two images. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopMultiply(Imaging imIn1, Imaging imIn2) { CHOP((int)in1[x] * (int)in2[x] / 255); } +/** + * Return a newly allocated image containing the screen blend of the two images. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopScreen(Imaging imIn1, Imaging imIn2) { CHOP(255 - ((int)(255 - in1[x]) * (int)(255 - in2[x])) / 255); } +/** + * Return a newly allocated image containing the per-pixel sum, divided by `scale` and + * shifted by `offset`, clipped to 0-255. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopAdd(Imaging imIn1, Imaging imIn2, float scale, int offset) { CHOP(((int)in1[x] + (int)in2[x]) / scale + offset); } +/** + * Return a newly allocated image containing the per-pixel difference, divided by + * `scale` and shifted by `offset`, clipped to 0-255. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopSubtract(Imaging imIn1, Imaging imIn2, float scale, int offset) { CHOP(((int)in1[x] - (int)in2[x]) / scale + offset); } +/** + * Return a newly allocated "1" image that is the logical AND of the two images. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopAnd(Imaging imIn1, Imaging imIn2) { CHOP2((in1[x] && in2[x]) ? 255 : 0, IMAGING_MODE_1); } +/** + * Return a newly allocated "1" image that is the logical OR of the two images. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopOr(Imaging imIn1, Imaging imIn2) { CHOP2((in1[x] || in2[x]) ? 255 : 0, IMAGING_MODE_1); } +/** + * Return a newly allocated "1" image that is the logical XOR of the two images. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopXor(Imaging imIn1, Imaging imIn2) { CHOP2(((in1[x] != 0) ^ (in2[x] != 0)) ? 255 : 0, IMAGING_MODE_1); } +/** + * Return a newly allocated image containing the per-pixel sum of the two images, + * wrapping on overflow (no clipping). + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopAddModulo(Imaging imIn1, Imaging imIn2) { CHOP2(in1[x] + in2[x], IMAGING_MODE_UNKNOWN); } +/** + * Return a newly allocated image containing the per-pixel difference of the two images, + * wrapping on underflow (no clipping). + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopSubtractModulo(Imaging imIn1, Imaging imIn2) { CHOP2(in1[x] - in2[x], IMAGING_MODE_UNKNOWN); } +/** + * Return a newly allocated image containing the soft-light blend of the two images. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopSoftLight(Imaging imIn1, Imaging imIn2) { CHOP2( @@ -153,6 +211,10 @@ ImagingChopSoftLight(Imaging imIn1, Imaging imIn2) { ); } +/** + * Return a newly allocated image containing the hard-light blend of the two images. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingChopHardLight(Imaging imIn1, Imaging imIn2) { CHOP2( @@ -162,6 +224,10 @@ ImagingChopHardLight(Imaging imIn1, Imaging imIn2) { ); } +/** + * Return a newly allocated image containing the overlay blend of the two images. + * Contract: imIn1 and imIn2 are read-only and may alias each other. + */ Imaging ImagingOverlay(Imaging imIn1, Imaging imIn2) { CHOP2( diff --git a/src/libImaging/ColorLUT.c b/src/libImaging/ColorLUT.c index 5559de689b2..55fc808f04a 100644 --- a/src/libImaging/ColorLUT.c +++ b/src/libImaging/ColorLUT.c @@ -55,6 +55,12 @@ table_index3D(int index1D, int index2D, int index3D, int size1D, int size1D_2D) Each element is signed 16-bit int where 0 is lowest output value and 255 << PRECISION_BITS (16320) is highest value. */ +/** + * Apply a 3D colour lookup table to imIn, writing the result to imOut. + * + * Contract: unlike the other point-style loops in libImaging, + * imIn and imOut MAY be the same image to support running in place. + */ Imaging ImagingColorLUT3D_linear( Imaging imOut, diff --git a/src/libImaging/Fill.c b/src/libImaging/Fill.c index 6f730921358..70001bdf87d 100644 --- a/src/libImaging/Fill.c +++ b/src/libImaging/Fill.c @@ -19,6 +19,9 @@ #include +/** + * Fill an entire image with a constant colour, in place. + */ Imaging ImagingFill(Imaging im, const void *colour) { ImagingSectionCookie cookie; diff --git a/src/libImaging/Filter.c b/src/libImaging/Filter.c index 3492a871f21..d03aec1d85f 100644 --- a/src/libImaging/Filter.c +++ b/src/libImaging/Filter.c @@ -127,6 +127,11 @@ kernel_i16(int size, UINT8 *in0, int x, const float *kernel, int bigendian) { return result; } +/** + * Convolve `im` with a 3x3 kernel, writing the result to imOut. + * + * Contract: imOut and im *MUST* be distinct images. + */ static void ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) { #define KERNEL1x3(in0, x, kernel, d) \ @@ -281,6 +286,11 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) { memcpy(imOut->image[y], im->image[y], im->linesize); } +/** + * Convolve `im` with a 5x5 kernel, writing the result to imOut. + * + * Contract: imOut and im *MUST* be distinct images. + */ static void ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) { #define KERNEL1x5(in0, x, kernel, d) \ diff --git a/src/libImaging/Histo.c b/src/libImaging/Histo.c index 7af60003511..c51677901c6 100644 --- a/src/libImaging/Histo.c +++ b/src/libImaging/Histo.c @@ -56,6 +56,11 @@ ImagingHistogramNew(Imaging im) { return h; } +/** + * Accumulate a histogram over `im`, optionally restricted to imMask. + * + * Contract: Both im and imMask are read-only. + */ ImagingHistogram ImagingGetHistogram(Imaging im, Imaging imMask, void *minmax) { ImagingSectionCookie cookie; diff --git a/src/libImaging/Matrix.c b/src/libImaging/Matrix.c index 02676c9e31a..cd317ba264e 100644 --- a/src/libImaging/Matrix.c +++ b/src/libImaging/Matrix.c @@ -17,6 +17,12 @@ #define CLIPF(v) ((v <= 0.0) ? 0 : (v >= 255.0F) ? 255 : (UINT8)v) +/** + * Convert `im` to `mode` by applying the colour matrix `m`, + * returning a newly allocated result. + * + * Contract: im is read-only. + */ Imaging ImagingConvertMatrix(Imaging im, const ModeID mode, const float m[12]) { Imaging imOut; diff --git a/src/libImaging/Negative.c b/src/libImaging/Negative.c index 114e8608e87..ea04563851d 100644 --- a/src/libImaging/Negative.c +++ b/src/libImaging/Negative.c @@ -18,6 +18,11 @@ #include "Imaging.h" +/** + * Invert every byte of `im`, returning a newly allocated result. + * + * Contract: im is read-only. + */ Imaging ImagingNegative(Imaging im) { Imaging imOut; diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c index 99c2dbac3af..5712f6bd39c 100644 --- a/src/libImaging/Quant.c +++ b/src/libImaging/Quant.c @@ -1663,6 +1663,12 @@ quantize2( return 0; } +/** + * Quantize `im` down to at most `colors` palette entries, + * returning a newly allocated result. + * + * Contract: im is read-only. + */ Imaging ImagingQuantize(Imaging im, int colors, int mode, int kmeans) { int i, j; From 4f0d9b413b2cf5aa8f40010b443517dd45e78be5 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sun, 13 Sep 2026 22:53:51 +0300 Subject: [PATCH 2/4] Apply suggestions Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/libImaging/Bands.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libImaging/Bands.c b/src/libImaging/Bands.c index 4c0652a9978..d65d8a33ee2 100644 --- a/src/libImaging/Bands.c +++ b/src/libImaging/Bands.c @@ -74,9 +74,10 @@ ImagingGetBand(Imaging imIn, int band) { /** * Split imIn into its component bands. - * The caller must provide an array of 4 Imaging pointers, - * which will be allocated and filled with the individual bands. - * The number of bands returned is the number of bands in imIn. + * The caller must provide an array of 4 null Imaging pointers. + * Some of these will be allocated and filled with the individual bands, + * up to the number of bands in imIn. + * The number returned is the number of bands in imIn. * * Contract: imIn is read-only. */ @@ -188,7 +189,7 @@ ImagingSplit(Imaging imIn, Imaging bands[4]) { /** * Insert single-band imIn into `band` of imOut, in place. * - * Contract: imIn and imOut MUST be distinct images and not alias. + * Contract: imIn and imOut MUST be distinct images and not alias each other. */ Imaging ImagingPutBand(Imaging imOut, Imaging imIn, int band) { From fe4094481803067135c62b1caaaae2e4d0393e3c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Sep 2026 14:44:32 +1000 Subject: [PATCH 3/4] Update comments --- src/libImaging/AlphaComposite.c | 1 + src/libImaging/Bands.c | 5 +++-- src/libImaging/Chops.c | 15 +++++++++++++++ src/libImaging/ColorLUT.c | 28 +++++++++++++--------------- src/libImaging/Matrix.c | 2 +- 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/libImaging/AlphaComposite.c b/src/libImaging/AlphaComposite.c index fff9d8537e1..f7db1769b0d 100644 --- a/src/libImaging/AlphaComposite.c +++ b/src/libImaging/AlphaComposite.c @@ -21,6 +21,7 @@ typedef struct { /** * Alpha-composite imSrc over imDst, returning a newly allocated result. + * * Contract: imDst and imSrc are read-only and may alias each other. */ Imaging diff --git a/src/libImaging/Bands.c b/src/libImaging/Bands.c index 4edf4f0d09e..5a9fce8f7ab 100644 --- a/src/libImaging/Bands.c +++ b/src/libImaging/Bands.c @@ -19,7 +19,8 @@ /** * Extract a single band from imIn into a newly allocated single-band image. - * Contract: imIn is read-only and the returned image is a distinct allocation. + * + * Contract: imIn is read-only and the returned image is a newly allocated result. */ Imaging ImagingGetBand(Imaging imIn, int band) { @@ -279,7 +280,7 @@ ImagingFillBand(Imaging imOut, int band, int color) { /** * Merge the caller-supplied bands[] into a newly allocated multi-band image. * - * Contract: the bands[] inputs are read-only, and the output is new. + * Contract: the bands[] inputs are read-only, and the output is newly allocated. */ Imaging ImagingMerge(const ModeID mode, Imaging bands[4]) { diff --git a/src/libImaging/Chops.c b/src/libImaging/Chops.c index bd18a06ace0..0a0244cd932 100644 --- a/src/libImaging/Chops.c +++ b/src/libImaging/Chops.c @@ -86,6 +86,7 @@ create(Imaging im1, Imaging im2, const ModeID mode) { /** * Return a newly allocated image containing the lighter pixels of the two images. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -95,6 +96,7 @@ ImagingChopLighter(Imaging imIn1, Imaging imIn2) { /** * Return a newly allocated image containing the darker pixels of the two images. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -105,6 +107,7 @@ ImagingChopDarker(Imaging imIn1, Imaging imIn2) { /** * Return a newly allocated image containing the absolute per-pixel difference of the * two images. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -115,6 +118,7 @@ ImagingChopDifference(Imaging imIn1, Imaging imIn2) { /** * Return a newly allocated image containing the per-pixel product (scaled to 0-255) of * the two images. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -124,6 +128,7 @@ ImagingChopMultiply(Imaging imIn1, Imaging imIn2) { /** * Return a newly allocated image containing the screen blend of the two images. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -134,6 +139,7 @@ ImagingChopScreen(Imaging imIn1, Imaging imIn2) { /** * Return a newly allocated image containing the per-pixel sum, divided by `scale` and * shifted by `offset`, clipped to 0-255. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -144,6 +150,7 @@ ImagingChopAdd(Imaging imIn1, Imaging imIn2, float scale, int offset) { /** * Return a newly allocated image containing the per-pixel difference, divided by * `scale` and shifted by `offset`, clipped to 0-255. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -153,6 +160,7 @@ ImagingChopSubtract(Imaging imIn1, Imaging imIn2, float scale, int offset) { /** * Return a newly allocated "1" image that is the logical AND of the two images. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -162,6 +170,7 @@ ImagingChopAnd(Imaging imIn1, Imaging imIn2) { /** * Return a newly allocated "1" image that is the logical OR of the two images. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -171,6 +180,7 @@ ImagingChopOr(Imaging imIn1, Imaging imIn2) { /** * Return a newly allocated "1" image that is the logical XOR of the two images. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -181,6 +191,7 @@ ImagingChopXor(Imaging imIn1, Imaging imIn2) { /** * Return a newly allocated image containing the per-pixel sum of the two images, * wrapping on overflow (no clipping). + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -191,6 +202,7 @@ ImagingChopAddModulo(Imaging imIn1, Imaging imIn2) { /** * Return a newly allocated image containing the per-pixel difference of the two images, * wrapping on underflow (no clipping). + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -200,6 +212,7 @@ ImagingChopSubtractModulo(Imaging imIn1, Imaging imIn2) { /** * Return a newly allocated image containing the soft-light blend of the two images. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -213,6 +226,7 @@ ImagingChopSoftLight(Imaging imIn1, Imaging imIn2) { /** * Return a newly allocated image containing the hard-light blend of the two images. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging @@ -226,6 +240,7 @@ ImagingChopHardLight(Imaging imIn1, Imaging imIn2) { /** * Return a newly allocated image containing the overlay blend of the two images. + * * Contract: imIn1 and imIn2 are read-only and may alias each other. */ Imaging diff --git a/src/libImaging/ColorLUT.c b/src/libImaging/ColorLUT.c index 55fc808f04a..03d038e6365 100644 --- a/src/libImaging/ColorLUT.c +++ b/src/libImaging/ColorLUT.c @@ -40,26 +40,24 @@ table_index3D(int index1D, int index2D, int index3D, int size1D, int size1D_2D) return index1D + index2D * size1D + index3D * size1D_2D; } -/* - Transforms colors of imIn using provided 3D lookup table - and puts the result in imOut. Returns imOut on success or 0 on error. - - imOut, imIn - images, should be the same size and may be the same image. - Should have 3 or 4 channels. - table_channels - number of channels in the lookup table, 3 or 4. - Should be less or equal than number of channels in imOut image; - size1D, size_2D and size3D - dimensions of provided table; - table - flat table, - array with table_channels * size1D * size2D * size3D elements, - where channels are changed first, then 1D, then 2D, then 3D. - Each element is signed 16-bit int where 0 is lowest output value - and 255 << PRECISION_BITS (16320) is highest value. -*/ /** * Apply a 3D colour lookup table to imIn, writing the result to imOut. * + * imOut, imIn - images, should be the same size and may be the same image. + * Should have 3 or 4 channels. + * table_channels - number of channels in the lookup table, 3 or 4. + * Should be less or equal than number of channels in imOut image; + * size1D, size_2D and size3D - dimensions of provided table; + * table - flat table, + * array with table_channels * size1D * size2D * size3D elements, + * where channels are changed first, then 1D, then 2D, then 3D. + * Each element is signed 16-bit int where 0 is lowest output value + * and 255 << PRECISION_BITS (16320) is highest value. + * * Contract: unlike the other point-style loops in libImaging, * imIn and imOut MAY be the same image to support running in place. + * + * @return imOut on success or NULL on error. */ Imaging ImagingColorLUT3D_linear( diff --git a/src/libImaging/Matrix.c b/src/libImaging/Matrix.c index cd317ba264e..3e47d346bfa 100644 --- a/src/libImaging/Matrix.c +++ b/src/libImaging/Matrix.c @@ -18,7 +18,7 @@ #define CLIPF(v) ((v <= 0.0) ? 0 : (v >= 255.0F) ? 255 : (UINT8)v) /** - * Convert `im` to `mode` by applying the colour matrix `m`, + * Convert `im` to `mode` by applying the matrix `m`, * returning a newly allocated result. * * Contract: im is read-only. From 985a6b8321dcece67ad9e28b7ceffb966945cd6a Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 16 Sep 2026 08:35:08 +0300 Subject: [PATCH 4/4] Adjust histogram wording --- src/libImaging/Histo.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libImaging/Histo.c b/src/libImaging/Histo.c index c51677901c6..8cdb76eb1a0 100644 --- a/src/libImaging/Histo.c +++ b/src/libImaging/Histo.c @@ -57,7 +57,8 @@ ImagingHistogramNew(Imaging im) { } /** - * Accumulate a histogram over `im`, optionally restricted to imMask. + * Compute a histogram of `im`'s value distribution, + * optionally restricted to imMask. * * Contract: Both im and imMask are read-only. */