1 /**
2 Wrapper for stb_image_resize.h port.
3 Makes it trivial to use.
4 Copyright: (c) Guillaume Piolat (2021)
5 */
6 module dplug.graphics.resizer;
7 
8 import std.math: PI;
9 import dplug.core.math;
10 import dplug.core.vec;
11 import dplug.graphics.color;
12 import dplug.graphics.image;
13 import dplug.graphics.stb_image_resize;
14 
15 
16 version = STB_image_resize;
17 
18 /// Image resizer.
19 /// To minimize CPU, it is advised to reuse that object for similar resize.
20 /// To minimize memory allocation, it is advised to reuse that object even across different resize.
21 struct ImageResizer
22 {
23 public:
24 nothrow:
25 @nogc:
26 
27     @disable this(this);
28 
29     /**
30     * Function resizes image. There are several other function for specialized treatment.
31     *
32     * Params:
33     *   input Input image.
34     *   output Output image.
35     */
36     void resizeImageGeneric(ImageRef!RGBA input, ImageRef!RGBA output)
37     {
38         if (sameSizeResize(input, output))
39             return;
40 
41         stbir_filter filter = STBIR_FILTER_DEFAULT;
42         int res = stbir_resize_uint8(cast(const(ubyte*))input.pixels, input.w, input.h, cast(int)input.pitch,
43                                      cast(      ubyte* )output.pixels, output.w, output.h, cast(int)output.pitch, 4, filter, &alloc_context);
44         assert(res);
45     }
46 
47     ///ditto
48     void resizeImageSmoother(ImageRef!RGBA input, ImageRef!RGBA output)
49     {
50         if (sameSizeResize(input, output))
51             return;
52 
53         // suitable when depth is encoded in a RGB8 triplet, such as in UIImageKnob
54         stbir_filter filter = STBIR_FILTER_CUBICBSPLINE;
55         int res = stbir_resize_uint8(cast(const(ubyte*))input.pixels, input.w, input.h, cast(int)input.pitch,
56                                      cast(      ubyte* )output.pixels, output.w, output.h, cast(int)output.pitch, 4, filter, &alloc_context);
57         assert(res);
58     }
59 
60     ///ditto
61     void resizeImageNearest(ImageRef!RGBA input, ImageRef!RGBA output) // same but with nearest filter
62     {
63         if (sameSizeResize(input, output))
64             return;
65 
66         stbir_filter filter = STBIR_FILTER_BOX;
67         int res = stbir_resize_uint8(cast(const(ubyte*))input.pixels, input.w, input.h, cast(int)input.pitch,
68                                      cast(      ubyte* )output.pixels, output.w, output.h, cast(int)output.pitch, 4, filter, &alloc_context);
69         assert(res);
70     }
71 
72     ///ditto
73     void resizeImageGeneric(ImageRef!L16 input, ImageRef!L16 output)
74     {
75         if (sameSizeResize(input, output))
76             return;
77 
78         stbir_filter filter = STBIR_FILTER_DEFAULT;
79         int res = stbir_resize_uint16(cast(const(ushort*))input.pixels, input.w, input.h, cast(int)input.pitch,
80                                       cast(      ushort* )output.pixels, output.w, output.h, cast(int)output.pitch, 1, filter, &alloc_context);
81         assert(res);
82     }
83 
84     ///ditto
85     void resizeImageDepth(ImageRef!L16 input, ImageRef!L16 output)
86     {
87         if (sameSizeResize(input, output))
88             return;
89 
90         stbir_filter filter = STBIR_FILTER_MKS_2021;
91         int res = stbir_resize_uint16(cast(const(ushort*))input.pixels, input.w, input.h, cast(int)input.pitch,
92                                       cast(      ushort* )output.pixels, output.w, output.h, cast(int)output.pitch, 1, filter, &alloc_context);
93         assert(res);
94     }
95 
96     ///ditto
97     void resizeImageCoverage(ImageRef!L8 input, ImageRef!L8 output)
98     {
99         if (sameSizeResize(input, output))
100             return;
101 
102         int res = stbir_resize_uint8(cast(const(ubyte*))input.pixels, input.w, input.h, cast(int)input.pitch,
103                                      cast(      ubyte* )output.pixels, output.w, output.h, cast(int)output.pitch, 1, STBIR_FILTER_DEFAULT, &alloc_context);
104         assert(res);
105     }
106 
107     ///ditto
108     void resizeImage_sRGBNoAlpha(ImageRef!RGBA input, ImageRef!RGBA output)
109     {
110         if (sameSizeResize(input, output))
111             return;
112         stbir_filter filter = STBIR_FILTER_MKS_2013_86;
113         int res = stbir_resize_uint8_srgb(cast(const(ubyte*))input.pixels, input.w, input.h, cast(int)input.pitch,
114                                           cast(      ubyte* )output.pixels, output.w, output.h, cast(int)output.pitch,
115                                           4, STBIR_ALPHA_CHANNEL_NONE, 0, &alloc_context, filter);
116         assert(res);
117     }
118 
119     ///ditto
120     void resizeImage_sRGBWithAlpha(ImageRef!RGBA input, ImageRef!RGBA output)
121     {
122         if (sameSizeResize(input, output))
123             return;
124         stbir_filter filter = STBIR_FILTER_MKS_2013_86;
125         int res = stbir_resize_uint8_srgb(cast(const(ubyte*))input.pixels, input.w, input.h, cast(int)input.pitch,
126                                           cast(      ubyte* )output.pixels, output.w, output.h, cast(int)output.pitch,
127                                           4, 3, 0, &alloc_context, filter);
128         assert(res);
129     }
130 
131     ///ditto
132     void resizeImageDiffuse(ImageRef!RGBA input, ImageRef!RGBA output)
133     {
134         if (sameSizeResize(input, output))
135             return;
136         // Note: as the primary use case is downsampling, it was found it is helpful to have a relatively sharp filter
137         // since the diffuse map may contain text, and downsampling text is too blurry as of today.
138         stbir_filter filter = STBIR_FILTER_MKS_2013_86;
139         int res = stbir_resize_uint8(cast(const(ubyte*))input.pixels, input.w, input.h, cast(int)input.pitch,
140                                      cast(      ubyte* )output.pixels, output.w, output.h, cast(int)output.pitch, 4, filter, &alloc_context);
141         assert(res);
142     }
143 
144     // Note: no special treatment for material images
145     // No particular quality gain when using lanczos 3.
146     alias resizeImageMaterial = resizeImageGeneric;
147 
148 private:
149 
150     STBAllocatorContext alloc_context;
151 }
152 
153 
154 private:
155 
156 
157 bool sameSizeResize(COLOR)(ImageRef!COLOR input, ImageRef!COLOR output) nothrow @nogc
158 {
159     if (input.w == output.w && input.h == output.h)
160     {
161         // Just copy the pixels over
162         input.blitTo(output);
163         return true;
164     }
165     else
166         return false;
167 
168 
169 }