Blender V4.3
IMB_scaling_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "testing/testing.h"
6
8#include "IMB_imbuf.hh"
9
11
13{
14 ImBuf *img = IMB_allocImBuf(6, 2, 32, IB_rect);
15 uchar4 *col = reinterpret_cast<uchar4 *>(img->byte_buffer.data);
16
17 /* Source pixels are spelled out in 2x2 blocks below:
18 * nearest filter results in corner pixel from each block, bilinear
19 * is average of each block. */
20 col[0] = uchar4(0, 0, 0, 255);
21 col[1] = uchar4(255, 0, 0, 255);
22 col[6] = uchar4(255, 255, 0, 255);
23 col[7] = uchar4(255, 255, 255, 255);
24
25 col[2] = uchar4(133, 55, 31, 13);
26 col[3] = uchar4(133, 55, 31, 15);
27 col[8] = uchar4(133, 55, 31, 17);
28 col[9] = uchar4(133, 55, 31, 19);
29
30 col[4] = uchar4(50, 200, 0, 255);
31 col[5] = uchar4(55, 0, 32, 254);
32 col[10] = uchar4(56, 0, 64, 253);
33 col[11] = uchar4(57, 0, 96, 252);
34
35 return img;
36}
37
38static ImBuf *create_6x2_test_image_fl(int channels)
39{
40 ImBuf *img = IMB_allocImBuf(6, 2, 32, IB_rectfloat);
41 img->channels = channels;
42 float *col = img->float_buffer.data;
43
44 for (int y = 0; y < img->y; y++) {
45 for (int x = 0; x < img->x; x++) {
46 for (int ch = 0; ch < channels; ch++) {
47 *col = x * 1.25f + y * 0.5f + ch * 0.125f;
48 col++;
49 }
50 }
51 }
52 return img;
53}
54
55static ImBuf *scale_2x_smaller(bool nearest, bool threaded, int float_channels = 0)
56{
57 ImBuf *img = float_channels > 0 ? create_6x2_test_image_fl(float_channels) :
59 int ww = 3, hh = 1;
60 if (threaded) {
61 IMB_scale(img, ww, hh, IMBScaleFilter::Bilinear, true);
62 }
63 else if (nearest) {
64 IMB_scale(img, ww, hh, IMBScaleFilter::Nearest, false);
65 }
66 else {
67 IMB_scale(img, ww, hh, IMBScaleFilter::Box, false);
68 }
69 return img;
70}
71
72static ImBuf *scale_to_1x1(bool nearest, bool threaded, int float_channels = 0)
73{
74 ImBuf *img = float_channels > 0 ? create_6x2_test_image_fl(float_channels) :
76 int ww = 1, hh = 1;
77 if (threaded) {
78 IMB_scale(img, ww, hh, IMBScaleFilter::Bilinear, true);
79 }
80 else if (nearest) {
81 IMB_scale(img, ww, hh, IMBScaleFilter::Nearest, false);
82 }
83 else {
84 IMB_scale(img, ww, hh, IMBScaleFilter::Box, false);
85 }
86 return img;
87}
88
89static ImBuf *scale_fractional_larger(bool nearest, bool threaded, int float_channels = 0)
90{
91 ImBuf *img = float_channels > 0 ? create_6x2_test_image_fl(float_channels) :
93 int ww = 9, hh = 7;
94 if (threaded) {
95 IMB_scale(img, ww, hh, IMBScaleFilter::Bilinear, true);
96 }
97 else if (nearest) {
98 IMB_scale(img, ww, hh, IMBScaleFilter::Nearest, false);
99 }
100 else {
101 IMB_scale(img, ww, hh, IMBScaleFilter::Box, false);
102 }
103 return img;
104}
105
106TEST(imbuf_scaling, nearest_2x_smaller)
107{
108 ImBuf *res = scale_2x_smaller(true, false);
109 const uchar4 *got = reinterpret_cast<uchar4 *>(res->byte_buffer.data);
110 EXPECT_EQ(uint4(got[0]), uint4(0, 0, 0, 255));
111 EXPECT_EQ(uint4(got[1]), uint4(133, 55, 31, 13));
112 EXPECT_EQ(uint4(got[2]), uint4(50, 200, 0, 255));
113 IMB_freeImBuf(res);
114}
115
116TEST(imbuf_scaling, threaded_2x_smaller)
117{
118 ImBuf *res = scale_2x_smaller(false, true);
119 const uchar4 *got = reinterpret_cast<uchar4 *>(res->byte_buffer.data);
120 EXPECT_EQ(uint4(got[0]), uint4(191, 128, 64, 255));
121 EXPECT_EQ(uint4(got[1]), uint4(133, 55, 31, 16));
122 EXPECT_EQ(uint4(got[2]), uint4(55, 50, 48, 254));
123 IMB_freeImBuf(res);
124}
125
126TEST(imbuf_scaling, bilinear_2x_smaller)
127{
128 ImBuf *res = scale_2x_smaller(false, false);
129 const uchar4 *got = reinterpret_cast<uchar4 *>(res->byte_buffer.data);
130 /* NOTE: #IMB_transform results in (191, 128, 64, 255), <same>,
131 * (55, 50, 48, 254) i.e. different rounding. */
132 EXPECT_EQ(uint4(got[0]), uint4(191, 127, 63, 255));
133 EXPECT_EQ(uint4(got[1]), uint4(133, 55, 31, 16));
134 EXPECT_EQ(uint4(got[2]), uint4(55, 50, 48, 253));
135 IMB_freeImBuf(res);
136}
137
138TEST(imbuf_scaling, nearest_to_1x1)
139{
140 ImBuf *res = scale_to_1x1(true, false);
141 const uchar4 *got = reinterpret_cast<uchar4 *>(res->byte_buffer.data);
142 EXPECT_EQ(uint4(got[0]), uint4(0, 0, 0, 255));
143 IMB_freeImBuf(res);
144}
145
146TEST(imbuf_scaling, threaded_to_1x1)
147{
148 ImBuf *res = scale_to_1x1(false, true);
149 const uchar4 *got = reinterpret_cast<uchar4 *>(res->byte_buffer.data);
150 EXPECT_EQ(uint4(got[0]), uint4(133, 55, 31, 16));
151 IMB_freeImBuf(res);
152}
153
154TEST(imbuf_scaling, bilinear_to_1x1)
155{
156 ImBuf *res = scale_to_1x1(false, false);
157 const uchar4 *got = reinterpret_cast<uchar4 *>(res->byte_buffer.data);
158 EXPECT_EQ(uint4(got[0]), uint4(126, 78, 47, 174));
159 IMB_freeImBuf(res);
160}
161
162TEST(imbuf_scaling, nearest_fractional_larger)
163{
164 ImBuf *res = scale_fractional_larger(true, false);
165 const uchar4 *got = reinterpret_cast<uchar4 *>(res->byte_buffer.data);
166 EXPECT_EQ(uint4(got[0 + 0 * res->x]), uint4(0, 0, 0, 255));
167 EXPECT_EQ(uint4(got[1 + 0 * res->x]), uint4(0, 0, 0, 255));
168 EXPECT_EQ(uint4(got[7 + 0 * res->x]), uint4(50, 200, 0, 255));
169 EXPECT_EQ(uint4(got[2 + 2 * res->x]), uint4(255, 0, 0, 255));
170 EXPECT_EQ(uint4(got[3 + 2 * res->x]), uint4(133, 55, 31, 13));
171 EXPECT_EQ(uint4(got[8 + 6 * res->x]), uint4(57, 0, 96, 252));
172 IMB_freeImBuf(res);
173}
174
175TEST(imbuf_scaling, bilinear_fractional_larger)
176{
177 ImBuf *res = scale_fractional_larger(false, false);
178 const uchar4 *got = reinterpret_cast<uchar4 *>(res->byte_buffer.data);
179 EXPECT_EQ(uint4(got[0 + 0 * res->x]), uint4(0, 0, 0, 255));
180 EXPECT_EQ(uint4(got[1 + 0 * res->x]), uint4(127, 0, 0, 255));
181 EXPECT_EQ(uint4(got[7 + 0 * res->x]), uint4(52, 100, 16, 255));
182 EXPECT_EQ(uint4(got[2 + 2 * res->x]), uint4(235, 55, 51, 215));
183 EXPECT_EQ(uint4(got[3 + 2 * res->x]), uint4(153, 55, 35, 54));
184 EXPECT_EQ(uint4(got[8 + 5 * res->x]), uint4(57, 0, 91, 252));
185 EXPECT_EQ(uint4(got[0 + 6 * res->x]), uint4(164, 164, 0, 255));
186 EXPECT_EQ(uint4(got[7 + 6 * res->x]), uint4(55, 36, 57, 254));
187 EXPECT_EQ(uint4(got[8 + 6 * res->x]), uint4(56, 0, 73, 253));
188 IMB_freeImBuf(res);
189}
190
191static constexpr float EPS = 0.0001f;
192
193TEST(imbuf_scaling, nearest_2x_smaller_fl1)
194{
195 ImBuf *res = scale_2x_smaller(true, false, 1);
196 const float *got = res->float_buffer.data;
197 EXPECT_NEAR(got[0], 0.0f, EPS);
198 EXPECT_NEAR(got[1], 2.5f, EPS);
199 EXPECT_NEAR(got[2], 5.0f, EPS);
200 IMB_freeImBuf(res);
201}
202
203TEST(imbuf_scaling, nearest_2x_smaller_fl2)
204{
205 ImBuf *res = scale_2x_smaller(true, false, 2);
206 const float2 *got = reinterpret_cast<float2 *>(res->float_buffer.data);
207 EXPECT_V2_NEAR(got[0], float2(0.0f, 0.125f), EPS);
208 EXPECT_V2_NEAR(got[1], float2(2.5f, 2.625f), EPS);
209 EXPECT_V2_NEAR(got[2], float2(5.0f, 5.125f), EPS);
210 IMB_freeImBuf(res);
211}
212
213TEST(imbuf_scaling, nearest_2x_smaller_fl3)
214{
215 ImBuf *res = scale_2x_smaller(true, false, 3);
216 const float3 *got = reinterpret_cast<float3 *>(res->float_buffer.data);
217 EXPECT_V3_NEAR(got[0], float3(0.0f, 0.125f, 0.25f), EPS);
218 EXPECT_V3_NEAR(got[1], float3(2.5f, 2.625f, 2.75f), EPS);
219 EXPECT_V3_NEAR(got[2], float3(5.0f, 5.125f, 5.25f), EPS);
220 IMB_freeImBuf(res);
221}
222
223TEST(imbuf_scaling, nearest_2x_smaller_fl4)
224{
225 ImBuf *res = scale_2x_smaller(true, false, 4);
226 const float4 *got = reinterpret_cast<float4 *>(res->float_buffer.data);
227 EXPECT_V4_NEAR(got[0], float4(0.0f, 0.125f, 0.25f, 0.375f), EPS);
228 EXPECT_V4_NEAR(got[1], float4(2.5f, 2.625f, 2.75f, 2.875f), EPS);
229 EXPECT_V4_NEAR(got[2], float4(5.0f, 5.125f, 5.25f, 5.375f), EPS);
230 IMB_freeImBuf(res);
231}
232
233TEST(imbuf_scaling, nearest_to_1x1_fl3)
234{
235 ImBuf *res = scale_to_1x1(true, false, 3);
236 const float3 *got = reinterpret_cast<float3 *>(res->float_buffer.data);
237 EXPECT_V3_NEAR(got[0], float3(0, 0.125f, 0.25f), EPS);
238 IMB_freeImBuf(res);
239}
240
241TEST(imbuf_scaling, threaded_to_1x1_fl3)
242{
243 ImBuf *res = scale_to_1x1(false, true, 3);
244 const float3 *got = reinterpret_cast<float3 *>(res->float_buffer.data);
245 EXPECT_V3_NEAR(got[0], float3(3.375f, 3.5f, 3.625f), EPS);
246 IMB_freeImBuf(res);
247}
248
249TEST(imbuf_scaling, bilinear_to_1x1_fl3)
250{
251 ImBuf *res = scale_to_1x1(false, false, 3);
252 const float3 *got = reinterpret_cast<float3 *>(res->float_buffer.data);
253 EXPECT_V3_NEAR(got[0], float3(3.36853f, 3.49353f, 3.61853f), EPS);
254 IMB_freeImBuf(res);
255}
256
257TEST(imbuf_scaling, bilinear_2x_smaller_fl3)
258{
259 ImBuf *res = scale_2x_smaller(false, false, 3);
260 const float3 *got = reinterpret_cast<float3 *>(res->float_buffer.data);
261 EXPECT_V3_NEAR(got[0], float3(0.87270f, 0.99770f, 1.12270f), EPS);
262 EXPECT_V3_NEAR(got[1], float3(3.36853f, 3.49353f, 3.61853f), EPS);
263 EXPECT_V3_NEAR(got[2], float3(5.86435f, 5.98935f, 6.11435f), EPS);
264 IMB_freeImBuf(res);
265}
266
267TEST(imbuf_scaling, bilinear_2x_smaller_fl4)
268{
269 ImBuf *res = scale_2x_smaller(false, false, 4);
270 const float4 *got = reinterpret_cast<float4 *>(res->float_buffer.data);
271 EXPECT_V4_NEAR(got[0], float4(0.87270f, 0.99770f, 1.12270f, 1.24770f), EPS);
272 EXPECT_V4_NEAR(got[1], float4(3.36853f, 3.49353f, 3.61853f, 3.74353f), EPS);
273 EXPECT_V4_NEAR(got[2], float4(5.86435f, 5.98935f, 6.11435f, 6.23935f), EPS);
274 IMB_freeImBuf(res);
275}
276
277TEST(imbuf_scaling, threaded_2x_smaller_fl3)
278{
279 ImBuf *res = scale_2x_smaller(false, true, 3);
280 const float3 *got = reinterpret_cast<float3 *>(res->float_buffer.data);
281 EXPECT_V3_NEAR(got[0], float3(0.875f, 1.0f, 1.125f), EPS);
282 EXPECT_V3_NEAR(got[1], float3(3.375f, 3.5f, 3.625f), EPS);
283 EXPECT_V3_NEAR(got[2], float3(5.875f, 6.0f, 6.125f), EPS);
284 IMB_freeImBuf(res);
285}
286
287TEST(imbuf_scaling, threaded_2x_smaller_fl4)
288{
289 ImBuf *res = scale_2x_smaller(false, true, 4);
290 const float4 *got = reinterpret_cast<float4 *>(res->float_buffer.data);
291 EXPECT_V4_NEAR(got[0], float4(0.875f, 1.0f, 1.125f, 1.25f), EPS);
292 EXPECT_V4_NEAR(got[1], float4(3.375f, 3.5f, 3.625f, 3.75f), EPS);
293 EXPECT_V4_NEAR(got[2], float4(5.875f, 6.0f, 6.125f, 6.25f), EPS);
294 IMB_freeImBuf(res);
295}
296
297} // namespace blender::imbuf::tests
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:779
@ IB_rectfloat
@ IB_rect
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a producing a negative Combine Generate a color from its and blue channels(Deprecated)") DefNode(ShaderNode
uint col
struct ImBuf * IMB_allocImBuf(unsigned int, unsigned int, unsigned char, unsigned int)
void IMB_freeImBuf(ImBuf *)
TEST(imbuf_scaling, nearest_2x_smaller)
static constexpr float EPS
static ImBuf * create_6x2_test_image_fl(int channels)
static ImBuf * create_6x2_test_image()
static ImBuf * scale_fractional_larger(bool nearest, bool threaded, int float_channels=0)
static ImBuf * scale_2x_smaller(bool nearest, bool threaded, int float_channels=0)
static ImBuf * scale_to_1x1(bool nearest, bool threaded, int float_channels=0)
VecBase< uint32_t, 4 > uint4
blender::VecBase< uint8_t, 4 > uchar4
VecBase< float, 4 > float4
VecBase< float, 2 > float2
VecBase< float, 3 > float3
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer