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
9
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) {
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));
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
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.
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 color
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)
DO_INLINE void filter(lfVector *V, fmatrix3x3 *S)
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)
#define T
static void add(blender::Map< std::string, std::string > &messages, Message &msg)
Definition msgfmt.cc:227
uchar4 interpolate_bilinear_byte(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:53
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)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
blender::VecBase< uint8_t, 4 > uchar4
VecBase< float, 4 > float4
VecBase< float, 2 > float2
VecBase< float, 3 > float3
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