Skip to content

Commit 5fa9eee

Browse files
committed
Split I16 ImageFilter loops so they are optimized by compiler
1 parent 02178c8 commit 5fa9eee

2 files changed

Lines changed: 78 additions & 69 deletions

File tree

Tests/benchmarks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ def test_crop(
218218
],
219219
ids=lambda f: f.name,
220220
)
221-
@pytest.mark.parametrize("mode", MODES)
221+
@pytest.mark.parametrize("mode", [*MODES, "I;16"])
222222
@pytest.mark.parametrize("size", SIZES, ids=_format_size)
223223
def test_filter(
224224
bench: BenchmarkFixture,

src/libImaging/Filter.c

Lines changed: 77 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -161,17 +161,16 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
161161
}
162162
out[x] = in0[x];
163163
}
164-
} else {
164+
} else if (im->type == IMAGING_TYPE_SPECIAL) {
165+
// Check for I;16 mode once, not per pixel
165166
int bigendian = 0;
166-
if (im->type == IMAGING_TYPE_SPECIAL) {
167-
if (
168-
im->mode == IMAGING_MODE_I_16B
167+
if (
168+
im->mode == IMAGING_MODE_I_16B
169169
#ifdef WORDS_BIGENDIAN
170-
|| im->mode == IMAGING_MODE_I_16N
170+
|| im->mode == IMAGING_MODE_I_16N
171171
#endif
172-
) {
173-
bigendian = 1;
174-
}
172+
) {
173+
bigendian = 1;
175174
}
176175
for (y = 1; y < ysize - 1; y++) {
177176
UINT8 *restrict in_1 = (UINT8 *)im->image[y - 1];
@@ -180,32 +179,36 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
180179
UINT8 *restrict out = (UINT8 *)imOut->image[y];
181180

182181
out[0] = in0[0];
183-
if (im->type == IMAGING_TYPE_SPECIAL) {
184-
out[1] = in0[1];
185-
}
182+
out[1] = in0[1];
186183
for (x = 1; x < xsize - 1; x++) {
187184
float ss = offset;
188-
if (im->type == IMAGING_TYPE_SPECIAL) {
189-
ss += kernel_i16(3, in1, x, &kernel[0], bigendian);
190-
ss += kernel_i16(3, in0, x, &kernel[3], bigendian);
191-
ss += kernel_i16(3, in_1, x, &kernel[6], bigendian);
192-
// NOT rounding here because `offset` already has a +0.5 bias.
193-
int ss_int = clip16(ss);
194-
out[x * 2 + (bigendian ? 1 : 0)] = (UINT8)(ss_int & 0xff);
195-
out[x * 2 + (bigendian ? 0 : 1)] = (UINT8)(ss_int >> 8);
196-
} else {
197-
ss += KERNEL1x3(in1, x, &kernel[0], 1);
198-
ss += KERNEL1x3(in0, x, &kernel[3], 1);
199-
ss += KERNEL1x3(in_1, x, &kernel[6], 1);
200-
out[x] = clip8(ss);
201-
}
185+
ss += kernel_i16(3, in1, x, &kernel[0], bigendian);
186+
ss += kernel_i16(3, in0, x, &kernel[3], bigendian);
187+
ss += kernel_i16(3, in_1, x, &kernel[6], bigendian);
188+
// NOT rounding here because `offset` already has a +0.5 bias.
189+
int ss_int = clip16(ss);
190+
out[x * 2 + (bigendian ? 1 : 0)] = (UINT8)(ss_int & 0xff);
191+
out[x * 2 + (bigendian ? 0 : 1)] = (UINT8)(ss_int >> 8);
202192
}
203-
if (im->type == IMAGING_TYPE_SPECIAL) {
204-
out[x * 2] = in0[x * 2];
205-
out[x * 2 + 1] = in0[x * 2 + 1];
206-
} else {
207-
out[x] = in0[x];
193+
out[x * 2] = in0[x * 2];
194+
out[x * 2 + 1] = in0[x * 2 + 1];
195+
}
196+
} else {
197+
for (y = 1; y < ysize - 1; y++) {
198+
UINT8 *restrict in_1 = (UINT8 *)im->image[y - 1];
199+
UINT8 *restrict in0 = (UINT8 *)im->image[y];
200+
UINT8 *restrict in1 = (UINT8 *)im->image[y + 1];
201+
UINT8 *restrict out = (UINT8 *)imOut->image[y];
202+
203+
out[0] = in0[0];
204+
for (x = 1; x < xsize - 1; x++) {
205+
float ss = offset;
206+
ss += KERNEL1x3(in1, x, &kernel[0], 1);
207+
ss += KERNEL1x3(in0, x, &kernel[3], 1);
208+
ss += KERNEL1x3(in_1, x, &kernel[6], 1);
209+
out[x] = clip8(ss);
208210
}
211+
out[x] = in0[x];
209212
}
210213
}
211214
} else {
@@ -322,17 +325,16 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
322325
out[x + 0] = in0[x + 0];
323326
out[x + 1] = in0[x + 1];
324327
}
325-
} else {
328+
} else if (im->type == IMAGING_TYPE_SPECIAL) {
329+
// Check for I;16 mode once, not per pixel
326330
int bigendian = 0;
327-
if (im->type == IMAGING_TYPE_SPECIAL) {
328-
if (
329-
im->mode == IMAGING_MODE_I_16B
331+
if (
332+
im->mode == IMAGING_MODE_I_16B
330333
#ifdef WORDS_BIGENDIAN
331-
|| im->mode == IMAGING_MODE_I_16N
334+
|| im->mode == IMAGING_MODE_I_16N
332335
#endif
333-
) {
334-
bigendian = 1;
335-
}
336+
) {
337+
bigendian = 1;
336338
}
337339
for (y = 2; y < ysize - 2; y++) {
338340
UINT8 *restrict in_2 = (UINT8 *)im->image[y - 2];
@@ -344,40 +346,47 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
344346

345347
out[0] = in0[0];
346348
out[1] = in0[1];
347-
if (im->type == IMAGING_TYPE_SPECIAL) {
348-
out[2] = in0[2];
349-
out[3] = in0[3];
350-
}
349+
out[2] = in0[2];
350+
out[3] = in0[3];
351351
for (x = 2; x < xsize - 2; x++) {
352352
float ss = offset;
353-
if (im->type == IMAGING_TYPE_SPECIAL) {
354-
ss += kernel_i16(5, in2, x, &kernel[0], bigendian);
355-
ss += kernel_i16(5, in1, x, &kernel[5], bigendian);
356-
ss += kernel_i16(5, in0, x, &kernel[10], bigendian);
357-
ss += kernel_i16(5, in_1, x, &kernel[15], bigendian);
358-
ss += kernel_i16(5, in_2, x, &kernel[20], bigendian);
359-
// NOT rounding here because `offset` already has a +0.5 bias.
360-
int ss_int = clip16(ss);
361-
out[x * 2 + (bigendian ? 1 : 0)] = (UINT8)(ss_int & 0xff);
362-
out[x * 2 + (bigendian ? 0 : 1)] = (UINT8)(ss_int >> 8);
363-
} else {
364-
ss += KERNEL1x5(in2, x, &kernel[0], 1);
365-
ss += KERNEL1x5(in1, x, &kernel[5], 1);
366-
ss += KERNEL1x5(in0, x, &kernel[10], 1);
367-
ss += KERNEL1x5(in_1, x, &kernel[15], 1);
368-
ss += KERNEL1x5(in_2, x, &kernel[20], 1);
369-
out[x] = clip8(ss);
370-
}
353+
ss += kernel_i16(5, in2, x, &kernel[0], bigendian);
354+
ss += kernel_i16(5, in1, x, &kernel[5], bigendian);
355+
ss += kernel_i16(5, in0, x, &kernel[10], bigendian);
356+
ss += kernel_i16(5, in_1, x, &kernel[15], bigendian);
357+
ss += kernel_i16(5, in_2, x, &kernel[20], bigendian);
358+
// NOT rounding here because `offset` already has a +0.5 bias.
359+
int ss_int = clip16(ss);
360+
out[x * 2 + (bigendian ? 1 : 0)] = (UINT8)(ss_int & 0xff);
361+
out[x * 2 + (bigendian ? 0 : 1)] = (UINT8)(ss_int >> 8);
371362
}
372-
if (im->type == IMAGING_TYPE_SPECIAL) {
373-
out[x * 2 + 0] = in0[x * 2 + 0];
374-
out[x * 2 + 1] = in0[x * 2 + 1];
375-
out[x * 2 + 2] = in0[x * 2 + 2];
376-
out[x * 2 + 3] = in0[x * 2 + 3];
377-
} else {
378-
out[x + 0] = in0[x + 0];
379-
out[x + 1] = in0[x + 1];
363+
out[x * 2 + 0] = in0[x * 2 + 0];
364+
out[x * 2 + 1] = in0[x * 2 + 1];
365+
out[x * 2 + 2] = in0[x * 2 + 2];
366+
out[x * 2 + 3] = in0[x * 2 + 3];
367+
}
368+
} else {
369+
for (y = 2; y < ysize - 2; y++) {
370+
UINT8 *restrict in_2 = (UINT8 *)im->image[y - 2];
371+
UINT8 *restrict in_1 = (UINT8 *)im->image[y - 1];
372+
UINT8 *restrict in0 = (UINT8 *)im->image[y];
373+
UINT8 *restrict in1 = (UINT8 *)im->image[y + 1];
374+
UINT8 *restrict in2 = (UINT8 *)im->image[y + 2];
375+
UINT8 *restrict out = (UINT8 *)imOut->image[y];
376+
377+
out[0] = in0[0];
378+
out[1] = in0[1];
379+
for (x = 2; x < xsize - 2; x++) {
380+
float ss = offset;
381+
ss += KERNEL1x5(in2, x, &kernel[0], 1);
382+
ss += KERNEL1x5(in1, x, &kernel[5], 1);
383+
ss += KERNEL1x5(in0, x, &kernel[10], 1);
384+
ss += KERNEL1x5(in_1, x, &kernel[15], 1);
385+
ss += KERNEL1x5(in_2, x, &kernel[20], 1);
386+
out[x] = clip8(ss);
380387
}
388+
out[x + 0] = in0[x + 0];
389+
out[x + 1] = in0[x + 1];
381390
}
382391
}
383392
} else {

0 commit comments

Comments
 (0)