From b430b02a31e5452c61c11ccfdb8a14941ee2f405 Mon Sep 17 00:00:00 2001 From: drhead Date: Tue, 11 Mar 2025 15:27:14 -0400 Subject: [PATCH] Add support for Magic Kernel Sharp 2021 resampling --- src/PIL/Image.py | 7 ++++++- src/libImaging/Imaging.h | 1 + src/libImaging/Resample.c | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 884659882..1a43e723d 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -169,6 +169,7 @@ class Resampling(IntEnum): HAMMING = 5 BICUBIC = 3 LANCZOS = 1 + MKS2021 = 6 _filters_support = { @@ -177,6 +178,7 @@ _filters_support = { Resampling.HAMMING: 1.0, Resampling.BICUBIC: 2.0, Resampling.LANCZOS: 3.0, + Resampling.MKS2021: 4.5, } @@ -2242,6 +2244,7 @@ class Image: Resampling.LANCZOS, Resampling.BOX, Resampling.HAMMING, + Resampling.MKS2021, ): msg = f"Unknown resampling filter ({resample})." @@ -2254,6 +2257,7 @@ class Image: (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), (Resampling.BOX, "Image.Resampling.BOX"), (Resampling.HAMMING, "Image.Resampling.HAMMING"), + (Resampling.MKS2021, "Image.Resampling.MKS2021"), ) ] msg += f" Use {', '.join(filters[:-1])} or {filters[-1]}" @@ -2904,11 +2908,12 @@ class Image: Resampling.BILINEAR, Resampling.BICUBIC, ): - if resample in (Resampling.BOX, Resampling.HAMMING, Resampling.LANCZOS): + if resample in (Resampling.BOX, Resampling.HAMMING, Resampling.LANCZOS, Resampling.MKS2021): unusable: dict[int, str] = { Resampling.BOX: "Image.Resampling.BOX", Resampling.HAMMING: "Image.Resampling.HAMMING", Resampling.LANCZOS: "Image.Resampling.LANCZOS", + Resampling.MKS2021: "Image.Resampling.MKS2021", } msg = unusable[resample] + f" ({resample}) cannot be used." else: diff --git a/src/libImaging/Imaging.h b/src/libImaging/Imaging.h index 0c2d3fc2e..573a08f0a 100644 --- a/src/libImaging/Imaging.h +++ b/src/libImaging/Imaging.h @@ -269,6 +269,7 @@ ImagingError_Clear(void); #define IMAGING_TRANSFORM_HAMMING 5 #define IMAGING_TRANSFORM_BICUBIC 3 #define IMAGING_TRANSFORM_LANCZOS 1 +#define IMAGING_TRANSFORM_MKS2021 6 typedef int (*ImagingTransformMap)(double *X, double *Y, int x, int y, void *data); typedef int (*ImagingTransformFilter)(void *out, Imaging im, double x, double y); diff --git a/src/libImaging/Resample.c b/src/libImaging/Resample.c index f5e386dc2..b723f6f7b 100644 --- a/src/libImaging/Resample.c +++ b/src/libImaging/Resample.c @@ -79,11 +79,30 @@ lanczos_filter(double x) { return 0.0; } +static inline double +mks_2021_filter(double x) { + /* https://johncostella.com/magic/ */ + if (x < 0.0) + x = -x; + if (x < 0.5) + return 577.0/576.0 - 239.0/144.0 * pow(x, 2.0); + if (x < 1.5) + return 35.0/36.0 * (x - 1.0) * (x - 239.0/140.0); + if (x < 2.5) + return 1.0/6.0 * (x - 2.0) * (65.0/24.0 - x); + if (x < 3.5) + return 1.0/36.0 * (x - 3.0) * (x - 15.0/4.0); + if (x < 4.5) + return -1.0/288.0 * pow(x - 9.0/2.0, 2.0); + return(0.0); +} + static struct filter BOX = {box_filter, 0.5}; static struct filter BILINEAR = {bilinear_filter, 1.0}; static struct filter HAMMING = {hamming_filter, 1.0}; static struct filter BICUBIC = {bicubic_filter, 2.0}; static struct filter LANCZOS = {lanczos_filter, 3.0}; +static struct filter MKS2021 = {mks_2021_filter, 4.5}; /* 8 bits for result. Filter can have negative areas. In one cases the sum of the coefficients will be negative, @@ -693,6 +712,9 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]) { case IMAGING_TRANSFORM_LANCZOS: filterp = &LANCZOS; break; + case IMAGING_TRANSFORM_MKS2021: + filterp = &MKS2021; + break; default: return (Imaging)ImagingError_ValueError("unsupported resampling filter"); }