Blender V5.0
colormanagement.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
11
12#include <cmath>
13#include <cstring>
14
15#include "DNA_ID.h"
16#include "DNA_color_types.h"
17#include "DNA_image_types.h"
18#include "DNA_movieclip_types.h"
19#include "DNA_node_types.h"
20#include "DNA_scene_types.h"
21#include "DNA_sequence_types.h"
22#include "DNA_space_types.h"
23
24#include "IMB_filetype.hh"
25#include "IMB_filter.hh"
26#include "IMB_imbuf.hh"
27#include "IMB_imbuf_types.hh"
28#include "IMB_metadata.hh"
29#include "IMB_moviecache.hh"
30
31#include "MEM_guardedalloc.h"
32
33#include "BLI_color.hh"
34#include "BLI_colorspace.hh"
35#include "BLI_fileops.hh"
36#include "BLI_listbase.h"
37#include "BLI_math_color.h"
38#include "BLI_math_color.hh"
39#include "BLI_math_matrix.hh"
42#include "BLI_path_utils.hh"
43#include "BLI_rect.h"
44#include "BLI_string_ref.hh"
45#include "BLI_string_utf8.h"
46#include "BLI_task.hh"
47#include "BLI_threads.h"
48#include "BLI_vector_set.hh"
49
50#include "BKE_appdir.hh"
51#include "BKE_colortools.hh"
52#include "BKE_context.hh"
53#include "BKE_global.hh"
54#include "BKE_idtype.hh"
55#include "BKE_image_format.hh"
56#include "BKE_library.hh"
57#include "BKE_main.hh"
58#include "BKE_node.hh"
60
61#include "GPU_capabilities.hh"
62
63#include "RNA_define.hh"
64
65#include "SEQ_iterator.hh"
66
67#include "DEG_depsgraph.hh"
68
69#include "CLG_log.h"
70
71#include "OCIO_api.hh"
72
73static CLG_LogRef LOG = {"color_management"};
74
75using blender::float3;
79
80namespace ocio = blender::ocio;
81namespace math = blender::math;
82
83/* -------------------------------------------------------------------- */
86
87static std::unique_ptr<ocio::Config> g_config = nullptr;
88static bool g_config_is_custom = false;
90
91#define DISPLAY_BUFFER_CHANNELS 4
92
93/* ** list of all supported color spaces, displays and views */
102
103/* Defaults from the config that never change with working space. */
106
108
109/* lock used by pre-cached processors getters, so processor wouldn't
110 * be created several times
111 * LOCK_COLORMANAGE can not be used since this mutex could be needed to
112 * be locked before pre-cached processor are creating
113 */
114static pthread_mutex_t processor_lock = BLI_MUTEX_INITIALIZER;
115
117 std::shared_ptr<const ocio::CPUProcessor> cpu_processor;
120};
121
122static struct GlobalGPUState {
123 GlobalGPUState() = default;
124
131
132 /* GPU shader currently bound. */
133 bool gpu_shader_bound = false;
134
135 /* Curve mapping. */
137 bool use_curve_mapping = false;
140
143
144 /* Cached processor for color picking conversion. */
145 std::shared_ptr<const ocio::CPUProcessor> cpu_processor_to;
146 std::shared_ptr<const ocio::CPUProcessor> cpu_processor_from;
147 bool failed = false;
149
151
152/* -------------------------------------------------------------------- */
155
211
212/* NOTE: ColormanageCacheViewSettings and ColormanageCacheDisplaySettings are
213 * quite the same as ColorManagedViewSettings and ColorManageDisplaySettings
214 * but they holds indexes of all transformations and color spaces, not
215 * their names.
216 *
217 * This helps avoid extra colorspace / display / view lookup without
218 * requiring to pass all variables which affects on display buffer
219 * to color management cache system and keeps calls small and nice.
220 */
232
236
238 int view; /* view transformation used for display buffer */
239 int display; /* display device name */
240};
241
243 int flag; /* view flags of cached buffer */
244 int look; /* Additional artistic transform. */
245 float exposure; /* exposure value cached buffer is calculated with */
246 float gamma; /* gamma value cached buffer is calculated with */
247 float dither; /* dither value cached buffer is calculated with */
248 float temperature; /* temperature value cached buffer is calculated with */
249 float tint; /* tint value cached buffer is calculated with */
250 CurveMapping *curve_mapping; /* curve mapping used for cached buffer */
251 int curve_mapping_timestamp; /* time stamp of curve mapping used for cached buffer */
252};
253
259
261{
262 if (!ibuf->colormanage_cache) {
263 return nullptr;
264 }
265
266 return ibuf->colormanage_cache->moviecache;
267}
268
270{
271 if (!ibuf->colormanage_cache) {
272 return nullptr;
273 }
274
275 return ibuf->colormanage_cache->data;
276}
277
278static uint colormanage_hashhash(const void *key_v)
279{
280 const ColormanageCacheKey *key = static_cast<const ColormanageCacheKey *>(key_v);
281
282 uint rval = (key->display << 16) | (key->view % 0xffff);
283
284 return rval;
285}
286
287static bool colormanage_hashcmp(const void *av, const void *bv)
288{
289 const ColormanageCacheKey *a = static_cast<const ColormanageCacheKey *>(av);
290 const ColormanageCacheKey *b = static_cast<const ColormanageCacheKey *>(bv);
291
292 return ((a->view != b->view) || (a->display != b->display));
293}
294
296{
297 if (!ibuf->colormanage_cache) {
298 ibuf->colormanage_cache = MEM_callocN<ColormanageCache>("imbuf colormanage cache");
299 }
300
301 if (!ibuf->colormanage_cache->moviecache) {
302 MovieCache *moviecache;
303
304 moviecache = IMB_moviecache_create("colormanage cache",
305 sizeof(ColormanageCacheKey),
308
309 ibuf->colormanage_cache->moviecache = moviecache;
310 }
311
312 return ibuf->colormanage_cache->moviecache;
313}
314
316{
317 if (!ibuf->colormanage_cache) {
318 ibuf->colormanage_cache = MEM_callocN<ColormanageCache>("imbuf colormanage cache");
319 }
320
321 ibuf->colormanage_cache->data = data;
322}
323
325 ColormanageCacheViewSettings *cache_view_settings,
326 const ColorManagedViewSettings *view_settings)
327{
328 int look = IMB_colormanagement_look_get_named_index(view_settings->look);
330
331 cache_view_settings->look = look;
332 cache_view_settings->view = view;
333 cache_view_settings->exposure = view_settings->exposure;
334 cache_view_settings->gamma = view_settings->gamma;
335 cache_view_settings->dither = ibuf->dither;
336 cache_view_settings->temperature = view_settings->temperature;
337 cache_view_settings->tint = view_settings->tint;
338 cache_view_settings->flag = view_settings->flag;
339 cache_view_settings->curve_mapping = view_settings->curve_mapping;
340}
341
343 ColormanageCacheDisplaySettings *cache_display_settings,
344 const ColorManagedDisplaySettings *display_settings)
345{
346 int display = IMB_colormanagement_display_get_named_index(display_settings->display_device);
347
348 cache_display_settings->display = display;
349}
350
352 const ColormanageCacheViewSettings *view_settings,
353 const ColormanageCacheDisplaySettings *display_settings)
354{
355 key->view = view_settings->view;
356 key->display = display_settings->display;
357}
358
361 void **cache_handle)
362{
363 ImBuf *cache_ibuf;
364 MovieCache *moviecache = colormanage_moviecache_get(ibuf);
365
366 if (!moviecache) {
367 /* If there's no moviecache it means no color management was applied
368 * on given image buffer before. */
369 return nullptr;
370 }
371
372 *cache_handle = nullptr;
373
374 cache_ibuf = IMB_moviecache_get(moviecache, key, nullptr);
375
376 *cache_handle = cache_ibuf;
377
378 return cache_ibuf;
379}
380
382 const ColormanageCacheViewSettings *view_settings,
383 const ColormanageCacheDisplaySettings *display_settings,
384 void **cache_handle)
385{
387 ImBuf *cache_ibuf;
388 int view_flag = 1 << view_settings->view;
389 CurveMapping *curve_mapping = view_settings->curve_mapping;
390 int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
391
392 colormanage_settings_to_key(&key, view_settings, display_settings);
393
394 /* check whether image was marked as dirty for requested transform */
395 if ((ibuf->display_buffer_flags[display_settings->display] & view_flag) == 0) {
396 return nullptr;
397 }
398
399 cache_ibuf = colormanage_cache_get_ibuf(ibuf, &key, cache_handle);
400
401 if (cache_ibuf) {
402
403 BLI_assert(cache_ibuf->x == ibuf->x && cache_ibuf->y == ibuf->y);
404
405 /* only buffers with different color space conversions are being stored
406 * in cache separately. buffer which were used only different exposure/gamma
407 * are re-suing the same cached buffer
408 *
409 * check here which exposure/gamma/curve was used for cached buffer and if they're
410 * different from requested buffer should be re-generated
411 */
412 const ColormanageCacheData *cache_data = colormanage_cachedata_get(cache_ibuf);
413
414 if (cache_data->look != view_settings->look ||
415 cache_data->exposure != view_settings->exposure ||
416 cache_data->gamma != view_settings->gamma || cache_data->dither != view_settings->dither ||
417 cache_data->temperature != view_settings->temperature ||
418 cache_data->tint != view_settings->tint || cache_data->flag != view_settings->flag ||
419 cache_data->curve_mapping != curve_mapping ||
420 cache_data->curve_mapping_timestamp != curve_mapping_timestamp)
421 {
422 *cache_handle = nullptr;
423
424 IMB_freeImBuf(cache_ibuf);
425
426 return nullptr;
427 }
428
429 return (uchar *)cache_ibuf->byte_buffer.data;
430 }
431
432 return nullptr;
433}
434
435static void colormanage_cache_put(ImBuf *ibuf,
436 const ColormanageCacheViewSettings *view_settings,
437 const ColormanageCacheDisplaySettings *display_settings,
438 uchar *display_buffer,
439 void **cache_handle)
440{
442 ImBuf *cache_ibuf;
443 ColormanageCacheData *cache_data;
444 int view_flag = 1 << view_settings->view;
445 MovieCache *moviecache = colormanage_moviecache_ensure(ibuf);
446 CurveMapping *curve_mapping = view_settings->curve_mapping;
447 int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
448
449 colormanage_settings_to_key(&key, view_settings, display_settings);
450
451 /* mark display buffer as valid */
452 ibuf->display_buffer_flags[display_settings->display] |= view_flag;
453
454 /* buffer itself */
455 cache_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0);
456 IMB_assign_byte_buffer(cache_ibuf, display_buffer, IB_TAKE_OWNERSHIP);
457
458 /* Store data which is needed to check whether cached buffer
459 * could be used for color managed display settings. */
460 cache_data = MEM_callocN<ColormanageCacheData>("color manage cache imbuf data");
461 cache_data->look = view_settings->look;
462 cache_data->exposure = view_settings->exposure;
463 cache_data->gamma = view_settings->gamma;
464 cache_data->dither = view_settings->dither;
465 cache_data->temperature = view_settings->temperature;
466 cache_data->tint = view_settings->tint;
467 cache_data->flag = view_settings->flag;
468 cache_data->curve_mapping = curve_mapping;
469 cache_data->curve_mapping_timestamp = curve_mapping_timestamp;
470
471 colormanage_cachedata_set(cache_ibuf, cache_data);
472
473 *cache_handle = cache_ibuf;
474
475 IMB_moviecache_put(moviecache, &key, cache_ibuf);
476}
477
478static void colormanage_cache_handle_release(void *cache_handle)
479{
480 ImBuf *cache_ibuf = static_cast<ImBuf *>(cache_handle);
481
482 IMB_freeImBuf(cache_ibuf);
483}
484
486
487/* -------------------------------------------------------------------- */
490
492 char *colorspace_name,
493 const char *role,
494 const char *backup_role,
495 const bool optional = false)
496{
497 const ColorSpace *ociocs = config.get_color_space(role);
498
499 if (ociocs == nullptr && backup_role) {
500 ociocs = config.get_color_space(backup_role);
501 }
502
503 if (ociocs == nullptr) {
504 /* Overall fallback role. */
505 ociocs = config.get_color_space("default");
506 }
507
508 if (ociocs == nullptr) {
509 if (!optional) {
510 CLOG_ERROR(&LOG, "Could not find role \"%s\"", role);
511 }
512 colorspace_name[0] = '\0';
513 return false;
514 }
515
516 /* assume function was called with buffer properly allocated to MAX_COLORSPACE_NAME chars */
517 BLI_strncpy_utf8(colorspace_name, ociocs->name().c_str(), MAX_COLORSPACE_NAME);
518 return true;
519}
520
522{
523 /* Load luminance coefficients. */
524 blender::colorspace::luma_coefficients = g_config->get_default_luma_coefs();
525
526 /* Load standard color spaces. */
527 blender::colorspace::xyz_to_scene_linear = g_config->get_xyz_to_scene_linear_matrix();
530
535
540
545
550
553}
554
556{
557 bool ok = true;
558
559 /* get roles */
573
576
577 if (g_config->get_num_displays() == 0) {
578 CLOG_ERROR(&LOG, "Could not find any displays");
579 ok = false;
580 }
581 /* NOTE: The look "None" is expected to be hard-coded to exist in the OpenColorIO integration. */
582 if (g_config->get_num_looks() == 0) {
583 CLOG_ERROR(&LOG, "Could not find any looks");
584 ok = false;
585 }
586
587 for (const int display_index : blender::IndexRange(g_config->get_num_displays())) {
588 const ocio::Display *display = g_config->get_display_by_index(display_index);
589 const int num_views = display->get_num_views();
590 if (num_views <= 0) {
591 CLOG_ERROR(&LOG, "Could not find any views for display %s", display->name().c_str());
592 ok = false;
593 break;
594 }
595
596 for (const int view_index : blender::IndexRange(num_views)) {
597 const ocio::View *view = display->get_view_by_index(view_index);
598 g_all_view_names.add(view->name());
599 }
600 }
601
603
604 /* Defaults that don't change with file working space. */
608
609 return ok;
610}
611
613{
614 g_config = nullptr;
615 g_all_view_names.clear();
616}
617
619{
620 /* Handle Blender specific override. */
621 const char *blender_ocio_env = BLI_getenv("BLENDER_OCIO");
622 if (blender_ocio_env) {
623 BLI_setenv("OCIO", blender_ocio_env);
624 }
625
626 /* First try config from environment variable. */
627 const char *ocio_env = BLI_getenv("OCIO");
628
629 if (ocio_env && ocio_env[0] != '\0') {
631 if (g_config != nullptr) {
633 &LOG, "Using %s=%s", (blender_ocio_env) ? "BLENDER_OCIO" : "OCIO", ocio_env);
634 const bool ok = colormanage_load_config(*g_config);
635
636 if (ok) {
637 g_config_is_custom = true;
638 }
639 else {
640 CLOG_ERROR(&LOG, "Failed to load config from environment");
642 }
643 }
644 }
645
646 /* Then try bundled configuration file. */
647 if (g_config == nullptr) {
648 const std::optional<std::string> configdir = BKE_appdir_folder_id(BLENDER_DATAFILES,
649 "colormanagement");
650 if (configdir.has_value()) {
651 char configfile[FILE_MAX];
652 BLI_path_join(configfile, sizeof(configfile), configdir->c_str(), BCM_CONFIG_FILE);
653
655
656 if (g_config != nullptr) {
657 const bool ok = colormanage_load_config(*g_config);
658 if (!ok) {
659 CLOG_ERROR(&LOG, "Failed to load bundled config");
661 }
662 }
663 }
664 }
665
666 /* Then use fallback. */
667 if (g_config == nullptr) {
668 CLOG_STR_INFO_NOCHECK(&LOG, "Using fallback mode for management");
671 }
672
674}
675
683
685
686/* -------------------------------------------------------------------- */
689
691{
692 if (view_name.is_empty()) {
693 return StringRef();
694 }
695
696 /* First try to find any looks with the full name prefix. */
697 for (const int look_index : blender::IndexRange(g_config->get_num_looks())) {
698 const ocio::Look *look = g_config->get_look_by_index(look_index);
699 if (look->view() == view_name) {
700 return view_name;
701 }
702 }
703
704 /* Then try with the short name prefix. */
705 const int64_t separator_offset = view_name.find(" - ");
706 if (separator_offset == -1) {
707 return StringRef();
708 }
709 StringRef view_short_name = view_name.substr(0, separator_offset);
710
711 for (const int look_index : blender::IndexRange(g_config->get_num_looks())) {
712 const ocio::Look *look = g_config->get_look_by_index(look_index);
713 if (look->view() == view_short_name) {
714 return view_short_name;
715 }
716 }
717
718 return StringRef();
719}
720
721static bool colormanage_compatible_look(const ocio::Look *look, StringRef view_filter)
722{
723 if (look->is_noop) {
724 return true;
725 }
726
727 /* Skip looks only relevant to specific view transforms.
728 * If the view transform has view-specific look ignore non-specific looks. */
729 return view_filter.is_empty() ? look->view().is_empty() : look->view() == view_filter;
730}
731
732static bool colormanage_compatible_look(const ocio::Look *look, const char *view_name)
733{
734 const StringRef view_filter = view_filter_for_look(view_name);
735 return colormanage_compatible_look(look, view_filter);
736}
737
738static bool colormanage_use_look(const char *look_name, const char *view_name)
739{
740 const ocio::Look *look = g_config->get_look_by_name(look_name);
741 return (look && look->is_noop == false && colormanage_compatible_look(look, view_name));
742}
743
745{
747
748 if (ibuf->colormanage_cache) {
750 MovieCache *moviecache = colormanage_moviecache_get(ibuf);
751
752 if (cache_data) {
753 MEM_freeN(cache_data);
754 }
755
756 if (moviecache) {
757 IMB_moviecache_free(moviecache);
758 }
759
761
762 ibuf->colormanage_cache = nullptr;
763 }
764}
765
767 const bContext *C,
768 ColorManagedViewSettings **r_view_settings,
769 ColorManagedDisplaySettings **r_display_settings)
770{
771 Scene *scene = CTX_data_scene(C);
773
774 *r_view_settings = &scene->view_settings;
775 *r_display_settings = &scene->display_settings;
776
777 if (sima && sima->image) {
778 if ((sima->image->flag & IMA_VIEW_AS_RENDER) == 0) {
779 *r_view_settings = nullptr;
780 }
781 }
782}
783
784static bool get_display_emulation(const ColorManagedDisplaySettings &display_settings)
785{
786 switch (display_settings.emulation) {
788 return false;
790 return true;
791 }
792
793 return true;
794}
795
796static std::shared_ptr<const ocio::CPUProcessor> get_display_buffer_processor(
797 const ColorManagedDisplaySettings &display_settings,
798 const char *look,
799 const char *view_transform,
800 const float exposure,
801 const float gamma,
802 const float temperature,
803 const float tint,
804 const bool use_white_balance,
805 const char *from_colorspace,
806 const ColorManagedDisplaySpace target,
807 const bool inverse = false)
808{
809 ocio::DisplayParameters display_parameters;
810 display_parameters.from_colorspace = from_colorspace;
811 display_parameters.view = view_transform;
812 display_parameters.display = display_settings.display_device;
813 display_parameters.look = colormanage_use_look(look, view_transform) ? look : "";
814 display_parameters.scale = (exposure == 0.0f) ? 1.0f : powf(2.0f, exposure);
815 display_parameters.exponent = (gamma == 1.0f) ? 1.0f : 1.0f / max_ff(FLT_EPSILON, gamma);
816 display_parameters.temperature = temperature;
817 display_parameters.tint = tint;
818 display_parameters.use_white_balance = use_white_balance;
819 display_parameters.inverse = inverse;
820 display_parameters.use_hdr_buffer = GPU_hdr_support();
821 display_parameters.use_hdr_display = IMB_colormanagement_display_is_hdr(&display_settings,
822 view_transform);
823 display_parameters.is_image_output = (target == DISPLAY_SPACE_IMAGE_OUTPUT);
824 display_parameters.use_display_emulation = (target == DISPLAY_SPACE_DRAW) ?
825 get_display_emulation(display_settings) :
826 false;
827
828 return g_config->get_display_cpu_processor(display_parameters);
829}
830
832 ColorManagedViewSettings *view_settings,
833 const ColorManagedDisplaySettings * /*display_settings*/)
834{
835 /* Empty view transform name means skip tone mapping. */
836 view_settings->view_transform[0] = '\0';
837 /* TODO(sergey): Find a way to safely/reliable un-hardcode this. */
838 STRNCPY_UTF8(view_settings->look, "None");
839 /* Initialize rest of the settings. */
840 view_settings->flag = 0;
841 view_settings->gamma = 1.0f;
842 view_settings->exposure = 0.0f;
843 view_settings->temperature = 6500.0f;
844 view_settings->tint = 10.0f;
845 view_settings->curve_mapping = nullptr;
846}
847
848static void curve_mapping_apply_pixel(const CurveMapping *curve_mapping,
849 float *pixel,
850 int channels)
851{
852 if (channels == 1) {
853 pixel[0] = BKE_curvemap_evaluateF(curve_mapping, curve_mapping->cm, pixel[0]);
854 }
855 else if (channels == 2) {
856 pixel[0] = BKE_curvemap_evaluateF(curve_mapping, curve_mapping->cm, pixel[0]);
857 pixel[1] = BKE_curvemap_evaluateF(curve_mapping, curve_mapping->cm, pixel[1]);
858 }
859 else {
860 BKE_curvemapping_evaluate_premulRGBF(curve_mapping, pixel, pixel);
861 }
862}
863
865{
866 /* Regression tests are allocating ImBuf. Guard against access of uninitialized color
867 * management configuration. */
868 /* TODO(sergey): Always allocate the fallback color management configuration for such cases? */
869 if (!g_config) {
870 return;
871 }
873}
874
876 const char *from_colorspace,
878{
879 const ColorSpace *colorspace = g_config->get_color_space(from_colorspace);
880
881 if (colorspace && colorspace->is_data()) {
883 return;
884 }
885
886 if (ibuf->float_buffer.data) {
887 const char *to_colorspace = global_role_scene_linear;
888 const bool predivide = IMB_alpha_affects_rgb(ibuf);
889
890 if (ibuf->byte_buffer.data) {
892 }
893
895 const ColorSpace *image_colorspace = g_config->get_color_space_for_hdr_image(
896 from_colorspace);
897 if (image_colorspace) {
898 from_colorspace = image_colorspace->name().c_str();
899 }
900 }
901
903 ibuf->x,
904 ibuf->y,
905 ibuf->channels,
906 from_colorspace,
907 to_colorspace,
908 predivide);
909 ibuf->float_buffer.colorspace = nullptr;
910 }
911}
912
914
915/* -------------------------------------------------------------------- */
918
920 const char *what,
921 const ocio::Display *default_display)
922{
923 StringRefNull display_name = display_settings->display_device;
924
925 if (display_name.is_empty()) {
926 STRNCPY_UTF8(display_settings->display_device, default_display->name().c_str());
927 return true;
928 }
929
930 const ocio::Display *display = g_config->get_display_by_name(display_name);
931 if (display) {
932 return true;
933 }
934
935 StringRefNull new_display_name = default_display->name();
936
937 /* Try to find a similar name, so that we can match e.g. "sRGB - Display" and "sRGB".
938 * There are aliases for color spaces, but not displays. */
939 for (const int display_index : blender::IndexRange(g_config->get_num_displays())) {
940 display = g_config->get_display_by_index(display_index);
941 if (display->name().startswith(display_name) || display_name.startswith(display->name())) {
942 new_display_name = display->name();
943 break;
944 }
945 }
946
947 CLOG_WARN(&LOG,
948 "Display \"%s\" used by %s not found, setting to \"%s\".",
949 display_settings->display_device,
950 what,
951 new_display_name.c_str());
952
953 STRNCPY_UTF8(display_settings->display_device, new_display_name.c_str());
954 return false;
955}
956
958 StringRefNull view_name)
959{
960 /* Match untonemapped view conventions between Blender and ACES 2.0. */
961 if (view_name == "Standard" && display->get_view_by_name("Un-tone-mapped")) {
962 return "Un-tone-mapped";
963 }
964 if (view_name == "Un-tone-mapped" && display->get_view_by_name("Standard")) {
965 return "Standard";
966 }
967
968 /* Try to find a similar name, so that we can match e.g. "ACES 2.0" and "ACES 2.0 - HDR
969 * 1000 when switching between SDR and HDR displays. */
970 for (const int view_index : blender::IndexRange(display->get_num_views())) {
971 const ocio::View *view = display->get_view_by_index(view_index);
972 if (view->name().startswith(view_name) || view_name.startswith(view->name())) {
973 return view->name();
974 }
975 }
976
977 const int64_t separator_offset = view_name.find(" - ");
978 if (separator_offset != -1) {
979 const StringRef view_short_name = view_name.substr(0, separator_offset);
980 for (const int view_index : blender::IndexRange(display->get_num_views())) {
981 const ocio::View *view = display->get_view_by_index(view_index);
982 if (view->name().startswith(view_short_name)) {
983 return view->name();
984 }
985 }
986 }
987
988 if (const ocio::View *view = display->get_default_view()) {
989 return view->name();
990 }
991
992 return "";
993}
994
996 ColorManagedViewSettings *view_settings,
997 const char *what)
998{
999 const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
1000 if (!display) {
1001 return false;
1002 }
1003
1004 bool ok = true;
1005
1006 const char *default_look_name = IMB_colormanagement_look_get_default_name();
1007 StringRefNull view_name = view_settings->view_transform;
1008
1009 if (view_name.is_empty()) {
1010 const ocio::View *default_view = display->get_default_view();
1011 if (default_view) {
1012 STRNCPY_UTF8(view_settings->view_transform, default_view->name().c_str());
1013 }
1014 }
1015 else {
1016 const ocio::View *view = display->get_view_by_name(view_name);
1017 if (!view) {
1018 StringRefNull new_view_name = colormanage_find_matching_view_name(display, view_name);
1019 if (!new_view_name.is_empty()) {
1020 CLOG_WARN(&LOG,
1021 "%s view \"%s\" not found, setting to \"%s\".",
1022 what,
1023 view_settings->view_transform,
1024 new_view_name.c_str());
1025 STRNCPY_UTF8(view_settings->view_transform, new_view_name.c_str());
1026 ok = false;
1027 }
1028 }
1029 }
1030
1031 if (view_settings->look[0] == '\0') {
1032 STRNCPY_UTF8(view_settings->look, default_look_name);
1033 }
1034 else {
1035 const ocio::Look *look = g_config->get_look_by_name(view_settings->look);
1036 if (look == nullptr) {
1037 CLOG_WARN(&LOG,
1038 "%s look \"%s\" not found, setting default \"%s\".",
1039 what,
1040 view_settings->look,
1041 default_look_name);
1042
1043 STRNCPY_UTF8(view_settings->look, default_look_name);
1044 ok = false;
1045 }
1046 else if (!colormanage_compatible_look(look, view_settings->view_transform)) {
1047 CLOG_INFO(&LOG,
1048 "%s look \"%s\" is not compatible with view \"%s\", setting "
1049 "default "
1050 "\"%s\".",
1051 what,
1052 view_settings->look,
1053 view_settings->view_transform,
1054 default_look_name);
1055
1056 STRNCPY_UTF8(view_settings->look, default_look_name);
1057 ok = false;
1058 }
1059 }
1060
1061 /* OCIO_TODO: move to do_versions() */
1062 if (view_settings->exposure == 0.0f && view_settings->gamma == 0.0f) {
1063 view_settings->exposure = 0.0f;
1064 view_settings->gamma = 1.0f;
1065 }
1066
1067 return ok;
1068}
1069
1070static bool colormanage_check_colorspace_name(char *name, const char *what)
1071{
1072 bool ok = true;
1073 if (name[0] == '\0') {
1074 /* pass */
1075 }
1076 else {
1077 const ColorSpace *colorspace = g_config->get_color_space(name);
1078
1079 if (!colorspace) {
1080 CLOG_WARN(&LOG, "%s colorspace \"%s\" not found, will use default instead.", what, name);
1081 name[0] = '\0';
1082 ok = false;
1083 }
1084 }
1085
1086 (void)what;
1087 return ok;
1088}
1089
1091 const char *what)
1092{
1093 return colormanage_check_colorspace_name(settings->name, what);
1094}
1095
1097{
1098 const ocio::Display *default_display = g_config->get_default_display();
1099 if (!default_display) {
1100 /* happens when OCIO configuration is incorrect */
1101 return;
1102 }
1103
1104 /* Check display, view and colorspace names in datablocks. */
1105 bool is_missing_opencolorio_config = false;
1106
1107 /* Check scenes. */
1108 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
1109 ColorManagedColorspaceSettings *sequencer_colorspace_settings;
1110 bool ok = true;
1111
1112 /* check scene color management settings */
1113 ok &= colormanage_check_display_settings(&scene->display_settings, "scene", default_display);
1115 &scene->display_settings, &scene->view_settings, "scene");
1116
1118 &scene->r.im_format.display_settings, "scene output", default_display);
1120 &scene->r.im_format.display_settings, &scene->r.im_format.view_settings, "scene output");
1121
1122 sequencer_colorspace_settings = &scene->sequencer_colorspace_settings;
1123
1124 ok &= colormanage_check_colorspace_settings(sequencer_colorspace_settings, "sequencer");
1125
1126 if (sequencer_colorspace_settings->name[0] == '\0') {
1127 STRNCPY_UTF8(sequencer_colorspace_settings->name, global_role_default_sequencer);
1128 }
1129
1130 /* Check sequencer strip input colorspace. */
1131 if (scene->ed != nullptr) {
1132 blender::seq::foreach_strip(&scene->ed->seqbase, [&](Strip *strip) {
1133 if (strip->data) {
1134 ok &= colormanage_check_colorspace_settings(&strip->data->colorspace_settings,
1135 "sequencer strip");
1136 }
1137 return true;
1138 });
1139 }
1140
1141 is_missing_opencolorio_config |= (!ok && !ID_IS_LINKED(&scene->id));
1142 }
1143
1144 /* Check image and movie input colorspace. */
1145 LISTBASE_FOREACH (Image *, image, &bmain->images) {
1146 const bool ok = colormanage_check_colorspace_settings(&image->colorspace_settings, "image");
1147 is_missing_opencolorio_config |= (!ok && !ID_IS_LINKED(&image->id));
1148 }
1149
1150 LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) {
1151 const bool ok = colormanage_check_colorspace_settings(&clip->colorspace_settings, "clip");
1152 is_missing_opencolorio_config |= (!ok && !ID_IS_LINKED(&clip->id));
1153 }
1154
1155 /* Check compositing nodes. */
1156 LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
1157 if (ntree->type == NTREE_COMPOSIT) {
1158 bool ok = true;
1159 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
1160 if (node->type_legacy == CMP_NODE_CONVERT_TO_DISPLAY) {
1161 NodeConvertToDisplay *nctd = static_cast<NodeConvertToDisplay *>(node->storage);
1163 &nctd->display_settings, "node", default_display);
1165 &nctd->display_settings, &nctd->view_settings, "node");
1166 }
1167 else if (node->type_legacy == CMP_NODE_CONVERT_COLOR_SPACE) {
1168 NodeConvertColorSpace *ncs = static_cast<NodeConvertColorSpace *>(node->storage);
1171 }
1172 }
1173 is_missing_opencolorio_config |= (!ok && !ID_IS_LINKED(&ntree->id));
1174 }
1175 }
1176
1177 /* Inform users about mismatch, but not for new files. Linked datablocks are also ignored,
1178 * as we are not overwriting them on blend file save which is the main purpose of this
1179 * warning. */
1180 if (bmain->filepath[0] != '\0' && is_missing_opencolorio_config) {
1181 bmain->colorspace.is_missing_opencolorio_config = true;
1182 }
1183}
1184
1186 ColorManagedViewSettings *view_settings)
1187{
1188 const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
1189
1190 bool found = false;
1191 for (const int view_index : blender::IndexRange(display->get_num_views())) {
1192 const ocio::View *view = display->get_view_by_index(view_index);
1193 if (view->name() == view_settings->view_transform) {
1194 found = true;
1195 break;
1196 }
1197 }
1198
1199 if (!found) {
1201 display, view_settings->view_transform);
1202 if (!new_view_name.is_empty()) {
1203 STRNCPY_UTF8(view_settings->view_transform, new_view_name.c_str());
1204 }
1205 }
1206}
1207
1209{
1210 switch (role) {
1211 case COLOR_ROLE_DATA:
1212 return global_role_data;
1227 default:
1228 CLOG_WARN(&LOG, "Unknown role was passed to %s", __func__);
1229 BLI_assert(0);
1230 break;
1231 }
1232
1233 return nullptr;
1234}
1235
1237{
1238 const ColorSpace *colorspace = g_config->get_color_space(name);
1239
1240 if (colorspace && colorspace->is_data()) {
1242 }
1243 else {
1245 }
1246}
1247
1249{
1254 if (ibuf_src->flags & IB_alphamode_premul) {
1255 ibuf_dst->flags |= IB_alphamode_premul;
1256 }
1257 else if (ibuf_src->flags & IB_alphamode_channel_packed) {
1259 }
1260 else if (ibuf_src->flags & IB_alphamode_ignore) {
1261 ibuf_dst->flags |= IB_alphamode_ignore;
1262 }
1263}
1264
1266{
1267 const ColorSpace *colorspace = g_config->get_color_space(name);
1268
1269 ibuf->float_buffer.colorspace = colorspace;
1270
1271 if (colorspace && colorspace->is_data()) {
1273 }
1274 else {
1276 }
1277}
1278
1280{
1281 const ColorSpace *colorspace = g_config->get_color_space(name);
1282
1283 ibuf->byte_buffer.colorspace = colorspace;
1284
1285 if (colorspace && colorspace->is_data()) {
1287 }
1288 else {
1290 }
1291}
1292
1294{
1295 if (ibuf->float_buffer.colorspace) {
1296 return ibuf->float_buffer.colorspace->name().c_str();
1297 }
1298
1300}
1301
1303{
1304 if (ibuf->byte_buffer.colorspace) {
1305 return ibuf->byte_buffer.colorspace->name().c_str();
1306 }
1307
1309}
1310
1311const char *IMB_colormanagement_space_from_filepath_rules(const char *filepath)
1312{
1313 return g_config->get_color_space_from_filepath(filepath);
1314}
1315
1317{
1318 return g_config->get_color_space(name);
1319}
1320
1322{
1323 return (colorspace && colorspace->is_data());
1324}
1325
1327{
1328 return (colorspace && colorspace->is_scene_linear());
1329}
1330
1332{
1333 return (colorspace && colorspace->is_srgb());
1334}
1335
1337{
1338 const ColorSpace *colorspace = g_config->get_color_space(name);
1339 return (colorspace && colorspace->is_data());
1340}
1341
1343{
1344 const ColorSpace *colorspace = g_config->get_color_space(name);
1345 return (colorspace && IMB_colormanagement_space_is_scene_linear(colorspace));
1346}
1347
1349{
1350 const ColorSpace *colorspace = g_config->get_color_space(name);
1351 return (colorspace && IMB_colormanagement_space_is_srgb(colorspace));
1352}
1353
1355{
1356 /* Make a best effort to find by common names. First two are from the ColorInterop forum. */
1357 const char *names[] = {"sRGB Encoded Rec.709 (sRGB)",
1358 "srgb_rec709_scene",
1359 "Utility - sRGB - Texture",
1360 "sRGB - Texture",
1361 "sRGB",
1362 nullptr};
1363 for (int i = 0; names[i]; i++) {
1364 const ColorSpace *colorspace = g_config->get_color_space(names[i]);
1365 if (colorspace) {
1366 return colorspace->name().c_str();
1367 }
1368 }
1369
1370 /* Fallback if nothing can be found. */
1372}
1373
1375{
1376 /* ICC profiles shipped with Blender are named after the OpenColorIO interop ID. */
1377 blender::Vector<char> icc_profile;
1378
1379 const StringRefNull interop_id = colorspace->interop_id();
1380 if (interop_id.is_empty()) {
1381 return icc_profile;
1382 }
1383
1384 const std::optional<std::string> dir = BKE_appdir_folder_id(BLENDER_DATAFILES,
1385 "colormanagement");
1386 if (!dir.has_value()) {
1387 return icc_profile;
1388 }
1389
1390 char icc_filename[FILE_MAX];
1391 STRNCPY(icc_filename, (interop_id + ".icc").c_str());
1392 BLI_path_make_safe_filename(icc_filename);
1393
1394 char icc_filepath[FILE_MAX];
1395 BLI_path_join(icc_filepath, sizeof(icc_filepath), dir->c_str(), "icc", icc_filename);
1396
1397 blender::fstream f(icc_filepath, std::ios::binary | std::ios::in | std::ios::ate);
1398 if (!f.is_open()) {
1399 /* If we can't find a scene referred filename, try display referred. */
1400 StringRef icc_filepath_ref = icc_filepath;
1401 if (icc_filepath_ref.endswith("_scene.icc")) {
1402 std::string icc_filepath_display = icc_filepath_ref.drop_suffix(strlen("_scene.icc")) +
1403 "_display.icc";
1404 f.open(icc_filepath_display, std::ios::binary | std::ios::in | std::ios::ate);
1405 }
1406
1407 if (!f.is_open()) {
1408 return icc_profile;
1409 }
1410 }
1411
1412 std::streamsize size = f.tellg();
1413 if (size <= 0) {
1414 return icc_profile;
1415 }
1416 icc_profile.resize(size);
1417
1418 f.seekg(0, std::ios::beg);
1419 if (!f.read(icc_profile.data(), icc_profile.size())) {
1420 icc_profile.clear();
1421 }
1422
1423 return icc_profile;
1424}
1425
1426/* Primaries */
1427static const int CICP_PRI_REC709 = 1;
1428static const int CICP_PRI_REC2020 = 9;
1429static const int CICP_PRI_P3D65 = 12;
1430/* Transfer functions */
1431static const int CICP_TRC_BT709 = 1;
1432static const int CICP_TRC_G22 = 4;
1433static const int CICP_TRC_SRGB = 13;
1434static const int CICP_TRC_PQ = 16;
1435static const int CICP_TRC_G26 = 17;
1436static const int CICP_TRC_HLG = 18;
1437/* Matrix */
1438static const int CICP_MATRIX_RGB = 0;
1439static const int CICP_MATRIX_BT709 = 1;
1440static const int CICP_MATRIX_REC2020_NCL = 9;
1441/* Range */
1442static const int CICP_RANGE_FULL = 1;
1443
1446 const bool rgb_matrix,
1447 int cicp[4])
1448{
1449 const StringRefNull interop_id = colorspace->interop_id();
1450 if (interop_id.is_empty()) {
1451 return false;
1452 }
1453
1454 /* References:
1455 * ASWF Color Interop Forum defined display spaces.
1456 * https://en.wikipedia.org/wiki/Coding-independent_code_points
1457 * https://www.w3.org/TR/png-3/#cICP-chunk
1458 */
1459 if (interop_id == "pq_rec2020_display") {
1460 cicp[0] = CICP_PRI_REC2020;
1461 cicp[1] = CICP_TRC_PQ;
1462 cicp[2] = (rgb_matrix) ? CICP_MATRIX_RGB : CICP_MATRIX_REC2020_NCL;
1463 cicp[3] = CICP_RANGE_FULL;
1464 return true;
1465 }
1466 if (interop_id == "hlg_rec2020_display") {
1467 cicp[0] = CICP_PRI_REC2020;
1468 cicp[1] = CICP_TRC_HLG;
1469 cicp[2] = (rgb_matrix) ? CICP_MATRIX_RGB : CICP_MATRIX_REC2020_NCL;
1470 cicp[3] = CICP_RANGE_FULL;
1471 return true;
1472 }
1473 if (interop_id == "pq_p3d65_display") {
1474 /* Rec.2020 matrix may seem odd, but follows Color Interop Forum recommendation. */
1475 cicp[0] = CICP_PRI_P3D65;
1476 cicp[1] = CICP_TRC_PQ;
1477 cicp[2] = (rgb_matrix) ? CICP_MATRIX_RGB : CICP_MATRIX_REC2020_NCL;
1478 cicp[3] = CICP_RANGE_FULL;
1479 return true;
1480 }
1481 if (interop_id == "g26_p3d65_display") {
1482 /* BT.709 matrix may seem odd, but follows Color Interop Forum recommendation. */
1483 cicp[0] = CICP_PRI_P3D65;
1484 cicp[1] = CICP_TRC_G26;
1485 cicp[2] = (rgb_matrix) ? CICP_MATRIX_RGB : CICP_MATRIX_BT709;
1486 cicp[3] = CICP_RANGE_FULL;
1487 return true;
1488 }
1489 if (interop_id == "g22_rec709_display") {
1490 cicp[0] = CICP_PRI_REC709;
1491 cicp[1] = CICP_TRC_G22;
1492 cicp[2] = (rgb_matrix) ? CICP_MATRIX_RGB : CICP_MATRIX_BT709;
1493 cicp[3] = CICP_RANGE_FULL;
1494 return true;
1495 }
1496 if (interop_id == "g24_rec2020_display") {
1497 /* There is no gamma 2.4 trc, but BT.709 is close. */
1498 cicp[0] = CICP_PRI_REC2020;
1499 cicp[1] = CICP_TRC_BT709;
1500 cicp[2] = (rgb_matrix) ? CICP_MATRIX_RGB : CICP_MATRIX_REC2020_NCL;
1501 cicp[3] = CICP_RANGE_FULL;
1502 return true;
1503 }
1504 if (interop_id == "g24_rec709_display") {
1505 /* There is no gamma 2.4 trc, but BT.709 is close. */
1506 cicp[0] = CICP_PRI_REC709;
1507 cicp[1] = CICP_TRC_BT709;
1508 cicp[2] = (rgb_matrix) ? CICP_MATRIX_RGB : CICP_MATRIX_BT709;
1509 cicp[3] = CICP_RANGE_FULL;
1510 return true;
1511 }
1512 if (ELEM(interop_id, "srgb_p3d65_display", "srgbe_p3d65_display")) {
1513 /* For video we use BT.709 to match default sRGB writing, even though it is wrong.
1514 * But we have been writing sRGB like this forever, and there is the so called
1515 * "Quicktime gamma shift bug" that complicates things. */
1516 cicp[0] = CICP_PRI_P3D65;
1518 cicp[2] = (rgb_matrix) ? CICP_MATRIX_RGB : CICP_MATRIX_BT709;
1519 cicp[3] = CICP_RANGE_FULL;
1520 return true;
1521 }
1522 if (interop_id == "srgb_rec709_display") {
1523 /* Don't write anything for backwards compatibility. Is fine for PNG
1524 * and video but may reconsider when JXL or AVIF get added. */
1525 return false;
1526 }
1527
1528 return false;
1529}
1530
1533{
1534 StringRefNull interop_id;
1535
1536 /* We don't care about matrix or range, we assume decoding handles that and we get
1537 * full range RGB values out. */
1538 if (cicp[0] == CICP_PRI_REC2020 && cicp[1] == CICP_TRC_PQ) {
1539 interop_id = "pq_rec2020_display";
1540 }
1541 else if (cicp[0] == CICP_PRI_REC2020 && cicp[1] == CICP_TRC_HLG) {
1542 interop_id = "hlg_rec2020_display";
1543 }
1544 else if (cicp[0] == CICP_PRI_P3D65 && cicp[1] == CICP_TRC_PQ) {
1545 interop_id = "pq_p3d65_display";
1546 }
1547 else if (cicp[0] == CICP_PRI_P3D65 && cicp[1] == CICP_TRC_G26) {
1548 interop_id = "g26_p3d65_display";
1549 }
1550 else if (cicp[0] == CICP_PRI_REC709 && cicp[1] == CICP_TRC_G22) {
1551 interop_id = "g22_rec709_display";
1552 }
1553 else if (cicp[0] == CICP_PRI_REC2020 && cicp[1] == CICP_TRC_BT709) {
1554 interop_id = "g24_rec2020_display";
1555 }
1556 else if (cicp[0] == CICP_PRI_REC709 && cicp[1] == CICP_TRC_BT709) {
1558 /* Arguably this should be g24_rec709_display, but we write sRGB like this.
1559 * So there is an exception for now. */
1560 interop_id = "srgb_rec709_display";
1561 }
1562 else {
1563 interop_id = "g24_rec709_display";
1564 }
1565 }
1566 else if (cicp[0] == CICP_PRI_P3D65 && ELEM(cicp[1], CICP_TRC_SRGB, CICP_TRC_BT709)) {
1567 interop_id = "srgb_p3d65_display";
1568 }
1569 else if (cicp[0] == CICP_PRI_REC709 && cicp[1] == CICP_TRC_SRGB) {
1570 interop_id = "srgb_rec709_display";
1571 }
1572
1573 return interop_id.is_empty() ? nullptr : g_config->get_color_space_by_interop_id(interop_id);
1574}
1575
1577{
1578 return colorspace->interop_id();
1579}
1580
1582{
1583 return g_config->get_color_space_by_interop_id(interop_id);
1584}
1585
1590
1595
1597
1598/* -------------------------------------------------------------------- */
1601
1602void IMB_colormanagement_get_whitepoint(const float temperature,
1603 const float tint,
1604 float whitepoint[3])
1605{
1608}
1609
1610bool IMB_colormanagement_set_whitepoint(const float whitepoint[3], float &temperature, float &tint)
1611{
1612 blender::float3 xyz;
1614 return blender::math::whitepoint_to_temp_tint(xyz, temperature, tint);
1615}
1616
1618
1619/* -------------------------------------------------------------------- */
1622
1644
1659
1661 int start_line,
1662 int tot_line,
1664{
1665 ImBuf *ibuf = init_data->ibuf;
1666
1667 int channels = ibuf->channels;
1668 float dither = ibuf->dither;
1669 bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
1670
1671 size_t offset = size_t(channels) * start_line * ibuf->x;
1672 size_t display_buffer_byte_offset = size_t(DISPLAY_BUFFER_CHANNELS) * start_line * ibuf->x;
1673
1674 memset(handle, 0, sizeof(DisplayBufferThread));
1675
1676 handle->cm_processor = init_data->cm_processor;
1677
1678 if (init_data->buffer) {
1679 handle->buffer = init_data->buffer + offset;
1680 }
1681
1682 if (init_data->byte_buffer) {
1683 handle->byte_buffer = init_data->byte_buffer + offset;
1684 }
1685
1686 if (init_data->display_buffer) {
1687 handle->display_buffer = init_data->display_buffer + offset;
1688 }
1689
1690 if (init_data->display_buffer_byte) {
1691 handle->display_buffer_byte = init_data->display_buffer_byte + display_buffer_byte_offset;
1692 }
1693
1694 handle->width = ibuf->x;
1695
1696 handle->start_line = start_line;
1697 handle->tot_line = tot_line;
1698
1699 handle->channels = channels;
1700 handle->dither = dither;
1701 handle->is_data = is_data;
1702 handle->predivide = IMB_alpha_affects_rgb(ibuf);
1703
1704 handle->byte_colorspace = init_data->byte_colorspace;
1705 handle->float_colorspace = init_data->float_colorspace;
1706}
1707
1709 int height,
1710 float *linear_buffer,
1711 bool *is_straight_alpha)
1712{
1713 int channels = handle->channels;
1714 int width = handle->width;
1715
1716 size_t buffer_size = size_t(channels) * width * height;
1717
1718 bool is_data = handle->is_data;
1719 bool is_data_display = handle->cm_processor->is_data_result;
1720 bool predivide = handle->predivide;
1721
1722 if (!handle->buffer) {
1723 uchar *byte_buffer = handle->byte_buffer;
1724
1725 const char *from_colorspace = handle->byte_colorspace;
1726 const char *to_colorspace = global_role_scene_linear;
1727
1728 float *fp;
1729 uchar *cp;
1730 const size_t i_last = size_t(width) * height;
1731 size_t i;
1732
1733 /* first convert byte buffer to float, keep in image space */
1734 for (i = 0, fp = linear_buffer, cp = byte_buffer; i != i_last;
1735 i++, fp += channels, cp += channels)
1736 {
1737 if (channels == 3) {
1738 rgb_uchar_to_float(fp, cp);
1739 }
1740 else if (channels == 4) {
1741 rgba_uchar_to_float(fp, cp);
1742 }
1743 else {
1744 BLI_assert_msg(0, "Buffers of 3 or 4 channels are only supported here");
1745 }
1746 }
1747
1748 if (!is_data && !is_data_display) {
1749 /* convert float buffer to scene linear space */
1751 linear_buffer, width, height, channels, from_colorspace, to_colorspace, false);
1752 }
1753
1754 *is_straight_alpha = true;
1755 }
1756 else if (handle->float_colorspace) {
1757 /* currently float is non-linear only in sequencer, which is working
1758 * in its own color space even to handle float buffers.
1759 * This color space is the same for byte and float images.
1760 * Need to convert float buffer to linear space before applying display transform
1761 */
1762
1763 const char *from_colorspace = handle->float_colorspace;
1764 const char *to_colorspace = global_role_scene_linear;
1765
1766 memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1767
1768 if (!is_data && !is_data_display) {
1770 linear_buffer, width, height, channels, from_colorspace, to_colorspace, predivide);
1771 }
1772
1773 *is_straight_alpha = false;
1774 }
1775 else {
1776 /* some processors would want to modify float original buffer
1777 * before converting it into display byte buffer, so we need to
1778 * make sure original's ImBuf buffers wouldn't be modified by
1779 * using duplicated buffer here
1780 */
1781
1782 memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1783
1784 *is_straight_alpha = false;
1785 }
1786}
1787
1789{
1790 const int width = handle->width;
1791 const int height = handle->tot_line;
1792 if (handle->display_buffer_byte && handle->display_buffer_byte != handle->byte_buffer) {
1793 if (handle->byte_buffer) {
1795 handle->byte_buffer,
1798 false,
1799 width,
1800 height,
1801 width,
1802 width);
1803 }
1804 else if (handle->buffer) {
1806 handle->buffer,
1807 handle->channels,
1808 handle->dither,
1811 handle->predivide,
1812 width,
1813 height,
1814 width,
1815 width,
1816 handle->start_line);
1817 }
1818 }
1819
1820 if (handle->display_buffer) {
1821 if (handle->byte_buffer) {
1823 handle->byte_buffer,
1826 false,
1827 width,
1828 height,
1829 width,
1830 width);
1831 }
1832 else if (handle->buffer && handle->display_buffer != handle->buffer) {
1834 handle->buffer,
1835 handle->channels,
1838 handle->predivide,
1839 width,
1840 height,
1841 width,
1842 width);
1843 }
1844 }
1845}
1846
1848{
1849 ColormanageProcessor *cm_processor = handle->cm_processor;
1850 if (cm_processor == nullptr) {
1852 return;
1853 }
1854
1855 float *display_buffer = handle->display_buffer;
1856 uchar *display_buffer_byte = handle->display_buffer_byte;
1857 int channels = handle->channels;
1858 int width = handle->width;
1859 int height = handle->tot_line;
1860 float *linear_buffer = MEM_malloc_arrayN<float>(
1861 size_t(channels) * size_t(width) * size_t(height), "color conversion linear buffer");
1862
1863 bool is_straight_alpha;
1864 display_buffer_apply_get_linear_buffer(handle, height, linear_buffer, &is_straight_alpha);
1865
1866 bool predivide = handle->predivide && (is_straight_alpha == false);
1867
1868 /* Apply processor (note: data buffers never get color space conversions). */
1869 if (!handle->is_data) {
1871 cm_processor, linear_buffer, width, height, channels, predivide);
1872 }
1873
1874 /* copy result to output buffers */
1875 if (display_buffer_byte) {
1876 /* do conversion */
1877 IMB_buffer_byte_from_float(display_buffer_byte,
1878 linear_buffer,
1879 channels,
1880 handle->dither,
1883 predivide,
1884 width,
1885 height,
1886 width,
1887 width,
1888 handle->start_line);
1889 }
1890
1891 if (display_buffer) {
1892 memcpy(display_buffer, linear_buffer, size_t(width) * height * channels * sizeof(float));
1893
1894 if (is_straight_alpha && channels == 4) {
1895 const size_t i_last = size_t(width) * height;
1896 size_t i;
1897 float *fp;
1898
1899 for (i = 0, fp = display_buffer; i != i_last; i++, fp += channels) {
1901 }
1902 }
1903 }
1904
1905 MEM_freeN(linear_buffer);
1906}
1907
1909 const float *buffer,
1910 uchar *byte_buffer,
1911 float *display_buffer,
1912 uchar *display_buffer_byte,
1913 ColormanageProcessor *cm_processor)
1914{
1915 using namespace blender;
1917
1918 init_data.ibuf = ibuf;
1919 init_data.cm_processor = cm_processor;
1920 init_data.buffer = buffer;
1921 init_data.byte_buffer = byte_buffer;
1922 init_data.display_buffer = display_buffer;
1923 init_data.display_buffer_byte = display_buffer_byte;
1924
1925 if (ibuf->byte_buffer.colorspace != nullptr) {
1926 init_data.byte_colorspace = ibuf->byte_buffer.colorspace->name().c_str();
1927 }
1928 else {
1929 /* happens for viewer images, which are not so simple to determine where to
1930 * set image buffer's color spaces
1931 */
1932 init_data.byte_colorspace = global_role_default_byte;
1933 }
1934
1935 if (ibuf->float_buffer.colorspace != nullptr) {
1936 /* sequencer stores float buffers in non-linear space */
1937 init_data.float_colorspace = ibuf->float_buffer.colorspace->name().c_str();
1938 }
1939 else {
1940 init_data.float_colorspace = nullptr;
1941 }
1942
1943 threading::parallel_for(IndexRange(ibuf->y), 64, [&](const IndexRange y_range) {
1944 DisplayBufferThread handle;
1945 display_buffer_init_handle(&handle, y_range.first(), y_range.size(), &init_data);
1946 do_display_buffer_apply_thread(&handle);
1947 });
1948}
1949
1950/* Checks if given colorspace can be used for display as-is:
1951 * - View settings do not have extra curves, exposure, gamma or look applied, and
1952 * - Display colorspace is the same as given colorspace. */
1954 const ColorManagedViewSettings *view_settings,
1955 const ColorManagedDisplaySettings *display_settings)
1956{
1957 if ((view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) != 0 ||
1958 (view_settings->flag & COLORMANAGE_VIEW_USE_WHITE_BALANCE) != 0 ||
1959 view_settings->exposure != 0.0f || view_settings->gamma != 1.0f)
1960 {
1961 return false;
1962 }
1963
1964 const ocio::Look *look = g_config->get_look_by_name(view_settings->look);
1965 if (look != nullptr && !look->process_space().is_empty()) {
1966 return false;
1967 }
1968
1969 const ColorSpace *display_colorspace = IMB_colormangement_display_get_color_space(
1970 view_settings, display_settings);
1971 if (display_colorspace != colorspace) {
1972 return false;
1973 }
1974
1975 const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
1976 const ocio::View *untonemapped_view = (display) ? display->get_untonemapped_view() : nullptr;
1977 return untonemapped_view && untonemapped_view->name() == view_settings->view_transform;
1978}
1979
1981 const ImBuf *ibuf,
1982 const ColorManagedViewSettings *view_settings,
1983 const ColorManagedDisplaySettings *display_settings)
1984{
1985 if (ibuf->float_buffer.data == nullptr && ibuf->byte_buffer.colorspace) {
1987 ibuf->byte_buffer.colorspace, view_settings, display_settings);
1988 }
1989 if (ibuf->byte_buffer.data == nullptr && ibuf->float_buffer.colorspace) {
1991 ibuf->float_buffer.colorspace, view_settings, display_settings);
1992 }
1993 return true;
1994}
1995
1997 const ImBuf *ibuf,
1998 const ColorManagedViewSettings *view_settings,
1999 const ColorManagedDisplaySettings *display_settings,
2000 const ColorManagedDisplaySpace display_space)
2001{
2002 if (!IMB_colormanagement_display_processor_needed(ibuf, view_settings, display_settings)) {
2003 return nullptr;
2004 }
2005 return IMB_colormanagement_display_processor_new(view_settings, display_settings, display_space);
2006}
2007
2009 ImBuf *ibuf,
2010 float *display_buffer,
2011 uchar *display_buffer_byte,
2012 const ColorManagedViewSettings *view_settings,
2013 const ColorManagedDisplaySettings *display_settings,
2014 const ColorManagedDisplaySpace display_space)
2015{
2017 ibuf, view_settings, display_settings, display_space);
2019 ibuf->float_buffer.data,
2020 ibuf->byte_buffer.data,
2021 display_buffer,
2022 display_buffer_byte,
2023 cm_processor);
2024
2025 if (cm_processor) {
2027 }
2028}
2029
2031 uchar *display_buffer,
2032 const ColorManagedViewSettings *view_settings,
2033 const ColorManagedDisplaySettings *display_settings,
2034 const ColorManagedDisplaySpace display_space)
2035{
2037 ibuf, nullptr, display_buffer, view_settings, display_settings, display_space);
2038}
2039
2041
2042/* -------------------------------------------------------------------- */
2045
2057
2068
2070 int start_line,
2071 int tot_line,
2073{
2074 const int channels = init_data->channels;
2075 const int width = init_data->width;
2076 const bool predivide = init_data->predivide;
2077 const bool float_from_byte = init_data->float_from_byte;
2078
2079 const size_t offset = size_t(channels) * start_line * width;
2080
2081 memset(handle, 0, sizeof(ProcessorTransformThread));
2082
2083 handle->cm_processor = init_data->cm_processor;
2084
2085 if (init_data->byte_buffer != nullptr) {
2086 /* TODO(serge): Offset might be different for byte and float buffers. */
2087 handle->byte_buffer = init_data->byte_buffer + offset;
2088 }
2089 if (init_data->float_buffer != nullptr) {
2090 handle->float_buffer = init_data->float_buffer + offset;
2091 }
2092
2093 handle->width = width;
2094
2095 handle->start_line = start_line;
2096 handle->tot_line = tot_line;
2097
2098 handle->channels = channels;
2099 handle->predivide = predivide;
2100 handle->float_from_byte = float_from_byte;
2101}
2102
2104{
2105 uchar *byte_buffer = handle->byte_buffer;
2106 float *float_buffer = handle->float_buffer;
2107 const int channels = handle->channels;
2108 const int width = handle->width;
2109 const int height = handle->tot_line;
2110 const bool predivide = handle->predivide;
2111 const bool float_from_byte = handle->float_from_byte;
2112
2113 if (float_from_byte) {
2114 IMB_buffer_float_from_byte(float_buffer,
2115 byte_buffer,
2118 false,
2119 width,
2120 height,
2121 width,
2122 width);
2124 handle->cm_processor, float_buffer, width, height, channels, predivide);
2125 IMB_premultiply_rect_float(float_buffer, 4, width, height);
2126 }
2127 else {
2128 if (byte_buffer != nullptr) {
2130 handle->cm_processor, byte_buffer, width, height, channels);
2131 }
2132 if (float_buffer != nullptr) {
2134 handle->cm_processor, float_buffer, width, height, channels, predivide);
2135 }
2136 }
2137}
2138
2140 float *float_buffer,
2141 const int width,
2142 const int height,
2143 const int channels,
2144 ColormanageProcessor *cm_processor,
2145 const bool predivide,
2146 const bool float_from_byte)
2147{
2148 using namespace blender;
2150
2151 init_data.cm_processor = cm_processor;
2152 init_data.byte_buffer = byte_buffer;
2153 init_data.float_buffer = float_buffer;
2154 init_data.width = width;
2155 init_data.height = height;
2156 init_data.channels = channels;
2157 init_data.predivide = predivide;
2158 init_data.float_from_byte = float_from_byte;
2159
2160 threading::parallel_for(IndexRange(height), 64, [&](const IndexRange y_range) {
2162 processor_transform_init_handle(&handle, y_range.first(), y_range.size(), &init_data);
2164 });
2165}
2166
2168
2169/* -------------------------------------------------------------------- */
2172
2173/* Convert the whole buffer from specified by name color space to another -
2174 * internal implementation. */
2175static void colormanagement_transform_ex(uchar *byte_buffer,
2176 float *float_buffer,
2177 int width,
2178 int height,
2179 int channels,
2180 const char *from_colorspace,
2181 const char *to_colorspace,
2182 bool predivide)
2183{
2184 if (from_colorspace[0] == '\0') {
2185 return;
2186 }
2187
2188 if (STREQ(from_colorspace, to_colorspace)) {
2189 /* if source and destination color spaces are identical, do nothing. */
2190 return;
2191 }
2192
2194 from_colorspace, to_colorspace);
2195 if (IMB_colormanagement_processor_is_noop(cm_processor)) {
2197 return;
2198 }
2199
2201 byte_buffer, float_buffer, width, height, channels, cm_processor, predivide, false);
2203}
2204
2206 int width,
2207 int height,
2208 int channels,
2209 const char *from_colorspace,
2210 const char *to_colorspace,
2211 bool predivide)
2212{
2214 nullptr, buffer, width, height, channels, from_colorspace, to_colorspace, predivide);
2215}
2216
2218 int width,
2219 int height,
2220 int channels,
2221 const char *from_colorspace,
2222 const char *to_colorspace)
2223{
2225 buffer, nullptr, width, height, channels, from_colorspace, to_colorspace, false);
2226}
2227
2229 uchar *byte_buffer,
2230 int width,
2231 int height,
2232 int channels,
2233 const char *from_colorspace,
2234 const char *to_colorspace)
2235{
2236 using namespace blender;
2237 ColormanageProcessor *cm_processor;
2238 if (from_colorspace == nullptr || from_colorspace[0] == '\0') {
2239 return;
2240 }
2241 if (STREQ(from_colorspace, to_colorspace) && channels == 4) {
2242 /* Color spaces are the same, just do a simple byte->float conversion. */
2243 int64_t pixel_count = int64_t(width) * height;
2244 threading::parallel_for(IndexRange(pixel_count), 256 * 1024, [&](IndexRange pix_range) {
2245 float *dst_ptr = float_buffer + pix_range.first() * channels;
2246 uchar *src_ptr = byte_buffer + pix_range.first() * channels;
2247 for ([[maybe_unused]] const int i : pix_range) {
2248 /* Equivalent of rgba_uchar_to_float + premultiply. */
2249 float cr = float(src_ptr[0]) * (1.0f / 255.0f);
2250 float cg = float(src_ptr[1]) * (1.0f / 255.0f);
2251 float cb = float(src_ptr[2]) * (1.0f / 255.0f);
2252 float ca = float(src_ptr[3]) * (1.0f / 255.0f);
2253 dst_ptr[0] = cr * ca;
2254 dst_ptr[1] = cg * ca;
2255 dst_ptr[2] = cb * ca;
2256 dst_ptr[3] = ca;
2257 src_ptr += 4;
2258 dst_ptr += 4;
2259 }
2260 });
2261 return;
2262 }
2263 cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
2265 byte_buffer, float_buffer, width, height, channels, cm_processor, false, true);
2267}
2268
2270 const char *from_colorspace,
2271 const char *to_colorspace)
2272{
2273 ColormanageProcessor *cm_processor;
2274
2275 if (from_colorspace[0] == '\0') {
2276 return;
2277 }
2278
2279 if (STREQ(from_colorspace, to_colorspace)) {
2280 /* if source and destination color spaces are identical, skip
2281 * threading overhead and simply do nothing
2282 */
2283 return;
2284 }
2285
2286 cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
2287
2288 IMB_colormanagement_processor_apply_v4(cm_processor, pixel);
2289
2291}
2292
2294 const ColorSpace *colorspace)
2295{
2296 if (colorspace == nullptr) { /* should never happen */
2297 printf("%s: perform conversion from unknown color space\n", __func__);
2298 return;
2299 }
2300 const ocio::CPUProcessor *processor = colorspace->get_to_scene_linear_cpu_processor();
2301 if (processor == nullptr) {
2302 return;
2303 }
2304 processor->apply_rgb(pixel);
2305}
2306
2308 const ColorSpace *colorspace)
2309{
2310 if (colorspace == nullptr) { /* should never happen */
2311 printf("%s: perform conversion from unknown color space\n", __func__);
2312 return;
2313 }
2314 const ocio::CPUProcessor *processor = colorspace->get_from_scene_linear_cpu_processor();
2315 if (processor == nullptr) {
2316 return;
2317 }
2318 processor->apply_rgb(pixel);
2319}
2320
2322 const bool predivide,
2323 const ColorSpace *colorspace)
2324{
2325 if (colorspace == nullptr) { /* should never happen */
2326 printf("%s: perform conversion from unknown color space\n", __func__);
2327 return;
2328 }
2329 const ocio::CPUProcessor *processor = colorspace->get_to_scene_linear_cpu_processor();
2330 if (processor == nullptr) {
2331 return;
2332 }
2333 if (predivide) {
2334 processor->apply_rgba_predivide(pixel);
2335 }
2336 else {
2337 processor->apply_rgba(pixel);
2338 }
2339}
2340
2342 const int width,
2343 const int height,
2344 const int channels,
2345 const ColorSpace *colorspace,
2346 const bool predivide)
2347{
2348 if (colorspace == nullptr) { /* should never happen */
2349 printf("%s: perform conversion from unknown color space\n", __func__);
2350 return;
2351 }
2352 const ocio::CPUProcessor *processor = colorspace->get_to_scene_linear_cpu_processor();
2353 if (processor == nullptr) {
2354 return;
2355 }
2356 ocio::PackedImage img(buffer,
2357 width,
2358 height,
2359 channels,
2361 sizeof(float),
2362 size_t(channels) * sizeof(float),
2363 size_t(channels) * sizeof(float) * width);
2364
2365 if (predivide) {
2366 processor->apply_predivide(img);
2367 }
2368 else {
2369 processor->apply(img);
2370 }
2371}
2372
2374 const int width,
2375 const int height,
2376 const int channels,
2377 const ColorSpace *colorspace)
2378{
2379 if (colorspace == nullptr) { /* should never happen */
2380 printf("%s: perform conversion from unknown color space\n", __func__);
2381 return;
2382 }
2383 const ocio::CPUProcessor *processor = colorspace->get_from_scene_linear_cpu_processor();
2384 if (processor == nullptr) {
2385 return;
2386 }
2387 const ocio::PackedImage img(buffer,
2388 width,
2389 height,
2390 channels,
2392 sizeof(float),
2393 size_t(channels) * sizeof(float),
2394 size_t(channels) * sizeof(float) * width);
2395 processor->apply(img);
2396}
2397
2399 const int offset_x,
2400 const int offset_y,
2401 const int width,
2402 const int height,
2403 const ImBuf *ibuf,
2404 const bool store_premultiplied)
2405{
2406 /* Byte buffer storage, only for sRGB, scene linear and data texture since other
2407 * color space conversions can't be done on the GPU. */
2409 BLI_assert(ibuf->float_buffer.data == nullptr);
2413
2414 const uchar *in_buffer = ibuf->byte_buffer.data;
2415 const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied;
2416
2417 for (int y = 0; y < height; y++) {
2418 const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
2419 const size_t out_offset = y * width;
2420 const uchar *in = in_buffer + in_offset * 4;
2421 uchar *out = out_buffer + out_offset * 4;
2422
2423 if (use_premultiply) {
2424 /* Premultiply only. */
2425 for (int x = 0; x < width; x++, in += 4, out += 4) {
2426 out[0] = (in[0] * in[3]) >> 8;
2427 out[1] = (in[1] * in[3]) >> 8;
2428 out[2] = (in[2] * in[3]) >> 8;
2429 out[3] = in[3];
2430 }
2431 }
2432 else {
2433 /* Copy only. */
2434 for (int x = 0; x < width; x++, in += 4, out += 4) {
2435 out[0] = in[0];
2436 out[1] = in[1];
2437 out[2] = in[2];
2438 out[3] = in[3];
2439 }
2440 }
2441 }
2442}
2443
2445 const int offset_x,
2446 const int offset_y,
2447 const int width,
2448 const int height,
2449 const ImBuf *ibuf,
2450 const bool store_premultiplied)
2451{
2452 using namespace blender;
2453
2454 /* Float texture are stored in scene linear color space, with premultiplied
2455 * alpha depending on the image alpha mode. */
2456 if (ibuf->float_buffer.data) {
2457 /* Float source buffer. */
2458 const float *in_buffer = ibuf->float_buffer.data;
2459 const int in_channels = ibuf->channels;
2460 const bool use_unpremultiply = IMB_alpha_affects_rgb(ibuf) && !store_premultiplied;
2461
2462 for (int y = 0; y < height; y++) {
2463 const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
2464 const size_t out_offset = y * width;
2465 const float *in = in_buffer + in_offset * in_channels;
2466 float *out = out_buffer + out_offset * 4;
2467
2468 if (in_channels == 1) {
2469 /* Copy single channel. */
2470 for (int x = 0; x < width; x++, in += 1, out += 4) {
2471 out[0] = in[0];
2472 out[1] = in[0];
2473 out[2] = in[0];
2474 out[3] = in[0];
2475 }
2476 }
2477 else if (in_channels == 3) {
2478 /* Copy RGB. */
2479 for (int x = 0; x < width; x++, in += 3, out += 4) {
2480 out[0] = in[0];
2481 out[1] = in[1];
2482 out[2] = in[2];
2483 out[3] = 1.0f;
2484 }
2485 }
2486 else if (in_channels == 4) {
2487 /* Copy or convert RGBA. */
2488 if (use_unpremultiply) {
2489 for (int x = 0; x < width; x++, in += 4, out += 4) {
2491 }
2492 }
2493 else {
2494 memcpy(out, in, sizeof(float[4]) * width);
2495 }
2496 }
2497 }
2498 }
2499 else {
2500 /* Byte source buffer. */
2501 const uchar *in_buffer = ibuf->byte_buffer.data;
2502 const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied;
2503
2504 const ocio::CPUProcessor *processor =
2505 (ibuf->byte_buffer.colorspace) ?
2507 nullptr;
2508
2509 threading::parallel_for(IndexRange(height), 128, [&](const IndexRange y_range) {
2510 for (const int y : y_range) {
2511 const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
2512 const size_t out_offset = y * width;
2513 const uchar *in = in_buffer + in_offset * 4;
2514 float *out = out_buffer + out_offset * 4;
2515 for (int x = 0; x < width; x++, in += 4, out += 4) {
2516 /* Convert to scene linear and premultiply. */
2517 float pixel[4];
2518 rgba_uchar_to_float(pixel, in);
2519 if (processor) {
2520 processor->apply_rgb(pixel);
2521 }
2522 else {
2523 srgb_to_linearrgb_v3_v3(pixel, pixel);
2524 }
2525 if (use_premultiply) {
2526 mul_v3_fl(pixel, pixel[3]);
2527 }
2528 copy_v4_v4(out, pixel);
2529 }
2530 }
2531 });
2532 }
2533}
2534
2536 const float scene_linear[3])
2537{
2538 if (!global_color_picking_state.cpu_processor_to && !global_color_picking_state.failed) {
2539 /* Create processor if none exists. */
2541
2542 if (!global_color_picking_state.cpu_processor_to && !global_color_picking_state.failed) {
2543 std::shared_ptr<const ocio::CPUProcessor> cpu_processor = g_config->get_cpu_processor(
2545
2546 if (cpu_processor) {
2547 global_color_picking_state.cpu_processor_to = cpu_processor;
2548 }
2549 else {
2550 global_color_picking_state.failed = true;
2551 }
2552 }
2553
2555 }
2556
2557 copy_v3_v3(color_picking, scene_linear);
2558
2559 if (global_color_picking_state.cpu_processor_to) {
2560 global_color_picking_state.cpu_processor_to->apply_rgb(color_picking);
2561 }
2562}
2563
2565 const float color_picking[3])
2566{
2567 if (!global_color_picking_state.cpu_processor_from && !global_color_picking_state.failed) {
2568 /* Create processor if none exists. */
2570
2571 if (!global_color_picking_state.cpu_processor_from && !global_color_picking_state.failed) {
2572 std::shared_ptr<const ocio::CPUProcessor> cpu_processor = g_config->get_cpu_processor(
2574
2575 if (cpu_processor) {
2576 global_color_picking_state.cpu_processor_from = cpu_processor;
2577 }
2578 else {
2579 global_color_picking_state.failed = true;
2580 }
2581 }
2582
2584 }
2585
2586 copy_v3_v3(scene_linear, color_picking);
2587
2588 if (global_color_picking_state.cpu_processor_from) {
2589 global_color_picking_state.cpu_processor_from->apply_rgb(scene_linear);
2590 }
2591}
2592
2594 const ColorManagedDisplay *display,
2595 const ColorManagedDisplaySpace display_space)
2596{
2597 const ocio::CPUProcessor *processor = display->get_from_scene_linear_cpu_processor(
2598 display_space == DISPLAY_SPACE_DRAW);
2599 if (processor != nullptr) {
2600 processor->apply_rgb(pixel);
2601 }
2602}
2603
2605 const ColorManagedDisplay *display,
2606 const ColorManagedDisplaySpace display_space)
2607{
2608 const ocio::CPUProcessor *processor = display->get_to_scene_linear_cpu_processor(
2609 display_space == DISPLAY_SPACE_DRAW);
2610 if (processor != nullptr) {
2611 processor->apply_rgb(pixel);
2612 }
2613}
2614
2616 float result[4],
2617 const float pixel[4],
2618 const ColorManagedViewSettings *view_settings,
2619 const ColorManagedDisplaySettings *display_settings,
2620 const ColorManagedDisplaySpace display_space)
2621{
2622 ColormanageProcessor *cm_processor;
2623
2624 copy_v4_v4(result, pixel);
2625
2627 view_settings, display_settings, display_space);
2630}
2631
2633 ImBuf *ibuf,
2634 const ColorManagedViewSettings *view_settings,
2635 const ColorManagedDisplaySettings *display_settings,
2636 const ColorManagedDisplaySpace display_space,
2637 bool make_byte)
2638{
2639 if (!ibuf->byte_buffer.data && make_byte) {
2641 }
2642
2644 ibuf->float_buffer.data,
2645 ibuf->byte_buffer.data,
2646 view_settings,
2647 display_settings,
2648 display_space);
2649}
2650
2652 ImBuf *ibuf,
2653 const ColorManagedViewSettings *view_settings,
2654 const ColorManagedDisplaySettings *display_settings,
2655 const ColorManagedDisplaySpace display_space)
2656{
2658 ibuf, view_settings, display_settings, display_space, false);
2659}
2660
2661static ImBuf *imbuf_ensure_editable(ImBuf *ibuf, ImBuf *colormanaged_ibuf, bool allocate_result)
2662{
2663 if (colormanaged_ibuf != ibuf) {
2664 /* Is already an editable copy. */
2665 return colormanaged_ibuf;
2666 }
2667
2668 if (allocate_result) {
2669 /* Copy full image buffer. */
2670 colormanaged_ibuf = IMB_dupImBuf(ibuf);
2671 IMB_metadata_copy(colormanaged_ibuf, ibuf);
2672 return colormanaged_ibuf;
2673 }
2674
2675 /* Render pipeline is constructing image buffer itself,
2676 * but it's re-using byte and float buffers from render result make copy of this buffers
2677 * here sine this buffers would be transformed to other color space here. */
2680
2681 return ibuf;
2682}
2683
2685 bool save_as_render,
2686 bool allocate_result,
2687 const ImageFormatData *image_format)
2688{
2689 ImBuf *colormanaged_ibuf = ibuf;
2690
2691 /* Update byte buffer if exists but invalid. */
2692 if (ibuf->float_buffer.data && ibuf->byte_buffer.data &&
2694 {
2695 IMB_byte_from_float(ibuf);
2697 }
2698
2699 /* Detect if we are writing to a file format that needs a linear float buffer. */
2700 const bool linear_float_output = BKE_imtype_requires_linear_float(image_format->imtype);
2701
2702 /* Detect if we are writing output a byte buffer, which we would need to create
2703 * with color management conversions applied. This may be for either applying the
2704 * display transform for renders, or a user specified color space for the file. */
2705 const bool byte_output = BKE_image_format_is_byte(image_format);
2706
2707 /* If we're saving from RGBA to RGB buffer then it's not so much useful to just ignore alpha --
2708 * it leads to bad artifacts especially when saving byte images.
2709 *
2710 * What we do here is we're overlaying our image on top of background color (which is currently
2711 * black). This is quite much the same as what Gimp does and it seems to be what artists
2712 * expects from saving.
2713 *
2714 * Do a conversion here, so image format writers could happily assume all the alpha tricks were
2715 * made already. helps keep things locally here, not spreading it to all possible image writers
2716 * we've got.
2717 */
2718 if (image_format->planes != R_IMF_PLANES_RGBA) {
2719 float color[3] = {0, 0, 0};
2720
2721 colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result);
2722
2723 if (colormanaged_ibuf->float_buffer.data && colormanaged_ibuf->channels == 4) {
2725 colormanaged_ibuf->float_buffer.data, colormanaged_ibuf->x, colormanaged_ibuf->y, color);
2726 }
2727
2728 if (colormanaged_ibuf->byte_buffer.data) {
2730 colormanaged_ibuf->byte_buffer.data, colormanaged_ibuf->x, colormanaged_ibuf->y, color);
2731 }
2732 }
2733
2734 if (save_as_render && !linear_float_output) {
2735 /* Render output: perform conversion to display space using view transform. */
2736 colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result);
2737
2739 &image_format->view_settings,
2740 &image_format->display_settings,
2741 image_format->media_type == MEDIA_TYPE_VIDEO ?
2744 byte_output);
2745
2746 if (colormanaged_ibuf->float_buffer.data) {
2747 /* Float buffer isn't linear anymore.
2748 * - Image format write callback checks for this flag and assumes no space
2749 * conversion should happen if ibuf->float_buffer.colorspace != nullptr. */
2751 &image_format->view_settings, &image_format->display_settings);
2752 if (byte_output) {
2753 colormanaged_ibuf->byte_buffer.colorspace = colormanaged_ibuf->float_buffer.colorspace;
2754 }
2755 }
2756 }
2757 else {
2758 /* Linear render or regular file output: conversion between two color spaces. */
2759
2760 /* Detect which color space we need to convert between. */
2761 const char *from_colorspace = (ibuf->float_buffer.data &&
2762 !(byte_output && ibuf->byte_buffer.data)) ?
2763 /* From float buffer. */
2764 (ibuf->float_buffer.colorspace) ?
2765 ibuf->float_buffer.colorspace->name().c_str() :
2767 /* From byte buffer. */
2768 (ibuf->byte_buffer.colorspace) ?
2769 ibuf->byte_buffer.colorspace->name().c_str() :
2771
2772 const char *to_colorspace = image_format->linear_colorspace_settings.name;
2773
2774 /* to_colorspace may need to modified to compensate for 100 vs 203 nits conventions. */
2775 if (image_format->media_type != MEDIA_TYPE_VIDEO) {
2776 const ColorSpace *image_colorspace = g_config->get_color_space_for_hdr_image(to_colorspace);
2777 if (image_colorspace) {
2778 to_colorspace = image_colorspace->name().c_str();
2779 }
2780 }
2781
2782 /* TODO: can we check with OCIO if color spaces are the same but have different names? */
2783 if (to_colorspace[0] == '\0' || STREQ(from_colorspace, to_colorspace)) {
2784 /* No conversion needed, but may still need to allocate byte buffer for output. */
2785 if (byte_output && !ibuf->byte_buffer.data) {
2787 IMB_byte_from_float(ibuf);
2788 }
2789 }
2790 else {
2791 /* Color space conversion needed. */
2792 colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result);
2793
2794 if (byte_output) {
2796 to_colorspace);
2797
2798 if (colormanaged_ibuf->byte_buffer.data) {
2799 /* Byte to byte. */
2801 colormanaged_ibuf->x,
2802 colormanaged_ibuf->y,
2803 colormanaged_ibuf->channels,
2804 from_colorspace,
2805 to_colorspace);
2806 }
2807 else {
2808 /* Float to byte. */
2809 IMB_byte_from_float(colormanaged_ibuf);
2810 }
2811 }
2812 else {
2813 if (!colormanaged_ibuf->float_buffer.data) {
2814 /* Byte to float. */
2815 IMB_float_from_byte(colormanaged_ibuf);
2816 IMB_free_byte_pixels(colormanaged_ibuf);
2817
2818 /* This conversion always goes to scene linear. */
2819 from_colorspace = global_role_scene_linear;
2820 }
2821
2822 if (colormanaged_ibuf->float_buffer.data) {
2823 /* Float to float. */
2825 colormanaged_ibuf->x,
2826 colormanaged_ibuf->y,
2827 colormanaged_ibuf->channels,
2828 from_colorspace,
2829 to_colorspace,
2830 false);
2831
2833 to_colorspace);
2834 }
2835 }
2836 }
2837 }
2838
2839 return colormanaged_ibuf;
2840}
2841
2843
2844/* -------------------------------------------------------------------- */
2847
2849 const ColorManagedViewSettings *view_settings,
2850 const ColorManagedDisplaySettings *display_settings,
2851 void **cache_handle)
2852{
2853 uchar *display_buffer;
2854 ColormanageCacheViewSettings cache_view_settings;
2855 ColormanageCacheDisplaySettings cache_display_settings;
2856 ColorManagedViewSettings untonemapped_view_settings;
2857 const ColorManagedViewSettings *applied_view_settings;
2858
2859 *cache_handle = nullptr;
2860
2861 if (!ibuf->x || !ibuf->y) {
2862 return nullptr;
2863 }
2864
2865 if (view_settings) {
2866 applied_view_settings = view_settings;
2867 }
2868 else {
2869 /* If no view settings were specified, use default ones, which will
2870 * attempt not to do any extra color correction. */
2871 IMB_colormanagement_init_untonemapped_view_settings(&untonemapped_view_settings,
2872 display_settings);
2873 applied_view_settings = &untonemapped_view_settings;
2874 }
2875
2876 /* No float buffer and byte buffer is already in display space, let's just use it. */
2877 if (ibuf->float_buffer.data == nullptr && ibuf->byte_buffer.colorspace && ibuf->channels == 4) {
2879 ibuf->byte_buffer.colorspace, applied_view_settings, display_settings))
2880 {
2881 return ibuf->byte_buffer.data;
2882 }
2883 }
2884
2885 colormanage_view_settings_to_cache(ibuf, &cache_view_settings, applied_view_settings);
2886 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
2887
2888 if (ibuf->invalid_rect.xmin != ibuf->invalid_rect.xmax) {
2889 if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
2891 ibuf->float_buffer.data,
2892 ibuf->byte_buffer.data,
2893 ibuf->x,
2894 0,
2895 0,
2896 applied_view_settings,
2897 display_settings,
2898 ibuf->invalid_rect.xmin,
2899 ibuf->invalid_rect.ymin,
2900 ibuf->invalid_rect.xmax,
2901 ibuf->invalid_rect.ymax);
2902 }
2903
2904 BLI_rcti_init(&ibuf->invalid_rect, 0, 0, 0, 0);
2905 }
2906
2908
2909 /* ensure color management bit fields exists */
2910 if (!ibuf->display_buffer_flags) {
2911 ibuf->display_buffer_flags = MEM_calloc_arrayN<uint>(g_config->get_num_displays(),
2912 "imbuf display_buffer_flags");
2913 }
2914 else if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) {
2915 /* all display buffers were marked as invalid from other areas,
2916 * now propagate this flag to internal color management routines
2917 */
2918 memset(ibuf->display_buffer_flags, 0, g_config->get_num_displays() * sizeof(uint));
2919
2921 }
2922
2923 display_buffer = colormanage_cache_get(
2924 ibuf, &cache_view_settings, &cache_display_settings, cache_handle);
2925
2926 if (display_buffer) {
2928 return display_buffer;
2929 }
2930
2931 display_buffer = MEM_malloc_arrayN<uchar>(
2932 DISPLAY_BUFFER_CHANNELS * size_t(ibuf->x) * size_t(ibuf->y), "imbuf display buffer");
2933
2935 ibuf, display_buffer, applied_view_settings, display_settings, DISPLAY_SPACE_DRAW);
2936
2938 ibuf, &cache_view_settings, &cache_display_settings, display_buffer, cache_handle);
2939
2941
2942 return display_buffer;
2943}
2944
2945uchar *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle)
2946{
2947 ColorManagedViewSettings *view_settings;
2948 ColorManagedDisplaySettings *display_settings;
2949
2950 IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
2951
2952 return IMB_display_buffer_acquire(ibuf, view_settings, display_settings, cache_handle);
2953}
2954
2956 float *linear_buffer,
2957 int width,
2958 int height,
2959 int channels,
2960 const ColorManagedViewSettings *view_settings,
2961 const ColorManagedDisplaySettings *display_settings,
2962 bool predivide)
2963{
2964 float *buffer;
2966 display_settings);
2967
2968 buffer = MEM_malloc_arrayN<float>(size_t(channels) * size_t(width) * size_t(height),
2969 "display transform temp buffer");
2970 memcpy(buffer, linear_buffer, size_t(channels) * width * height * sizeof(float));
2971
2972 IMB_colormanagement_processor_apply(cm_processor, buffer, width, height, channels, predivide);
2973
2975
2976 IMB_buffer_byte_from_float(display_buffer,
2977 buffer,
2978 channels,
2979 0.0f,
2982 false,
2983 width,
2984 height,
2985 width,
2986 width);
2987
2988 MEM_freeN(buffer);
2989}
2990
2991void IMB_display_buffer_release(void *cache_handle)
2992{
2993 if (cache_handle) {
2995
2997
2999 }
3000}
3001
3003
3004/* -------------------------------------------------------------------- */
3007
3009{
3010 const ocio::Display *display = g_config->get_display_by_name(name);
3011 if (display) {
3012 return display->index;
3013 }
3014 return -1;
3015}
3016
3018{
3019 const ocio::Display *display = g_config->get_display_by_index(index);
3020 if (!display) {
3021 return "";
3022 }
3023 return display->name().c_str();
3024}
3025
3027{
3028 const ocio::Display *display = g_config->get_default_display();
3029 return display->name().c_str();
3030}
3031
3033{
3034 return g_config->get_display_by_name(name);
3035}
3036
3038{
3039 if (g_config->get_display_by_name("None") != nullptr) {
3040 return "None";
3041 }
3043}
3044
3046 const ColorManagedDisplay *display)
3047{
3048 const ocio::View *default_view = display->get_default_view();
3049 if (!default_view) {
3050 return "";
3051 }
3052 return default_view->name().c_str();
3053}
3054
3056 const ColorManagedViewSettings *view_settings,
3057 const ColorManagedDisplaySettings *display_settings)
3058{
3059 /* Get the colorspace that the image is in after applying this view and display
3060 * transform. If we are going to a display referred colorspace we can use that. */
3061 const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
3062 const ocio::View *view = (display) ? display->get_view_by_name(view_settings->view_transform) :
3063 nullptr;
3064 const ColorSpace *colorspace = (view) ? view->display_colorspace() : nullptr;
3065 if (colorspace && colorspace->is_display_referred()) {
3066 return colorspace;
3067 }
3068 /* If not available, try to guess what the untonemapped view is and use its colorspace.
3069 * This is especially needed for v1 configs. */
3070 const ocio::View *untonemapped_view = (display) ? display->get_untonemapped_view() : nullptr;
3071 const ocio::ColorSpace *untonemapped_colorspace = (untonemapped_view) ?
3072 g_config->get_display_view_color_space(
3073 display_settings->display_device,
3074 untonemapped_view->name()) :
3075 nullptr;
3076 return (untonemapped_colorspace) ? untonemapped_colorspace : colorspace;
3077}
3078
3080 const char *view_name)
3081{
3082 const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
3083 if (display == nullptr) {
3084 return false;
3085 }
3086 const ocio::View *view = display->get_view_by_name(view_name);
3087 return (view) ? view->is_hdr() : false;
3088}
3089
3091 const char *view_name)
3092{
3093 const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
3094 if (display == nullptr) {
3095 return false;
3096 }
3097 const ocio::View *view = display->get_view_by_name(view_name);
3098 return (view) ? view->gamut() != ocio::Gamut::Rec709 : false;
3099}
3100
3102 const ColorManagedDisplaySettings *display_settings, const char *view_name)
3103{
3104 const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
3105 if (display == nullptr) {
3106 return false;
3107 }
3108 const ocio::View *view = display->get_view_by_name(view_name);
3109 return (view) ? view->support_emulation() : false;
3110}
3111
3113
3114/* -------------------------------------------------------------------- */
3117
3119{
3120 return g_all_view_names.index_of_try(name);
3121}
3122
3124{
3125 /* The code is expected to be used for the purposes of the EnumPropertyItem and maintaining the
3126 * DNA values from RNA's get() and set(). It is unexpected that an invalid index will be passed
3127 * here, as it will indicate a coding error somewhere else. */
3128 if (index < 0 || index >= g_all_view_names.size()) {
3129 BLI_assert(0);
3130 return "";
3131 }
3132
3133 return g_all_view_names[index].c_str();
3134}
3135
3136const char *IMB_colormanagement_view_get_default_name(const char *display_name)
3137{
3138 const ocio::Display *display = g_config->get_display_by_name(display_name);
3139 if (!display) {
3140 return "";
3141 }
3142
3143 const ocio::View *view = display->get_default_view();
3144 if (!view) {
3145 return "";
3146 }
3147
3148 return view->name().c_str();
3149}
3150
3151const char *IMB_colormanagement_view_get_raw_or_default_name(const char *display_name)
3152{
3153 const ocio::Display *display = g_config->get_display_by_name(display_name);
3154 if (!display) {
3155 return "";
3156 }
3157
3158 const ocio::View *view = nullptr;
3159
3160 if (!view) {
3161 view = display->get_view_by_name("Raw");
3162 }
3163
3164 if (!view) {
3165 view = display->get_default_view();
3166 }
3167
3168 if (!view) {
3169 return "";
3170 }
3171
3172 return view->name().c_str();
3173}
3174
3176
3177/* -------------------------------------------------------------------- */
3180
3182{
3183 return g_config->get_color_space(name);
3184}
3185
3187{
3188 const char *role_colorspace = IMB_colormanagement_role_colorspace_name_get(role);
3189
3190 return colormanage_colorspace_get_named(role_colorspace);
3191}
3192
3194{
3195 /* Roles. */
3197 return g_config->get_num_color_spaces();
3198 }
3199
3200 /* Regular color spaces. */
3202 if (colorspace) {
3203 return colorspace->index;
3204 }
3205 return -1;
3206}
3207
3209{
3210 /* Roles. */
3211 if (index == g_config->get_num_color_spaces()) {
3213 }
3214
3215 /* Regular color spaces. */
3216 const ColorSpace *colorspace = g_config->get_color_space_by_index(index);
3217 if (colorspace) {
3218 return colorspace->name().c_str();
3219 }
3220 return "";
3221}
3222
3224{
3225 return colorspace->name().c_str();
3226}
3227
3229 ColorManagedColorspaceSettings *colorspace_settings, ImBuf *ibuf)
3230{
3231 /* Don't modify non-color data space, it does not change with file type. */
3232 const ColorSpace *colorspace = g_config->get_color_space(colorspace_settings->name);
3233
3234 if (colorspace && colorspace->is_data()) {
3235 return;
3236 }
3237
3238 /* Get color space from file type. */
3239 const ImFileType *type = IMB_file_type_from_ibuf(ibuf);
3240 if (type != nullptr) {
3241 if (type->save != nullptr) {
3242 const char *role_colorspace = IMB_colormanagement_role_colorspace_name_get(
3243 type->default_save_role);
3244 STRNCPY_UTF8(colorspace_settings->name, role_colorspace);
3245 }
3246 }
3247}
3248
3250
3251/* -------------------------------------------------------------------- */
3254
3256{
3257 const ocio::Look *look = g_config->get_look_by_name(name);
3258 if (look) {
3259 return look->index;
3260 }
3261 return -1;
3262}
3263
3265{
3266 const ocio::Look *look = g_config->get_look_by_index(index);
3267 if (!look) {
3268 return "";
3269 }
3270 return look->name().c_str();
3271}
3272
3274{
3275 const ocio::Look *look = g_config->get_look_by_index(0);
3276 if (!look) {
3277 return "";
3278 }
3279 return look->name().c_str();
3280}
3281
3282const char *IMB_colormanagement_look_validate_for_view(const char *view_name,
3283 const char *look_name)
3284{
3285 const ocio::Look *look = g_config->get_look_by_name(look_name);
3286 if (!look) {
3287 return look_name;
3288 }
3289
3290 /* Keep same look if compatible. */
3291 if (colormanage_compatible_look(look, view_name)) {
3292 return look_name;
3293 }
3294
3295 /* Try to find another compatible look with the same UI name, in case of looks specialized for
3296 * view transform, */
3297 for (const int other_look_index : blender::IndexRange(g_config->get_num_looks())) {
3298 const ocio::Look *other_look = g_config->get_look_by_index(other_look_index);
3299 if (look->ui_name() == other_look->ui_name() &&
3300 colormanage_compatible_look(other_look, view_name))
3301 {
3302 return other_look->name().c_str();
3303 }
3304 }
3305
3307}
3308
3310
3311/* -------------------------------------------------------------------- */
3314
3315/* Should have enough bits of precision, and this can be reasonably high assuming
3316 * that if colorspaces are really this close, no point converting anyway. */
3317static const float imb_working_space_compare_threshold = 0.001f;
3318
3323
3328
3333
3335{
3336 const ColorSpace *scene_linear = g_config->get_color_space(global_role_scene_linear_default);
3337
3338 /* Keep this in sync with known color spaces in
3339 * imb_colormanagement_working_space_set_from_matrix. */
3340 blender::Vector<const ColorSpace *> working_spaces = {
3342 IMB_colormanagement_space_from_interop_id("lin_rec2020_scene"),
3344
3345 if (!working_spaces.contains(scene_linear)) {
3346 working_spaces.prepend(scene_linear);
3347 }
3348
3349 for (const ColorSpace *colorspace : working_spaces) {
3350 if (colorspace == nullptr) {
3351 continue;
3352 }
3353
3354 EnumPropertyItem item;
3355
3356 item.value = colorspace->index;
3357 item.name = colorspace->name().c_str();
3358 item.identifier = colorspace->name().c_str();
3359 item.icon = 0;
3360 item.description = colorspace->description().c_str();
3361
3362 RNA_enum_item_add(items, totitem, &item);
3363 }
3364}
3365
3370
3372{
3374 return false;
3375 }
3376
3377 CLOG_DEBUG(&LOG, "Setting blend file working color space to '%s'", name);
3378
3379 /* Change default float along with working space for convenience, if it was the same. */
3382 }
3383 else {
3385 }
3386
3388 g_config->set_scene_linear_role(name);
3389
3390 global_color_picking_state.cpu_processor_from.reset();
3391 global_color_picking_state.cpu_processor_to.reset();
3393
3394 return true;
3395}
3396
3398 Main *bmain, const char *name, const blender::float3x3 &scene_linear_to_xyz)
3399{
3400 StringRefNull interop_id;
3401
3402 /* Check if we match the working space defined by the config. */
3403 if (blender::math::is_equal(scene_linear_to_xyz,
3406 {
3407 /* Update scene linear name in case it is different for this config. */
3410 }
3411
3412 /* Check if we match a known working space made available in
3413 * IMB_colormanagement_working_space_items_add, that hopefully exists in the config. */
3416 {
3417 interop_id = "lin_ap1_scene";
3418 }
3419 else if (blender::math::is_equal(scene_linear_to_xyz,
3422 {
3423 interop_id = "lin_rec709_scene";
3424 }
3425 else if (blender::math::is_equal(scene_linear_to_xyz,
3428 {
3429 interop_id = "lin_rec2020_scene";
3430 }
3431
3432 if (!interop_id.is_empty()) {
3433 const ColorSpace *colorspace = g_config->get_color_space_by_interop_id(interop_id);
3434 if (colorspace) {
3435 /* Update scene linear name in case it is different for this config. */
3436 STRNCPY(bmain->colorspace.scene_linear_name, colorspace->name().c_str());
3438 }
3439 }
3440
3441 /* We couldn't find a matching colorspace, set to the default and inform users.
3442 * We could try to preserve the original scene linear space, but that would require
3443 * editing the config at runtime to add it. Not trying to do that for now. */
3446
3447 if (bmain->filepath[0] != '\0') {
3448 CLOG_ERROR(
3449 &LOG, "Unknown scene linear working space '%s'. Missing OpenColorIO configuration?", name);
3451 }
3452
3454}
3455
3457 const bool for_undo,
3458 const bool have_editable_assets)
3459{
3460 /* For old files without info, assume current OpenColorIO config. */
3464 CLOG_DEBUG(&LOG,
3465 "Blend file has unknown scene linear working color space, setting to default");
3466 }
3467
3468 const blender::float3x3 current_scene_linear_to_xyz = blender::colorspace::scene_linear_to_xyz;
3469
3470 /* Change the working space to the one from the blend file. */
3471 const bool working_space_changed = imb_colormanagement_working_space_set_from_matrix(
3473 if (!working_space_changed) {
3474 return;
3475 }
3476
3477 /* For undo, we need to convert the linked datablocks as they were left unchanged by undo.
3478 * For file load, we need to convert editable assets that came from the previous main. */
3479 if (!(for_undo || have_editable_assets)) {
3480 return;
3481 }
3482
3484 bmain,
3485 current_scene_linear_to_xyz,
3487 for_undo,
3488 for_undo,
3489 !for_undo && have_editable_assets);
3490}
3491
3493 const bool is_smaller_gamut,
3494 const blender::float3 in_rgb)
3495{
3496 blender::float3 rgb = m * in_rgb;
3497
3498 for (int i = 0; i < 3; i++) {
3499 /* Round to nicer fractions. */
3500 rgb[i] = 1e-5f * roundf(rgb[i] * 1e5f);
3501 /* Snap to 0 and 1. */
3502 if (fabsf(rgb[i]) < 5e-5) {
3503 rgb[i] = 0.0f;
3504 }
3505 else if (fabsf(1.0f - rgb[i]) < 5e-5) {
3506 rgb[i] = 1.0f;
3507 }
3508 /* Clamp when goig to smaller gamut. We can't really distinguish
3509 * between HDR and out of gamut colors. */
3510 if (is_smaller_gamut) {
3511 rgb[i] = blender::math::clamp(rgb[i], 0.0f, 1.0f);
3512 }
3513 }
3514
3515 return rgb;
3516}
3517
3519 const bool is_smaller_gamut,
3521{
3522 using namespace blender;
3524 const float3 rgb = imb_working_space_convert(m, is_smaller_gamut, in_rgb);
3525 return color::premultiply_alpha(ColorGeometry4f(rgb[0], rgb[1], rgb[2], color[3]));
3526}
3527
3529 Main *bmain,
3530 const blender::float3x3 &current_scene_linear_to_xyz,
3531 const blender::float3x3 &new_xyz_to_scene_linear,
3532 const bool depsgraph_tag,
3533 const bool linked_only,
3534 const bool editable_assets_only)
3535{
3536 using namespace blender;
3537 /* If unknown, assume it's the OpenColorIO config scene linear space. */
3538 float3x3 bmain_scene_linear_to_xyz = math::is_zero(current_scene_linear_to_xyz) ?
3540 current_scene_linear_to_xyz;
3541
3542 float3x3 M = new_xyz_to_scene_linear * bmain_scene_linear_to_xyz;
3543
3544 /* Already in the same space? */
3546 return;
3547 }
3548
3549 if (math::determinant(M) == 0.0f) {
3550 CLOG_ERROR(&LOG, "Working space conversion matrix is not invertible");
3551 return;
3552 }
3553
3554 /* Determine if we are going to a smaller gamut and need to clamp. We prefer not to,
3555 * to preserve HDR colors, although they should not be common in properties. */
3556 bool is_smaller_gamut = false;
3557 for (int i = 0; i < 3; i++) {
3558 for (int j = 0; j < 3; j++) {
3559 if (M[i][j] < 0.0f) {
3560 is_smaller_gamut = true;
3561 }
3562 }
3563 }
3564
3565 /* Single color. */
3566 const auto single = [&M, is_smaller_gamut](float rgb[3]) {
3567 copy_v3_v3(rgb, imb_working_space_convert(M, is_smaller_gamut, float3(rgb)));
3568 };
3569
3570 /* Array with implicit sharing.
3571 *
3572 * We store references to all color arrays, so we can efficiently preserve implicit
3573 * sharing and write in place when possible. */
3574 struct ColorArrayInfo {
3576 Vector<ImplicitSharingPtr<> *> sharing_info_ptrs;
3577 /* Though it is unlikely, the same data array could be used among multiple geometries with
3578 * different domain sizes, so keep track of the maximum size among all users. */
3579 size_t max_size;
3580 };
3582
3583 const auto implicit_sharing_array =
3584 [&](ImplicitSharingPtr<> &sharing_info, ColorGeometry4f *&data, size_t size) {
3585 /* No data? */
3586 if (!sharing_info) {
3587 BLI_assert(size == 0);
3588 return;
3589 }
3590 color_array_map.add_or_modify(
3591 sharing_info.get(),
3592 [&](ColorArrayInfo *value) {
3593 new (value) ColorArrayInfo();
3594 value->data_ptrs.append(&data);
3595 value->sharing_info_ptrs.append(&sharing_info);
3596 value->max_size = size;
3597 },
3598 [&](ColorArrayInfo *value) {
3599 BLI_assert(data == *value->data_ptrs.last());
3600 value->data_ptrs.append(&data);
3601 value->sharing_info_ptrs.append(&sharing_info);
3602 value->max_size = std::max(value->max_size, size);
3603 });
3604 };
3605
3606 IDTypeForeachColorFunctionCallback fn = {single, implicit_sharing_array};
3607
3608 /* Iterate over IDs and embedded IDs. No need to do it for master collections
3609 * though, they don't have colors. */
3610 /* TODO: Multithreading over IDs? */
3611 ID *id_iter;
3612 FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
3613 if (linked_only) {
3614 if (!id_iter->lib) {
3615 continue;
3616 }
3617 }
3618 if (editable_assets_only) {
3619 if (!(id_iter->lib && (id_iter->lib->runtime->tag & LIBRARY_ASSET_EDITABLE))) {
3620 continue;
3621 }
3622 }
3623
3624 const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id_iter);
3625 if (id_type->foreach_working_space_color) {
3626 id_type->foreach_working_space_color(id_iter, fn);
3627 if (depsgraph_tag) {
3629 }
3630 }
3631
3632 if (bNodeTree *node_tree = bke::node_tree_from_id(id_iter)) {
3633 const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&node_tree->id);
3634 if (id_type->foreach_working_space_color) {
3635 id_type->foreach_working_space_color(&node_tree->id, fn);
3636 }
3637 }
3638 }
3640
3641 /* Handle implicit sharing arrays. */
3643 color_array_map.items().begin(), color_array_map.items().end());
3644
3645 threading::parallel_for(color_array_items.index_range(), 64, [&](const IndexRange range) {
3646 for (const int item_index : range) {
3647 const auto &item = color_array_items[item_index];
3648
3649 if (item.value.data_ptrs.size() == item.key->strong_users()) {
3650 /* All of the users of the array data are from the main we're converting, so we can change
3651 * the data array in place without allocating a new version. */
3652 item.key->tag_ensured_mutable();
3653 MutableSpan<ColorGeometry4f> data(*item.value.data_ptrs.first(), item.value.max_size);
3654 threading::parallel_for(data.index_range(), 1024, [&](const IndexRange range) {
3655 for (const int64_t i : range) {
3656 data[i] = imb_working_space_convert(M, is_smaller_gamut, data[i]);
3657 }
3658 });
3659 }
3660 else {
3661 /* Somehow the data is used by something outside of the Main we're currently converting, it
3662 * has to be duplicated before being converted to avoid changing the original. */
3663 const Span<ColorGeometry4f> src_data(*item.value.data_ptrs.first(), item.value.max_size);
3664
3665 auto *dst_data = MEM_malloc_arrayN<ColorGeometry4f>(
3666 src_data.size(), "IMB_colormanagement_working_space_convert");
3667 const ImplicitSharingPtr<> sharing_ptr(implicit_sharing::info_for_mem_free(dst_data));
3668
3669 threading::parallel_for(src_data.index_range(), 1024, [&](const IndexRange range) {
3670 for (const int64_t i : range) {
3671 dst_data[i] = imb_working_space_convert(M, is_smaller_gamut, src_data[i]);
3672 }
3673 });
3674
3675 /* Replace the data pointer and the sharing info pointer with the new data in all of the
3676 * users from the main data-base. The sharing pointer assignment adds a user. */
3677 for (ColorGeometry4f **pointer : item.value.data_ptrs) {
3678 *pointer = dst_data;
3679 }
3680 for (ImplicitSharingPtr<> *pointer : item.value.sharing_info_ptrs) {
3681 *pointer = sharing_ptr;
3682 }
3683 }
3684 }
3685 });
3686}
3687
3688void IMB_colormanagement_working_space_convert(Main *bmain, const Main *reference_bmain)
3689{
3690 /* If unknown, assume it's the OpenColorIO config scene linear space. */
3691 float3x3 reference_scene_linear_to_xyz = blender::math::is_zero(
3692 reference_bmain->colorspace.scene_linear_to_xyz) ?
3694 reference_bmain->colorspace.scene_linear_to_xyz;
3695
3698 blender::math::invert(reference_scene_linear_to_xyz),
3699 false);
3700
3703}
3704
3710
3712{
3713 /* If using the default config, keep the one saved in the startup blend.
3714 * If using the non-default OCIO config, assume we want the working space from that config. */
3717 }
3718}
3719
3721
3722/* -------------------------------------------------------------------- */
3725
3727{
3728 /* Group by SDR/HDR, to help communicate what obscure Rec.XXXX names do. */
3729 for (const bool hdr : {false, true}) {
3730 bool first = true;
3731
3732 for (const int display_index : blender::IndexRange(g_config->get_num_displays())) {
3733 const ocio::Display *display = g_config->get_display_by_index(display_index);
3734
3735 if (display->is_hdr() != hdr) {
3736 continue;
3737 }
3738
3739 if (first) {
3740 EnumPropertyItem item;
3741 item.value = -1;
3742 item.name = (hdr) ? "HDR" : "SDR";
3743 item.identifier = "";
3744 item.icon = 0;
3745 item.description = "";
3746 RNA_enum_item_add(items, totitem, &item);
3747
3748 first = false;
3749 }
3750
3751 EnumPropertyItem item;
3752
3753 item.value = display->index;
3754 item.name = display->ui_name().c_str();
3755 item.identifier = display->name().c_str();
3756 item.icon = 0;
3757 item.description = display->description().c_str();
3758
3759 RNA_enum_item_add(items, totitem, &item);
3760 }
3761 }
3762}
3763
3765 int *totitem,
3766 const char *display_name)
3767{
3768 const ocio::Display *display = g_config->get_display_by_name(display_name);
3769 if (!display) {
3770 return;
3771 }
3772
3773 for (const int view_index : blender::IndexRange(display->get_num_views())) {
3774 const ocio::View *view = display->get_view_by_index(view_index);
3775
3776 EnumPropertyItem item;
3777
3778 item.value = IMB_colormanagement_view_get_id_by_name(view->name().c_str());
3779 item.name = view->name().c_str();
3780 item.identifier = view->name().c_str();
3781 item.icon = 0;
3782 item.description = view->description().c_str();
3783
3784 RNA_enum_item_add(items, totitem, &item);
3785 }
3786}
3787
3789 int *totitem,
3790 const char *view_name)
3791{
3792 const StringRef view_filter = view_filter_for_look(view_name);
3793
3794 for (const int look_index : blender::IndexRange(g_config->get_num_looks())) {
3795 const ocio::Look *look = g_config->get_look_by_index(look_index);
3796 if (!colormanage_compatible_look(look, view_filter)) {
3797 continue;
3798 }
3799
3800 EnumPropertyItem item;
3801
3802 item.value = look->index;
3803 item.name = look->ui_name().c_str();
3804 item.identifier = look->name().c_str();
3805 item.icon = 0;
3806 item.description = look->description().c_str();
3807
3808 RNA_enum_item_add(items, totitem, &item);
3809 }
3810}
3811
3813{
3814 /* Regular color spaces. */
3815 for (const int colorspace_index : blender::IndexRange(g_config->get_num_color_spaces())) {
3816 const ColorSpace *colorspace = g_config->get_sorted_color_space_by_index(colorspace_index);
3817 if (!colorspace->is_invertible()) {
3818 continue;
3819 }
3820
3821 EnumPropertyItem item;
3822
3823 item.value = colorspace->index;
3824 item.name = colorspace->name().c_str();
3825 item.identifier = colorspace->name().c_str();
3826 item.icon = 0;
3827 item.description = colorspace->description().c_str();
3828
3829 RNA_enum_item_add(items, totitem, &item);
3830 }
3831
3832 /* Scene linear role. This is useful for example to create compositing convert colorspace
3833 * nodes that work the same regardless of working space. */
3834 EnumPropertyItem item;
3835
3836 item.value = g_config->get_num_color_spaces();
3837 item.name = "Working Space";
3839 item.icon = 0;
3840 item.description = "Working color space of the current file";
3841
3842 RNA_enum_item_add(items, totitem, &item);
3843}
3844
3846
3847/* -------------------------------------------------------------------- */
3850
3851/*
3852 * Partial display update is supposed to be used by such areas as
3853 * compositor and renderer, This areas are calculating tiles of the
3854 * images and because of performance reasons only this tiles should
3855 * be color managed.
3856 * This gives nice visual feedback without slowing things down.
3857 *
3858 * Updating happens for active display transformation only, all
3859 * the rest buffers would be marked as dirty
3860 */
3861
3863 uchar *display_buffer,
3864 const float *linear_buffer,
3865 const uchar *byte_buffer,
3866 int display_stride,
3867 int linear_stride,
3868 int linear_offset_x,
3869 int linear_offset_y,
3870 ColormanageProcessor *cm_processor,
3871 const int xmin,
3872 const int ymin,
3873 const int xmax,
3874 const int ymax)
3875{
3876 int x, y;
3877 int channels = ibuf->channels;
3878 float dither = ibuf->dither;
3879 const ColorSpace *rect_colorspace = ibuf->byte_buffer.colorspace;
3880 float *display_buffer_float = nullptr;
3881 const int width = xmax - xmin;
3882 const int height = ymax - ymin;
3883 bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
3884
3885 if (dither != 0.0f) {
3886 /* cm_processor is nullptr in cases byte_buffer's space matches display
3887 * buffer's space
3888 * in this case we could skip extra transform and only apply dither
3889 * use 4 channels for easier byte->float->byte conversion here so
3890 * (this is only needed to apply dither, in other cases we'll convert
3891 * byte buffer to display directly)
3892 */
3893 if (!cm_processor) {
3894 channels = 4;
3895 }
3896
3897 display_buffer_float = MEM_malloc_arrayN<float>(
3898 size_t(channels) * size_t(width) * size_t(height), "display buffer for dither");
3899 }
3900
3901 if (cm_processor) {
3902 for (y = ymin; y < ymax; y++) {
3903 for (x = xmin; x < xmax; x++) {
3904 size_t display_index = (size_t(y) * display_stride + x) * 4;
3905 size_t linear_index = (size_t(y - linear_offset_y) * linear_stride +
3906 (x - linear_offset_x)) *
3907 channels;
3908 float pixel[4];
3909
3910 if (linear_buffer) {
3911 if (channels == 4) {
3912 copy_v4_v4(pixel, (float *)linear_buffer + linear_index);
3913 }
3914 else if (channels == 3) {
3915 copy_v3_v3(pixel, (float *)linear_buffer + linear_index);
3916 pixel[3] = 1.0f;
3917 }
3918 else if (channels == 1) {
3919 pixel[0] = linear_buffer[linear_index];
3920 }
3921 else {
3922 BLI_assert_msg(0, "Unsupported number of channels in partial buffer update");
3923 }
3924 }
3925 else if (byte_buffer) {
3926 rgba_uchar_to_float(pixel, byte_buffer + linear_index);
3928 straight_to_premul_v4(pixel);
3929 }
3930
3931 if (!is_data) {
3932 IMB_colormanagement_processor_apply_pixel(cm_processor, pixel, channels);
3933 }
3934
3935 if (display_buffer_float) {
3936 size_t index = (size_t(y - ymin) * width + (x - xmin)) * channels;
3937
3938 if (channels == 4) {
3939 copy_v4_v4(display_buffer_float + index, pixel);
3940 }
3941 else if (channels == 3) {
3942 copy_v3_v3(display_buffer_float + index, pixel);
3943 }
3944 else /* if (channels == 1) */ {
3945 display_buffer_float[index] = pixel[0];
3946 }
3947 }
3948 else {
3949 if (channels == 4) {
3950 float pixel_straight[4];
3951 premul_to_straight_v4_v4(pixel_straight, pixel);
3952 rgba_float_to_uchar(display_buffer + display_index, pixel_straight);
3953 }
3954 else if (channels == 3) {
3955 rgb_float_to_uchar(display_buffer + display_index, pixel);
3956 display_buffer[display_index + 3] = 255;
3957 }
3958 else /* if (channels == 1) */ {
3959 display_buffer[display_index] = display_buffer[display_index + 1] =
3960 display_buffer[display_index + 2] = display_buffer[display_index + 3] =
3961 unit_float_to_uchar_clamp(pixel[0]);
3962 }
3963 }
3964 }
3965 }
3966 }
3967 else {
3968 if (display_buffer_float) {
3969 /* huh, for dither we need float buffer first, no cheaper way. currently */
3970 IMB_buffer_float_from_byte(display_buffer_float,
3971 byte_buffer,
3974 true,
3975 width,
3976 height,
3977 width,
3978 display_stride);
3979 }
3980 else {
3981 int i;
3982
3983 for (i = ymin; i < ymax; i++) {
3984 size_t byte_offset = (size_t(linear_stride) * i + xmin) * 4;
3985 size_t display_offset = (size_t(display_stride) * i + xmin) * 4;
3986
3987 memcpy(
3988 display_buffer + display_offset, byte_buffer + byte_offset, sizeof(char[4]) * width);
3989 }
3990 }
3991 }
3992
3993 if (display_buffer_float) {
3994 size_t display_index = (size_t(ymin) * display_stride + xmin) * channels;
3995
3996 IMB_buffer_byte_from_float(display_buffer + display_index,
3997 display_buffer_float,
3998 channels,
3999 dither,
4002 true,
4003 width,
4004 height,
4005 display_stride,
4006 width,
4007 ymin);
4008
4009 MEM_freeN(display_buffer_float);
4010 }
4011}
4012
4014 ImBuf *ibuf,
4015 const float *linear_buffer,
4016 const uchar *byte_buffer,
4017 int stride,
4018 int offset_x,
4019 int offset_y,
4020 const ColorManagedViewSettings *view_settings,
4021 const ColorManagedDisplaySettings *display_settings,
4022 int xmin,
4023 int ymin,
4024 int xmax,
4025 int ymax,
4026 bool do_threads)
4027{
4028 using namespace blender;
4029 ColormanageCacheViewSettings cache_view_settings;
4030 ColormanageCacheDisplaySettings cache_display_settings;
4031 void *cache_handle = nullptr;
4032 uchar *display_buffer = nullptr;
4033 int buffer_width = ibuf->x;
4034
4035 if (ibuf->display_buffer_flags) {
4036 int view_flag, display_index;
4037
4038 colormanage_view_settings_to_cache(ibuf, &cache_view_settings, view_settings);
4039 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
4040
4041 view_flag = 1 << cache_view_settings.view;
4042 display_index = cache_display_settings.display;
4043
4045
4046 if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
4047 display_buffer = colormanage_cache_get(
4048 ibuf, &cache_view_settings, &cache_display_settings, &cache_handle);
4049 }
4050
4051 /* In some rare cases buffer's dimension could be changing directly from
4052 * different thread
4053 * this i.e. happens when image editor acquires render result
4054 */
4055 buffer_width = ibuf->x;
4056
4057 /* Mark all other buffers as invalid. */
4058 memset(ibuf->display_buffer_flags, 0, g_config->get_num_displays() * sizeof(uint));
4059 ibuf->display_buffer_flags[display_index] |= view_flag;
4060
4062 }
4063
4064 if (display_buffer) {
4065 ColormanageProcessor *cm_processor = nullptr;
4066 bool skip_transform = false;
4067
4068 /* If we only have a byte or a float buffer, and color space already
4069 * matches display, there's no need to do color transforms.
4070 * However if both float and byte buffers exist, it is likely that
4071 * some operation was performed on float buffer first, and the byte
4072 * buffer is out of date. */
4073 if (linear_buffer == nullptr && byte_buffer != nullptr) {
4074 skip_transform = is_colorspace_same_as_display(
4075 ibuf->byte_buffer.colorspace, view_settings, display_settings);
4076 }
4077 if (byte_buffer == nullptr && linear_buffer != nullptr) {
4078 skip_transform = is_colorspace_same_as_display(
4079 ibuf->float_buffer.colorspace, view_settings, display_settings);
4080 }
4081
4082 if (!skip_transform) {
4083 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
4084 }
4085
4086 threading::parallel_for(IndexRange(ymin, ymax - ymin),
4087 do_threads ? 64 : ymax - ymin,
4088 [&](const IndexRange y_range) {
4090 display_buffer,
4091 linear_buffer,
4092 byte_buffer,
4093 buffer_width,
4094 stride,
4095 offset_x,
4096 offset_y,
4097 cm_processor,
4098 xmin,
4099 y_range.first(),
4100 xmax,
4101 y_range.one_after_last());
4102 });
4103
4104 if (cm_processor) {
4106 }
4107
4108 IMB_display_buffer_release(cache_handle);
4109 }
4110}
4111
4113 const float *linear_buffer,
4114 const uchar *byte_buffer,
4115 int stride,
4116 int offset_x,
4117 int offset_y,
4118 const ColorManagedViewSettings *view_settings,
4119 const ColorManagedDisplaySettings *display_settings,
4120 int xmin,
4121 int ymin,
4122 int xmax,
4123 int ymax)
4124{
4126 linear_buffer,
4127 byte_buffer,
4128 stride,
4129 offset_x,
4130 offset_y,
4131 view_settings,
4132 display_settings,
4133 xmin,
4134 ymin,
4135 xmax,
4136 ymax,
4137 false);
4138}
4139
4141 ImBuf *ibuf,
4142 const float *linear_buffer,
4143 const uchar *byte_buffer,
4144 int stride,
4145 int offset_x,
4146 int offset_y,
4147 const ColorManagedViewSettings *view_settings,
4148 const ColorManagedDisplaySettings *display_settings,
4149 int xmin,
4150 int ymin,
4151 int xmax,
4152 int ymax)
4153{
4154 int width = xmax - xmin;
4155 int height = ymax - ymin;
4156 bool do_threads = (size_t(width) * height >= 64 * 64);
4158 linear_buffer,
4159 byte_buffer,
4160 stride,
4161 offset_x,
4162 offset_y,
4163 view_settings,
4164 display_settings,
4165 xmin,
4166 ymin,
4167 xmax,
4168 ymax,
4169 do_threads);
4170}
4171
4172void IMB_partial_display_buffer_update_delayed(ImBuf *ibuf, int xmin, int ymin, int xmax, int ymax)
4173{
4174 if (ibuf->invalid_rect.xmin == ibuf->invalid_rect.xmax) {
4175 BLI_rcti_init(&ibuf->invalid_rect, xmin, xmax, ymin, ymax);
4176 }
4177 else {
4178 rcti rect;
4179 BLI_rcti_init(&rect, xmin, xmax, ymin, ymax);
4180 BLI_rcti_union(&ibuf->invalid_rect, &rect);
4181 }
4182}
4183
4185
4186/* -------------------------------------------------------------------- */
4189
4191 const ColorManagedViewSettings *view_settings,
4192 const ColorManagedDisplaySettings *display_settings,
4193 const ColorManagedDisplaySpace display_space,
4194 const bool inverse)
4195{
4196 ColormanageProcessor *cm_processor;
4197 ColorManagedViewSettings untonemapped_view_settings;
4198 const ColorManagedViewSettings *applied_view_settings;
4199 const ColorSpace *display_colorspace;
4200
4201 cm_processor = MEM_new<ColormanageProcessor>("colormanagement processor");
4202
4203 if (view_settings) {
4204 applied_view_settings = view_settings;
4205 }
4206 else {
4207 IMB_colormanagement_init_untonemapped_view_settings(&untonemapped_view_settings,
4208 display_settings);
4209 applied_view_settings = &untonemapped_view_settings;
4210 }
4211
4212 display_colorspace = IMB_colormangement_display_get_color_space(applied_view_settings,
4213 display_settings);
4214 if (display_colorspace) {
4215 cm_processor->is_data_result = display_colorspace->is_data();
4216 }
4217
4218 const bool use_white_balance = applied_view_settings->flag & COLORMANAGE_VIEW_USE_WHITE_BALANCE;
4219 cm_processor->cpu_processor = get_display_buffer_processor(*display_settings,
4220 applied_view_settings->look,
4221 applied_view_settings->view_transform,
4222 applied_view_settings->exposure,
4223 applied_view_settings->gamma,
4224 applied_view_settings->temperature,
4225 applied_view_settings->tint,
4226 use_white_balance,
4228 display_space,
4229 inverse);
4230
4231 if (applied_view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) {
4232 cm_processor->curve_mapping = BKE_curvemapping_copy(applied_view_settings->curve_mapping);
4233 BKE_curvemapping_premultiply(cm_processor->curve_mapping, false);
4234 }
4235
4236 return cm_processor;
4237}
4238
4240 const char *to_colorspace)
4241{
4242 ColormanageProcessor *cm_processor;
4243
4244 cm_processor = MEM_new<ColormanageProcessor>("colormanagement processor");
4245 cm_processor->is_data_result = IMB_colormanagement_space_name_is_data(to_colorspace);
4246
4247 cm_processor->cpu_processor = g_config->get_cpu_processor(from_colorspace, to_colorspace);
4248
4249 return cm_processor;
4250}
4251
4253{
4254 if (cm_processor->curve_mapping) {
4255 /* Consider processor which has curve mapping as a non no-op.
4256 * This is mainly for the simplicity of the check, since the current cases where this
4257 * function is used the curve mapping is never assigned. */
4258 return false;
4259 }
4260
4261 if (!cm_processor->cpu_processor) {
4262 /* The CPU processor might have failed to be created, for example when the requested color
4263 * space does not exist in the configuration, or if there is a missing lookup table, or the
4264 * configuration is invalid due to other reasons.
4265 *
4266 * The actual processing checks for the cpu_processor not being null pointer, and it if is
4267 * then processing does not apply it. However, processing could still apply curve mapping.
4268 *
4269 * Hence a null-pointer here, which happens after the curve mapping check, but before
4270 * accessing cpu_processor. */
4271 return true;
4272 }
4273
4274 return cm_processor->cpu_processor->is_noop();
4275}
4276
4278{
4279 if (cm_processor->curve_mapping) {
4280 BKE_curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
4281 }
4282
4283 if (cm_processor->cpu_processor) {
4284 cm_processor->cpu_processor->apply_rgba(pixel);
4285 }
4286}
4287
4289 float pixel[4])
4290{
4291 if (cm_processor->curve_mapping) {
4292 BKE_curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
4293 }
4294
4295 if (cm_processor->cpu_processor) {
4296 cm_processor->cpu_processor->apply_rgba_predivide(pixel);
4297 ;
4298 }
4299}
4300
4302{
4303 if (cm_processor->curve_mapping) {
4304 BKE_curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
4305 }
4306
4307 if (cm_processor->cpu_processor) {
4308 cm_processor->cpu_processor->apply_rgb(pixel);
4309 }
4310}
4311
4313 float *pixel,
4314 int channels)
4315{
4316 if (channels == 4) {
4318 }
4319 else if (channels == 3) {
4320 IMB_colormanagement_processor_apply_v3(cm_processor, pixel);
4321 }
4322 else if (channels == 1) {
4323 if (cm_processor->curve_mapping) {
4324 curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, 1);
4325 }
4326 }
4327 else {
4329 false, "Incorrect number of channels passed to IMB_colormanagement_processor_apply_pixel");
4330 }
4331}
4332
4334 float *buffer,
4335 int width,
4336 int height,
4337 int channels,
4338 bool predivide)
4339{
4340 /* apply curve mapping */
4341 if (cm_processor->curve_mapping) {
4342 int x, y;
4343
4344 for (y = 0; y < height; y++) {
4345 for (x = 0; x < width; x++) {
4346 float *pixel = buffer + channels * (size_t(y) * width + x);
4347
4348 curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, channels);
4349 }
4350 }
4351 }
4352
4353 if (cm_processor->cpu_processor && channels >= 3) {
4354 /* apply OCIO processor */
4355 const ocio::PackedImage img(buffer,
4356 width,
4357 height,
4358 channels,
4360 sizeof(float),
4361 size_t(channels) * sizeof(float),
4362 size_t(channels) * sizeof(float) * width);
4363
4364 if (predivide) {
4365 cm_processor->cpu_processor->apply_predivide(img);
4366 }
4367 else {
4368 cm_processor->cpu_processor->apply(img);
4369 }
4370 }
4371}
4372
4374 ColormanageProcessor *cm_processor, uchar *buffer, int width, int height, int channels)
4375{
4376 /* TODO(sergey): Would be nice to support arbitrary channels configurations,
4377 * but for now it's not so important.
4378 */
4379 BLI_assert(channels == 4);
4380 float pixel[4];
4381 for (int y = 0; y < height; y++) {
4382 for (int x = 0; x < width; x++) {
4383 size_t offset = channels * (size_t(y) * width + x);
4384 rgba_uchar_to_float(pixel, buffer + offset);
4385 IMB_colormanagement_processor_apply_v4(cm_processor, pixel);
4386 rgba_float_to_uchar(buffer + offset, pixel);
4387 }
4388 }
4389}
4390
4392{
4393 if (cm_processor->curve_mapping) {
4394 BKE_curvemapping_free(cm_processor->curve_mapping);
4395 }
4396
4397 MEM_delete(cm_processor);
4398}
4399
4400/* **** OpenGL drawing routines using GLSL for color space transform ***** */
4401
4403{
4404 /* Using curve mapping? */
4405 const bool use_curve_mapping = (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) != 0;
4406 if (!use_curve_mapping) {
4407 return nullptr;
4408 }
4409
4410 /* Already up to date? */
4411 if (view_settings->curve_mapping->changed_timestamp ==
4412 global_gpu_state.curve_mapping_timestamp &&
4413 view_settings->curve_mapping == global_gpu_state.orig_curve_mapping)
4414 {
4415 return view_settings->curve_mapping;
4416 }
4417
4418 /* We're using curve mapping's address as a cache ID, so we need to make sure re-allocation
4419 * gives new address here. We do this by allocating new curve mapping before freeing old one.
4420 */
4421 CurveMapping *new_curve_mapping = BKE_curvemapping_copy(view_settings->curve_mapping);
4422
4423 if (global_gpu_state.curve_mapping) {
4425 global_gpu_state.curve_mapping = nullptr;
4426 }
4427
4428 /* Fill in OCIO's curve mapping settings. */
4429 global_gpu_state.curve_mapping = new_curve_mapping;
4430 global_gpu_state.curve_mapping_timestamp = view_settings->curve_mapping->changed_timestamp;
4431 global_gpu_state.orig_curve_mapping = view_settings->curve_mapping;
4432 global_gpu_state.use_curve_mapping = true;
4433
4434 return global_gpu_state.curve_mapping;
4435}
4436
4438 const ColorManagedViewSettings *view_settings,
4439 const ColorManagedDisplaySettings *display_settings,
4440 const ColorSpace *from_colorspace,
4441 float dither,
4442 bool predivide,
4443 bool do_overlay_merge)
4444{
4445 ColorManagedViewSettings untonemapped_view_settings;
4446 const ColorManagedViewSettings *applied_view_settings;
4447
4448 if (view_settings) {
4449 applied_view_settings = view_settings;
4450 }
4451 else {
4452 /* If no view settings were specified, use default ones, which will attempt not to do any
4453 * extra color correction. */
4454 IMB_colormanagement_init_untonemapped_view_settings(&untonemapped_view_settings,
4455 display_settings);
4456 applied_view_settings = &untonemapped_view_settings;
4457 }
4458
4459 /* Ensure curve mapping is up to date. */
4460 CurveMapping *applied_curve_mapping = update_glsl_curve_mapping(applied_view_settings);
4461
4462 /* GPU shader parameters. */
4463 const bool use_look = colormanage_use_look(applied_view_settings->look,
4464 applied_view_settings->view_transform);
4465 const float exposure = applied_view_settings->exposure;
4466 const float gamma = applied_view_settings->gamma;
4467
4468 /* TODO)sergey): Use designated initializer. */
4469 ocio::GPUDisplayParameters display_parameters;
4470 display_parameters.from_colorspace = from_colorspace ? from_colorspace->name().c_str() :
4472 display_parameters.view = applied_view_settings->view_transform;
4473 display_parameters.display = display_settings->display_device;
4474 display_parameters.look = (use_look) ? applied_view_settings->look : "";
4475 display_parameters.curve_mapping = applied_curve_mapping;
4476 display_parameters.scale = (exposure == 0.0f) ? 1.0f : powf(2.0f, exposure);
4477 display_parameters.exponent = (gamma == 1.0f) ? 1.0f : 1.0f / max_ff(FLT_EPSILON, gamma);
4478 display_parameters.dither = dither;
4479 display_parameters.temperature = applied_view_settings->temperature;
4480 display_parameters.tint = applied_view_settings->tint;
4481 display_parameters.use_white_balance = (applied_view_settings->flag &
4483 display_parameters.use_predivide = predivide;
4484 display_parameters.do_overlay_merge = do_overlay_merge;
4485 display_parameters.use_hdr_buffer = GPU_hdr_support();
4487 display_settings, display_parameters.view.c_str());
4488 display_parameters.use_display_emulation = get_display_emulation(*display_settings);
4489
4490 /* Bind shader. Internally GPU shaders are created and cached on demand. */
4491 global_gpu_state.gpu_shader_bound = g_config->get_gpu_shader_binder().display_bind(
4492 display_parameters);
4493
4494 return global_gpu_state.gpu_shader_bound;
4495}
4496
4498 const ColorManagedDisplaySettings *display_settings,
4499 float dither,
4500 bool predivide)
4501{
4503 view_settings, display_settings, nullptr, dither, predivide, false);
4504}
4505
4507 const ColorSpace *from_colorspace,
4508 float dither,
4509 bool predivide)
4510{
4511 ColorManagedViewSettings *view_settings;
4512 ColorManagedDisplaySettings *display_settings;
4513
4514 IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
4515
4517 view_settings, display_settings, from_colorspace, dither, predivide, false);
4518}
4519
4520bool IMB_colormanagement_setup_glsl_draw_ctx(const bContext *C, float dither, bool predivide)
4521{
4522 return IMB_colormanagement_setup_glsl_draw_from_space_ctx(C, nullptr, dither, predivide);
4523}
4524
4525bool IMB_colormanagement_setup_glsl_draw_to_scene_linear(const char *from_colorspace_name,
4526 const bool predivide)
4527{
4528 global_gpu_state.gpu_shader_bound = g_config->get_gpu_shader_binder().to_scene_linear_bind(
4529 from_colorspace_name, predivide);
4530 return global_gpu_state.gpu_shader_bound;
4531}
4532
4534{
4535 if (global_gpu_state.gpu_shader_bound) {
4536 g_config->get_gpu_shader_binder().unbind();
4537 global_gpu_state.gpu_shader_bound = false;
4538 }
4539}
4540
4542
4543/* -------------------------------------------------------------------- */
4546
4547/* Calculate color in range 800..12000 using an approximation
4548 * a/x+bx+c for R and G and ((at + b)t + c)t + d) for B
4549 *
4550 * The result of this can be negative to support gamut wider than
4551 * than rec.709, just needs to be clamped. */
4552
4553static const float blackbody_table_r[7][3] = {{1.61919106e+03f, -2.05010916e-03f, 5.02995757e+00f},
4554 {2.48845471e+03f, -1.11330907e-03f, 3.22621544e+00f},
4555 {3.34143193e+03f, -4.86551192e-04f, 1.76486769e+00f},
4556 {4.09461742e+03f, -1.27446582e-04f, 7.25731635e-01f},
4557 {4.67028036e+03f, 2.91258199e-05f, 1.26703442e-01f},
4558 {4.59509185e+03f, 2.87495649e-05f, 1.50345020e-01f},
4559 {3.78717450e+03f, 9.35907826e-06f, 3.99075871e-01f}};
4560
4561static const float blackbody_table_g[7][3] = {
4562 {-4.88999748e+02f, 6.04330754e-04f, -7.55807526e-02f},
4563 {-7.55994277e+02f, 3.16730098e-04f, 4.78306139e-01f},
4564 {-1.02363977e+03f, 1.20223470e-04f, 9.36662319e-01f},
4565 {-1.26571316e+03f, 4.87340896e-06f, 1.27054498e+00f},
4566 {-1.42529332e+03f, -4.01150431e-05f, 1.43972784e+00f},
4567 {-1.17554822e+03f, -2.16378048e-05f, 1.30408023e+00f},
4568 {-5.00799571e+02f, -4.59832026e-06f, 1.09098763e+00f}};
4569
4570static const float blackbody_table_b[7][4] = {
4571 {5.96945309e-11f, -4.85742887e-08f, -9.70622247e-05f, -4.07936148e-03f},
4572 {2.40430366e-11f, 5.55021075e-08f, -1.98503712e-04f, 2.89312858e-02f},
4573 {-1.40949732e-11f, 1.89878968e-07f, -3.56632824e-04f, 9.10767778e-02f},
4574 {-3.61460868e-11f, 2.84822009e-07f, -4.93211319e-04f, 1.56723440e-01f},
4575 {-1.97075738e-11f, 1.75359352e-07f, -2.50542825e-04f, -2.22783266e-02f},
4576 {-1.61997957e-13f, -1.64216008e-08f, 3.86216271e-04f, -7.38077418e-01f},
4577 {6.72650283e-13f, -2.73078809e-08f, 4.24098264e-04f, -7.52335691e-01f}};
4578
4579static void blackbody_temperature_to_rec709(float rec709[3], float t)
4580{
4581 if (t >= 12000.0f) {
4582 rec709[0] = 0.8262954810464208f;
4583 rec709[1] = 0.9945080501520986f;
4584 rec709[2] = 1.566307710274283f;
4585 }
4586 else if (t < 800.0f) {
4587 rec709[0] = 5.413294490189271f;
4588 rec709[1] = -0.20319390035873933f;
4589 rec709[2] = -0.0822535242887164f;
4590 }
4591 else {
4592 int i = (t >= 6365.0f) ? 6 :
4593 (t >= 3315.0f) ? 5 :
4594 (t >= 1902.0f) ? 4 :
4595 (t >= 1449.0f) ? 3 :
4596 (t >= 1167.0f) ? 2 :
4597 (t >= 965.0f) ? 1 :
4598 0;
4599
4600 const float *r = blackbody_table_r[i];
4601 const float *g = blackbody_table_g[i];
4602 const float *b = blackbody_table_b[i];
4603
4604 const float t_inv = 1.0f / t;
4605 rec709[0] = r[0] * t_inv + r[1] * t + r[2];
4606 rec709[1] = g[0] * t_inv + g[1] * t + g[2];
4607 rec709[2] = ((b[0] * t + b[1]) * t + b[2]) * t + b[3];
4608 }
4609}
4610
4612{
4613 float rec709[3];
4614 blackbody_temperature_to_rec709(rec709, value);
4615
4616 float rgb[3];
4618 clamp_v3(rgb, 0.0f, FLT_MAX);
4619
4620 copy_v3_v3(r_dest, rgb);
4621 r_dest[3] = 1.0f;
4622}
4623
4625 const int width,
4626 const float min,
4627 const float max)
4628{
4629 for (int i = 0; i < width; i++) {
4630 float temperature = min + (max - min) / float(width) * float(i);
4631 IMB_colormanagement_blackbody_temperature_to_rgb(&r_table[i * 4], temperature);
4632 }
4633}
4634
4646
4647static float cie_color_match[81][3] = {
4648 {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f},
4649 {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f},
4650 {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f},
4651 {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f},
4652 {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f},
4653 {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f},
4654 {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f},
4655 {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f},
4656 {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f},
4657 {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f},
4658 {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f},
4659 {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f},
4660 {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f},
4661 {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f},
4662 {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f},
4663 {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f},
4664 {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f},
4665 {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f},
4666 {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f},
4667 {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f},
4668 {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f},
4669 {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f},
4670 {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f},
4671 {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f},
4672 {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f},
4673 {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f},
4674 {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}};
4675
4676static void wavelength_to_xyz(float xyz[3], float lambda_nm)
4677{
4678 float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); /* Scaled 0..80. */
4679 int i = int(ii);
4680
4681 if (i < 0 || i >= 80) {
4682 xyz[0] = 0.0f;
4683 xyz[1] = 0.0f;
4684 xyz[2] = 0.0f;
4685 }
4686 else {
4687 ii -= float(i);
4688 const float *c = cie_color_match[i];
4689 xyz[0] = c[0] + ii * (c[3] - c[0]);
4690 xyz[1] = c[1] + ii * (c[4] - c[1]);
4691 xyz[2] = c[2] + ii * (c[5] - c[2]);
4692 }
4693}
4694
4695void IMB_colormanagement_wavelength_to_rgb(float r_dest[4], float value)
4696{
4697 float xyz[3];
4698 wavelength_to_xyz(xyz, value);
4699
4700 float rgb[3];
4702 clamp_v3(rgb, 0.0f, FLT_MAX);
4703
4704 copy_v3_v3(r_dest, rgb);
4705 r_dest[3] = 1.0f;
4706}
4707
4708void IMB_colormanagement_wavelength_to_rgb_table(float *r_table, const int width)
4709{
4710 for (int i = 0; i < width; i++) {
4711 float wavelength = 380 + 400 / float(width) * float(i);
4712 IMB_colormanagement_wavelength_to_rgb(&r_table[i * 4], wavelength);
4713 }
4714}
4715
std::optional< std::string > BKE_appdir_folder_id(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT
Definition appdir.cc:721
@ BLENDER_DATAFILES
void BKE_curvemapping_evaluate_premulRGBF(const CurveMapping *cumap, float vecout[3], const float vecin[3])
void BKE_curvemapping_premultiply(CurveMapping *cumap, bool restore)
CurveMapping * BKE_curvemapping_copy(const CurveMapping *cumap)
void BKE_curvemapping_free(CurveMapping *cumap)
float BKE_curvemap_evaluateF(const CurveMapping *cumap, const CurveMap *cuma, float value)
SpaceImage * CTX_wm_space_image(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
const IDTypeInfo * BKE_idtype_get_info_from_id(const ID *id)
Definition idtype.cc:146
bool BKE_image_format_is_byte(const ImageFormatData *imf)
bool BKE_imtype_requires_linear_float(char imtype)
@ LIBRARY_ASSET_EDITABLE
#define FOREACH_MAIN_ID_END
Definition BKE_main.hh:583
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition BKE_main.hh:577
#define CMP_NODE_CONVERT_COLOR_SPACE
#define CMP_NODE_CONVERT_TO_DISPLAY
blender::ocio::ColorSpace ColorSpace
Definition BLF_api.hh:38
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
File and directory operations.
#define LISTBASE_FOREACH(type, var, list)
MINLINE float max_ff(float a, float b)
MINLINE void straight_to_premul_v4(float color[4])
MINLINE void rgb_uchar_to_float(float r_col[3], const unsigned char col_ub[3])
void BLI_init_srgb_conversion(void)
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 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 copy_v4_v4(float r[4], const float a[4])
MINLINE void clamp_v3(float vec[3], float min, float max)
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define FILE_MAX
void BLI_setenv(const char *env, const char *val) ATTR_NONNULL(1)
#define BLI_path_join(...)
const char * BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool BLI_path_make_safe_filename(char *filename) ATTR_NONNULL(1)
void BLI_rcti_union(struct rcti *rct_a, const struct rcti *rct_b)
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.cc:414
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
unsigned char uchar
unsigned int uint
void BLI_thread_unlock(int type)
Definition threads.cc:333
void BLI_thread_lock(int type)
Definition threads.cc:328
#define BLI_MUTEX_INITIALIZER
Definition BLI_threads.h:80
void BLI_mutex_lock(ThreadMutex *mutex)
Definition threads.cc:345
void BLI_mutex_unlock(ThreadMutex *mutex)
Definition threads.cc:350
@ LOCK_COLORMANAGE
Definition BLI_threads.h:69
#define ELEM(...)
#define STREQ(a, b)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
#define CLOG_DEBUG(clg_ref,...)
Definition CLG_log.h:191
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
#define CLOG_STR_INFO_NOCHECK(clg_ref, str)
Definition CLG_log.h:206
#define CLOG_INFO_NOCHECK(clg_ref, format,...)
Definition CLG_log.h:204
#define CLOG_INFO(clg_ref,...)
Definition CLG_log.h:190
void DEG_id_tag_update(ID *id, unsigned int flags)
ID and Library types, which are fundamental for SDNA.
@ ID_RECALC_ALL
Definition DNA_ID.h:1188
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
@ COLORMANAGE_VIEW_USE_WHITE_BALANCE
@ COLORMANAGE_VIEW_USE_CURVES
@ COLORMANAGE_DISPLAY_EMULATION_OFF
@ COLORMANAGE_DISPLAY_EMULATION_AUTO
@ IMA_VIEW_AS_RENDER
@ NTREE_COMPOSIT
@ MEDIA_TYPE_VIDEO
@ R_IMF_PLANES_RGBA
static AppView * view
bool GPU_hdr_support()
ColorManagedDisplaySpace
@ DISPLAY_SPACE_VIDEO_OUTPUT
@ DISPLAY_SPACE_IMAGE_OUTPUT
@ DISPLAY_SPACE_DRAW
bool IMB_colormanagement_space_is_scene_linear(const ColorSpace *colorspace)
ColorManagedFileOutput
bool IMB_colormanagement_space_is_data(const ColorSpace *colorspace)
@ COLOR_ROLE_DEFAULT_FLOAT
@ COLOR_ROLE_ACES_INTERCHANGE
@ COLOR_ROLE_DATA
@ COLOR_ROLE_DEFAULT_BYTE
@ COLOR_ROLE_SCENE_LINEAR
@ COLOR_ROLE_COLOR_PICKING
@ COLOR_ROLE_DEFAULT_SEQUENCER
@ COLOR_ROLE_TEXTURE_PAINTING
BLI_INLINE void IMB_colormanagement_xyz_to_scene_linear(float scene_linear[3], const float xyz[3])
#define BCM_CONFIG_FILE
BLI_INLINE void IMB_colormanagement_rec709_to_scene_linear(float scene_linear[3], const float rec709[3])
BLI_INLINE void IMB_colormanagement_scene_linear_to_xyz(float xyz[3], const float scene_linear[3])
#define MAX_COLORSPACE_NAME
Function declarations for filter.cc.
ImBuf * IMB_dupImBuf(const ImBuf *ibuf1)
void IMB_make_writable_byte_buffer(ImBuf *ibuf)
void IMB_buffer_byte_from_float(unsigned char *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=0)
Definition conversion.cc:71
bool IMB_alpha_affects_rgb(const ImBuf *ibuf)
Definition conversion.cc:66
bool IMB_alloc_byte_pixels(ImBuf *ibuf, bool initialize_pixels=true)
void IMB_buffer_byte_from_byte(unsigned char *rect_to, const unsigned char *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_byte(float *rect_to, const unsigned char *rect_from, int profile_to, int profile_from, bool predivide, int width, int height, int stride_to, int stride_from)
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_make_writable_float_buffer(ImBuf *ibuf)
void IMB_freeImBuf(ImBuf *ibuf)
ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
void IMB_free_byte_pixels(ImBuf *ibuf)
void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[3])
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, ImBufOwnership ownership)
void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float backcol[3])
void IMB_float_from_byte(ImBuf *ibuf)
@ IB_RECT_INVALID
@ IB_DISPLAY_BUFFER_INVALID
#define IB_PROFILE_SRGB
@ IB_TAKE_OWNERSHIP
@ IMB_COLORMANAGE_IS_DATA
@ IB_alphamode_channel_packed
@ IB_alphamode_premul
@ IB_alphamode_ignore
void IMB_metadata_copy(ImBuf *ibuf_dst, const ImBuf *ibuf_src)
Definition metadata.cc:59
void IMB_moviecache_free(MovieCache *cache)
ImBuf * IMB_moviecache_get(MovieCache *cache, void *userkey, bool *r_is_cached_empty)
void IMB_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf)
MovieCache * IMB_moviecache_create(const char *name, int keysize, GHashHashFP hashfp, GHashCmpFP cmpfp)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
static void init_data(ModifierData *md)
#define OCIO_ROLE_COLOR_PICKING
#define OCIO_ROLE_TEXTURE_PAINT
#define OCIO_ROLE_DEFAULT_FLOAT
#define OCIO_ROLE_DEFAULT_SEQUENCER
#define OCIO_ROLE_DEFAULT_BYTE
#define OCIO_ROLE_ACES_INTERCHANGE
#define OCIO_ROLE_DATA
#define OCIO_ROLE_SCENE_LINEAR
#define C
Definition RandGen.cpp:29
blender::ocio::Display ColorManagedDisplay
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr int64_t first() const
constexpr int64_t one_after_last() const
constexpr int64_t size() const
SubIterator begin() const
Definition BLI_map.hh:768
SubIterator end() const
Definition BLI_map.hh:778
auto add_or_modify(const Key &key, const CreateValueF &create_value, const ModifyValueF &modify_value) -> decltype(create_value(nullptr))
Definition BLI_map.hh:481
ItemIterator items() const &
Definition BLI_map.hh:902
IndexRange index_range() const
constexpr int64_t find(char c, int64_t pos=0) const
constexpr bool is_empty() const
constexpr StringRef substr(int64_t start, int64_t size) const
constexpr bool startswith(StringRef prefix) const
constexpr bool endswith(StringRef suffix) const
constexpr const char * c_str() const
constexpr StringRef drop_suffix(int64_t n) const
int64_t size() const
void prepend(const T &value)
bool contains(const T &value) const
void resize(const int64_t new_size)
void open(StringRefNull filepath, ios_base::openmode mode=ios_base::in|ios_base::out)
Definition fileops.cc:26
virtual void apply_rgba_predivide(float rgba[4]) const =0
virtual void apply_predivide(const PackedImage &image) const =0
virtual void apply_rgb(float rgb[3]) const =0
virtual void apply(const PackedImage &image) const =0
virtual void apply_rgba(float rgba[4]) const =0
virtual StringRefNull name() const =0
virtual const CPUProcessor * get_to_scene_linear_cpu_processor() const =0
virtual bool is_data() const =0
virtual bool is_scene_linear() const =0
virtual bool is_srgb() const =0
virtual StringRefNull interop_id() const =0
static std::unique_ptr< Config > create_fallback()
Definition config.cc:35
virtual const ColorSpace * get_color_space(StringRefNull name) const =0
static std::unique_ptr< Config > create_from_environment()
Definition config.cc:15
static std::unique_ptr< Config > create_from_file(StringRefNull filename)
Definition config.cc:24
virtual StringRefNull name() const =0
virtual const View * get_view_by_name(StringRefNull name) const =0
virtual const CPUProcessor * get_to_scene_linear_cpu_processor(bool use_display_emulation) const =0
virtual const CPUProcessor * get_from_scene_linear_cpu_processor(bool use_display_emulation) const =0
virtual const View * get_default_view() const =0
virtual bool is_hdr() const =0
virtual int get_num_views() const =0
virtual StringRefNull ui_name() const =0
virtual const View * get_untonemapped_view() const =0
virtual StringRefNull description() const =0
virtual const View * get_view_by_index(int index) const =0
virtual StringRefNull view() const =0
virtual StringRefNull description() const =0
virtual StringRefNull name() const =0
virtual StringRefNull ui_name() const =0
virtual StringRefNull process_space() const =0
virtual StringRefNull name() const =0
nullptr float
static void partial_buffer_update_rect(ImBuf *ibuf, uchar *display_buffer, const float *linear_buffer, const uchar *byte_buffer, int display_stride, int linear_stride, int linear_offset_x, int linear_offset_y, ColormanageProcessor *cm_processor, const int xmin, const int ymin, const int xmax, const int ymax)
static char global_role_data[MAX_COLORSPACE_NAME]
void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], const ColorSpace *colorspace)
bool IMB_colormanagement_display_support_emulation(const ColorManagedDisplaySettings *display_settings, const char *view_name)
bool IMB_colormanagement_working_space_set_from_name(const char *name)
static CurveMapping * update_glsl_curve_mapping(const ColorManagedViewSettings *view_settings)
static void display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle, int height, float *linear_buffer, bool *is_straight_alpha)
static std::shared_ptr< const ocio::CPUProcessor > get_display_buffer_processor(const ColorManagedDisplaySettings &display_settings, const char *look, const char *view_transform, const float exposure, const float gamma, const float temperature, const float tint, const bool use_white_balance, const char *from_colorspace, const ColorManagedDisplaySpace target, const bool inverse=false)
static const float blackbody_table_r[7][3]
void IMB_colormanagement_assign_byte_colorspace(ImBuf *ibuf, const char *name)
static const float imb_working_space_compare_threshold
const char * IMB_colormanagement_get_rect_colorspace(const ImBuf *ibuf)
static void blackbody_temperature_to_rec709(float rec709[3], float t)
static uint colormanage_hashhash(const void *key_v)
void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], const ColorSpace *colorspace)
static bool colormanage_check_colorspace_settings(ColorManagedColorspaceSettings *settings, const char *what)
void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *totitem)
bool IMB_colormanagement_set_whitepoint(const float whitepoint[3], float &temperature, float &tint)
const char * IMB_colormanagement_colorspace_get_indexed_name(const int index)
void colormanage_cache_free(ImBuf *ibuf)
static float cie_color_match[81][3]
ColormanageProcessor * IMB_colormanagement_colorspace_processor_new(const char *from_colorspace, const char *to_colorspace)
static bool colormanage_check_view_settings(ColorManagedDisplaySettings *display_settings, ColorManagedViewSettings *view_settings, const char *what)
static const int CICP_TRC_BT709
static const int CICP_TRC_SRGB
static blender::VectorSet< blender::StringRefNull > g_all_view_names
static bool g_config_is_custom
void IMB_colormanagement_assign_float_colorspace(ImBuf *ibuf, const char *name)
void IMB_colormanagement_wavelength_to_rgb(float r_dest[4], float value)
const char * IMB_colormanagement_display_get_none_name()
const ColorSpace * IMB_colormanagement_space_get_named(const char *name)
uchar * IMB_display_buffer_acquire(ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, void **cache_handle)
bool IMB_colormanagement_display_is_hdr(const ColorManagedDisplaySettings *display_settings, const char *view_name)
void IMB_colormanagement_transform_byte(uchar *buffer, int width, int height, int channels, const char *from_colorspace, const char *to_colorspace)
static const int CICP_TRC_G26
static void colormanage_cachedata_set(ImBuf *ibuf, ColormanageCacheData *data)
int IMB_colormanagement_working_space_get_named_index(const char *name)
blender::float3x3 IMB_colormanagement_get_scene_linear_to_xyz()
static std::unique_ptr< ocio::Config > g_config
bool IMB_colormanagement_space_is_scene_linear(const ColorSpace *colorspace)
const ColorSpace * colormanage_colorspace_get_roled(const int role)
static const int CICP_TRC_PQ
void IMB_colormanagement_processor_apply_byte(ColormanageProcessor *cm_processor, uchar *buffer, int width, int height, int channels)
const ColorSpace * colormanage_colorspace_get_named(const char *name)
void IMB_colormanagement_color_picking_to_scene_linear_v3(float scene_linear[3], const float color_picking[3])
uchar * IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle)
void IMB_colormanagement_view_items_add(EnumPropertyItem **items, int *totitem, const char *display_name)
const char * IMB_colormanagement_view_get_raw_or_default_name(const char *display_name)
static ImBuf * colormanage_cache_get_ibuf(ImBuf *ibuf, ColormanageCacheKey *key, void **cache_handle)
static char global_role_default_sequencer[MAX_COLORSPACE_NAME]
void colormanagement_exit()
const char * IMB_colormanagement_look_get_default_name()
void IMB_colormanagement_check_is_data(ImBuf *ibuf, const char *name)
void colormanagement_init()
static StringRefNull colormanage_find_matching_view_name(const ocio::Display *display, StringRefNull view_name)
bool IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, float dither, bool predivide)
void IMB_colormanagement_pixel_to_display_space_v4(float result[4], const float pixel[4], const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, const ColorManagedDisplaySpace display_space)
bool IMB_colormanagement_space_name_is_srgb(const char *name)
static const int CICP_MATRIX_BT709
bool IMB_colormanagement_space_name_is_scene_linear(const char *name)
const char * IMB_colormanagement_look_validate_for_view(const char *view_name, const char *look_name)
void IMB_colormanagement_working_space_init_startup(Main *bmain)
static pthread_mutex_t processor_lock
static char global_role_scene_linear_default[MAX_COLORSPACE_NAME]
ColormanageProcessor * IMB_colormanagement_display_processor_new(const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, const ColorManagedDisplaySpace display_space, const bool inverse)
static const int CICP_TRC_G22
void IMB_colormanagement_transform_byte_to_float(float *float_buffer, uchar *byte_buffer, int width, int height, int channels, const char *from_colorspace, const char *to_colorspace)
static void colormanage_display_buffer_process(ImBuf *ibuf, uchar *display_buffer, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, const ColorManagedDisplaySpace display_space)
void IMB_display_buffer_transform_apply(uchar *display_buffer, float *linear_buffer, int width, int height, int channels, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, bool predivide)
void IMB_colormanagement_transform_float(float *buffer, int width, int height, int channels, const char *from_colorspace, const char *to_colorspace, bool predivide)
bool IMB_colormanagement_space_is_data(const ColorSpace *colorspace)
void IMB_partial_display_buffer_update(ImBuf *ibuf, const float *linear_buffer, const uchar *byte_buffer, int stride, int offset_x, int offset_y, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, int xmin, int ymin, int xmax, int ymax)
static const int CICP_MATRIX_RGB
bool IMB_colormanagement_processor_is_noop(ColormanageProcessor *cm_processor)
static MovieCache * colormanage_moviecache_get(const ImBuf *ibuf)
void IMB_colormanagement_check_file_config(Main *bmain)
static struct GlobalColorPickingState global_color_picking_state
static void colormanage_update_matrices()
static ColormanageCacheData * colormanage_cachedata_get(const ImBuf *ibuf)
bool IMB_colormanagement_setup_glsl_draw_to_scene_linear(const char *from_colorspace_name, const bool predivide)
ImBuf * IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, bool save_as_render, bool allocate_result, const ImageFormatData *image_format)
static struct GlobalGPUState global_gpu_state
bool IMB_colormanagement_space_name_is_data(const char *name)
bool IMB_colormanagement_setup_glsl_draw_from_space_ctx(const bContext *C, const ColorSpace *from_colorspace, float dither, bool predivide)
static bool colormanage_compatible_look(const ocio::Look *look, StringRef view_filter)
void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer, const int offset_x, const int offset_y, const int width, const int height, const ImBuf *ibuf, const bool store_premultiplied)
bool IMB_colormanagement_display_processor_needed(const ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
const char * IMB_colormanagement_display_get_indexed_name(const int index)
const ColorSpace * IMB_colormanagement_space_from_interop_id(StringRefNull interop_id)
void IMB_colormanagement_init_untonemapped_view_settings(ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *)
static bool colormanage_hashcmp(const void *av, const void *bv)
const char * IMB_colormanagement_working_space_get()
bool IMB_colormanagement_space_to_cicp(const ColorSpace *colorspace, const ColorManagedFileOutput output, const bool rgb_matrix, int cicp[4])
const char * IMB_colormanagement_srgb_colorspace_name_get()
static bool is_colorspace_same_as_display(const ColorSpace *colorspace, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
static bool imb_colormanagement_working_space_set_from_matrix(Main *bmain, const char *name, const blender::float3x3 &scene_linear_to_xyz)
const char * IMB_colormanagement_look_get_indexed_name(const int index)
void IMB_colormanagement_validate_settings(const ColorManagedDisplaySettings *display_settings, ColorManagedViewSettings *view_settings)
void IMB_colormanagement_colorspace_to_scene_linear_v4(float pixel[4], const bool predivide, const ColorSpace *colorspace)
static void do_processor_transform_thread(ProcessorTransformThread *handle)
void IMB_colormanagement_wavelength_to_rgb_table(float *r_table, const int width)
void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], const ColorManagedDisplay *display, const ColorManagedDisplaySpace display_space)
void IMB_colormanagement_colorspace_from_ibuf_ftype(ColorManagedColorspaceSettings *colorspace_settings, ImBuf *ibuf)
void IMB_colormanagement_processor_apply_v3(ColormanageProcessor *cm_processor, float pixel[3])
static const float blackbody_table_g[7][3]
void IMB_colormanagegent_copy_settings(ImBuf *ibuf_src, ImBuf *ibuf_dst)
static void processor_transform_apply_threaded(uchar *byte_buffer, float *float_buffer, const int width, const int height, const int channels, ColormanageProcessor *cm_processor, const bool predivide, const bool float_from_byte)
static void colormanagement_transform_ex(uchar *byte_buffer, float *float_buffer, int width, int height, int channels, const char *from_colorspace, const char *to_colorspace, bool predivide)
void IMB_colormanagement_processor_apply(ColormanageProcessor *cm_processor, float *buffer, int width, int height, int channels, bool predivide)
const char * IMB_colormanagement_colorspace_get_name(const ColorSpace *colorspace)
const char * IMB_colormanagement_space_from_filepath_rules(const char *filepath)
bool IMB_colormanagement_setup_glsl_draw_ctx(const bContext *C, float dither, bool predivide)
const char * IMB_colormanagement_view_get_name_by_id(const int index)
static void colormanagement_imbuf_make_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, const ColorManagedDisplaySpace display_space, bool make_byte)
blender::Vector< char > IMB_colormanagement_space_to_icc_profile(const ColorSpace *colorspace)
void IMB_colormanagement_working_space_convert(Main *bmain, const blender::float3x3 &current_scene_linear_to_xyz, const blender::float3x3 &new_xyz_to_scene_linear, const bool depsgraph_tag, const bool linked_only, const bool editable_assets_only)
void IMB_display_buffer_release(void *cache_handle)
void IMB_colormanagement_get_whitepoint(const float temperature, const float tint, float whitepoint[3])
int IMB_colormanagement_view_get_id_by_name(const char *name)
static const int CICP_TRC_HLG
const char * IMB_colormanagement_role_colorspace_name_get(int role)
void IMB_colormanagement_display_settings_from_ctx(const bContext *C, ColorManagedViewSettings **r_view_settings, ColorManagedDisplaySettings **r_display_settings)
const char * IMB_colormanagement_get_float_colorspace(const ImBuf *ibuf)
blender::float3x3 IMB_colormanagement_get_xyz_to_scene_linear()
static bool colormanage_load_config(ocio::Config &config)
void IMB_colormanagement_scene_linear_to_colorspace(float *buffer, const int width, const int height, const int channels, const ColorSpace *colorspace)
void IMB_partial_display_buffer_update_threaded(ImBuf *ibuf, const float *linear_buffer, const uchar *byte_buffer, int stride, int offset_x, int offset_y, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, int xmin, int ymin, int xmax, int ymax)
static blender::float3 imb_working_space_convert(const blender::float3x3 &m, const bool is_smaller_gamut, const blender::float3 in_rgb)
const char * IMB_colormanagement_view_get_default_name(const char *display_name)
const ColorSpace * IMB_colormanagement_space_from_cicp(const int cicp[4], const ColorManagedFileOutput output)
static void colormanage_cache_handle_release(void *cache_handle)
static void do_display_buffer_apply_no_processor(DisplayBufferThread *handle)
static void colormanage_display_buffer_process_ex(ImBuf *ibuf, float *display_buffer, uchar *display_buffer_byte, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, const ColorManagedDisplaySpace display_space)
static void do_display_buffer_apply_thread(DisplayBufferThread *handle)
static void processor_transform_init_handle(ProcessorTransformThread *handle, int start_line, int tot_line, ProcessorTransformInitData *init_data)
#define DISPLAY_BUFFER_CHANNELS
void IMB_colormanagement_finish_glsl_draw()
static bool colormanage_check_colorspace_name(char *name, const char *what)
void IMB_colormanagement_imbuf_to_byte_texture(uchar *out_buffer, const int offset_x, const int offset_y, const int width, const int height, const ImBuf *ibuf, const bool store_premultiplied)
void colormanage_imbuf_set_default_spaces(ImBuf *ibuf)
static const float blackbody_table_b[7][4]
int IMB_colormanagement_look_get_named_index(const char *name)
static ImBuf * imbuf_ensure_editable(ImBuf *ibuf, ImBuf *colormanaged_ibuf, bool allocate_result)
const char * IMB_colormanagement_display_get_default_view_transform_name(const ColorManagedDisplay *display)
static void colormanage_display_settings_to_cache(ColormanageCacheDisplaySettings *cache_display_settings, const ColorManagedDisplaySettings *display_settings)
static void display_buffer_apply_threaded(ImBuf *ibuf, const float *buffer, uchar *byte_buffer, float *display_buffer, uchar *display_buffer_byte, ColormanageProcessor *cm_processor)
void IMB_colormanagement_colorspace_to_scene_linear(float *buffer, const int width, const int height, const int channels, const ColorSpace *colorspace, const bool predivide)
void IMB_colormanagement_imbuf_make_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, const ColorManagedDisplaySpace display_space)
ColormanageProcessor * IMB_colormanagement_display_processor_for_imbuf(const ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, const ColorManagedDisplaySpace display_space)
void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], const ColorManagedDisplay *display, const ColorManagedDisplaySpace display_space)
static void imb_partial_display_buffer_update_ex(ImBuf *ibuf, const float *linear_buffer, const uchar *byte_buffer, int stride, int offset_x, int offset_y, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, int xmin, int ymin, int xmax, int ymax, bool do_threads)
static const int CICP_PRI_REC2020
static const int CICP_PRI_P3D65
const char * IMB_colormanagement_working_space_get_default()
float3x3 global_scene_linear_to_xyz_default
static void colormanage_cache_put(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings, const ColormanageCacheDisplaySettings *display_settings, uchar *display_buffer, void **cache_handle)
void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totitem)
const ColorManagedDisplay * IMB_colormanagement_display_get_named(const char *name)
void IMB_colormanagement_blackbody_temperature_to_rgb_table(float *r_table, const int width, const float min, const float max)
void IMB_colormanagement_scene_linear_to_color_picking_v3(float color_picking[3], const float scene_linear[3])
StringRefNull IMB_colormanagement_space_get_interop_id(const ColorSpace *colorspace)
static char global_role_scene_linear[MAX_COLORSPACE_NAME]
void IMB_colormanagement_working_space_init_default(Main *bmain)
void IMB_colormanagement_blackbody_temperature_to_rgb(float r_dest[4], float value)
static StringRef view_filter_for_look(StringRefNull view_name)
int IMB_colormanagement_colorspace_get_named_index(const char *name)
static bool get_display_emulation(const ColorManagedDisplaySettings &display_settings)
static bool colormanage_role_color_space_name_get(ocio::Config &config, char *colorspace_name, const char *role, const char *backup_role, const bool optional=false)
static char global_role_texture_painting[MAX_COLORSPACE_NAME]
static char global_role_default_float_default[MAX_COLORSPACE_NAME]
static void colormanage_settings_to_key(ColormanageCacheKey *key, const ColormanageCacheViewSettings *view_settings, const ColormanageCacheDisplaySettings *display_settings)
static void curve_mapping_apply_pixel(const CurveMapping *curve_mapping, float *pixel, int channels)
void IMB_colormanagement_processor_apply_v4_predivide(ColormanageProcessor *cm_processor, float pixel[4])
void IMB_colormanagement_look_items_add(EnumPropertyItem **items, int *totitem, const char *view_name)
static bool colormanage_use_look(const char *look_name, const char *view_name)
static char global_role_aces_interchange[MAX_COLORSPACE_NAME]
const ColorSpace * IMB_colormangement_display_get_color_space(const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
void IMB_colormanagement_working_space_items_add(EnumPropertyItem **items, int *totitem)
static char global_role_default_byte[MAX_COLORSPACE_NAME]
static char global_role_default_float[MAX_COLORSPACE_NAME]
void IMB_colormanagement_processor_free(ColormanageProcessor *cm_processor)
void IMB_partial_display_buffer_update_delayed(ImBuf *ibuf, int xmin, int ymin, int xmax, int ymax)
static bool colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings, const char *what, const ocio::Display *default_display)
static void colormanage_free_config()
void IMB_colormanagement_transform_v4(float pixel[4], const char *from_colorspace, const char *to_colorspace)
static const int CICP_PRI_REC709
void IMB_colormanagement_working_space_check(Main *bmain, const bool for_undo, const bool have_editable_assets)
static void colormanage_view_settings_to_cache(ImBuf *ibuf, ColormanageCacheViewSettings *cache_view_settings, const ColorManagedViewSettings *view_settings)
void IMB_colormanagement_processor_apply_v4(ColormanageProcessor *cm_processor, float pixel[4])
int IMB_colormanagement_display_get_named_index(const char *name)
static char global_role_color_picking[MAX_COLORSPACE_NAME]
static uchar * colormanage_cache_get(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings, const ColormanageCacheDisplaySettings *display_settings, void **cache_handle)
static void wavelength_to_xyz(float xyz[3], float lambda_nm)
static const int CICP_MATRIX_REC2020_NCL
static const int CICP_RANGE_FULL
const char * IMB_colormanagement_display_get_default_name()
static MovieCache * colormanage_moviecache_ensure(ImBuf *ibuf)
void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace, const ColorManagedFileOutput output)
bool IMB_colormanagement_space_is_srgb(const ColorSpace *colorspace)
bool IMB_colormanagement_display_is_wide_gamut(const ColorManagedDisplaySettings *display_settings, const char *view_name)
bool IMB_colormanagement_setup_glsl_draw_from_space(const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, const ColorSpace *from_colorspace, float dither, bool predivide, bool do_overlay_merge)
void IMB_colormanagement_processor_apply_pixel(ColormanageProcessor *cm_processor, float *pixel, int channels)
const char * IMB_colormanagement_working_space_get_indexed_name(int index)
static void display_buffer_init_handle(DisplayBufferThread *handle, int start_line, int tot_line, DisplayBufferInitData *init_data)
#define powf(x, y)
#define roundf(x)
const ImFileType * IMB_file_type_from_ibuf(const ImBuf *ibuf)
Definition filetype.cc:232
void IMB_premultiply_rect_float(float *rect_float, int channels, int w, int h)
Definition filter.cc:363
#define in
#define out
#define printf(...)
#define output
MatBase< C, R > inverse(MatBase< C, R >) RET
#define LOG(level)
Definition log.h:97
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
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_float_to_uchar_clamp(float val)
#define M
bNodeTree * node_tree_from_id(ID *id)
Definition node.cc:4568
ColorSceneLinear4f< eAlpha::Straight > unpremultiply_alpha(const ColorSceneLinear4f< Alpha > &color)
Definition BLI_color.hh:111
ColorSceneLinear4f< eAlpha::Premultiplied > premultiply_alpha(const ColorSceneLinear4f< Alpha > &color)
Definition BLI_color.hh:93
float3x3 rec2020_to_scene_linear
float3x3 acescg_to_scene_linear
float3x3 scene_linear_to_rec2020
float3x3 rec709_to_scene_linear
float3x3 scene_linear_to_rec709
float3x3 scene_linear_to_acescg
T clamp(const T &a, const T &min, const T &max)
float3 whitepoint_from_temp_tint(float temperature, float tint)
bool whitepoint_to_temp_tint(const float3 &white, float &temperature, float &tint)
bool is_zero(const T &a)
CartesianBasis invert(const CartesianBasis &basis)
bool is_equal(const MatBase< T, NumCol, NumRow > &a, const MatBase< T, NumCol, NumRow > &b, const T epsilon=T(0))
T determinant(const MatBase< T, Size, Size > &mat)
static const float3x3 ACES_TO_XYZ
static const float3x3 XYZ_TO_REC2020
static const float3x3 ACESCG_TO_XYZ
static const float3x3 XYZ_TO_REC709
void foreach_strip(ListBase *seqbase, ForEachFunc callback, void *user_data)
Definition iterator.cc:59
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
MatBase< float, 3, 3 > float3x3
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
VecBase< float, 3 > float3
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
const char * name
#define fabsf
void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item)
#define min(a, b)
Definition sort.cc:36
#define FLT_MAX
Definition stdcycles.h:14
struct CurveMapping * curve_mapping
CurveMapping * curve_mapping
ColormanageCacheData * data
MovieCache * moviecache
CurveMapping * curve_mapping
std::shared_ptr< const ocio::CPUProcessor > cpu_processor
CurveMap cm[4]
ColormanageProcessor * cm_processor
ColormanageProcessor * cm_processor
const char * float_colorspace
const char * identifier
Definition RNA_types.hh:657
const char * name
Definition RNA_types.hh:661
const char * description
Definition RNA_types.hh:663
GlobalColorPickingState()=default
std::shared_ptr< const ocio::CPUProcessor > cpu_processor_to
std::shared_ptr< const ocio::CPUProcessor > cpu_processor_from
GlobalGPUState()=default
CurveMapping * curve_mapping
CurveMapping * orig_curve_mapping
IDTypeForeachColorFunction foreach_working_space_color
Definition DNA_ID.h:414
struct Library * lib
Definition DNA_ID.h:420
const ColorSpace * colorspace
const ColorSpace * colorspace
rcti invalid_rect
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
int colormanage_flag
unsigned char planes
unsigned int * display_buffer_flags
ColormanageCache * colormanage_cache
bool(* save)(ImBuf *ibuf, const char *filepath, int flags)
int default_save_role
ColorManagedColorspaceSettings linear_colorspace_settings
ColorManagedDisplaySettings display_settings
ColorManagedViewSettings view_settings
LibraryRuntimeHandle * runtime
Definition DNA_ID.h:579
char scene_linear_name[64]
Definition BKE_main.hh:150
bool is_missing_opencolorio_config
Definition BKE_main.hh:157
blender::float3x3 scene_linear_to_xyz
Definition BKE_main.hh:151
ListBase scenes
Definition BKE_main.hh:278
char filepath[1024]
Definition BKE_main.hh:179
MainColorspace colorspace
Definition BKE_main.hh:274
ColorManagedDisplaySettings display_settings
ColorManagedViewSettings view_settings
ColormanageProcessor * cm_processor
ColormanageProcessor * cm_processor
ColorManagedViewSettings view_settings
ColorManagedDisplaySettings display_settings
struct Image * image
int ymin
int ymax
int xmin
int xmax
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251