Skip to content
Open
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
9 changes: 1 addition & 8 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -2442,14 +2442,7 @@ def resize(
(box[3] - reduce_box[1]) / factor_y,
)

if self.size[1] > self.size[0] * 100 and size[1] < self.size[1]:
im = self.im.resize(
(self.size[0], size[1]), resample, (0, box[1], self.size[0], box[3])
)
im = im.resize(size, resample, (box[0], 0, box[2], size[1]))
else:
im = self.im.resize(size, resample, box)
return self._new(im)
return self._new(self.im.resize(size, resample, box))

def reduce(
self,
Expand Down
140 changes: 89 additions & 51 deletions src/libImaging/Resample.c
Original file line number Diff line number Diff line change
Expand Up @@ -828,82 +828,120 @@ ImagingResampleInner(
Imaging imTemp = NULL;
Imaging imOut = NULL;

int i, need_horizontal, need_vertical;
int i, second_pass, need_horizontal, need_vertical, error = 0;
int ybox_first, ybox_last;
int ksize_horiz, ksize_vert;
int ksize_horiz = 0, ksize_vert = 0;
int *bounds_horiz, *bounds_vert;
double *kk_horiz, *kk_vert;

need_horizontal = xsize != imIn->xsize || box[0] || box[2] != xsize;
need_vertical = ysize != imIn->ysize || box[1] || box[3] != ysize;

ksize_vert = precompute_coeffs(
imIn->ysize, box[1], box[3], ysize, filterp, &bounds_vert, &kk_vert
);
if (!ksize_vert) {
return NULL;
}
// If height is being scaled down more than twice the amount the width is,
// run the vertical pass first, to make the horizontal pass faster
int horizontal_first =
!((imIn->ysize - ysize) > 0 &&
(imIn->ysize - ysize) > (imIn->xsize - xsize) * 2);

// First used row in the source image
ybox_first = bounds_vert[0];
// Last used row in the source image
ybox_last = bounds_vert[ysize * 2 - 2] + bounds_vert[ysize * 2 - 1];

/* two-pass resize, horizontal pass */
if ((need_horizontal && horizontal_first) || need_vertical) {
ksize_vert = precompute_coeffs(
imIn->ysize, box[1], box[3], ysize, filterp, &bounds_vert, &kk_vert
);
if (!ksize_vert) {
return NULL;
}
}
if (need_horizontal) {
if (horizontal_first) {
// First used row in the source image
ybox_first = bounds_vert[0];
// Last used row in the source image
ybox_last = bounds_vert[ysize * 2 - 2] + bounds_vert[ysize * 2 - 1];

// Shift bounds for vertical pass
if (ybox_first != 0) {
for (i = 0; i < ysize; i++) {
bounds_vert[i * 2] -= ybox_first;
}
}
}

ksize_horiz = precompute_coeffs(
imIn->xsize, box[0], box[2], xsize, filterp, &bounds_horiz, &kk_horiz
);
if (!ksize_horiz) {
free(bounds_vert);
free(kk_vert);
return NULL;
error = 1;
goto end;
}
}

// Shift bounds for vertical pass
for (i = 0; i < ysize; i++) {
bounds_vert[i * 2] -= ybox_first;
#define PASS(function, w, h, offset, ksize, bounds, kk) \
second_pass = imTemp != NULL; \
imTemp = ImagingNewDirty(imIn->mode, w, h); \

@hugovk hugovk Sep 7, 2026

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.

Possible memory leak here: if the second pass's ImagingNewDirty fails, imTemp is overwritten with NULL before we goto end, so the intermediate image from the first pass (now only referenced by imIn) is never deleted. We had an ImagingDelete(imTemp) call before.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch. I've pushed a commit.

if (!imTemp) { \
if (second_pass) { \
ImagingDelete(imIn); \
} \
error = 1; \
goto end; \
} \
function(imTemp, imIn, offset, ksize, bounds, kk); \
if (second_pass) { \
ImagingDelete(imIn); \
} \
imIn = imTemp;

if (horizontal_first) {
if (need_horizontal) {
PASS(
ResampleHorizontal,
xsize,
ybox_last - ybox_first,
ybox_first,
ksize_horiz,
bounds_horiz,
kk_horiz
);
}

imTemp = ImagingNewDirty(imIn->mode, xsize, ybox_last - ybox_first);
if (imTemp) {
ResampleHorizontal(
imTemp, imIn, ybox_first, ksize_horiz, bounds_horiz, kk_horiz
if (need_vertical) {
PASS(ResampleVertical, xsize, ysize, 0, ksize_vert, bounds_vert, kk_vert);
}
} else {
if (need_vertical) {
PASS(
ResampleVertical,
imIn->xsize,
ysize,
0,
ksize_vert,
bounds_vert,
kk_vert
);
}
free(bounds_horiz);
free(kk_horiz);
if (!imTemp) {
free(bounds_vert);
free(kk_vert);
return NULL;
if (need_horizontal) {
PASS(
ResampleHorizontal, xsize, ysize, 0, ksize_horiz, bounds_horiz, kk_horiz
);
}
imOut = imIn = imTemp;
}

/* vertical pass */
if (need_vertical) {
imOut = ImagingNewDirty(imIn->mode, imIn->xsize, ysize);
if (imOut) {
/* imIn can be the original image or horizontally resampled one */
ResampleVertical(imOut, imIn, 0, ksize_vert, bounds_vert, kk_vert);
}
/* it's safe to call ImagingDelete with empty value
if previous step was not performed. */
ImagingDelete(imTemp);
free(bounds_vert);
free(kk_vert);
if (!imOut) {
return NULL;
}
} else {
// Free in any case
end:
if (ksize_horiz) {
free(bounds_horiz);
free(kk_horiz);
}
if (ksize_vert) {
free(bounds_vert);
free(kk_vert);
}
if (error) {
return NULL;
}

/* none of the previous steps are performed, copying */
if (!imOut) {
if (imTemp) {
imOut = imTemp;
} else {
// none of the previous steps are performed, copying
imOut = ImagingCopy(imIn);
}

Expand Down
Loading