Blender V4.3
scaling.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 * SPDX-FileCopyrightText: 2024 Blender Authors
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later */
5
10#include <cmath>
11
12#include "BLI_math_vector.hh"
13#include "BLI_task.hh"
14#include "BLI_utildefines.h"
15#include "MEM_guardedalloc.h"
16
17#include "IMB_filter.hh"
18#include "IMB_imbuf.hh"
19#include "IMB_imbuf_types.hh"
20#include "IMB_interp.hh"
21
22#include "BLI_sys_types.h" /* for intptr_t support */
23
24using blender::float2;
25using blender::float3;
26using blender::float4;
27using blender::uchar4;
28
29static void imb_half_x_no_alloc(ImBuf *ibuf2, ImBuf *ibuf1)
30{
31 uchar *p1, *_p1, *dest;
32 short a, r, g, b;
33 int x, y;
34 float af, rf, gf, bf, *p1f, *_p1f, *destf;
35 bool do_rect, do_float;
36
37 do_rect = (ibuf1->byte_buffer.data != nullptr);
38 do_float = (ibuf1->float_buffer.data != nullptr && ibuf2->float_buffer.data != nullptr);
39
40 _p1 = ibuf1->byte_buffer.data;
41 dest = ibuf2->byte_buffer.data;
42
43 _p1f = ibuf1->float_buffer.data;
44 destf = ibuf2->float_buffer.data;
45
46 for (y = ibuf2->y; y > 0; y--) {
47 p1 = _p1;
48 p1f = _p1f;
49 for (x = ibuf2->x; x > 0; x--) {
50 if (do_rect) {
51 a = *(p1++);
52 b = *(p1++);
53 g = *(p1++);
54 r = *(p1++);
55 a += *(p1++);
56 b += *(p1++);
57 g += *(p1++);
58 r += *(p1++);
59 *(dest++) = a >> 1;
60 *(dest++) = b >> 1;
61 *(dest++) = g >> 1;
62 *(dest++) = r >> 1;
63 }
64 if (do_float) {
65 af = *(p1f++);
66 bf = *(p1f++);
67 gf = *(p1f++);
68 rf = *(p1f++);
69 af += *(p1f++);
70 bf += *(p1f++);
71 gf += *(p1f++);
72 rf += *(p1f++);
73 *(destf++) = 0.5f * af;
74 *(destf++) = 0.5f * bf;
75 *(destf++) = 0.5f * gf;
76 *(destf++) = 0.5f * rf;
77 }
78 }
79 if (do_rect) {
80 _p1 += (ibuf1->x << 2);
81 }
82 if (do_float) {
83 _p1f += (ibuf1->x << 2);
84 }
85 }
86}
87
89{
90 ImBuf *ibuf2;
91
92 if (ibuf1 == nullptr) {
93 return nullptr;
94 }
95 if (ibuf1->byte_buffer.data == nullptr && ibuf1->float_buffer.data == nullptr) {
96 return nullptr;
97 }
98
99 if (ibuf1->x <= 1) {
100 return IMB_dupImBuf(ibuf1);
101 }
102
103 ibuf2 = IMB_allocImBuf((ibuf1->x) / 2, ibuf1->y, ibuf1->planes, ibuf1->flags);
104 if (ibuf2 == nullptr) {
105 return nullptr;
106 }
107
108 imb_half_x_no_alloc(ibuf2, ibuf1);
109
110 return ibuf2;
111}
112
113static void imb_half_y_no_alloc(ImBuf *ibuf2, ImBuf *ibuf1)
114{
115 uchar *p1, *p2, *_p1, *dest;
116 short a, r, g, b;
117 int x, y;
118 float af, rf, gf, bf, *p1f, *p2f, *_p1f, *destf;
119
120 p1 = p2 = nullptr;
121 p1f = p2f = nullptr;
122
123 const bool do_rect = (ibuf1->byte_buffer.data != nullptr);
124 const bool do_float = (ibuf1->float_buffer.data != nullptr &&
125 ibuf2->float_buffer.data != nullptr);
126
127 _p1 = ibuf1->byte_buffer.data;
128 dest = ibuf2->byte_buffer.data;
129 _p1f = (float *)ibuf1->float_buffer.data;
130 destf = (float *)ibuf2->float_buffer.data;
131
132 for (y = ibuf2->y; y > 0; y--) {
133 if (do_rect) {
134 p1 = _p1;
135 p2 = _p1 + (ibuf1->x << 2);
136 }
137 if (do_float) {
138 p1f = _p1f;
139 p2f = _p1f + (ibuf1->x << 2);
140 }
141 for (x = ibuf2->x; x > 0; x--) {
142 if (do_rect) {
143 a = *(p1++);
144 b = *(p1++);
145 g = *(p1++);
146 r = *(p1++);
147 a += *(p2++);
148 b += *(p2++);
149 g += *(p2++);
150 r += *(p2++);
151 *(dest++) = a >> 1;
152 *(dest++) = b >> 1;
153 *(dest++) = g >> 1;
154 *(dest++) = r >> 1;
155 }
156 if (do_float) {
157 af = *(p1f++);
158 bf = *(p1f++);
159 gf = *(p1f++);
160 rf = *(p1f++);
161 af += *(p2f++);
162 bf += *(p2f++);
163 gf += *(p2f++);
164 rf += *(p2f++);
165 *(destf++) = 0.5f * af;
166 *(destf++) = 0.5f * bf;
167 *(destf++) = 0.5f * gf;
168 *(destf++) = 0.5f * rf;
169 }
170 }
171 if (do_rect) {
172 _p1 += (ibuf1->x << 3);
173 }
174 if (do_float) {
175 _p1f += (ibuf1->x << 3);
176 }
177 }
178}
179
181{
182 ImBuf *ibuf2;
183
184 if (ibuf1 == nullptr) {
185 return nullptr;
186 }
187 if (ibuf1->byte_buffer.data == nullptr && ibuf1->float_buffer.data == nullptr) {
188 return nullptr;
189 }
190
191 if (ibuf1->y <= 1) {
192 return IMB_dupImBuf(ibuf1);
193 }
194
195 ibuf2 = IMB_allocImBuf(ibuf1->x, (ibuf1->y) / 2, ibuf1->planes, ibuf1->flags);
196 if (ibuf2 == nullptr) {
197 return nullptr;
198 }
199
200 imb_half_y_no_alloc(ibuf2, ibuf1);
201
202 return ibuf2;
203}
204
205/* pretty much specific functions which converts uchar <-> ushort but assumes
206 * ushort range of 255*255 which is more convenient here
207 */
209{
210 ushort alpha = color[3];
211
212 result[0] = color[0] * alpha;
213 result[1] = color[1] * alpha;
214 result[2] = color[2] * alpha;
215 result[3] = alpha * 256;
216}
217
219{
220 if (color[3] <= 255) {
221 result[0] = unit_ushort_to_uchar(color[0]);
222 result[1] = unit_ushort_to_uchar(color[1]);
223 result[2] = unit_ushort_to_uchar(color[2]);
224 result[3] = unit_ushort_to_uchar(color[3]);
225 }
226 else {
227 ushort alpha = color[3] / 256;
228
229 result[0] = unit_ushort_to_uchar(ushort(color[0] / alpha * 256));
230 result[1] = unit_ushort_to_uchar(ushort(color[1] / alpha * 256));
231 result[2] = unit_ushort_to_uchar(ushort(color[2] / alpha * 256));
232 result[3] = unit_ushort_to_uchar(color[3]);
233 }
234}
235
236void imb_onehalf_no_alloc(ImBuf *ibuf2, ImBuf *ibuf1)
237{
238 int x, y;
239 const bool do_rect = (ibuf1->byte_buffer.data != nullptr);
240 const bool do_float = (ibuf1->float_buffer.data != nullptr) &&
241 (ibuf2->float_buffer.data != nullptr);
242
243 if (do_rect && (ibuf2->byte_buffer.data == nullptr)) {
244 imb_addrectImBuf(ibuf2);
245 }
246
247 if (ibuf1->x <= 1) {
248 imb_half_y_no_alloc(ibuf2, ibuf1);
249 return;
250 }
251 if (ibuf1->y <= 1) {
252 imb_half_x_no_alloc(ibuf2, ibuf1);
253 return;
254 }
255
256 if (do_rect) {
257 uchar *cp1, *cp2, *dest;
258
259 cp1 = ibuf1->byte_buffer.data;
260 dest = ibuf2->byte_buffer.data;
261
262 for (y = ibuf2->y; y > 0; y--) {
263 cp2 = cp1 + (ibuf1->x << 2);
264 for (x = ibuf2->x; x > 0; x--) {
265 ushort p1i[8], p2i[8], desti[4];
266
269 straight_uchar_to_premul_ushort(p1i + 4, cp1 + 4);
270 straight_uchar_to_premul_ushort(p2i + 4, cp2 + 4);
271
272 desti[0] = (uint(p1i[0]) + p2i[0] + p1i[4] + p2i[4]) >> 2;
273 desti[1] = (uint(p1i[1]) + p2i[1] + p1i[5] + p2i[5]) >> 2;
274 desti[2] = (uint(p1i[2]) + p2i[2] + p1i[6] + p2i[6]) >> 2;
275 desti[3] = (uint(p1i[3]) + p2i[3] + p1i[7] + p2i[7]) >> 2;
276
278
279 cp1 += 8;
280 cp2 += 8;
281 dest += 4;
282 }
283 cp1 = cp2;
284 if (ibuf1->x & 1) {
285 cp1 += 4;
286 }
287 }
288 }
289
290 if (do_float) {
291 float *p1f, *p2f, *destf;
292
293 p1f = ibuf1->float_buffer.data;
294 destf = ibuf2->float_buffer.data;
295 for (y = ibuf2->y; y > 0; y--) {
296 p2f = p1f + (ibuf1->x << 2);
297 for (x = ibuf2->x; x > 0; x--) {
298 destf[0] = 0.25f * (p1f[0] + p2f[0] + p1f[4] + p2f[4]);
299 destf[1] = 0.25f * (p1f[1] + p2f[1] + p1f[5] + p2f[5]);
300 destf[2] = 0.25f * (p1f[2] + p2f[2] + p1f[6] + p2f[6]);
301 destf[3] = 0.25f * (p1f[3] + p2f[3] + p1f[7] + p2f[7]);
302 p1f += 8;
303 p2f += 8;
304 destf += 4;
305 }
306 p1f = p2f;
307 if (ibuf1->x & 1) {
308 p1f += 4;
309 }
310 }
311 }
312}
313
315{
316 ImBuf *ibuf2;
317
318 if (ibuf1 == nullptr) {
319 return nullptr;
320 }
321 if (ibuf1->byte_buffer.data == nullptr && ibuf1->float_buffer.data == nullptr) {
322 return nullptr;
323 }
324
325 if (ibuf1->x <= 1) {
326 return IMB_half_y(ibuf1);
327 }
328 if (ibuf1->y <= 1) {
329 return IMB_half_x(ibuf1);
330 }
331
332 ibuf2 = IMB_allocImBuf((ibuf1->x) / 2, (ibuf1->y) / 2, ibuf1->planes, ibuf1->flags);
333 if (ibuf2 == nullptr) {
334 return nullptr;
335 }
336
337 imb_onehalf_no_alloc(ibuf2, ibuf1);
338
339 return ibuf2;
340}
341
343 const ImBuf *ibuf, uint newx, uint newy, uchar4 **r_dst_byte, float **r_dst_float)
344{
345 *r_dst_byte = nullptr;
346 if (ibuf->byte_buffer.data != nullptr) {
347 *r_dst_byte = static_cast<uchar4 *>(
348 MEM_mallocN(sizeof(uchar4) * newx * newy, "scale_buf_byte"));
349 if (*r_dst_byte == nullptr) {
350 return;
351 }
352 }
353 *r_dst_float = nullptr;
354 if (ibuf->float_buffer.data != nullptr) {
355 *r_dst_float = static_cast<float *>(
356 MEM_mallocN(sizeof(float) * ibuf->channels * newx * newy, "scale_buf_float"));
357 if (*r_dst_float == nullptr) {
358 if (*r_dst_byte) {
359 MEM_freeN(*r_dst_byte);
360 }
361 return;
362 }
363 }
364}
365
366static inline float4 load_pixel(const uchar4 *ptr)
367{
368 return float4(ptr[0]);
369}
370static inline float4 load_pixel(const float *ptr)
371{
372 return float4(ptr[0]);
373}
374static inline float4 load_pixel(const float2 *ptr)
375{
376 return float4(ptr[0]);
377}
378static inline float4 load_pixel(const float3 *ptr)
379{
380 return float4(ptr[0]);
381}
382static inline float4 load_pixel(const float4 *ptr)
383{
384 return float4(ptr[0]);
385}
386static inline void store_pixel(float4 pix, uchar4 *ptr)
387{
389}
390static inline void store_pixel(float4 pix, float *ptr)
391{
392 *ptr = pix.x;
393}
394static inline void store_pixel(float4 pix, float2 *ptr)
395{
396 memcpy(ptr, &pix, sizeof(*ptr));
397}
398static inline void store_pixel(float4 pix, float3 *ptr)
399{
400 memcpy(ptr, &pix, sizeof(*ptr));
401}
402static inline void store_pixel(float4 pix, float4 *ptr)
403{
404 *ptr = pix;
405}
406
408 template<typename T>
409 static void op(const T *src, T *dst, int ibufx, int ibufy, int newx, int /*newy*/, bool threaded)
410 {
411 using namespace blender;
412 const float add = (ibufx - 0.01f) / newx;
413 const float inv_add = 1.0f / add;
414
415 const int grain_size = threaded ? 32 : ibufy;
416 threading::parallel_for(IndexRange(ibufy), grain_size, [&](IndexRange range) {
417 for (const int y : range) {
418 const T *src_ptr = src + y * ibufx;
419 T *dst_ptr = dst + y * newx;
420 float sample = 0.0f;
421 float4 val(0.0f);
422
423 for (int x = 0; x < newx; x++) {
424 float4 nval = -val * sample;
425 sample += add;
426 while (sample >= 1.0f) {
427 sample -= 1.0f;
428 nval += load_pixel(src_ptr);
429 src_ptr++;
430 }
431
432 val = load_pixel(src_ptr);
433 src_ptr++;
434
435 float4 pix = (nval + sample * val) * inv_add;
436 store_pixel(pix, dst_ptr);
437 dst_ptr++;
438
439 sample -= 1.0f;
440 }
441 }
442 });
443 }
444};
445
447 template<typename T>
448 static void op(const T *src, T *dst, int ibufx, int ibufy, int /*newx*/, int newy, bool threaded)
449 {
450 using namespace blender;
451 const float add = (ibufy - 0.01f) / newy;
452 const float inv_add = 1.0f / add;
453
454 const int grain_size = threaded ? 32 : ibufx;
455 threading::parallel_for(IndexRange(ibufx), grain_size, [&](IndexRange range) {
456 for (const int x : range) {
457 const T *src_ptr = src + x;
458 T *dst_ptr = dst + x;
459 float sample = 0.0f;
460 float4 val(0.0f);
461
462 for (int y = 0; y < newy; y++) {
463 float4 nval = -val * sample;
464 sample += add;
465 while (sample >= 1.0f) {
466 sample -= 1.0f;
467 nval += load_pixel(src_ptr);
468 src_ptr += ibufx;
469 }
470
471 val = load_pixel(src_ptr);
472 src_ptr += ibufx;
473
474 float4 pix = (nval + sample * val) * inv_add;
475 store_pixel(pix, dst_ptr);
476 dst_ptr += ibufx;
477
478 sample -= 1.0f;
479 }
480 }
481 });
482 }
483};
484
485struct ScaleUpX {
486 template<typename T>
487 static void op(const T *src, T *dst, int ibufx, int ibufy, int newx, int /*newy*/, bool threaded)
488 {
489 using namespace blender;
490 const float add = (ibufx - 0.001f) / newx;
491 /* Special case: source is 1px wide (see #70356). */
492 if (UNLIKELY(ibufx == 1)) {
493 for (int y = ibufy; y > 0; y--) {
494 for (int x = newx; x > 0; x--) {
495 *dst = *src;
496 dst++;
497 }
498 src++;
499 }
500 }
501 else {
502 const int grain_size = threaded ? 32 : ibufy;
503 threading::parallel_for(IndexRange(ibufy), grain_size, [&](IndexRange range) {
504 for (const int y : range) {
505 float sample = -0.5f + add * 0.5f;
506 int counter = 0;
507 const T *src_ptr = src + y * ibufx;
508 T *dst_ptr = dst + y * newx;
509 float4 val = load_pixel(src_ptr);
510 float4 nval = load_pixel(src_ptr + 1);
511 float4 diff = nval - val;
512 if (ibufx > 2) {
513 src_ptr += 2;
514 counter += 2;
515 }
516 for (int x = 0; x < newx; x++) {
517 if (sample >= 1.0f) {
518 sample -= 1.0f;
519 val = nval;
520 nval = load_pixel(src_ptr);
521 diff = nval - val;
522 if (counter + 1 < ibufx) {
523 src_ptr++;
524 counter++;
525 }
526 }
527 float4 pix = val + blender::math::max(sample, 0.0f) * diff;
528 store_pixel(pix, dst_ptr);
529 dst_ptr++;
530 sample += add;
531 }
532 }
533 });
534 }
535 }
536};
537
538struct ScaleUpY {
539 template<typename T>
540 static void op(const T *src, T *dst, int ibufx, int ibufy, int /*newx*/, int newy, bool threaded)
541 {
542 using namespace blender;
543 const float add = (ibufy - 0.001f) / newy;
544 /* Special case: source is 1px high (see #70356). */
545 if (UNLIKELY(ibufy == 1)) {
546 for (int y = newy; y > 0; y--) {
547 memcpy(dst, src, sizeof(T) * ibufx);
548 dst += ibufx;
549 }
550 }
551 else {
552 const int grain_size = threaded ? 32 : ibufx;
553 threading::parallel_for(IndexRange(ibufx), grain_size, [&](IndexRange range) {
554 for (const int x : range) {
555 float sample = -0.5f + add * 0.5f;
556 int counter = 0;
557 const T *src_ptr = src + x;
558 T *dst_ptr = dst + x;
559
560 float4 val = load_pixel(src_ptr);
561 float4 nval = load_pixel(src_ptr + ibufx);
562 float4 diff = nval - val;
563 if (ibufy > 2) {
564 src_ptr += ibufx * 2;
565 counter += 2;
566 }
567
568 for (int y = 0; y < newy; y++) {
569 if (sample >= 1.0f) {
570 sample -= 1.0f;
571 val = nval;
572 nval = load_pixel(src_ptr);
573 diff = nval - val;
574 if (counter + 1 < ibufy) {
575 src_ptr += ibufx;
576 ++counter;
577 }
578 }
579 float4 pix = val + blender::math::max(sample, 0.0f) * diff;
580 store_pixel(pix, dst_ptr);
581 dst_ptr += ibufx;
582 sample += add;
583 }
584 }
585 });
586 }
587 }
588};
589
590template<typename T>
591static void instantiate_pixel_op(T & /*op*/,
592 const ImBuf *ibuf,
593 int newx,
594 int newy,
595 uchar4 *dst_byte,
596 float *dst_float,
597 bool threaded)
598{
599 if (dst_byte != nullptr) {
600 const uchar4 *src = (const uchar4 *)ibuf->byte_buffer.data;
601 T::op(src, dst_byte, ibuf->x, ibuf->y, newx, newy, threaded);
602 }
603 if (dst_float != nullptr) {
604 if (ibuf->channels == 1) {
605 T::op(ibuf->float_buffer.data, dst_float, ibuf->x, ibuf->y, newx, newy, threaded);
606 }
607 else if (ibuf->channels == 2) {
608 const float2 *src = (const float2 *)ibuf->float_buffer.data;
609 T::op(src, (float2 *)dst_float, ibuf->x, ibuf->y, newx, newy, threaded);
610 }
611 else if (ibuf->channels == 3) {
612 const float3 *src = (const float3 *)ibuf->float_buffer.data;
613 T::op(src, (float3 *)dst_float, ibuf->x, ibuf->y, newx, newy, threaded);
614 }
615 else if (ibuf->channels == 4) {
616 const float4 *src = (const float4 *)ibuf->float_buffer.data;
617 T::op(src, (float4 *)dst_float, ibuf->x, ibuf->y, newx, newy, threaded);
618 }
619 }
620}
621
623 const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
624{
625 ScaleDownX op;
626 instantiate_pixel_op(op, ibuf, newx, newy, dst_byte, dst_float, threaded);
627}
628
630 const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
631{
632 ScaleDownY op;
633 instantiate_pixel_op(op, ibuf, newx, newy, dst_byte, dst_float, threaded);
634}
635
636static void scale_up_x_func(
637 const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
638{
639 ScaleUpX op;
640 instantiate_pixel_op(op, ibuf, newx, newy, dst_byte, dst_float, threaded);
641}
642
643static void scale_up_y_func(
644 const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
645{
646 ScaleUpY op;
647 instantiate_pixel_op(op, ibuf, newx, newy, dst_byte, dst_float, threaded);
648}
649
650using ScaleFunction = void (*)(
651 const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded);
652
653static void scale_with_function(ImBuf *ibuf, int newx, int newy, ScaleFunction func, bool threaded)
654{
655 /* Allocate destination buffers. */
656 uchar4 *dst_byte = nullptr;
657 float *dst_float = nullptr;
658 alloc_scale_dst_buffers(ibuf, newx, newy, &dst_byte, &dst_float);
659 if (dst_byte == nullptr && dst_float == nullptr) {
660 return;
661 }
662
663 /* Do actual processing. */
664 func(ibuf, newx, newy, dst_byte, dst_float, threaded);
665
666 /* Modify image to point to new destination. */
667 if (dst_byte != nullptr) {
668 imb_freerectImBuf(ibuf);
669 IMB_assign_byte_buffer(ibuf, reinterpret_cast<uint8_t *>(dst_byte), IB_TAKE_OWNERSHIP);
670 }
671 if (dst_float != nullptr) {
674 }
675 ibuf->x = newx;
676 ibuf->y = newy;
677}
678
679static void imb_scale_box(ImBuf *ibuf, uint newx, uint newy, bool threaded)
680{
681 if (newx != 0 && (newx < ibuf->x)) {
682 scale_with_function(ibuf, newx, ibuf->y, scale_down_x_func, threaded);
683 }
684 if (newy != 0 && (newy < ibuf->y)) {
685 scale_with_function(ibuf, ibuf->x, newy, scale_down_y_func, threaded);
686 }
687 if (newx != 0 && (newx > ibuf->x)) {
688 scale_with_function(ibuf, newx, ibuf->y, scale_up_x_func, threaded);
689 }
690 if (newy != 0 && (newy > ibuf->y)) {
691 scale_with_function(ibuf, ibuf->x, newy, scale_up_y_func, threaded);
692 }
693}
694
695template<typename T>
696static void scale_nearest(
697 const T *src, T *dst, int ibufx, int ibufy, int newx, int newy, blender::IndexRange y_range)
698{
699 /* Nearest sample scaling. Step through pixels in fixed point coordinates. */
700 constexpr int FRAC_BITS = 16;
701 int64_t stepx = ((int64_t(ibufx) << FRAC_BITS) + newx / 2) / newx;
702 int64_t stepy = ((int64_t(ibufy) << FRAC_BITS) + newy / 2) / newy;
703 int64_t posy = y_range.first() * stepy;
704 dst += y_range.first() * newx;
705 for (const int y : y_range) {
706 UNUSED_VARS(y);
707 const T *row = src + (posy >> FRAC_BITS) * ibufx;
708 int64_t posx = 0;
709 for (int x = 0; x < newx; x++, posx += stepx) {
710 *dst = row[posx >> FRAC_BITS];
711 dst++;
712 }
713 posy += stepy;
714 }
715}
716
718 const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
719{
720 using namespace blender;
721
722 const int grain_size = threaded ? 64 : newy;
723 threading::parallel_for(IndexRange(newy), grain_size, [&](IndexRange y_range) {
724 /* Byte pixels. */
725 if (dst_byte != nullptr) {
726 const uchar4 *src = (const uchar4 *)ibuf->byte_buffer.data;
727 scale_nearest(src, dst_byte, ibuf->x, ibuf->y, newx, newy, y_range);
728 }
729 /* Float pixels. */
730 if (dst_float != nullptr) {
731 if (ibuf->channels == 1) {
732 scale_nearest(ibuf->float_buffer.data, dst_float, ibuf->x, ibuf->y, newx, newy, y_range);
733 }
734 else if (ibuf->channels == 2) {
735 const float2 *src = (const float2 *)ibuf->float_buffer.data;
736 scale_nearest(src, (float2 *)dst_float, ibuf->x, ibuf->y, newx, newy, y_range);
737 }
738 else if (ibuf->channels == 3) {
739 const float3 *src = (const float3 *)ibuf->float_buffer.data;
740 scale_nearest(src, (float3 *)dst_float, ibuf->x, ibuf->y, newx, newy, y_range);
741 }
742 else if (ibuf->channels == 4) {
743 const float4 *src = (const float4 *)ibuf->float_buffer.data;
744 scale_nearest(src, (float4 *)dst_float, ibuf->x, ibuf->y, newx, newy, y_range);
745 }
746 }
747 });
748}
749
751 const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
752{
753 using namespace blender;
754 using namespace blender::imbuf;
755
756 const int grain_size = threaded ? 32 : newy;
757 threading::parallel_for(IndexRange(newy), grain_size, [&](IndexRange y_range) {
758 float factor_x = float(ibuf->x) / newx;
759 float factor_y = float(ibuf->y) / newy;
760
761 for (const int y : y_range) {
762 float v = (float(y) + 0.5f) * factor_y - 0.5f;
763 for (int x = 0; x < newx; x++) {
764 float u = (float(x) + 0.5f) * factor_x - 0.5f;
765 int64_t offset = int64_t(y) * newx + x;
766 if (dst_byte) {
767 interpolate_bilinear_byte(ibuf, (uchar *)(dst_byte + offset), u, v);
768 }
769 if (dst_float) {
770 float *pixel = dst_float + ibuf->channels * offset;
772 ibuf->float_buffer.data, pixel, ibuf->x, ibuf->y, ibuf->channels, u, v);
773 }
774 }
775 }
776 });
777}
778
779bool IMB_scale(ImBuf *ibuf, uint newx, uint newy, IMBScaleFilter filter, bool threaded)
780{
781 BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!");
782 if (ibuf == nullptr) {
783 return false;
784 }
785 if (newx == ibuf->x && newy == ibuf->y) {
786 return false;
787 }
788
789 if (filter == IMBScaleFilter::Nearest) {
790 scale_with_function(ibuf, newx, newy, scale_nearest_func, threaded);
791 }
792 else if (filter == IMBScaleFilter::Bilinear) {
793 scale_with_function(ibuf, newx, newy, scale_bilinear_func, threaded);
794 }
795 else if (filter == IMBScaleFilter::Box) {
796 imb_scale_box(ibuf, newx, newy, threaded);
797 }
798 else {
800 return false;
801 }
802 return true;
803}
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
#define MINLINE
unsigned char uchar
unsigned short ushort
unsigned int uint
#define UNUSED_VARS(...)
#define UNLIKELY(x)
Function declarations for filter.cc.
void imb_freerectImBuf(ImBuf *ibuf)
void imb_freerectfloatImBuf(ImBuf *ibuf)
ImBuf * IMB_dupImBuf(const ImBuf *ibuf1)
IMBScaleFilter
Definition IMB_imbuf.hh:404
void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, ImBufOwnership ownership)
bool imb_addrectImBuf(ImBuf *ibuf, bool initialize_pixels=true)
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, ImBufOwnership ownership)
Contains defines and structs used throughout the imbuf module.
@ IB_TAKE_OWNERSHIP
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert * v
constexpr int64_t first() const
local_group_size(16, 16) .push_constant(Type b
draw_view in_light_buf[] float
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
struct ImBuf * IMB_allocImBuf(unsigned int, unsigned int, unsigned char, unsigned int)
IndexRange range
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
#define unit_ushort_to_uchar(val)
static void add(blender::Map< std::string, std::string > &messages, Message &msg)
Definition msgfmt.cc:227
float4 interpolate_bilinear_fl(const float *buffer, int width, int height, float u, float v)
T max(const T &a, const T &b)
T round(const T &a)
MINLINE void straight_uchar_to_premul_ushort(ushort result[4], const uchar color[4])
Definition scaling.cc:208
static void alloc_scale_dst_buffers(const ImBuf *ibuf, uint newx, uint newy, uchar4 **r_dst_byte, float **r_dst_float)
Definition scaling.cc:342
ImBuf * IMB_onehalf(ImBuf *ibuf1)
Definition scaling.cc:314
static void scale_nearest(const T *src, T *dst, int ibufx, int ibufy, int newx, int newy, blender::IndexRange y_range)
Definition scaling.cc:696
bool IMB_scale(ImBuf *ibuf, uint newx, uint newy, IMBScaleFilter filter, bool threaded)
Definition scaling.cc:779
ImBuf * IMB_half_x(ImBuf *ibuf1)
Definition scaling.cc:88
static void scale_with_function(ImBuf *ibuf, int newx, int newy, ScaleFunction func, bool threaded)
Definition scaling.cc:653
ImBuf * IMB_half_y(ImBuf *ibuf1)
Definition scaling.cc:180
void imb_onehalf_no_alloc(ImBuf *ibuf2, ImBuf *ibuf1)
Definition scaling.cc:236
static void imb_scale_box(ImBuf *ibuf, uint newx, uint newy, bool threaded)
Definition scaling.cc:679
static void imb_half_x_no_alloc(ImBuf *ibuf2, ImBuf *ibuf1)
Definition scaling.cc:29
static void scale_up_y_func(const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
Definition scaling.cc:643
MINLINE void premul_ushort_to_straight_uchar(uchar *result, const ushort color[4])
Definition scaling.cc:218
static void scale_down_y_func(const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
Definition scaling.cc:629
static void store_pixel(float4 pix, uchar4 *ptr)
Definition scaling.cc:386
static void imb_half_y_no_alloc(ImBuf *ibuf2, ImBuf *ibuf1)
Definition scaling.cc:113
static void scale_bilinear_func(const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
Definition scaling.cc:750
static void scale_nearest_func(const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
Definition scaling.cc:717
void(*)( const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded) ScaleFunction
Definition scaling.cc:650
static void scale_down_x_func(const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
Definition scaling.cc:622
static void instantiate_pixel_op(T &, const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
Definition scaling.cc:591
static void scale_up_x_func(const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
Definition scaling.cc:636
static float4 load_pixel(const uchar4 *ptr)
Definition scaling.cc:366
__int64 int64_t
Definition stdint.h:89
unsigned char uint8_t
Definition stdint.h:78
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
unsigned char planes
static void op(const T *src, T *dst, int ibufx, int ibufy, int newx, int, bool threaded)
Definition scaling.cc:409
static void op(const T *src, T *dst, int ibufx, int ibufy, int, int newy, bool threaded)
Definition scaling.cc:448
static void op(const T *src, T *dst, int ibufx, int ibufy, int newx, int, bool threaded)
Definition scaling.cc:487
static void op(const T *src, T *dst, int ibufx, int ibufy, int, int newy, bool threaded)
Definition scaling.cc:540
PointerRNA * ptr
Definition wm_files.cc:4126