Blender V5.0
conversion.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_array.hh"
11#include "BLI_rect.h"
12#include "BLI_task.hh"
13
14#include "IMB_filter.hh"
15#include "IMB_imbuf.hh"
16#include "IMB_imbuf_types.hh"
17
20
21#include "MEM_guardedalloc.h"
22
23#include "OCIO_colorspace.hh"
24
25/* -------------------------------------------------------------------- */
26
29
30MINLINE void ushort_to_byte_v4(uchar b[4], const ushort us[4])
31{
32 b[0] = unit_ushort_to_uchar(us[0]);
33 b[1] = unit_ushort_to_uchar(us[1]);
34 b[2] = unit_ushort_to_uchar(us[2]);
35 b[3] = unit_ushort_to_uchar(us[3]);
36}
37
38MINLINE uchar ftochar(float value)
39{
40 return unit_float_to_uchar_clamp(value);
41}
42
43MINLINE void ushort_to_byte_dither_v4(uchar b[4], const ushort us[4], float dither, int x, int y)
44{
45#define USHORTTOFLOAT(val) (float(val) / 65535.0f)
46 float dither_value = dither_random_value(x, y) * 0.0033f * dither;
47
48 b[0] = ftochar(dither_value + USHORTTOFLOAT(us[0]));
49 b[1] = ftochar(dither_value + USHORTTOFLOAT(us[1]));
50 b[2] = ftochar(dither_value + USHORTTOFLOAT(us[2]));
51 b[3] = unit_ushort_to_uchar(us[3]);
52
53#undef USHORTTOFLOAT
54}
55
56MINLINE void float_to_byte_dither_v4(uchar b[4], const float f[4], float dither, int x, int y)
57{
58 float dither_value = dither_random_value(x, y) * 0.0033f * dither;
59
60 b[0] = ftochar(dither_value + f[0]);
61 b[1] = ftochar(dither_value + f[1]);
62 b[2] = ftochar(dither_value + f[2]);
64}
65
67{
68 return ibuf && (ibuf->flags & IB_alphamode_channel_packed) == 0;
69}
70
72 const float *rect_from,
73 int channels_from,
74 float dither,
75 int profile_to,
76 int profile_from,
77 bool predivide,
78 int width,
79 int height,
80 int stride_to,
81 int stride_from,
82 int start_y)
83{
84 float tmp[4];
85 int x, y;
86
87 /* we need valid profiles */
88 BLI_assert(profile_to != IB_PROFILE_NONE);
89 BLI_assert(profile_from != IB_PROFILE_NONE);
90
91 for (y = 0; y < height; y++) {
92 if (channels_from == 1) {
93 /* single channel input */
94 const float *from = rect_from + size_t(stride_from) * y;
95 uchar *to = rect_to + size_t(stride_to) * y * 4;
96
97 for (x = 0; x < width; x++, from++, to += 4) {
98 to[0] = to[1] = to[2] = to[3] = unit_float_to_uchar_clamp(from[0]);
99 }
100 }
101 else if (channels_from == 3) {
102 /* RGB input */
103 const float *from = rect_from + size_t(stride_from) * y * 3;
104 uchar *to = rect_to + size_t(stride_to) * y * 4;
105
106 if (profile_to == profile_from) {
107 /* no color space conversion */
108 for (x = 0; x < width; x++, from += 3, to += 4) {
109 rgb_float_to_uchar(to, from);
110 to[3] = 255;
111 }
112 }
113 else if (profile_to == IB_PROFILE_SRGB) {
114 /* convert from linear to sRGB */
115 for (x = 0; x < width; x++, from += 3, to += 4) {
116 linearrgb_to_srgb_v3_v3(tmp, from);
117 rgb_float_to_uchar(to, tmp);
118 to[3] = 255;
119 }
120 }
121 else if (profile_to == IB_PROFILE_LINEAR_RGB) {
122 /* convert from sRGB to linear */
123 for (x = 0; x < width; x++, from += 3, to += 4) {
124 srgb_to_linearrgb_v3_v3(tmp, from);
125 rgb_float_to_uchar(to, tmp);
126 to[3] = 255;
127 }
128 }
129 }
130 else if (channels_from == 4) {
131 /* RGBA input */
132 const float *from = rect_from + size_t(stride_from) * y * 4;
133 uchar *to = rect_to + size_t(stride_to) * y * 4;
134
135 if (profile_to == profile_from) {
136 /* no color space conversion */
137 if (dither && predivide) {
138 float straight[4];
139 for (x = 0; x < width; x++, from += 4, to += 4) {
140 premul_to_straight_v4_v4(straight, from);
141 float_to_byte_dither_v4(to, straight, dither, x, y + start_y);
142 }
143 }
144 else if (dither) {
145 for (x = 0; x < width; x++, from += 4, to += 4) {
146 float_to_byte_dither_v4(to, from, dither, x, y + start_y);
147 }
148 }
149 else if (predivide) {
150 for (x = 0; x < width; x++, from += 4, to += 4) {
152 }
153 }
154 else {
155 for (x = 0; x < width; x++, from += 4, to += 4) {
156 rgba_float_to_uchar(to, from);
157 }
158 }
159 }
160 else if (profile_to == IB_PROFILE_SRGB) {
161 /* convert from linear to sRGB */
162 ushort us[4];
163 float straight[4];
164
165 if (dither && predivide) {
166 for (x = 0; x < width; x++, from += 4, to += 4) {
167 premul_to_straight_v4_v4(straight, from);
169 ushort_to_byte_dither_v4(to, us, dither, x, y + start_y);
170 }
171 }
172 else if (dither) {
173 for (x = 0; x < width; x++, from += 4, to += 4) {
175 ushort_to_byte_dither_v4(to, us, dither, x, y + start_y);
176 }
177 }
178 else if (predivide) {
179 for (x = 0; x < width; x++, from += 4, to += 4) {
180 premul_to_straight_v4_v4(straight, from);
182 ushort_to_byte_v4(to, us);
183 }
184 }
185 else {
186 for (x = 0; x < width; x++, from += 4, to += 4) {
188 ushort_to_byte_v4(to, us);
189 }
190 }
191 }
192 else if (profile_to == IB_PROFILE_LINEAR_RGB) {
193 /* convert from sRGB to linear */
194 if (dither && predivide) {
195 for (x = 0; x < width; x++, from += 4, to += 4) {
197 float_to_byte_dither_v4(to, tmp, dither, x, y + start_y);
198 }
199 }
200 else if (dither) {
201 for (x = 0; x < width; x++, from += 4, to += 4) {
202 srgb_to_linearrgb_v4(tmp, from);
203 float_to_byte_dither_v4(to, tmp, dither, x, y + start_y);
204 }
205 }
206 else if (predivide) {
207 for (x = 0; x < width; x++, from += 4, to += 4) {
209 rgba_float_to_uchar(to, tmp);
210 }
211 }
212 else {
213 for (x = 0; x < width; x++, from += 4, to += 4) {
214 srgb_to_linearrgb_v4(tmp, from);
215 rgba_float_to_uchar(to, tmp);
216 }
217 }
218 }
219 }
220 }
221}
222
224 const float *rect_from,
225 int channels_from,
226 float dither,
227 bool predivide,
228 int width,
229 int height,
230 int stride_to,
231 int stride_from,
232 char *mask)
233{
234 int x, y;
235
236 for (y = 0; y < height; y++) {
237 if (channels_from == 1) {
238 /* single channel input */
239 const float *from = rect_from + size_t(stride_from) * y;
240 uchar *to = rect_to + size_t(stride_to) * y * 4;
241
242 for (x = 0; x < width; x++, from++, to += 4) {
243 if (*mask++ == FILTER_MASK_USED) {
244 to[0] = to[1] = to[2] = to[3] = unit_float_to_uchar_clamp(from[0]);
245 }
246 }
247 }
248 else if (channels_from == 3) {
249 /* RGB input */
250 const float *from = rect_from + size_t(stride_from) * y * 3;
251 uchar *to = rect_to + size_t(stride_to) * y * 4;
252
253 for (x = 0; x < width; x++, from += 3, to += 4) {
254 if (*mask++ == FILTER_MASK_USED) {
255 rgb_float_to_uchar(to, from);
256 to[3] = 255;
257 }
258 }
259 }
260 else if (channels_from == 4) {
261 /* RGBA input */
262 const float *from = rect_from + size_t(stride_from) * y * 4;
263 uchar *to = rect_to + size_t(stride_to) * y * 4;
264
265 if (dither && predivide) {
266 float straight[4];
267 for (x = 0; x < width; x++, from += 4, to += 4) {
268 if (*mask++ == FILTER_MASK_USED) {
269 premul_to_straight_v4_v4(straight, from);
270 float_to_byte_dither_v4(to, straight, dither, x, y);
271 }
272 }
273 }
274 else if (dither) {
275 for (x = 0; x < width; x++, from += 4, to += 4) {
276 if (*mask++ == FILTER_MASK_USED) {
277 float_to_byte_dither_v4(to, from, dither, x, y);
278 }
279 }
280 }
281 else if (predivide) {
282 for (x = 0; x < width; x++, from += 4, to += 4) {
283 if (*mask++ == FILTER_MASK_USED) {
285 }
286 }
287 }
288 else {
289 for (x = 0; x < width; x++, from += 4, to += 4) {
290 if (*mask++ == FILTER_MASK_USED) {
291 rgba_float_to_uchar(to, from);
292 }
293 }
294 }
295 }
296 }
297}
298
299void IMB_buffer_float_from_byte(float *rect_to,
300 const uchar *rect_from,
301 int profile_to,
302 int profile_from,
303 bool predivide,
304 int width,
305 int height,
306 int stride_to,
307 int stride_from)
308{
309 float tmp[4];
310 int x, y;
311
312 /* we need valid profiles */
313 BLI_assert(profile_to != IB_PROFILE_NONE);
314 BLI_assert(profile_from != IB_PROFILE_NONE);
315
316 /* RGBA input */
317 for (y = 0; y < height; y++) {
318 const uchar *from = rect_from + size_t(stride_from) * y * 4;
319 float *to = rect_to + size_t(stride_to) * y * 4;
320
321 if (profile_to == profile_from) {
322 /* no color space conversion */
323 for (x = 0; x < width; x++, from += 4, to += 4) {
324 rgba_uchar_to_float(to, from);
325 }
326 }
327 else if (profile_to == IB_PROFILE_LINEAR_RGB) {
328 /* convert sRGB to linear */
329 if (predivide) {
330 for (x = 0; x < width; x++, from += 4, to += 4) {
332 }
333 }
334 else {
335 for (x = 0; x < width; x++, from += 4, to += 4) {
336 srgb_to_linearrgb_uchar4(to, from);
337 }
338 }
339 }
340 else if (profile_to == IB_PROFILE_SRGB) {
341 /* convert linear to sRGB */
342 if (predivide) {
343 for (x = 0; x < width; x++, from += 4, to += 4) {
344 rgba_uchar_to_float(tmp, from);
346 }
347 }
348 else {
349 for (x = 0; x < width; x++, from += 4, to += 4) {
350 rgba_uchar_to_float(tmp, from);
351 linearrgb_to_srgb_v4(to, tmp);
352 }
353 }
354 }
355 }
356}
357
358void IMB_buffer_float_from_float(float *rect_to,
359 const float *rect_from,
360 int channels_from,
361 int profile_to,
362 int profile_from,
363 bool predivide,
364 int width,
365 int height,
366 int stride_to,
367 int stride_from)
368{
369 int x, y;
370
371 /* we need valid profiles */
372 BLI_assert(profile_to != IB_PROFILE_NONE);
373 BLI_assert(profile_from != IB_PROFILE_NONE);
374
375 if (channels_from == 1) {
376 /* single channel input */
377 for (y = 0; y < height; y++) {
378 const float *from = rect_from + size_t(stride_from) * y;
379 float *to = rect_to + size_t(stride_to) * y * 4;
380
381 for (x = 0; x < width; x++, from++, to += 4) {
382 to[0] = to[1] = to[2] = to[3] = from[0];
383 }
384 }
385 }
386 else if (channels_from == 3) {
387 /* RGB input */
388 for (y = 0; y < height; y++) {
389 const float *from = rect_from + size_t(stride_from) * y * 3;
390 float *to = rect_to + size_t(stride_to) * y * 4;
391
392 if (profile_to == profile_from) {
393 /* no color space conversion */
394 for (x = 0; x < width; x++, from += 3, to += 4) {
395 copy_v3_v3(to, from);
396 to[3] = 1.0f;
397 }
398 }
399 else if (profile_to == IB_PROFILE_LINEAR_RGB) {
400 /* convert from sRGB to linear */
401 for (x = 0; x < width; x++, from += 3, to += 4) {
402 srgb_to_linearrgb_v3_v3(to, from);
403 to[3] = 1.0f;
404 }
405 }
406 else if (profile_to == IB_PROFILE_SRGB) {
407 /* convert from linear to sRGB */
408 for (x = 0; x < width; x++, from += 3, to += 4) {
409 linearrgb_to_srgb_v3_v3(to, from);
410 to[3] = 1.0f;
411 }
412 }
413 }
414 }
415 else if (channels_from == 4) {
416 /* RGBA input */
417 for (y = 0; y < height; y++) {
418 const float *from = rect_from + size_t(stride_from) * y * 4;
419 float *to = rect_to + size_t(stride_to) * y * 4;
420
421 if (profile_to == profile_from) {
422 /* same profile, copy */
423 memcpy(to, from, sizeof(float) * size_t(4) * width);
424 }
425 else if (profile_to == IB_PROFILE_LINEAR_RGB) {
426 /* convert to sRGB to linear */
427 if (predivide) {
428 for (x = 0; x < width; x++, from += 4, to += 4) {
430 }
431 }
432 else {
433 for (x = 0; x < width; x++, from += 4, to += 4) {
434 srgb_to_linearrgb_v4(to, from);
435 }
436 }
437 }
438 else if (profile_to == IB_PROFILE_SRGB) {
439 /* convert from linear to sRGB */
440 if (predivide) {
441 for (x = 0; x < width; x++, from += 4, to += 4) {
443 }
444 }
445 else {
446 for (x = 0; x < width; x++, from += 4, to += 4) {
447 linearrgb_to_srgb_v4(to, from);
448 }
449 }
450 }
451 }
452 }
453}
454
456 const float *rect_from,
457 int channels_from,
458 int profile_to,
459 int profile_from,
460 bool predivide,
461 int width,
462 int height,
463 int stride_to,
464 int stride_from)
465{
466 using namespace blender;
467 threading::parallel_for(IndexRange(height), 64, [&](const IndexRange y_range) {
468 int64_t offset_from = y_range.first() * stride_from * channels_from;
469 int64_t offset_to = y_range.first() * stride_to * 4;
470 IMB_buffer_float_from_float(rect_to + offset_to,
471 rect_from + offset_from,
472 channels_from,
473 profile_to,
474 profile_from,
475 predivide,
476 width,
477 y_range.size(),
478 stride_to,
479 stride_from);
480 });
481}
482
484 const float *rect_from,
485 int channels_from,
486 int width,
487 int height,
488 int stride_to,
489 int stride_from,
490 char *mask)
491{
492 int x, y;
493
494 if (channels_from == 1) {
495 /* single channel input */
496 for (y = 0; y < height; y++) {
497 const float *from = rect_from + size_t(stride_from) * y;
498 float *to = rect_to + size_t(stride_to) * y * 4;
499
500 for (x = 0; x < width; x++, from++, to += 4) {
501 if (*mask++ == FILTER_MASK_USED) {
502 to[0] = to[1] = to[2] = to[3] = from[0];
503 }
504 }
505 }
506 }
507 else if (channels_from == 3) {
508 /* RGB input */
509 for (y = 0; y < height; y++) {
510 const float *from = rect_from + size_t(stride_from) * y * 3;
511 float *to = rect_to + size_t(stride_to) * y * 4;
512
513 for (x = 0; x < width; x++, from += 3, to += 4) {
514 if (*mask++ == FILTER_MASK_USED) {
515 copy_v3_v3(to, from);
516 to[3] = 1.0f;
517 }
518 }
519 }
520 }
521 else if (channels_from == 4) {
522 /* RGBA input */
523 for (y = 0; y < height; y++) {
524 const float *from = rect_from + size_t(stride_from) * y * 4;
525 float *to = rect_to + size_t(stride_to) * y * 4;
526
527 for (x = 0; x < width; x++, from += 4, to += 4) {
528 if (*mask++ == FILTER_MASK_USED) {
529 copy_v4_v4(to, from);
530 }
531 }
532 }
533 }
534}
535
537 const uchar *rect_from,
538 int profile_to,
539 int profile_from,
540 bool predivide,
541 int width,
542 int height,
543 int stride_to,
544 int stride_from)
545{
546 float tmp[4];
547 int x, y;
548
549 /* we need valid profiles */
550 BLI_assert(profile_to != IB_PROFILE_NONE);
551 BLI_assert(profile_from != IB_PROFILE_NONE);
552
553 /* always RGBA input */
554 for (y = 0; y < height; y++) {
555 const uchar *from = rect_from + size_t(stride_from) * y * 4;
556 uchar *to = rect_to + size_t(stride_to) * y * 4;
557
558 if (profile_to == profile_from) {
559 /* same profile, copy */
560 memcpy(to, from, sizeof(uchar[4]) * width);
561 }
562 else if (profile_to == IB_PROFILE_LINEAR_RGB) {
563 /* convert to sRGB to linear */
564 if (predivide) {
565 for (x = 0; x < width; x++, from += 4, to += 4) {
566 rgba_uchar_to_float(tmp, from);
568 rgba_float_to_uchar(to, tmp);
569 }
570 }
571 else {
572 for (x = 0; x < width; x++, from += 4, to += 4) {
573 rgba_uchar_to_float(tmp, from);
574 srgb_to_linearrgb_v4(tmp, tmp);
575 rgba_float_to_uchar(to, tmp);
576 }
577 }
578 }
579 else if (profile_to == IB_PROFILE_SRGB) {
580 /* convert from linear to sRGB */
581 if (predivide) {
582 for (x = 0; x < width; x++, from += 4, to += 4) {
583 rgba_uchar_to_float(tmp, from);
585 rgba_float_to_uchar(to, tmp);
586 }
587 }
588 else {
589 for (x = 0; x < width; x++, from += 4, to += 4) {
590 rgba_uchar_to_float(tmp, from);
591 linearrgb_to_srgb_v4(tmp, tmp);
592 rgba_float_to_uchar(to, tmp);
593 }
594 }
595 }
596 }
597}
598
600
601/* -------------------------------------------------------------------- */
604
606{
607 using namespace blender;
608
609 /* Nothing to do if there's no float buffer */
610 if (ibuf->float_buffer.data == nullptr) {
611 return;
612 }
613
614 /* Allocate byte buffer if needed. */
615 if (ibuf->byte_buffer.data == nullptr) {
616 if (!IMB_alloc_byte_pixels(ibuf, false)) {
617 return;
618 }
619 }
620
621 const char *from_colorspace = (ibuf->float_buffer.colorspace == nullptr) ?
625 const char *to_colorspace = (ibuf->byte_buffer.colorspace == nullptr) ?
628 ibuf->byte_buffer.colorspace->name().c_str();
629 const bool predivide = IMB_alpha_affects_rgb(ibuf);
630 ColormanageProcessor *processor = STREQ(from_colorspace, to_colorspace) ?
631 nullptr :
633 from_colorspace, to_colorspace);
634 if (processor && IMB_colormanagement_processor_is_noop(processor)) {
636 processor = nullptr;
637 }
638
639 /* At 4 floats per pixel, this is 32KB of data, and fits into typical CPU L1 cache. */
640 static constexpr int grain_size = 2048;
642 IndexRange(IMB_get_pixel_count(ibuf)), grain_size, [&](const IndexRange range) {
643 /* Copy chunk of source float pixels into a local buffer. */
644 Array<float, grain_size * 4> buffer(range.size() * ibuf->channels);
645 buffer.as_mutable_span().copy_from(
646 Span(ibuf->float_buffer.data + range.first() * ibuf->channels, buffer.size()));
647 /* Unpremultiply alpha if needed. */
648 if (predivide) {
649 IMB_unpremultiply_rect_float(buffer.data(), ibuf->channels, range.size(), 1);
650 }
651 /* Convert to byte color space if needed. */
652 if (processor) {
654 processor, buffer.data(), range.size(), 1, ibuf->channels, false);
655 }
656 /* Convert to bytes. */
658 buffer.data(),
659 ibuf->channels,
660 ibuf->dither,
663 false,
664 range.size(),
665 1,
666 ibuf->x,
667 ibuf->x);
668 });
669 if (processor != nullptr) {
671 }
672
673 /* ensure user flag is reset */
675}
676
677void IMB_float_from_byte_ex(ImBuf *dst, const ImBuf *src, const rcti *region_to_update)
678{
679 using namespace blender;
680
681 BLI_assert_msg(dst->float_buffer.data != nullptr,
682 "Destination buffer should have a float buffer assigned.");
683 BLI_assert_msg(src->byte_buffer.data != nullptr,
684 "Source buffer should have a byte buffer assigned.");
685 BLI_assert_msg(dst->x == src->x, "Source and destination buffer should have the same dimension");
686 BLI_assert_msg(dst->y == src->y, "Source and destination buffer should have the same dimension");
687 BLI_assert_msg(dst->channels = 4, "Destination buffer should have 4 channels.");
688 BLI_assert_msg(region_to_update->xmin >= 0,
689 "Region to update should be clipped to the given buffers.");
690 BLI_assert_msg(region_to_update->ymin >= 0,
691 "Region to update should be clipped to the given buffers.");
692 BLI_assert_msg(region_to_update->xmax <= dst->x,
693 "Region to update should be clipped to the given buffers.");
694 BLI_assert_msg(region_to_update->ymax <= dst->y,
695 "Region to update should be clipped to the given buffers.");
696
697 const int region_width = BLI_rcti_size_x(region_to_update);
698 const int region_height = BLI_rcti_size_y(region_to_update);
699 const bool premultiply_alpha = IMB_alpha_affects_rgb(src);
700
702 IndexRange(region_to_update->ymin, region_height), 64, [&](const IndexRange y_range) {
703 const uchar *src_ptr = src->byte_buffer.data;
704 src_ptr += (region_to_update->xmin + y_range.first() * dst->x) * 4;
705 float *dst_ptr = dst->float_buffer.data;
706 dst_ptr += (region_to_update->xmin + y_range.first() * dst->x) * 4;
707
708 /* Convert byte -> float without color or alpha conversions. */
709 IMB_buffer_float_from_byte(dst_ptr,
710 src_ptr,
711 IB_PROFILE_SRGB,
712 IB_PROFILE_SRGB,
713 false,
714 region_width,
715 y_range.size(),
716 src->x,
717 dst->x);
718
719 /* Convert to scene linear color space, and premultiply alpha if needed. */
720 float *dst_ptr_line = dst_ptr;
721 for ([[maybe_unused]] const int64_t y : y_range) {
722 IMB_colormanagement_colorspace_to_scene_linear(
723 dst_ptr_line, region_width, 1, dst->channels, src->byte_buffer.colorspace, false);
724 if (premultiply_alpha) {
725 IMB_premultiply_rect_float(dst_ptr_line, dst->channels, region_width, 1);
726 }
727 dst_ptr_line += 4 * dst->x;
728 }
729 });
730}
731
733{
734 /* Nothing to do if there's no byte buffer. */
735 if (ibuf->byte_buffer.data == nullptr) {
736 return;
737 }
738
739 /* Allocate float buffer if needed. */
740 if (ibuf->float_buffer.data == nullptr) {
741 if (!IMB_alloc_float_pixels(ibuf, 4, false)) {
742 return;
743 }
744 }
745
746 rcti region_to_update;
747 BLI_rcti_init(&region_to_update, 0, ibuf->x, 0, ibuf->y);
748 IMB_float_from_byte_ex(ibuf, ibuf, &region_to_update);
749}
750
752
753/* -------------------------------------------------------------------- */
756
758{
759 float *rct_fl = ibuf->float_buffer.data;
760 uchar *rct = ibuf->byte_buffer.data;
761 size_t i;
762
763 if (rct_fl) {
764 if (ibuf->channels >= 3) {
765 for (i = IMB_get_pixel_count(ibuf); i > 0; i--, rct_fl += ibuf->channels) {
766 rct_fl[0] = rct_fl[1] = rct_fl[2] = IMB_colormanagement_get_luminance(rct_fl);
767 }
768 }
769 }
770
771 if (rct) {
772 for (i = IMB_get_pixel_count(ibuf); i > 0; i--, rct += 4) {
773 rct[0] = rct[1] = rct[2] = IMB_colormanagement_get_luminance_byte(rct);
774 }
775 }
776}
777
779
780/* -------------------------------------------------------------------- */
783
784void IMB_saturation(ImBuf *ibuf, float sat)
785{
786 using namespace blender;
787
788 const size_t pixel_count = IMB_get_pixel_count(ibuf);
789 if (ibuf->byte_buffer.data != nullptr) {
790 threading::parallel_for(IndexRange(pixel_count), 64 * 1024, [&](IndexRange range) {
791 uchar *ptr = ibuf->byte_buffer.data + range.first() * 4;
792 float rgb[3];
793 float hsv[3];
794 for ([[maybe_unused]] const int64_t i : range) {
796 rgb_to_hsv_v(rgb, hsv);
797 hsv_to_rgb(hsv[0], hsv[1] * sat, hsv[2], rgb + 0, rgb + 1, rgb + 2);
799 ptr += 4;
800 }
801 });
802 }
803
804 if (ibuf->float_buffer.data != nullptr && ibuf->channels >= 3) {
805 threading::parallel_for(IndexRange(pixel_count), 64 * 1024, [&](IndexRange range) {
806 const int channels = ibuf->channels;
807 float *ptr = ibuf->float_buffer.data + range.first() * channels;
808 float hsv[3];
809 for ([[maybe_unused]] const int64_t i : range) {
810 rgb_to_hsv_v(ptr, hsv);
811 hsv_to_rgb(hsv[0], hsv[1] * sat, hsv[2], ptr + 0, ptr + 1, ptr + 2);
812 ptr += channels;
813 }
814 });
815 }
816}
817
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
MINLINE void srgb_to_linearrgb_uchar4_predivide(float linear[4], const unsigned char srgb[4])
MINLINE void rgb_uchar_to_float(float r_col[3], const unsigned char col_ub[3])
MINLINE void linearrgb_to_srgb_ushort4(unsigned short srgb[4], const float linear[4])
void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3])
MINLINE void linearrgb_to_srgb_predivide_v4(float srgb[4], const float linear[4])
void hsv_to_rgb(float h, float s, float v, float *r_r, float *r_g, float *r_b)
Definition math_color.cc:21
MINLINE float dither_random_value(int x, int y)
MINLINE void srgb_to_linearrgb_uchar4(float linear[4], const unsigned char srgb[4])
MINLINE void rgba_float_to_uchar(unsigned char r_col[4], const float col_f[4])
MINLINE void rgb_float_to_uchar(unsigned char r_col[3], const float col_f[3])
MINLINE void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4])
MINLINE void srgb_to_linearrgb_v4(float linear[4], const float srgb[4])
MINLINE void premul_to_straight_v4_v4(float straight[4], const float premul[4])
void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
MINLINE void linearrgb_to_srgb_v4(float srgb[4], const float linear[4])
MINLINE void srgb_to_linearrgb_predivide_v4(float linear[4], const float srgb[4])
MINLINE void premul_float_to_straight_uchar(unsigned char *result, const float color[4])
#define MINLINE
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void copy_v3_v3(float r[3], const float a[3])
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.cc:414
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
unsigned char uchar
unsigned short ushort
#define STREQ(a, b)
BLI_INLINE unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char rgb[3])
ColormanageProcessor * IMB_colormanagement_colorspace_processor_new(const char *from_colorspace, const char *to_colorspace)
@ COLOR_ROLE_DEFAULT_BYTE
@ COLOR_ROLE_SCENE_LINEAR
bool IMB_colormanagement_processor_is_noop(ColormanageProcessor *cm_processor)
void IMB_colormanagement_processor_apply(ColormanageProcessor *cm_processor, float *buffer, int width, int height, int channels, bool predivide)
BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3])
const char * IMB_colormanagement_role_colorspace_name_get(int role)
void IMB_colormanagement_processor_free(ColormanageProcessor *cm_processor)
Function declarations for filter.cc.
#define FILTER_MASK_USED
Definition IMB_imbuf.hh:290
bool IMB_alloc_byte_pixels(ImBuf *ibuf, bool initialize_pixels=true)
size_t IMB_get_pixel_count(const ImBuf *ibuf)
Get the length of the data of the given image buffer in pixels.
bool IMB_alloc_float_pixels(ImBuf *ibuf, const unsigned int channels, bool initialize_pixels=true)
@ IB_RECT_INVALID
#define IB_PROFILE_NONE
#define IB_PROFILE_SRGB
#define IB_PROFILE_LINEAR_RGB
@ IB_alphamode_channel_packed
Read Guarded memory(de)allocation.
long long int int64_t
int64_t size() const
Definition BLI_array.hh:256
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
const T * data() const
Definition BLI_array.hh:312
constexpr int64_t first() const
constexpr int64_t size() const
constexpr const char * c_str() const
virtual StringRefNull name() const =0
void IMB_buffer_float_from_float_mask(float *rect_to, const float *rect_from, int channels_from, int width, int height, int stride_to, int stride_from, char *mask)
#define USHORTTOFLOAT(val)
void IMB_float_from_byte_ex(ImBuf *dst, const ImBuf *src, const rcti *region_to_update)
MINLINE void float_to_byte_dither_v4(uchar b[4], const float f[4], float dither, int x, int y)
Definition conversion.cc:56
MINLINE void ushort_to_byte_dither_v4(uchar b[4], const ushort us[4], float dither, int x, int y)
Definition conversion.cc:43
void IMB_buffer_byte_from_float_mask(uchar *rect_to, const float *rect_from, int channels_from, float dither, bool predivide, int width, int height, int stride_to, int stride_from, char *mask)
bool IMB_alpha_affects_rgb(const ImBuf *ibuf)
Definition conversion.cc:66
void IMB_byte_from_float(ImBuf *ibuf)
void IMB_buffer_float_from_float(float *rect_to, const float *rect_from, int channels_from, int profile_to, int profile_from, bool predivide, int width, int height, int stride_to, int stride_from)
void IMB_color_to_bw(ImBuf *ibuf)
void IMB_buffer_float_from_byte(float *rect_to, const uchar *rect_from, int profile_to, int profile_from, bool predivide, int width, int height, int stride_to, int stride_from)
void IMB_buffer_float_from_float_threaded(float *rect_to, const float *rect_from, int channels_from, int profile_to, int profile_from, bool predivide, int width, int height, int stride_to, int stride_from)
void IMB_saturation(ImBuf *ibuf, float sat)
MINLINE uchar ftochar(float value)
Definition conversion.cc:38
MINLINE void ushort_to_byte_v4(uchar b[4], const ushort us[4])
Definition conversion.cc:30
void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from, int channels_from, float dither, int profile_to, int profile_from, bool predivide, int width, int height, int stride_to, int stride_from, int start_y)
Definition conversion.cc:71
void IMB_buffer_byte_from_byte(uchar *rect_to, const uchar *rect_from, int profile_to, int profile_from, bool predivide, int width, int height, int stride_to, int stride_from)
void IMB_float_from_byte(ImBuf *ibuf)
void IMB_unpremultiply_rect_float(float *rect_float, int channels, int w, int h)
Definition filter.cc:425
MINLINE unsigned char unit_ushort_to_uchar(unsigned short val)
MINLINE unsigned char unit_float_to_uchar_clamp(float val)
ccl_device_inline float2 mask(const MaskType mask, const float2 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
const ColorSpace * colorspace
const ColorSpace * colorspace
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
int ymin
int ymax
int xmin
int xmax
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238