From a85933b25f2d13a8a5afd33eb72f3b3c4f5a0a84 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 2 Jun 2026 09:36:53 +0300 Subject: [PATCH 1/3] Blend: shortcut to copy when same image pointer is passed in twice Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/libImaging/Blend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libImaging/Blend.c b/src/libImaging/Blend.c index df94920f62e..7c32a1f03c8 100644 --- a/src/libImaging/Blend.c +++ b/src/libImaging/Blend.c @@ -35,7 +35,7 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) { } /* Shortcuts */ - if (alpha == 0.0) { + if (imIn1 == imIn2 || alpha == 0.0) { return ImagingCopy(imIn1); } else if (alpha == 1.0) { return ImagingCopy(imIn2); From 84af3f588d62e8a5f7038b6a08474ce5bf3cfb6a Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 2 Jun 2026 09:41:02 +0300 Subject: [PATCH 2/3] Blend: hoist static image size variables out of loops for optimization --- src/libImaging/Blend.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libImaging/Blend.c b/src/libImaging/Blend.c index 7c32a1f03c8..b883fd174eb 100644 --- a/src/libImaging/Blend.c +++ b/src/libImaging/Blend.c @@ -46,23 +46,28 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) { return NULL; } + // We know these won't change in the loops below, + // so the compiler can optimize better if we pull them out. + int ysize = imIn1->ysize; + int linesize = imIn1->linesize; + if (alpha >= 0 && alpha <= 1.0) { /* Interpolate between bands */ - for (y = 0; y < imIn1->ysize; y++) { + for (y = 0; y < ysize; y++) { UINT8 *in1 = (UINT8 *)imIn1->image[y]; UINT8 *in2 = (UINT8 *)imIn2->image[y]; UINT8 *out = (UINT8 *)imOut->image[y]; - for (x = 0; x < imIn1->linesize; x++) { + for (x = 0; x < linesize; x++) { out[x] = (UINT8)((int)in1[x] + alpha * ((int)in2[x] - (int)in1[x])); } } } else { /* Extrapolation; must make sure to clip resulting values */ - for (y = 0; y < imIn1->ysize; y++) { + for (y = 0; y < ysize; y++) { UINT8 *in1 = (UINT8 *)imIn1->image[y]; UINT8 *in2 = (UINT8 *)imIn2->image[y]; UINT8 *out = (UINT8 *)imOut->image[y]; - for (x = 0; x < imIn1->linesize; x++) { + for (x = 0; x < linesize; x++) { float temp = (float)((int)in1[x] + alpha * ((int)in2[x] - (int)in1[x])); if (temp <= 0.0) { out[x] = 0; From 00d1d4a171e1ffc987e11974977a3f5a42bbb9a6 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 2 Jun 2026 09:41:29 +0300 Subject: [PATCH 3/3] Blend: note imIn1/imIn2/imOut can't alias each other --- src/libImaging/Blend.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libImaging/Blend.c b/src/libImaging/Blend.c index b883fd174eb..19662abf044 100644 --- a/src/libImaging/Blend.c +++ b/src/libImaging/Blend.c @@ -54,9 +54,10 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) { if (alpha >= 0 && alpha <= 1.0) { /* Interpolate between bands */ for (y = 0; y < ysize; y++) { - UINT8 *in1 = (UINT8 *)imIn1->image[y]; - UINT8 *in2 = (UINT8 *)imIn2->image[y]; - UINT8 *out = (UINT8 *)imOut->image[y]; + // restrict safe: imIn1 and imIn2 are read-only; imOut is a new allocation + UINT8 *restrict in1 = (UINT8 *)imIn1->image[y]; + UINT8 *restrict in2 = (UINT8 *)imIn2->image[y]; + UINT8 *restrict out = (UINT8 *)imOut->image[y]; for (x = 0; x < linesize; x++) { out[x] = (UINT8)((int)in1[x] + alpha * ((int)in2[x] - (int)in1[x])); } @@ -64,9 +65,10 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) { } else { /* Extrapolation; must make sure to clip resulting values */ for (y = 0; y < ysize; y++) { - UINT8 *in1 = (UINT8 *)imIn1->image[y]; - UINT8 *in2 = (UINT8 *)imIn2->image[y]; - UINT8 *out = (UINT8 *)imOut->image[y]; + // restrict safe: imIn1 and imIn2 are read-only; imOut is a new allocation + UINT8 *restrict in1 = (UINT8 *)imIn1->image[y]; + UINT8 *restrict in2 = (UINT8 *)imIn2->image[y]; + UINT8 *restrict out = (UINT8 *)imOut->image[y]; for (x = 0; x < linesize; x++) { float temp = (float)((int)in1[x] + alpha * ((int)in2[x] - (int)in1[x])); if (temp <= 0.0) {