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
5 changes: 5 additions & 0 deletions src/libImaging/AlphaComposite.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ 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;
Expand Down
24 changes: 23 additions & 1 deletion src/libImaging/Bands.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@

#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 newly allocated result.
*/
Imaging
ImagingGetBand(Imaging imIn, int band) {
Imaging imOut;
Expand Down Expand Up @@ -69,7 +74,11 @@ ImagingGetBand(Imaging imIn, int band) {
}

/**
* Splits an image into its component bands.
* Splits imIn into its component bands.
* The provided null Imaging pointers will be allocated
* and filled with the individual bands, up to the number of bands in imIn.
*
* Contract: imIn is read-only.
*
* @param imIn The input image.
* @param bands An array of null pointers to the output band images.
Expand Down Expand Up @@ -180,6 +189,11 @@ ImagingSplit(Imaging imIn, Imaging bands[4]) {
return 0;
}

/**
* Insert single-band imIn into `band` of imOut, in place.
*
* Contract: imIn and imOut MUST be distinct images and not alias each other.
*/
Imaging
ImagingPutBand(Imaging imOut, Imaging imIn, int band) {
int x, y;
Expand Down Expand Up @@ -226,6 +240,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;
Expand Down Expand Up @@ -260,6 +277,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 newly allocated.
*/
Imaging
ImagingMerge(const ModeID mode, Imaging bands[4]) {
int i, x, y;
Expand Down
6 changes: 6 additions & 0 deletions src/libImaging/Blend.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
81 changes: 81 additions & 0 deletions src/libImaging/Chops.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,66 +84,137 @@ 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(
Expand All @@ -153,6 +224,11 @@ 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(
Expand All @@ -162,6 +238,11 @@ 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(
Expand Down
34 changes: 19 additions & 15 deletions src/libImaging/ColorLUT.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,25 @@ 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(
Imaging imOut,
Expand Down
3 changes: 3 additions & 0 deletions src/libImaging/Fill.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

#include <math.h>

/**
* Fill an entire image with a constant colour, in place.
*/
Imaging
ImagingFill(Imaging im, const void *colour) {
ImagingSectionCookie cookie;
Expand Down
10 changes: 10 additions & 0 deletions src/libImaging/Filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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) \
Expand Down
6 changes: 6 additions & 0 deletions src/libImaging/Histo.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ ImagingHistogramNew(Imaging im) {
return h;
}

/**
* Compute a histogram of `im`'s value distribution,
* optionally restricted to imMask.
*
* Contract: Both im and imMask are read-only.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this the contract? I expect that is how it is used, but I don't see any use of restrict in here.

@akx akx Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't get this far in the optimization series, looks like 😄
But this documents the contract nevertheless, that was never going to change with the series.

EDIT: See #10005. Turns out I had half-written optimizations but didn't push them before.

*/
ImagingHistogram
ImagingGetHistogram(Imaging im, Imaging imMask, void *minmax) {
ImagingSectionCookie cookie;
Expand Down
6 changes: 6 additions & 0 deletions src/libImaging/Matrix.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@

#define CLIPF(v) ((v <= 0.0) ? 0 : (v >= 255.0F) ? 255 : (UINT8)v)

/**
* Convert `im` to `mode` by applying the 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;
Expand Down
5 changes: 5 additions & 0 deletions src/libImaging/Negative.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions src/libImaging/Quant.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I don't see any restrict here.

@akx akx Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here as above: this still documents the contract. Quantize was touched a bit in #9740, though not with restrict bits.

*/
Imaging
ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
int i, j;
Expand Down
Loading