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