Blender V4.5
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_color_types.h"
16#include "DNA_image_types.h"
17#include "DNA_movieclip_types.h"
18#include "DNA_scene_types.h"
19#include "DNA_sequence_types.h"
20#include "DNA_space_types.h"
21
22#include "IMB_filetype.hh"
23#include "IMB_filter.hh"
24#include "IMB_imbuf.hh"
25#include "IMB_imbuf_types.hh"
26#include "IMB_metadata.hh"
27#include "IMB_moviecache.hh"
28
29#include "MEM_guardedalloc.h"
30
31#include "BLI_listbase.h"
32#include "BLI_math_color.h"
33#include "BLI_math_color.hh"
34#include "BLI_math_matrix.hh"
37#include "BLI_path_utils.hh"
38#include "BLI_rect.h"
39#include "BLI_string.h"
40#include "BLI_task.hh"
41#include "BLI_threads.h"
42#include "BLI_vector_set.hh"
43
44#include "BKE_appdir.hh"
45#include "BKE_colortools.hh"
46#include "BKE_context.hh"
47#include "BKE_global.hh"
48#include "BKE_image_format.hh"
49#include "BKE_main.hh"
50
51#include "GPU_capabilities.hh"
52
53#include "RNA_define.hh"
54
55#include "SEQ_iterator.hh"
56
57#include "OCIO_api.hh"
58
59using blender::float3;
62
63namespace ocio = blender::ocio;
64namespace math = blender::math;
65
66/* -------------------------------------------------------------------- */
69
70static std::unique_ptr<ocio::Config> g_config = nullptr;
72
73#define DISPLAY_BUFFER_CHANNELS 4
74
75/* ** list of all supported color spaces, displays and views */
84
85/* Luma coefficients and XYZ to RGB to be initialized by OCIO. */
86
94
95/* lock used by pre-cached processors getters, so processor wouldn't
96 * be created several times
97 * LOCK_COLORMANAGE can not be used since this mutex could be needed to
98 * be locked before pre-cached processor are creating
99 */
100static pthread_mutex_t processor_lock = BLI_MUTEX_INITIALIZER;
101
103 std::shared_ptr<const ocio::CPUProcessor> cpu_processor;
106};
107
108static struct GlobalGPUState {
109 GlobalGPUState() = default;
110
117
118 /* GPU shader currently bound. */
119 bool gpu_shader_bound = false;
120
121 /* Curve mapping. */
123 bool use_curve_mapping = false;
126
129
130 /* Cached processor for color picking conversion. */
131 std::shared_ptr<const ocio::CPUProcessor> cpu_processor_to;
132 std::shared_ptr<const ocio::CPUProcessor> cpu_processor_from;
133 bool failed = false;
135
137
138/* -------------------------------------------------------------------- */
141
197
198/* NOTE: ColormanageCacheViewSettings and ColormanageCacheDisplaySettings are
199 * quite the same as ColorManagedViewSettings and ColorManageDisplaySettings
200 * but they holds indexes of all transformations and color spaces, not
201 * their names.
202 *
203 * This helps avoid extra colorspace / display / view lookup without
204 * requiring to pass all variables which affects on display buffer
205 * to color management cache system and keeps calls small and nice.
206 */
218
222
224 int view; /* view transformation used for display buffer */
225 int display; /* display device name */
226};
227
229 int flag; /* view flags of cached buffer */
230 int look; /* Additional artistic transform. */
231 float exposure; /* exposure value cached buffer is calculated with */
232 float gamma; /* gamma value cached buffer is calculated with */
233 float dither; /* dither value cached buffer is calculated with */
234 float temperature; /* temperature value cached buffer is calculated with */
235 float tint; /* tint value cached buffer is calculated with */
236 CurveMapping *curve_mapping; /* curve mapping used for cached buffer */
237 int curve_mapping_timestamp; /* time stamp of curve mapping used for cached buffer */
238};
239
245
247{
248 if (!ibuf->colormanage_cache) {
249 return nullptr;
250 }
251
252 return ibuf->colormanage_cache->moviecache;
253}
254
256{
257 if (!ibuf->colormanage_cache) {
258 return nullptr;
259 }
260
261 return ibuf->colormanage_cache->data;
262}
263
264static uint colormanage_hashhash(const void *key_v)
265{
266 const ColormanageCacheKey *key = static_cast<const ColormanageCacheKey *>(key_v);
267
268 uint rval = (key->display << 16) | (key->view % 0xffff);
269
270 return rval;
271}
272
273static bool colormanage_hashcmp(const void *av, const void *bv)
274{
275 const ColormanageCacheKey *a = static_cast<const ColormanageCacheKey *>(av);
276 const ColormanageCacheKey *b = static_cast<const ColormanageCacheKey *>(bv);
277
278 return ((a->view != b->view) || (a->display != b->display));
279}
280
282{
283 if (!ibuf->colormanage_cache) {
284 ibuf->colormanage_cache = MEM_callocN<ColormanageCache>("imbuf colormanage cache");
285 }
286
287 if (!ibuf->colormanage_cache->moviecache) {
288 MovieCache *moviecache;
289
290 moviecache = IMB_moviecache_create("colormanage cache",
291 sizeof(ColormanageCacheKey),
294
295 ibuf->colormanage_cache->moviecache = moviecache;
296 }
297
298 return ibuf->colormanage_cache->moviecache;
299}
300
302{
303 if (!ibuf->colormanage_cache) {
304 ibuf->colormanage_cache = MEM_callocN<ColormanageCache>("imbuf colormanage cache");
305 }
306
307 ibuf->colormanage_cache->data = data;
308}
309
311 ColormanageCacheViewSettings *cache_view_settings,
312 const ColorManagedViewSettings *view_settings)
313{
314 int look = IMB_colormanagement_look_get_named_index(view_settings->look);
316
317 cache_view_settings->look = look;
318 cache_view_settings->view = view;
319 cache_view_settings->exposure = view_settings->exposure;
320 cache_view_settings->gamma = view_settings->gamma;
321 cache_view_settings->dither = ibuf->dither;
322 cache_view_settings->temperature = view_settings->temperature;
323 cache_view_settings->tint = view_settings->tint;
324 cache_view_settings->flag = view_settings->flag;
325 cache_view_settings->curve_mapping = view_settings->curve_mapping;
326}
327
329 ColormanageCacheDisplaySettings *cache_display_settings,
330 const ColorManagedDisplaySettings *display_settings)
331{
332 int display = IMB_colormanagement_display_get_named_index(display_settings->display_device);
333
334 cache_display_settings->display = display;
335}
336
338 const ColormanageCacheViewSettings *view_settings,
339 const ColormanageCacheDisplaySettings *display_settings)
340{
341 key->view = view_settings->view;
342 key->display = display_settings->display;
343}
344
347 void **cache_handle)
348{
349 ImBuf *cache_ibuf;
350 MovieCache *moviecache = colormanage_moviecache_get(ibuf);
351
352 if (!moviecache) {
353 /* If there's no moviecache it means no color management was applied
354 * on given image buffer before. */
355 return nullptr;
356 }
357
358 *cache_handle = nullptr;
359
360 cache_ibuf = IMB_moviecache_get(moviecache, key, nullptr);
361
362 *cache_handle = cache_ibuf;
363
364 return cache_ibuf;
365}
366
368 const ColormanageCacheViewSettings *view_settings,
369 const ColormanageCacheDisplaySettings *display_settings,
370 void **cache_handle)
371{
373 ImBuf *cache_ibuf;
374 int view_flag = 1 << view_settings->view;
375 CurveMapping *curve_mapping = view_settings->curve_mapping;
376 int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
377
378 colormanage_settings_to_key(&key, view_settings, display_settings);
379
380 /* check whether image was marked as dirty for requested transform */
381 if ((ibuf->display_buffer_flags[display_settings->display] & view_flag) == 0) {
382 return nullptr;
383 }
384
385 cache_ibuf = colormanage_cache_get_ibuf(ibuf, &key, cache_handle);
386
387 if (cache_ibuf) {
388
389 BLI_assert(cache_ibuf->x == ibuf->x && cache_ibuf->y == ibuf->y);
390
391 /* only buffers with different color space conversions are being stored
392 * in cache separately. buffer which were used only different exposure/gamma
393 * are re-suing the same cached buffer
394 *
395 * check here which exposure/gamma/curve was used for cached buffer and if they're
396 * different from requested buffer should be re-generated
397 */
398 const ColormanageCacheData *cache_data = colormanage_cachedata_get(cache_ibuf);
399
400 if (cache_data->look != view_settings->look ||
401 cache_data->exposure != view_settings->exposure ||
402 cache_data->gamma != view_settings->gamma || cache_data->dither != view_settings->dither ||
403 cache_data->temperature != view_settings->temperature ||
404 cache_data->tint != view_settings->tint || cache_data->flag != view_settings->flag ||
405 cache_data->curve_mapping != curve_mapping ||
406 cache_data->curve_mapping_timestamp != curve_mapping_timestamp)
407 {
408 *cache_handle = nullptr;
409
410 IMB_freeImBuf(cache_ibuf);
411
412 return nullptr;
413 }
414
415 return (uchar *)cache_ibuf->byte_buffer.data;
416 }
417
418 return nullptr;
419}
420
421static void colormanage_cache_put(ImBuf *ibuf,
422 const ColormanageCacheViewSettings *view_settings,
423 const ColormanageCacheDisplaySettings *display_settings,
424 uchar *display_buffer,
425 void **cache_handle)
426{
428 ImBuf *cache_ibuf;
429 ColormanageCacheData *cache_data;
430 int view_flag = 1 << view_settings->view;
431 MovieCache *moviecache = colormanage_moviecache_ensure(ibuf);
432 CurveMapping *curve_mapping = view_settings->curve_mapping;
433 int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
434
435 colormanage_settings_to_key(&key, view_settings, display_settings);
436
437 /* mark display buffer as valid */
438 ibuf->display_buffer_flags[display_settings->display] |= view_flag;
439
440 /* buffer itself */
441 cache_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0);
442 IMB_assign_byte_buffer(cache_ibuf, display_buffer, IB_TAKE_OWNERSHIP);
443
444 /* Store data which is needed to check whether cached buffer
445 * could be used for color managed display settings. */
446 cache_data = MEM_callocN<ColormanageCacheData>("color manage cache imbuf data");
447 cache_data->look = view_settings->look;
448 cache_data->exposure = view_settings->exposure;
449 cache_data->gamma = view_settings->gamma;
450 cache_data->dither = view_settings->dither;
451 cache_data->temperature = view_settings->temperature;
452 cache_data->tint = view_settings->tint;
453 cache_data->flag = view_settings->flag;
454 cache_data->curve_mapping = curve_mapping;
455 cache_data->curve_mapping_timestamp = curve_mapping_timestamp;
456
457 colormanage_cachedata_set(cache_ibuf, cache_data);
458
459 *cache_handle = cache_ibuf;
460
461 IMB_moviecache_put(moviecache, &key, cache_ibuf);
462}
463
464static void colormanage_cache_handle_release(void *cache_handle)
465{
466 ImBuf *cache_ibuf = static_cast<ImBuf *>(cache_handle);
467
468 IMB_freeImBuf(cache_ibuf);
469}
470
472
473/* -------------------------------------------------------------------- */
476
478 char *colorspace_name,
479 const char *role,
480 const char *backup_role,
481 const bool optional = false)
482{
483 const ColorSpace *ociocs = config.get_color_space(role);
484
485 if (ociocs == nullptr && backup_role) {
486 ociocs = config.get_color_space(backup_role);
487 }
488
489 if (ociocs == nullptr) {
490 /* Overall fallback role. */
491 ociocs = config.get_color_space("default");
492 }
493
494 if (ociocs == nullptr) {
495 if (!optional && !G.quiet) {
496 printf("Color management: Error, could not find role \"%s\"\n", role);
497 }
498 colorspace_name[0] = '\0';
499 return false;
500 }
501
502 /* assume function was called with buffer properly allocated to MAX_COLORSPACE_NAME chars */
503 BLI_strncpy(colorspace_name, ociocs->name().c_str(), MAX_COLORSPACE_NAME);
504 return true;
505}
506
508{
509 bool ok = true;
510
511 /* get roles */
525
528
529 if (g_config->get_num_displays() == 0) {
530 if (!G.quiet) {
531 printf("Color management: Error, could not find any displays\n");
532 }
533 ok = false;
534 }
535 /* NOTE: The look "None" is expected to be hard-coded to exist in the OpenColorIO integration. */
536 if (g_config->get_num_looks() == 0) {
537 if (!G.quiet) {
538 printf("Color management: Error, could not find any looks\n");
539 }
540 ok = false;
541 }
542
543 for (const int display_index : blender::IndexRange(g_config->get_num_displays())) {
544 const ocio::Display *display = g_config->get_display_by_index(display_index);
545 const int num_views = display->get_num_views();
546 if (num_views <= 0) {
547 if (!G.quiet) {
548 printf("Color management: Error, could not find any views for display %s\n",
549 display->name().c_str());
550 }
551 ok = false;
552 break;
553 }
554
555 for (const int view_index : blender::IndexRange(num_views)) {
556 const ocio::View *view = display->get_view_by_index(view_index);
557 g_all_view_names.add(view->name());
558 }
559 }
560
561 /* Load luminance coefficients. */
563
564 /* Load standard color spaces. */
567
570
573
574 return ok;
575}
576
578{
579 g_config = nullptr;
580 g_all_view_names.clear();
581}
582
584{
585 /* First try config from environment variable. */
586 const char *ocio_env = BLI_getenv("OCIO");
587
588 if (ocio_env && ocio_env[0] != '\0') {
590 if (g_config != nullptr) {
591 if (!G.quiet) {
592 printf("Color management: Using %s as a configuration file\n", ocio_env);
593 }
594 const bool ok = colormanage_load_config(*g_config);
595
596 if (!ok) {
597 if (!G.quiet) {
598 printf("Color management: Failed to load config from environment\n");
599 }
601 }
602 }
603 }
604
605 /* Then try bundled configuration file. */
606 if (g_config == nullptr) {
607 const std::optional<std::string> configdir = BKE_appdir_folder_id(BLENDER_DATAFILES,
608 "colormanagement");
609 if (configdir.has_value()) {
610 char configfile[FILE_MAX];
611 BLI_path_join(configfile, sizeof(configfile), configdir->c_str(), BCM_CONFIG_FILE);
612
614
615 if (g_config != nullptr) {
616 const bool ok = colormanage_load_config(*g_config);
617
618 if (!ok) {
619 if (!G.quiet) {
620 printf("Color management: Failed to load bundled config\n");
621 }
623 }
624 }
625 }
626 }
627
628 /* Then use fallback. */
629 if (g_config == nullptr) {
630 if (!G.quiet) {
631 printf("Color management: Using fallback mode for management\n");
632 }
635 }
636
638}
639
647
649
650/* -------------------------------------------------------------------- */
653
654static bool has_explicit_look_for_view(const char *view_name)
655{
656 if (!view_name) {
657 return false;
658 }
659
660 for (const int look_index : blender::IndexRange(g_config->get_num_looks())) {
661 const ocio::Look *look = g_config->get_look_by_index(look_index);
662 if (look->view() == view_name) {
663 return true;
664 }
665 }
666
667 return false;
668}
669
671 const char *view_name,
672 const bool has_explicit_look)
673{
674 if (look->is_noop) {
675 return true;
676 }
677
678 /* Skip looks only relevant to specific view transforms.
679 * If the view transform has view-specific look ignore non-specific looks. */
680
681 if (view_name && look->view() == view_name) {
682 return true;
683 }
684
685 if (has_explicit_look) {
686 return false;
687 }
688
689 return look->view().is_empty();
690}
691
692static bool colormanage_compatible_look(const ocio::Look *look, const char *view_name)
693{
694 const bool has_explicit_look = has_explicit_look_for_view(view_name);
695 return colormanage_compatible_look(look, view_name, has_explicit_look);
696}
697
698static bool colormanage_use_look(const char *look_name, const char *view_name)
699{
700 const ocio::Look *look = g_config->get_look_by_name(look_name);
701 return (look->is_noop == false && colormanage_compatible_look(look, view_name));
702}
703
705{
707
708 if (ibuf->colormanage_cache) {
710 MovieCache *moviecache = colormanage_moviecache_get(ibuf);
711
712 if (cache_data) {
713 MEM_freeN(cache_data);
714 }
715
716 if (moviecache) {
717 IMB_moviecache_free(moviecache);
718 }
719
721
722 ibuf->colormanage_cache = nullptr;
723 }
724}
725
727 const bContext *C,
728 ColorManagedViewSettings **r_view_settings,
729 ColorManagedDisplaySettings **r_display_settings)
730{
731 Scene *scene = CTX_data_scene(C);
733
734 *r_view_settings = &scene->view_settings;
735 *r_display_settings = &scene->display_settings;
736
737 if (sima && sima->image) {
738 if ((sima->image->flag & IMA_VIEW_AS_RENDER) == 0) {
739 *r_view_settings = nullptr;
740 }
741 }
742}
743
745 const ColorManagedViewSettings *view_settings,
746 const ColorManagedDisplaySettings *display_settings)
747{
748 return g_config->get_display_view_color_space(display_settings->display_device,
749 view_settings->view_transform);
750}
751
752static std::shared_ptr<const ocio::CPUProcessor> get_display_buffer_processor(
753 const char *look,
754 const char *view_transform,
755 const char *display,
756 const float exposure,
757 const float gamma,
758 const float temperature,
759 const float tint,
760 const bool use_white_balance,
761 const char *from_colorspace)
762{
763 ocio::DisplayParameters display_parameters;
764 display_parameters.from_colorspace = from_colorspace;
765 display_parameters.view = view_transform;
766 display_parameters.display = display;
767 display_parameters.look = (colormanage_use_look(look, view_transform)) ? look : "";
768 display_parameters.scale = (exposure == 0.0f) ? 1.0f : powf(2.0f, exposure);
769 display_parameters.exponent = (gamma == 1.0f) ? 1.0f : 1.0f / max_ff(FLT_EPSILON, gamma);
770 display_parameters.temperature = temperature;
771 display_parameters.tint = tint;
772 display_parameters.use_white_balance = use_white_balance;
773 display_parameters.inverse = false;
774
775 return g_config->get_display_cpu_processor(display_parameters);
776}
777
779 ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
780{
781 /* First, try use "Standard" view transform of the requested device. */
782 const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
783 if (!display) {
784 return;
785 }
786 const ocio::View *default_view = display->get_view_by_name("Standard");
787 /* If that fails, we fall back to the default view transform of the display
788 * as per OCIO configuration. */
789 if (default_view == nullptr) {
790 default_view = display->get_default_view();
791 }
792 if (default_view != nullptr) {
793 STRNCPY(view_settings->view_transform, default_view->name().c_str());
794 }
795 else {
796 view_settings->view_transform[0] = '\0';
797 }
798 /* TODO(sergey): Find a way to safely/reliable un-hardcode this. */
799 STRNCPY(view_settings->look, "None");
800 /* Initialize rest of the settings. */
801 view_settings->flag = 0;
802 view_settings->gamma = 1.0f;
803 view_settings->exposure = 0.0f;
804 view_settings->temperature = 6500.0f;
805 view_settings->tint = 10.0f;
806 view_settings->curve_mapping = nullptr;
807}
808
809static void curve_mapping_apply_pixel(const CurveMapping *curve_mapping,
810 float *pixel,
811 int channels)
812{
813 if (channels == 1) {
814 pixel[0] = BKE_curvemap_evaluateF(curve_mapping, curve_mapping->cm, pixel[0]);
815 }
816 else if (channels == 2) {
817 pixel[0] = BKE_curvemap_evaluateF(curve_mapping, curve_mapping->cm, pixel[0]);
818 pixel[1] = BKE_curvemap_evaluateF(curve_mapping, curve_mapping->cm, pixel[1]);
819 }
820 else {
821 BKE_curvemapping_evaluate_premulRGBF(curve_mapping, pixel, pixel);
822 }
823}
824
826{
827 /* Regression tests are allocating ImBuf. Guard against access of uninitialized color
828 * management configuration. */
829 /* TODO(sergey): Always allocate the fallback color management configuration for such cases? */
830 if (!g_config) {
831 return;
832 }
834}
835
836void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace)
837{
838 const ColorSpace *colorspace = g_config->get_color_space(from_colorspace);
839
840 if (colorspace && colorspace->is_data()) {
842 return;
843 }
844
845 if (ibuf->float_buffer.data) {
846 const char *to_colorspace = global_role_scene_linear;
847 const bool predivide = IMB_alpha_affects_rgb(ibuf);
848
849 if (ibuf->byte_buffer.data) {
851 }
852
854 ibuf->x,
855 ibuf->y,
856 ibuf->channels,
857 from_colorspace,
858 to_colorspace,
859 predivide);
860 ibuf->float_buffer.colorspace = nullptr;
861 }
862}
863
865
866/* -------------------------------------------------------------------- */
869
871 const char *what,
872 const ocio::Display *default_display)
873{
874 if (display_settings->display_device[0] == '\0') {
875 STRNCPY(display_settings->display_device, default_display->name().c_str());
876 }
877 else {
878 const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
879
880 if (!display) {
881 if (!G.quiet) {
882 printf(
883 "Color management: display \"%s\" used by %s not found, setting to default "
884 "(\"%s\").\n",
885 display_settings->display_device,
886 what,
887 default_display->name().c_str());
888 }
889
890 STRNCPY(display_settings->display_device, default_display->name().c_str());
891 }
892 }
893}
894
896 ColorManagedViewSettings *view_settings,
897 const char *what)
898{
899 const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
900 if (!display) {
901 return;
902 }
903 const char *default_look_name = IMB_colormanagement_look_get_default_name();
904
905 if (view_settings->view_transform[0] == '\0') {
906 const ocio::View *default_view = display->get_default_view();
907 if (default_view) {
908 STRNCPY(view_settings->view_transform, default_view->name().c_str());
909 }
910 }
911 else {
912 const ocio::View *view = display->get_view_by_name(view_settings->view_transform);
913 if (!view) {
914 const ocio::View *default_view = display->get_default_view();
915 if (default_view) {
916 if (!G.quiet) {
917 printf("Color management: %s view \"%s\" not found, setting default \"%s\".\n",
918 what,
919 view_settings->view_transform,
920 default_view->name().c_str());
921 }
922 STRNCPY(view_settings->view_transform, default_view->name().c_str());
923 }
924 }
925 }
926
927 if (view_settings->look[0] == '\0') {
928 STRNCPY(view_settings->look, default_look_name);
929 }
930 else {
931 const ocio::Look *look = g_config->get_look_by_name(view_settings->look);
932 if (look == nullptr) {
933 if (!G.quiet) {
934 printf("Color management: %s look \"%s\" not found, setting default \"%s\".\n",
935 what,
936 view_settings->look,
937 default_look_name);
938 }
939
940 STRNCPY(view_settings->look, default_look_name);
941 }
942 else if (!colormanage_compatible_look(look, view_settings->view_transform)) {
943 if (!G.quiet) {
944 printf(
945 "Color management: %s look \"%s\" is not compatible with view \"%s\", setting "
946 "default "
947 "\"%s\".\n",
948 what,
949 view_settings->look,
950 view_settings->view_transform,
951 default_look_name);
952 }
953
954 STRNCPY(view_settings->look, default_look_name);
955 }
956 }
957
958 /* OCIO_TODO: move to do_versions() */
959 if (view_settings->exposure == 0.0f && view_settings->gamma == 0.0f) {
960 view_settings->exposure = 0.0f;
961 view_settings->gamma = 1.0f;
962 }
963}
964
966 ColorManagedColorspaceSettings *colorspace_settings, const char *what)
967{
968 if (colorspace_settings->name[0] == '\0') {
969 /* pass */
970 }
971 else {
972 const ColorSpace *colorspace = g_config->get_color_space(colorspace_settings->name);
973
974 if (!colorspace) {
975 if (!G.quiet) {
976 printf("Color management: %s colorspace \"%s\" not found, will use default instead.\n",
977 what,
978 colorspace_settings->name);
979 }
980
981 STRNCPY(colorspace_settings->name, "");
982 }
983 }
984
985 (void)what;
986}
987
988static bool strip_callback(Strip *strip, void * /*user_data*/)
989{
990 if (strip->data) {
992 }
993 return true;
994}
995
997{
998 const ocio::Display *default_display = g_config->get_default_display();
999 if (!default_display) {
1000 /* happens when OCIO configuration is incorrect */
1001 return;
1002 }
1003
1004 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
1005 ColorManagedColorspaceSettings *sequencer_colorspace_settings;
1006
1007 /* check scene color management settings */
1008 colormanage_check_display_settings(&scene->display_settings, "scene", default_display);
1009 colormanage_check_view_settings(&scene->display_settings, &scene->view_settings, "scene");
1010
1011 sequencer_colorspace_settings = &scene->sequencer_colorspace_settings;
1012
1013 colormanage_check_colorspace_settings(sequencer_colorspace_settings, "sequencer");
1014
1015 if (sequencer_colorspace_settings->name[0] == '\0') {
1016 STRNCPY(sequencer_colorspace_settings->name, global_role_default_sequencer);
1017 }
1018
1019 /* check sequencer strip input color space settings */
1020 if (scene->ed != nullptr) {
1021 blender::seq::for_each_callback(&scene->ed->seqbase, strip_callback, nullptr);
1022 }
1023 }
1024
1025 /* ** check input color space settings ** */
1026
1027 LISTBASE_FOREACH (Image *, image, &bmain->images) {
1028 colormanage_check_colorspace_settings(&image->colorspace_settings, "image");
1029 }
1030
1031 LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) {
1032 colormanage_check_colorspace_settings(&clip->colorspace_settings, "clip");
1033 }
1034}
1035
1037 ColorManagedViewSettings *view_settings)
1038{
1039 const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
1040 const ocio::View *default_view = display->get_default_view();
1041
1042 bool found = false;
1043 for (const int view_index : blender::IndexRange(display->get_num_views())) {
1044 const ocio::View *view = display->get_view_by_index(view_index);
1045 if (view->name() == view_settings->view_transform) {
1046 found = true;
1047 break;
1048 }
1049 }
1050
1051 if (!found && default_view) {
1052 STRNCPY(view_settings->view_transform, default_view->name().c_str());
1053 }
1054}
1055
1057{
1058 switch (role) {
1059 case COLOR_ROLE_DATA:
1060 return global_role_data;
1075 default:
1076 if (!G.quiet) {
1077 printf("Unknown role was passed to %s\n", __func__);
1078 }
1079 BLI_assert(0);
1080 break;
1081 }
1082
1083 return nullptr;
1084}
1085
1086void IMB_colormanagement_check_is_data(ImBuf *ibuf, const char *name)
1087{
1088 const ColorSpace *colorspace = g_config->get_color_space(name);
1089
1090 if (colorspace && colorspace->is_data()) {
1092 }
1093 else {
1095 }
1096}
1097
1099{
1104 if (ibuf_src->flags & IB_alphamode_premul) {
1105 ibuf_dst->flags |= IB_alphamode_premul;
1106 }
1107 else if (ibuf_src->flags & IB_alphamode_channel_packed) {
1109 }
1110 else if (ibuf_src->flags & IB_alphamode_ignore) {
1111 ibuf_dst->flags |= IB_alphamode_ignore;
1112 }
1113}
1114
1116{
1117 const ColorSpace *colorspace = g_config->get_color_space(name);
1118
1119 ibuf->float_buffer.colorspace = colorspace;
1120
1121 if (colorspace && colorspace->is_data()) {
1123 }
1124 else {
1126 }
1127}
1128
1130{
1131 const ColorSpace *colorspace = g_config->get_color_space(name);
1132
1133 ibuf->byte_buffer.colorspace = colorspace;
1134
1135 if (colorspace && colorspace->is_data()) {
1137 }
1138 else {
1140 }
1141}
1142
1151
1160
1161const char *IMB_colormanagement_space_from_filepath_rules(const char *filepath)
1162{
1163 return g_config->get_color_space_from_filepath(filepath);
1164}
1165
1167{
1168 return g_config->get_color_space(name);
1169}
1170
1172{
1173 return (colorspace && colorspace->is_data());
1174}
1175
1177{
1178 return (colorspace && colorspace->is_scene_linear());
1179}
1180
1182{
1183 return (colorspace && colorspace->is_srgb());
1184}
1185
1187{
1188 const ColorSpace *colorspace = g_config->get_color_space(name);
1189 return (colorspace && colorspace->is_data());
1190}
1191
1193{
1194 const ColorSpace *colorspace = g_config->get_color_space(name);
1195 return (colorspace && IMB_colormanagement_space_is_scene_linear(colorspace));
1196}
1197
1199{
1200 const ColorSpace *colorspace = g_config->get_color_space(name);
1201 return (colorspace && IMB_colormanagement_space_is_srgb(colorspace));
1202}
1203
1205{
1206 /* Make a best effort to find by common names. First two are from the ColorInterop forum. */
1207 const char *names[] = {"sRGB Encoded Rec.709 (sRGB)",
1208 "srgb_rec709_scene",
1209 "Utility - sRGB - Texture",
1210 "sRGB - Texture",
1211 "sRGB",
1212 nullptr};
1213 for (int i = 0; names[i]; i++) {
1214 const ColorSpace *colorspace = g_config->get_color_space(names[i]);
1215 if (colorspace) {
1216 return colorspace->name().c_str();
1217 }
1218 }
1219
1220 /* Fallback if nothing can be found. */
1222}
1223
1228
1233
1235
1236/* -------------------------------------------------------------------- */
1239
1240void IMB_colormanagement_get_whitepoint(const float temperature,
1241 const float tint,
1242 float whitepoint[3])
1243{
1246}
1247
1248bool IMB_colormanagement_set_whitepoint(const float whitepoint[3], float &temperature, float &tint)
1249{
1250 blender::float3 xyz;
1252 return blender::math::whitepoint_to_temp_tint(xyz, temperature, tint);
1253}
1254
1256
1257/* -------------------------------------------------------------------- */
1260
1282
1297
1299 int start_line,
1300 int tot_line,
1302{
1303 ImBuf *ibuf = init_data->ibuf;
1304
1305 int channels = ibuf->channels;
1306 float dither = ibuf->dither;
1307 bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
1308
1309 size_t offset = size_t(channels) * start_line * ibuf->x;
1310 size_t display_buffer_byte_offset = size_t(DISPLAY_BUFFER_CHANNELS) * start_line * ibuf->x;
1311
1312 memset(handle, 0, sizeof(DisplayBufferThread));
1313
1314 handle->cm_processor = init_data->cm_processor;
1315
1316 if (init_data->buffer) {
1317 handle->buffer = init_data->buffer + offset;
1318 }
1319
1320 if (init_data->byte_buffer) {
1321 handle->byte_buffer = init_data->byte_buffer + offset;
1322 }
1323
1324 if (init_data->display_buffer) {
1325 handle->display_buffer = init_data->display_buffer + offset;
1326 }
1327
1328 if (init_data->display_buffer_byte) {
1329 handle->display_buffer_byte = init_data->display_buffer_byte + display_buffer_byte_offset;
1330 }
1331
1332 handle->width = ibuf->x;
1333
1334 handle->start_line = start_line;
1335 handle->tot_line = tot_line;
1336
1337 handle->channels = channels;
1338 handle->dither = dither;
1339 handle->is_data = is_data;
1340 handle->predivide = IMB_alpha_affects_rgb(ibuf);
1341
1342 handle->byte_colorspace = init_data->byte_colorspace;
1343 handle->float_colorspace = init_data->float_colorspace;
1344}
1345
1347 int height,
1348 float *linear_buffer,
1349 bool *is_straight_alpha)
1350{
1351 int channels = handle->channels;
1352 int width = handle->width;
1353
1354 size_t buffer_size = size_t(channels) * width * height;
1355
1356 bool is_data = handle->is_data;
1357 bool is_data_display = handle->cm_processor->is_data_result;
1358 bool predivide = handle->predivide;
1359
1360 if (!handle->buffer) {
1361 uchar *byte_buffer = handle->byte_buffer;
1362
1363 const char *from_colorspace = handle->byte_colorspace;
1364 const char *to_colorspace = global_role_scene_linear;
1365
1366 float *fp;
1367 uchar *cp;
1368 const size_t i_last = size_t(width) * height;
1369 size_t i;
1370
1371 /* first convert byte buffer to float, keep in image space */
1372 for (i = 0, fp = linear_buffer, cp = byte_buffer; i != i_last;
1373 i++, fp += channels, cp += channels)
1374 {
1375 if (channels == 3) {
1376 rgb_uchar_to_float(fp, cp);
1377 }
1378 else if (channels == 4) {
1379 rgba_uchar_to_float(fp, cp);
1380 }
1381 else {
1382 BLI_assert_msg(0, "Buffers of 3 or 4 channels are only supported here");
1383 }
1384 }
1385
1386 if (!is_data && !is_data_display) {
1387 /* convert float buffer to scene linear space */
1389 linear_buffer, width, height, channels, from_colorspace, to_colorspace, false);
1390 }
1391
1392 *is_straight_alpha = true;
1393 }
1394 else if (handle->float_colorspace) {
1395 /* currently float is non-linear only in sequencer, which is working
1396 * in its own color space even to handle float buffers.
1397 * This color space is the same for byte and float images.
1398 * Need to convert float buffer to linear space before applying display transform
1399 */
1400
1401 const char *from_colorspace = handle->float_colorspace;
1402 const char *to_colorspace = global_role_scene_linear;
1403
1404 memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1405
1406 if (!is_data && !is_data_display) {
1408 linear_buffer, width, height, channels, from_colorspace, to_colorspace, predivide);
1409 }
1410
1411 *is_straight_alpha = false;
1412 }
1413 else {
1414 /* some processors would want to modify float original buffer
1415 * before converting it into display byte buffer, so we need to
1416 * make sure original's ImBuf buffers wouldn't be modified by
1417 * using duplicated buffer here
1418 */
1419
1420 memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1421
1422 *is_straight_alpha = false;
1423 }
1424}
1425
1427{
1428 const int width = handle->width;
1429 const int height = handle->tot_line;
1430 if (handle->display_buffer_byte && handle->display_buffer_byte != handle->byte_buffer) {
1431 if (handle->byte_buffer) {
1433 handle->byte_buffer,
1436 false,
1437 width,
1438 height,
1439 width,
1440 width);
1441 }
1442 else if (handle->buffer) {
1444 handle->buffer,
1445 handle->channels,
1446 handle->dither,
1449 handle->predivide,
1450 width,
1451 height,
1452 width,
1453 width,
1454 handle->start_line);
1455 }
1456 }
1457
1458 if (handle->display_buffer) {
1459 if (handle->byte_buffer) {
1461 handle->byte_buffer,
1464 false,
1465 width,
1466 height,
1467 width,
1468 width);
1469 }
1470 else if (handle->buffer) {
1472 handle->buffer,
1473 handle->channels,
1476 handle->predivide,
1477 width,
1478 height,
1479 width,
1480 width);
1481 }
1482 }
1483}
1484
1486{
1487 ColormanageProcessor *cm_processor = handle->cm_processor;
1488 if (cm_processor == nullptr) {
1490 return;
1491 }
1492
1493 float *display_buffer = handle->display_buffer;
1494 uchar *display_buffer_byte = handle->display_buffer_byte;
1495 int channels = handle->channels;
1496 int width = handle->width;
1497 int height = handle->tot_line;
1498 float *linear_buffer = MEM_malloc_arrayN<float>(
1499 size_t(channels) * size_t(width) * size_t(height), "color conversion linear buffer");
1500
1501 bool is_straight_alpha;
1502 display_buffer_apply_get_linear_buffer(handle, height, linear_buffer, &is_straight_alpha);
1503
1504 bool predivide = handle->predivide && (is_straight_alpha == false);
1505
1506 /* Apply processor (note: data buffers never get color space conversions). */
1507 if (!handle->is_data) {
1509 cm_processor, linear_buffer, width, height, channels, predivide);
1510 }
1511
1512 /* copy result to output buffers */
1513 if (display_buffer_byte) {
1514 /* do conversion */
1515 IMB_buffer_byte_from_float(display_buffer_byte,
1516 linear_buffer,
1517 channels,
1518 handle->dither,
1521 predivide,
1522 width,
1523 height,
1524 width,
1525 width,
1526 handle->start_line);
1527 }
1528
1529 if (display_buffer) {
1530 memcpy(display_buffer, linear_buffer, size_t(width) * height * channels * sizeof(float));
1531
1532 if (is_straight_alpha && channels == 4) {
1533 const size_t i_last = size_t(width) * height;
1534 size_t i;
1535 float *fp;
1536
1537 for (i = 0, fp = display_buffer; i != i_last; i++, fp += channels) {
1539 }
1540 }
1541 }
1542
1543 MEM_freeN(linear_buffer);
1544}
1545
1547 const float *buffer,
1548 uchar *byte_buffer,
1549 float *display_buffer,
1550 uchar *display_buffer_byte,
1551 ColormanageProcessor *cm_processor)
1552{
1553 using namespace blender;
1555
1556 init_data.ibuf = ibuf;
1557 init_data.cm_processor = cm_processor;
1558 init_data.buffer = buffer;
1559 init_data.byte_buffer = byte_buffer;
1560 init_data.display_buffer = display_buffer;
1561 init_data.display_buffer_byte = display_buffer_byte;
1562
1563 if (ibuf->byte_buffer.colorspace != nullptr) {
1564 init_data.byte_colorspace = ibuf->byte_buffer.colorspace->name().c_str();
1565 }
1566 else {
1567 /* happens for viewer images, which are not so simple to determine where to
1568 * set image buffer's color spaces
1569 */
1570 init_data.byte_colorspace = global_role_default_byte;
1571 }
1572
1573 if (ibuf->float_buffer.colorspace != nullptr) {
1574 /* sequencer stores float buffers in non-linear space */
1575 init_data.float_colorspace = ibuf->float_buffer.colorspace->name().c_str();
1576 }
1577 else {
1578 init_data.float_colorspace = nullptr;
1579 }
1580
1581 threading::parallel_for(IndexRange(ibuf->y), 64, [&](const IndexRange y_range) {
1582 DisplayBufferThread handle;
1583 display_buffer_init_handle(&handle, y_range.first(), y_range.size(), &init_data);
1584 do_display_buffer_apply_thread(&handle);
1585 });
1586}
1587
1588/* Checks if given colorspace can be used for display as-is:
1589 * - View settings do not have extra curves, exposure, gamma or look applied, and
1590 * - Display colorspace is the same as given colorspace. */
1591static bool is_colorspace_same_as_display(const ColorSpace *colorspace,
1592 const ColorManagedViewSettings *view_settings,
1593 const ColorManagedDisplaySettings *display_settings)
1594{
1595 if ((view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) != 0 ||
1596 (view_settings->flag & COLORMANAGE_VIEW_USE_WHITE_BALANCE) != 0 ||
1597 view_settings->exposure != 0.0f || view_settings->gamma != 1.0f)
1598 {
1599 return false;
1600 }
1601
1602 const ocio::Look *look = g_config->get_look_by_name(view_settings->look);
1603 if (look != nullptr && !look->process_space().is_empty()) {
1604 return false;
1605 }
1606
1607 const ColorSpace *display_colorspace = get_display_colorspace(view_settings, display_settings);
1608 if (display_colorspace == colorspace) {
1609 return true;
1610 }
1611
1612 return false;
1613}
1614
1616 ImBuf *ibuf,
1617 float *display_buffer,
1618 uchar *display_buffer_byte,
1619 const ColorManagedViewSettings *view_settings,
1620 const ColorManagedDisplaySettings *display_settings)
1621{
1622 ColormanageProcessor *cm_processor = nullptr;
1623 /* Check if we can skip colorspace transforms. */
1624 bool skip_transform = false;
1625 if (ibuf->float_buffer.data == nullptr && ibuf->byte_buffer.colorspace) {
1626 skip_transform = is_colorspace_same_as_display(
1627 ibuf->byte_buffer.colorspace, view_settings, display_settings);
1628 }
1629 if (ibuf->byte_buffer.data == nullptr && ibuf->float_buffer.colorspace) {
1630 skip_transform = is_colorspace_same_as_display(
1631 ibuf->float_buffer.colorspace, view_settings, display_settings);
1632 }
1633
1634 if (skip_transform == false) {
1635 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1636 }
1637
1639 ibuf->float_buffer.data,
1640 ibuf->byte_buffer.data,
1641 display_buffer,
1642 display_buffer_byte,
1643 cm_processor);
1644
1645 if (cm_processor) {
1647 }
1648}
1649
1651 uchar *display_buffer,
1652 const ColorManagedViewSettings *view_settings,
1653 const ColorManagedDisplaySettings *display_settings)
1654{
1656 ibuf, nullptr, display_buffer, view_settings, display_settings);
1657}
1658
1660
1661/* -------------------------------------------------------------------- */
1664
1676
1687
1689 int start_line,
1690 int tot_line,
1692{
1693 const int channels = init_data->channels;
1694 const int width = init_data->width;
1695 const bool predivide = init_data->predivide;
1696 const bool float_from_byte = init_data->float_from_byte;
1697
1698 const size_t offset = size_t(channels) * start_line * width;
1699
1700 memset(handle, 0, sizeof(ProcessorTransformThread));
1701
1702 handle->cm_processor = init_data->cm_processor;
1703
1704 if (init_data->byte_buffer != nullptr) {
1705 /* TODO(serge): Offset might be different for byte and float buffers. */
1706 handle->byte_buffer = init_data->byte_buffer + offset;
1707 }
1708 if (init_data->float_buffer != nullptr) {
1709 handle->float_buffer = init_data->float_buffer + offset;
1710 }
1711
1712 handle->width = width;
1713
1714 handle->start_line = start_line;
1715 handle->tot_line = tot_line;
1716
1717 handle->channels = channels;
1718 handle->predivide = predivide;
1719 handle->float_from_byte = float_from_byte;
1720}
1721
1723{
1724 uchar *byte_buffer = handle->byte_buffer;
1725 float *float_buffer = handle->float_buffer;
1726 const int channels = handle->channels;
1727 const int width = handle->width;
1728 const int height = handle->tot_line;
1729 const bool predivide = handle->predivide;
1730 const bool float_from_byte = handle->float_from_byte;
1731
1732 if (float_from_byte) {
1733 IMB_buffer_float_from_byte(float_buffer,
1734 byte_buffer,
1737 false,
1738 width,
1739 height,
1740 width,
1741 width);
1743 handle->cm_processor, float_buffer, width, height, channels, predivide);
1744 IMB_premultiply_rect_float(float_buffer, 4, width, height);
1745 }
1746 else {
1747 if (byte_buffer != nullptr) {
1749 handle->cm_processor, byte_buffer, width, height, channels);
1750 }
1751 if (float_buffer != nullptr) {
1753 handle->cm_processor, float_buffer, width, height, channels, predivide);
1754 }
1755 }
1756}
1757
1759 float *float_buffer,
1760 const int width,
1761 const int height,
1762 const int channels,
1763 ColormanageProcessor *cm_processor,
1764 const bool predivide,
1765 const bool float_from_byte)
1766{
1767 using namespace blender;
1769
1770 init_data.cm_processor = cm_processor;
1771 init_data.byte_buffer = byte_buffer;
1772 init_data.float_buffer = float_buffer;
1773 init_data.width = width;
1774 init_data.height = height;
1775 init_data.channels = channels;
1776 init_data.predivide = predivide;
1777 init_data.float_from_byte = float_from_byte;
1778
1779 threading::parallel_for(IndexRange(height), 64, [&](const IndexRange y_range) {
1781 processor_transform_init_handle(&handle, y_range.first(), y_range.size(), &init_data);
1783 });
1784}
1785
1787
1788/* -------------------------------------------------------------------- */
1791
1792/* Convert the whole buffer from specified by name color space to another -
1793 * internal implementation. */
1794static void colormanagement_transform_ex(uchar *byte_buffer,
1795 float *float_buffer,
1796 int width,
1797 int height,
1798 int channels,
1799 const char *from_colorspace,
1800 const char *to_colorspace,
1801 bool predivide)
1802{
1803 if (from_colorspace[0] == '\0') {
1804 return;
1805 }
1806
1807 if (STREQ(from_colorspace, to_colorspace)) {
1808 /* if source and destination color spaces are identical, do nothing. */
1809 return;
1810 }
1811
1813 from_colorspace, to_colorspace);
1814 if (IMB_colormanagement_processor_is_noop(cm_processor)) {
1816 return;
1817 }
1818
1820 byte_buffer, float_buffer, width, height, channels, cm_processor, predivide, false);
1822}
1823
1825 int width,
1826 int height,
1827 int channels,
1828 const char *from_colorspace,
1829 const char *to_colorspace,
1830 bool predivide)
1831{
1833 nullptr, buffer, width, height, channels, from_colorspace, to_colorspace, predivide);
1834}
1835
1837 int width,
1838 int height,
1839 int channels,
1840 const char *from_colorspace,
1841 const char *to_colorspace)
1842{
1844 buffer, nullptr, width, height, channels, from_colorspace, to_colorspace, false);
1845}
1846
1848 uchar *byte_buffer,
1849 int width,
1850 int height,
1851 int channels,
1852 const char *from_colorspace,
1853 const char *to_colorspace)
1854{
1855 using namespace blender;
1856 ColormanageProcessor *cm_processor;
1857 if (from_colorspace == nullptr || from_colorspace[0] == '\0') {
1858 return;
1859 }
1860 if (STREQ(from_colorspace, to_colorspace) && channels == 4) {
1861 /* Color spaces are the same, just do a simple byte->float conversion. */
1862 int64_t pixel_count = int64_t(width) * height;
1863 threading::parallel_for(IndexRange(pixel_count), 256 * 1024, [&](IndexRange pix_range) {
1864 float *dst_ptr = float_buffer + pix_range.first() * channels;
1865 uchar *src_ptr = byte_buffer + pix_range.first() * channels;
1866 for ([[maybe_unused]] const int i : pix_range) {
1867 /* Equivalent of rgba_uchar_to_float + premultiply. */
1868 float cr = float(src_ptr[0]) * (1.0f / 255.0f);
1869 float cg = float(src_ptr[1]) * (1.0f / 255.0f);
1870 float cb = float(src_ptr[2]) * (1.0f / 255.0f);
1871 float ca = float(src_ptr[3]) * (1.0f / 255.0f);
1872 dst_ptr[0] = cr * ca;
1873 dst_ptr[1] = cg * ca;
1874 dst_ptr[2] = cb * ca;
1875 dst_ptr[3] = ca;
1876 src_ptr += 4;
1877 dst_ptr += 4;
1878 }
1879 });
1880 return;
1881 }
1882 cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
1884 byte_buffer, float_buffer, width, height, channels, cm_processor, false, true);
1886}
1887
1889 const char *from_colorspace,
1890 const char *to_colorspace)
1891{
1892 ColormanageProcessor *cm_processor;
1893
1894 if (from_colorspace[0] == '\0') {
1895 return;
1896 }
1897
1898 if (STREQ(from_colorspace, to_colorspace)) {
1899 /* if source and destination color spaces are identical, skip
1900 * threading overhead and simply do nothing
1901 */
1902 return;
1903 }
1904
1905 cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
1906
1907 IMB_colormanagement_processor_apply_v4(cm_processor, pixel);
1908
1910}
1911
1913 const ColorSpace *colorspace)
1914{
1915 if (colorspace == nullptr) { /* should never happen */
1916 printf("%s: perform conversion from unknown color space\n", __func__);
1917 return;
1918 }
1919 const ocio::CPUProcessor *processor = colorspace->get_to_scene_linear_cpu_processor();
1920 if (processor == nullptr) {
1921 return;
1922 }
1923 processor->apply_rgb(pixel);
1924}
1925
1927 const ColorSpace *colorspace)
1928{
1929 if (colorspace == nullptr) { /* should never happen */
1930 printf("%s: perform conversion from unknown color space\n", __func__);
1931 return;
1932 }
1933 const ocio::CPUProcessor *processor = colorspace->get_from_scene_linear_cpu_processor();
1934 if (processor == nullptr) {
1935 return;
1936 }
1937 processor->apply_rgb(pixel);
1938}
1939
1941 const bool predivide,
1942 const ColorSpace *colorspace)
1943{
1944 if (colorspace == nullptr) { /* should never happen */
1945 printf("%s: perform conversion from unknown color space\n", __func__);
1946 return;
1947 }
1948 const ocio::CPUProcessor *processor = colorspace->get_to_scene_linear_cpu_processor();
1949 if (processor == nullptr) {
1950 return;
1951 }
1952 if (predivide) {
1953 processor->apply_rgba_predivide(pixel);
1954 }
1955 else {
1956 processor->apply_rgba(pixel);
1957 }
1958}
1959
1961 const int width,
1962 const int height,
1963 const int channels,
1964 const ColorSpace *colorspace,
1965 const bool predivide)
1966{
1967 if (colorspace == nullptr) { /* should never happen */
1968 printf("%s: perform conversion from unknown color space\n", __func__);
1969 return;
1970 }
1971 const ocio::CPUProcessor *processor = colorspace->get_to_scene_linear_cpu_processor();
1972 if (processor == nullptr) {
1973 return;
1974 }
1975 ocio::PackedImage img(buffer,
1976 width,
1977 height,
1978 channels,
1980 sizeof(float),
1981 size_t(channels) * sizeof(float),
1982 size_t(channels) * sizeof(float) * width);
1983
1984 if (predivide) {
1985 processor->apply_predivide(img);
1986 }
1987 else {
1988 processor->apply(img);
1989 }
1990}
1991
1993 const int width,
1994 const int height,
1995 const int channels,
1996 const ColorSpace *colorspace)
1997{
1998 if (colorspace == nullptr) { /* should never happen */
1999 printf("%s: perform conversion from unknown color space\n", __func__);
2000 return;
2001 }
2002 const ocio::CPUProcessor *processor = colorspace->get_from_scene_linear_cpu_processor();
2003 if (processor == nullptr) {
2004 return;
2005 }
2006 const ocio::PackedImage img(buffer,
2007 width,
2008 height,
2009 channels,
2011 sizeof(float),
2012 size_t(channels) * sizeof(float),
2013 size_t(channels) * sizeof(float) * width);
2014 processor->apply(img);
2015}
2016
2018 const int offset_x,
2019 const int offset_y,
2020 const int width,
2021 const int height,
2022 const ImBuf *ibuf,
2023 const bool store_premultiplied)
2024{
2025 /* Byte buffer storage, only for sRGB, scene linear and data texture since other
2026 * color space conversions can't be done on the GPU. */
2028 BLI_assert(ibuf->float_buffer.data == nullptr);
2032
2033 const uchar *in_buffer = ibuf->byte_buffer.data;
2034 const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied;
2035
2036 for (int y = 0; y < height; y++) {
2037 const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
2038 const size_t out_offset = y * width;
2039 const uchar *in = in_buffer + in_offset * 4;
2040 uchar *out = out_buffer + out_offset * 4;
2041
2042 if (use_premultiply) {
2043 /* Premultiply only. */
2044 for (int x = 0; x < width; x++, in += 4, out += 4) {
2045 out[0] = (in[0] * in[3]) >> 8;
2046 out[1] = (in[1] * in[3]) >> 8;
2047 out[2] = (in[2] * in[3]) >> 8;
2048 out[3] = in[3];
2049 }
2050 }
2051 else {
2052 /* Copy only. */
2053 for (int x = 0; x < width; x++, in += 4, out += 4) {
2054 out[0] = in[0];
2055 out[1] = in[1];
2056 out[2] = in[2];
2057 out[3] = in[3];
2058 }
2059 }
2060 }
2061}
2062
2064 const int offset_x,
2065 const int offset_y,
2066 const int width,
2067 const int height,
2068 const ImBuf *ibuf,
2069 const bool store_premultiplied)
2070{
2071 using namespace blender;
2072
2073 /* Float texture are stored in scene linear color space, with premultiplied
2074 * alpha depending on the image alpha mode. */
2075 if (ibuf->float_buffer.data) {
2076 /* Float source buffer. */
2077 const float *in_buffer = ibuf->float_buffer.data;
2078 const int in_channels = ibuf->channels;
2079 const bool use_unpremultiply = IMB_alpha_affects_rgb(ibuf) && !store_premultiplied;
2080
2081 for (int y = 0; y < height; y++) {
2082 const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
2083 const size_t out_offset = y * width;
2084 const float *in = in_buffer + in_offset * in_channels;
2085 float *out = out_buffer + out_offset * 4;
2086
2087 if (in_channels == 1) {
2088 /* Copy single channel. */
2089 for (int x = 0; x < width; x++, in += 1, out += 4) {
2090 out[0] = in[0];
2091 out[1] = in[0];
2092 out[2] = in[0];
2093 out[3] = in[0];
2094 }
2095 }
2096 else if (in_channels == 3) {
2097 /* Copy RGB. */
2098 for (int x = 0; x < width; x++, in += 3, out += 4) {
2099 out[0] = in[0];
2100 out[1] = in[1];
2101 out[2] = in[2];
2102 out[3] = 1.0f;
2103 }
2104 }
2105 else if (in_channels == 4) {
2106 /* Copy or convert RGBA. */
2107 if (use_unpremultiply) {
2108 for (int x = 0; x < width; x++, in += 4, out += 4) {
2110 }
2111 }
2112 else {
2113 memcpy(out, in, sizeof(float[4]) * width);
2114 }
2115 }
2116 }
2117 }
2118 else {
2119 /* Byte source buffer. */
2120 const uchar *in_buffer = ibuf->byte_buffer.data;
2121 const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied;
2122
2123 const ocio::CPUProcessor *processor =
2124 (ibuf->byte_buffer.colorspace) ?
2126 nullptr;
2127
2128 threading::parallel_for(IndexRange(height), 128, [&](const IndexRange y_range) {
2129 for (const int y : y_range) {
2130 const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
2131 const size_t out_offset = y * width;
2132 const uchar *in = in_buffer + in_offset * 4;
2133 float *out = out_buffer + out_offset * 4;
2134 for (int x = 0; x < width; x++, in += 4, out += 4) {
2135 /* Convert to scene linear and premultiply. */
2136 float pixel[4];
2137 rgba_uchar_to_float(pixel, in);
2138 if (processor) {
2139 processor->apply_rgb(pixel);
2140 }
2141 else {
2142 srgb_to_linearrgb_v3_v3(pixel, pixel);
2143 }
2144 if (use_premultiply) {
2145 mul_v3_fl(pixel, pixel[3]);
2146 }
2147 copy_v4_v4(out, pixel);
2148 }
2149 }
2150 });
2151 }
2152}
2153
2155 const float scene_linear[3])
2156{
2157 if (!global_color_picking_state.cpu_processor_to && !global_color_picking_state.failed) {
2158 /* Create processor if none exists. */
2160
2161 if (!global_color_picking_state.cpu_processor_to && !global_color_picking_state.failed) {
2162 std::shared_ptr<const ocio::CPUProcessor> cpu_processor = g_config->get_cpu_processor(
2164
2165 if (cpu_processor) {
2166 global_color_picking_state.cpu_processor_to = cpu_processor;
2167 }
2168 else {
2169 global_color_picking_state.failed = true;
2170 }
2171 }
2172
2174 }
2175
2176 copy_v3_v3(color_picking, scene_linear);
2177
2178 if (global_color_picking_state.cpu_processor_to) {
2179 global_color_picking_state.cpu_processor_to->apply_rgb(color_picking);
2180 }
2181}
2182
2184 const float color_picking[3])
2185{
2186 if (!global_color_picking_state.cpu_processor_from && !global_color_picking_state.failed) {
2187 /* Create processor if none exists. */
2189
2190 if (!global_color_picking_state.cpu_processor_from && !global_color_picking_state.failed) {
2191 std::shared_ptr<const ocio::CPUProcessor> cpu_processor = g_config->get_cpu_processor(
2193
2194 if (cpu_processor) {
2195 global_color_picking_state.cpu_processor_from = cpu_processor;
2196 }
2197 else {
2198 global_color_picking_state.failed = true;
2199 }
2200 }
2201
2203 }
2204
2205 copy_v3_v3(scene_linear, color_picking);
2206
2207 if (global_color_picking_state.cpu_processor_from) {
2208 global_color_picking_state.cpu_processor_from->apply_rgb(scene_linear);
2209 }
2210}
2211
2213 const ColorManagedDisplay *display)
2214{
2215 const ocio::CPUProcessor *processor = display->get_from_scene_linear_cpu_processor();
2216 if (processor != nullptr) {
2217 processor->apply_rgb(pixel);
2218 }
2219}
2220
2222 const ColorManagedDisplay *display)
2223{
2224 const ocio::CPUProcessor *processor = display->get_to_scene_linear_cpu_processor();
2225 if (processor != nullptr) {
2226 processor->apply_rgb(pixel);
2227 }
2228}
2229
2231 float result[4],
2232 const float pixel[4],
2233 const ColorManagedViewSettings *view_settings,
2234 const ColorManagedDisplaySettings *display_settings)
2235{
2236 ColormanageProcessor *cm_processor;
2237
2238 copy_v4_v4(result, pixel);
2239
2240 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2243}
2244
2246 float result[3],
2247 const float pixel[3],
2248 const ColorManagedViewSettings *view_settings,
2249 const ColorManagedDisplaySettings *display_settings)
2250{
2251 ColormanageProcessor *cm_processor;
2252
2253 copy_v3_v3(result, pixel);
2254
2255 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2258}
2259
2261 ImBuf *ibuf,
2262 const ColorManagedViewSettings *view_settings,
2263 const ColorManagedDisplaySettings *display_settings,
2264 bool make_byte)
2265{
2266 if (!ibuf->byte_buffer.data && make_byte) {
2268 }
2269
2271 ibuf, ibuf->float_buffer.data, ibuf->byte_buffer.data, view_settings, display_settings);
2272}
2273
2275 ImBuf *ibuf,
2276 const ColorManagedViewSettings *view_settings,
2277 const ColorManagedDisplaySettings *display_settings)
2278{
2279 colormanagement_imbuf_make_display_space(ibuf, view_settings, display_settings, false);
2280}
2281
2282static ImBuf *imbuf_ensure_editable(ImBuf *ibuf, ImBuf *colormanaged_ibuf, bool allocate_result)
2283{
2284 if (colormanaged_ibuf != ibuf) {
2285 /* Is already an editable copy. */
2286 return colormanaged_ibuf;
2287 }
2288
2289 if (allocate_result) {
2290 /* Copy full image buffer. */
2291 colormanaged_ibuf = IMB_dupImBuf(ibuf);
2292 IMB_metadata_copy(colormanaged_ibuf, ibuf);
2293 return colormanaged_ibuf;
2294 }
2295
2296 /* Render pipeline is constructing image buffer itself,
2297 * but it's re-using byte and float buffers from render result make copy of this buffers
2298 * here sine this buffers would be transformed to other color space here. */
2301
2302 return ibuf;
2303}
2304
2306 bool save_as_render,
2307 bool allocate_result,
2308 const ImageFormatData *image_format)
2309{
2310 ImBuf *colormanaged_ibuf = ibuf;
2311
2312 /* Update byte buffer if exists but invalid. */
2313 if (ibuf->float_buffer.data && ibuf->byte_buffer.data &&
2315 {
2316 IMB_byte_from_float(ibuf);
2318 }
2319
2320 /* Detect if we are writing to a file format that needs a linear float buffer. */
2321 const bool linear_float_output = BKE_imtype_requires_linear_float(image_format->imtype);
2322
2323 /* Detect if we are writing output a byte buffer, which we would need to create
2324 * with color management conversions applied. This may be for either applying the
2325 * display transform for renders, or a user specified color space for the file. */
2326 const bool byte_output = BKE_image_format_is_byte(image_format);
2327
2328 /* If we're saving from RGBA to RGB buffer then it's not so much useful to just ignore alpha --
2329 * it leads to bad artifacts especially when saving byte images.
2330 *
2331 * What we do here is we're overlaying our image on top of background color (which is currently
2332 * black). This is quite much the same as what Gimp does and it seems to be what artists
2333 * expects from saving.
2334 *
2335 * Do a conversion here, so image format writers could happily assume all the alpha tricks were
2336 * made already. helps keep things locally here, not spreading it to all possible image writers
2337 * we've got.
2338 */
2339 if (image_format->planes != R_IMF_PLANES_RGBA) {
2340 float color[3] = {0, 0, 0};
2341
2342 colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result);
2343
2344 if (colormanaged_ibuf->float_buffer.data && colormanaged_ibuf->channels == 4) {
2346 colormanaged_ibuf->float_buffer.data, colormanaged_ibuf->x, colormanaged_ibuf->y, color);
2347 }
2348
2349 if (colormanaged_ibuf->byte_buffer.data) {
2351 colormanaged_ibuf->byte_buffer.data, colormanaged_ibuf->x, colormanaged_ibuf->y, color);
2352 }
2353 }
2354
2355 if (save_as_render && !linear_float_output) {
2356 /* Render output: perform conversion to display space using view transform. */
2357 colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result);
2358
2360 &image_format->view_settings,
2361 &image_format->display_settings,
2362 byte_output);
2363
2364 if (colormanaged_ibuf->float_buffer.data) {
2365 /* Float buffer isn't linear anymore,
2366 * image format write callback should check for this flag and assume
2367 * no space conversion should happen if ibuf->float_buffer.colorspace != nullptr. */
2368 colormanaged_ibuf->float_buffer.colorspace = get_display_colorspace(
2369 &image_format->view_settings, &image_format->display_settings);
2370 if (byte_output) {
2371 colormanaged_ibuf->byte_buffer.colorspace = colormanaged_ibuf->float_buffer.colorspace;
2372 }
2373 }
2374 }
2375 else {
2376 /* Linear render or regular file output: conversion between two color spaces. */
2377
2378 /* Detect which color space we need to convert between. */
2379 const char *from_colorspace = (ibuf->float_buffer.data &&
2380 !(byte_output && ibuf->byte_buffer.data)) ?
2381 /* From float buffer. */
2382 (ibuf->float_buffer.colorspace) ?
2383 ibuf->float_buffer.colorspace->name().c_str() :
2385 /* From byte buffer. */
2386 (ibuf->byte_buffer.colorspace) ?
2387 ibuf->byte_buffer.colorspace->name().c_str() :
2389
2390 const char *to_colorspace = image_format->linear_colorspace_settings.name;
2391
2392 /* TODO: can we check with OCIO if color spaces are the same but have different names? */
2393 if (to_colorspace[0] == '\0' || STREQ(from_colorspace, to_colorspace)) {
2394 /* No conversion needed, but may still need to allocate byte buffer for output. */
2395 if (byte_output && !ibuf->byte_buffer.data) {
2397 IMB_byte_from_float(ibuf);
2398 }
2399 }
2400 else {
2401 /* Color space conversion needed. */
2402 colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result);
2403
2404 if (byte_output) {
2406 to_colorspace);
2407
2408 if (colormanaged_ibuf->byte_buffer.data) {
2409 /* Byte to byte. */
2411 colormanaged_ibuf->x,
2412 colormanaged_ibuf->y,
2413 colormanaged_ibuf->channels,
2414 from_colorspace,
2415 to_colorspace);
2416 }
2417 else {
2418 /* Float to byte. */
2419 IMB_byte_from_float(colormanaged_ibuf);
2420 }
2421 }
2422 else {
2423 if (!colormanaged_ibuf->float_buffer.data) {
2424 /* Byte to float. */
2425 IMB_float_from_byte(colormanaged_ibuf);
2426 IMB_free_byte_pixels(colormanaged_ibuf);
2427
2428 /* This conversion always goes to scene linear. */
2429 from_colorspace = global_role_scene_linear;
2430 }
2431
2432 if (colormanaged_ibuf->float_buffer.data) {
2433 /* Float to float. */
2435 colormanaged_ibuf->x,
2436 colormanaged_ibuf->y,
2437 colormanaged_ibuf->channels,
2438 from_colorspace,
2439 to_colorspace,
2440 false);
2441
2443 to_colorspace);
2444 }
2445 }
2446 }
2447 }
2448
2449 return colormanaged_ibuf;
2450}
2451
2453
2454/* -------------------------------------------------------------------- */
2457
2459 const ColorManagedViewSettings *view_settings,
2460 const ColorManagedDisplaySettings *display_settings,
2461 void **cache_handle)
2462{
2463 uchar *display_buffer;
2464 ColormanageCacheViewSettings cache_view_settings;
2465 ColormanageCacheDisplaySettings cache_display_settings;
2466 ColorManagedViewSettings default_view_settings;
2467 const ColorManagedViewSettings *applied_view_settings;
2468
2469 *cache_handle = nullptr;
2470
2471 if (!ibuf->x || !ibuf->y) {
2472 return nullptr;
2473 }
2474
2475 if (view_settings) {
2476 applied_view_settings = view_settings;
2477 }
2478 else {
2479 /* If no view settings were specified, use default ones, which will
2480 * attempt not to do any extra color correction. */
2481 IMB_colormanagement_init_default_view_settings(&default_view_settings, display_settings);
2482 applied_view_settings = &default_view_settings;
2483 }
2484
2485 /* No float buffer and byte buffer is already in display space, let's just use it. */
2486 if (ibuf->float_buffer.data == nullptr && ibuf->byte_buffer.colorspace && ibuf->channels == 4) {
2488 ibuf->byte_buffer.colorspace, applied_view_settings, display_settings))
2489 {
2490 return ibuf->byte_buffer.data;
2491 }
2492 }
2493
2494 colormanage_view_settings_to_cache(ibuf, &cache_view_settings, applied_view_settings);
2495 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
2496
2497 if (ibuf->invalid_rect.xmin != ibuf->invalid_rect.xmax) {
2498 if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
2500 ibuf->float_buffer.data,
2501 ibuf->byte_buffer.data,
2502 ibuf->x,
2503 0,
2504 0,
2505 applied_view_settings,
2506 display_settings,
2507 ibuf->invalid_rect.xmin,
2508 ibuf->invalid_rect.ymin,
2509 ibuf->invalid_rect.xmax,
2510 ibuf->invalid_rect.ymax);
2511 }
2512
2513 BLI_rcti_init(&ibuf->invalid_rect, 0, 0, 0, 0);
2514 }
2515
2517
2518 /* ensure color management bit fields exists */
2519 if (!ibuf->display_buffer_flags) {
2520 ibuf->display_buffer_flags = MEM_calloc_arrayN<uint>(g_config->get_num_displays(),
2521 "imbuf display_buffer_flags");
2522 }
2523 else if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) {
2524 /* all display buffers were marked as invalid from other areas,
2525 * now propagate this flag to internal color management routines
2526 */
2527 memset(ibuf->display_buffer_flags, 0, g_config->get_num_displays() * sizeof(uint));
2528
2530 }
2531
2532 display_buffer = colormanage_cache_get(
2533 ibuf, &cache_view_settings, &cache_display_settings, cache_handle);
2534
2535 if (display_buffer) {
2537 return display_buffer;
2538 }
2539
2540 display_buffer = MEM_malloc_arrayN<uchar>(
2541 DISPLAY_BUFFER_CHANNELS * size_t(ibuf->x) * size_t(ibuf->y), "imbuf display buffer");
2542
2544 ibuf, display_buffer, applied_view_settings, display_settings);
2545
2547 ibuf, &cache_view_settings, &cache_display_settings, display_buffer, cache_handle);
2548
2550
2551 return display_buffer;
2552}
2553
2554uchar *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle)
2555{
2556 ColorManagedViewSettings *view_settings;
2557 ColorManagedDisplaySettings *display_settings;
2558
2559 IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
2560
2561 return IMB_display_buffer_acquire(ibuf, view_settings, display_settings, cache_handle);
2562}
2563
2565 float *linear_buffer,
2566 int width,
2567 int height,
2568 int channels,
2569 const ColorManagedViewSettings *view_settings,
2570 const ColorManagedDisplaySettings *display_settings,
2571 bool predivide)
2572{
2573 float *buffer;
2575 display_settings);
2576
2577 buffer = MEM_malloc_arrayN<float>(size_t(channels) * size_t(width) * size_t(height),
2578 "display transform temp buffer");
2579 memcpy(buffer, linear_buffer, size_t(channels) * width * height * sizeof(float));
2580
2581 IMB_colormanagement_processor_apply(cm_processor, buffer, width, height, channels, predivide);
2582
2584
2585 IMB_buffer_byte_from_float(display_buffer,
2586 buffer,
2587 channels,
2588 0.0f,
2591 false,
2592 width,
2593 height,
2594 width,
2595 width);
2596
2597 MEM_freeN(buffer);
2598}
2599
2600void IMB_display_buffer_transform_apply_float(float *float_display_buffer,
2601 float *linear_buffer,
2602 int width,
2603 int height,
2604 int channels,
2605 const ColorManagedViewSettings *view_settings,
2606 const ColorManagedDisplaySettings *display_settings,
2607 bool predivide)
2608{
2609 float *buffer;
2611 display_settings);
2612
2613 buffer = MEM_malloc_arrayN<float>(size_t(channels) * size_t(width) * size_t(height),
2614 "display transform temp buffer");
2615 memcpy(buffer, linear_buffer, size_t(channels) * width * height * sizeof(float));
2616
2617 IMB_colormanagement_processor_apply(cm_processor, buffer, width, height, channels, predivide);
2618
2620
2621 memcpy(float_display_buffer, buffer, size_t(channels) * width * height * sizeof(float));
2622 MEM_freeN(buffer);
2623}
2624
2625void IMB_display_buffer_release(void *cache_handle)
2626{
2627 if (cache_handle) {
2629
2631
2633 }
2634}
2635
2637
2638/* -------------------------------------------------------------------- */
2641
2643{
2644 const ocio::Display *display = g_config->get_display_by_name(name);
2645 if (display) {
2646 return display->index;
2647 }
2648 return -1;
2649}
2650
2652{
2653 const ocio::Display *display = g_config->get_display_by_index(index);
2654 if (!display) {
2655 return "";
2656 }
2657 return display->name().c_str();
2658}
2659
2661{
2662 const ocio::Display *display = g_config->get_default_display();
2663 return display->name().c_str();
2664}
2665
2667{
2668 return g_config->get_display_by_name(name);
2669}
2670
2672{
2673 if (g_config->get_display_by_name("None") != nullptr) {
2674 return "None";
2675 }
2677}
2678
2680 const ColorManagedDisplay *display)
2681{
2682 const ocio::View *default_view = display->get_default_view();
2683 if (!default_view) {
2684 return "";
2685 }
2686 return default_view->name().c_str();
2687}
2688
2690
2691/* -------------------------------------------------------------------- */
2694
2696{
2697 return g_all_view_names.index_of_try(name);
2698}
2699
2701{
2702 /* The code is expected to be used for the purposes of the EnumPropertyItem and maintaining the
2703 * DNA values from RNA's get() and set(). It is unexpected that an invalid index will be passed
2704 * here, as it will indicate a coding error somewhere else. */
2705 if (index < 0 || index >= g_all_view_names.size()) {
2706 BLI_assert(0);
2707 return "";
2708 }
2709
2710 return g_all_view_names[index].c_str();
2711}
2712
2713const char *IMB_colormanagement_view_get_default_name(const char *display_name)
2714{
2715 const ocio::Display *display = g_config->get_display_by_name(display_name);
2716 if (!display) {
2717 return "";
2718 }
2719
2720 const ocio::View *view = display->get_default_view();
2721 if (!view) {
2722 return "";
2723 }
2724
2725 return view->name().c_str();
2726}
2727
2728const char *IMB_colormanagement_view_get_raw_or_default_name(const char *display_name)
2729{
2730 const ocio::Display *display = g_config->get_display_by_name(display_name);
2731 if (!display) {
2732 return "";
2733 }
2734
2735 const ocio::View *view = nullptr;
2736
2737 if (!view) {
2738 view = display->get_view_by_name("Raw");
2739 }
2740
2741 if (!view) {
2742 view = display->get_default_view();
2743 }
2744
2745 if (!view) {
2746 return "";
2747 }
2748
2749 return view->name().c_str();
2750}
2751
2753
2754/* -------------------------------------------------------------------- */
2757
2759{
2760 return g_config->get_color_space(name);
2761}
2762
2764{
2765 const char *role_colorspace = IMB_colormanagement_role_colorspace_name_get(role);
2766
2767 return colormanage_colorspace_get_named(role_colorspace);
2768}
2769
2771{
2772 const ColorSpace *colorspace = colormanage_colorspace_get_named(name);
2773 if (colorspace) {
2774 return colorspace->index;
2775 }
2776 return -1;
2777}
2778
2780{
2781 const ColorSpace *colorspace = g_config->get_color_space_by_index(index);
2782 if (colorspace) {
2783 return colorspace->name().c_str();
2784 }
2785 return "";
2786}
2787
2789{
2790 return colorspace->name().c_str();
2791}
2792
2794 ColorManagedColorspaceSettings *colorspace_settings, ImBuf *ibuf)
2795{
2796 /* Don't modify non-color data space, it does not change with file type. */
2797 const ColorSpace *colorspace = g_config->get_color_space(colorspace_settings->name);
2798
2799 if (colorspace && colorspace->is_data()) {
2800 return;
2801 }
2802
2803 /* Get color space from file type. */
2804 const ImFileType *type = IMB_file_type_from_ibuf(ibuf);
2805 if (type != nullptr) {
2806 if (type->save != nullptr) {
2807 const char *role_colorspace = IMB_colormanagement_role_colorspace_name_get(
2808 type->default_save_role);
2809 STRNCPY(colorspace_settings->name, role_colorspace);
2810 }
2811 }
2812}
2813
2815
2816/* -------------------------------------------------------------------- */
2819
2821{
2822 const ocio::Look *look = g_config->get_look_by_name(name);
2823 if (look) {
2824 return look->index;
2825 }
2826 return -1;
2827}
2828
2830{
2831 const ocio::Look *look = g_config->get_look_by_index(index);
2832 if (!look) {
2833 return "";
2834 }
2835 return look->name().c_str();
2836}
2837
2839{
2840 const ocio::Look *look = g_config->get_look_by_index(0);
2841 if (!look) {
2842 return "";
2843 }
2844 return look->name().c_str();
2845}
2846
2847const char *IMB_colormanagement_look_validate_for_view(const char *view_name,
2848 const char *look_name)
2849{
2850 const ocio::Look *look = g_config->get_look_by_name(look_name);
2851 if (!look) {
2852 return look_name;
2853 }
2854
2855 /* Keep same look if compatible. */
2856 if (colormanage_compatible_look(look, view_name)) {
2857 return look_name;
2858 }
2859
2860 /* Try to find another compatible look with the same UI name, in case of looks specialized for
2861 * view transform, */
2862 for (const int other_look_index : blender::IndexRange(g_config->get_num_looks())) {
2863 const ocio::Look *other_look = g_config->get_look_by_index(other_look_index);
2864 if (look->ui_name() == other_look->ui_name() &&
2865 colormanage_compatible_look(other_look, view_name))
2866 {
2867 return other_look->name().c_str();
2868 }
2869 }
2870
2872}
2873
2875
2876/* -------------------------------------------------------------------- */
2879
2881{
2882 for (const int display_index : blender::IndexRange(g_config->get_num_displays())) {
2883 const ocio::Display *display = g_config->get_display_by_index(display_index);
2884
2885 EnumPropertyItem item;
2886
2887 item.value = display->index;
2888 item.name = display->name().c_str();
2889 item.identifier = display->name().c_str();
2890 item.icon = 0;
2891 item.description = "";
2892
2893 RNA_enum_item_add(items, totitem, &item);
2894 }
2895}
2896
2898 int *totitem,
2899 const char *display_name)
2900{
2901 const ocio::Display *display = g_config->get_display_by_name(display_name);
2902 if (!display) {
2903 return;
2904 }
2905
2906 for (const int view_index : blender::IndexRange(display->get_num_views())) {
2907 const ocio::View *view = display->get_view_by_index(view_index);
2908
2909 EnumPropertyItem item;
2910
2911 item.value = IMB_colormanagement_view_get_id_by_name(view->name().c_str());
2912 item.name = view->name().c_str();
2913 item.identifier = view->name().c_str();
2914 item.icon = 0;
2915 item.description = "";
2916
2917 RNA_enum_item_add(items, totitem, &item);
2918 }
2919}
2920
2922 int *totitem,
2923 const char *view_name)
2924{
2925 const bool has_explicit_look = has_explicit_look_for_view(view_name);
2926
2927 for (const int look_index : blender::IndexRange(g_config->get_num_looks())) {
2928 const ocio::Look *look = g_config->get_look_by_index(look_index);
2929 if (!colormanage_compatible_look(look, view_name, has_explicit_look)) {
2930 continue;
2931 }
2932
2933 EnumPropertyItem item;
2934
2935 item.value = look->index;
2936 item.name = look->ui_name().c_str();
2937 item.identifier = look->name().c_str();
2938 item.icon = 0;
2939 item.description = "";
2940
2941 RNA_enum_item_add(items, totitem, &item);
2942 }
2943}
2944
2946{
2947 for (const int colorspace_index : blender::IndexRange(g_config->get_num_color_spaces())) {
2948 const ColorSpace *colorspace = g_config->get_sorted_color_space_by_index(colorspace_index);
2949 if (!colorspace->is_invertible()) {
2950 continue;
2951 }
2952
2953 EnumPropertyItem item;
2954
2955 item.value = colorspace->index;
2956 item.name = colorspace->name().c_str();
2957 item.identifier = colorspace->name().c_str();
2958 item.icon = 0;
2959 item.description = colorspace->description().c_str();
2960
2961 RNA_enum_item_add(items, totitem, &item);
2962 }
2963}
2964
2966
2967/* -------------------------------------------------------------------- */
2970
2971/*
2972 * Partial display update is supposed to be used by such areas as
2973 * compositor and renderer, This areas are calculating tiles of the
2974 * images and because of performance reasons only this tiles should
2975 * be color managed.
2976 * This gives nice visual feedback without slowing things down.
2977 *
2978 * Updating happens for active display transformation only, all
2979 * the rest buffers would be marked as dirty
2980 */
2981
2983 uchar *display_buffer,
2984 const float *linear_buffer,
2985 const uchar *byte_buffer,
2986 int display_stride,
2987 int linear_stride,
2988 int linear_offset_x,
2989 int linear_offset_y,
2990 ColormanageProcessor *cm_processor,
2991 const int xmin,
2992 const int ymin,
2993 const int xmax,
2994 const int ymax)
2995{
2996 int x, y;
2997 int channels = ibuf->channels;
2998 float dither = ibuf->dither;
2999 const ColorSpace *rect_colorspace = ibuf->byte_buffer.colorspace;
3000 float *display_buffer_float = nullptr;
3001 const int width = xmax - xmin;
3002 const int height = ymax - ymin;
3003 bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
3004
3005 if (dither != 0.0f) {
3006 /* cm_processor is nullptr in cases byte_buffer's space matches display
3007 * buffer's space
3008 * in this case we could skip extra transform and only apply dither
3009 * use 4 channels for easier byte->float->byte conversion here so
3010 * (this is only needed to apply dither, in other cases we'll convert
3011 * byte buffer to display directly)
3012 */
3013 if (!cm_processor) {
3014 channels = 4;
3015 }
3016
3017 display_buffer_float = MEM_malloc_arrayN<float>(
3018 size_t(channels) * size_t(width) * size_t(height), "display buffer for dither");
3019 }
3020
3021 if (cm_processor) {
3022 for (y = ymin; y < ymax; y++) {
3023 for (x = xmin; x < xmax; x++) {
3024 size_t display_index = (size_t(y) * display_stride + x) * 4;
3025 size_t linear_index = (size_t(y - linear_offset_y) * linear_stride +
3026 (x - linear_offset_x)) *
3027 channels;
3028 float pixel[4];
3029
3030 if (linear_buffer) {
3031 if (channels == 4) {
3032 copy_v4_v4(pixel, (float *)linear_buffer + linear_index);
3033 }
3034 else if (channels == 3) {
3035 copy_v3_v3(pixel, (float *)linear_buffer + linear_index);
3036 pixel[3] = 1.0f;
3037 }
3038 else if (channels == 1) {
3039 pixel[0] = linear_buffer[linear_index];
3040 }
3041 else {
3042 BLI_assert_msg(0, "Unsupported number of channels in partial buffer update");
3043 }
3044 }
3045 else if (byte_buffer) {
3046 rgba_uchar_to_float(pixel, byte_buffer + linear_index);
3048 straight_to_premul_v4(pixel);
3049 }
3050
3051 if (!is_data) {
3052 IMB_colormanagement_processor_apply_pixel(cm_processor, pixel, channels);
3053 }
3054
3055 if (display_buffer_float) {
3056 size_t index = (size_t(y - ymin) * width + (x - xmin)) * channels;
3057
3058 if (channels == 4) {
3059 copy_v4_v4(display_buffer_float + index, pixel);
3060 }
3061 else if (channels == 3) {
3062 copy_v3_v3(display_buffer_float + index, pixel);
3063 }
3064 else /* if (channels == 1) */ {
3065 display_buffer_float[index] = pixel[0];
3066 }
3067 }
3068 else {
3069 if (channels == 4) {
3070 float pixel_straight[4];
3071 premul_to_straight_v4_v4(pixel_straight, pixel);
3072 rgba_float_to_uchar(display_buffer + display_index, pixel_straight);
3073 }
3074 else if (channels == 3) {
3075 rgb_float_to_uchar(display_buffer + display_index, pixel);
3076 display_buffer[display_index + 3] = 255;
3077 }
3078 else /* if (channels == 1) */ {
3079 display_buffer[display_index] = display_buffer[display_index + 1] =
3080 display_buffer[display_index + 2] = display_buffer[display_index + 3] =
3081 unit_float_to_uchar_clamp(pixel[0]);
3082 }
3083 }
3084 }
3085 }
3086 }
3087 else {
3088 if (display_buffer_float) {
3089 /* huh, for dither we need float buffer first, no cheaper way. currently */
3090 IMB_buffer_float_from_byte(display_buffer_float,
3091 byte_buffer,
3094 true,
3095 width,
3096 height,
3097 width,
3098 display_stride);
3099 }
3100 else {
3101 int i;
3102
3103 for (i = ymin; i < ymax; i++) {
3104 size_t byte_offset = (size_t(linear_stride) * i + xmin) * 4;
3105 size_t display_offset = (size_t(display_stride) * i + xmin) * 4;
3106
3107 memcpy(
3108 display_buffer + display_offset, byte_buffer + byte_offset, sizeof(char[4]) * width);
3109 }
3110 }
3111 }
3112
3113 if (display_buffer_float) {
3114 size_t display_index = (size_t(ymin) * display_stride + xmin) * channels;
3115
3116 IMB_buffer_byte_from_float(display_buffer + display_index,
3117 display_buffer_float,
3118 channels,
3119 dither,
3122 true,
3123 width,
3124 height,
3125 display_stride,
3126 width,
3127 ymin);
3128
3129 MEM_freeN(display_buffer_float);
3130 }
3131}
3132
3134 ImBuf *ibuf,
3135 const float *linear_buffer,
3136 const uchar *byte_buffer,
3137 int stride,
3138 int offset_x,
3139 int offset_y,
3140 const ColorManagedViewSettings *view_settings,
3141 const ColorManagedDisplaySettings *display_settings,
3142 int xmin,
3143 int ymin,
3144 int xmax,
3145 int ymax,
3146 bool do_threads)
3147{
3148 using namespace blender;
3149 ColormanageCacheViewSettings cache_view_settings;
3150 ColormanageCacheDisplaySettings cache_display_settings;
3151 void *cache_handle = nullptr;
3152 uchar *display_buffer = nullptr;
3153 int buffer_width = ibuf->x;
3154
3155 if (ibuf->display_buffer_flags) {
3156 int view_flag, display_index;
3157
3158 colormanage_view_settings_to_cache(ibuf, &cache_view_settings, view_settings);
3159 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
3160
3161 view_flag = 1 << cache_view_settings.view;
3162 display_index = cache_display_settings.display;
3163
3165
3166 if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
3167 display_buffer = colormanage_cache_get(
3168 ibuf, &cache_view_settings, &cache_display_settings, &cache_handle);
3169 }
3170
3171 /* In some rare cases buffer's dimension could be changing directly from
3172 * different thread
3173 * this i.e. happens when image editor acquires render result
3174 */
3175 buffer_width = ibuf->x;
3176
3177 /* Mark all other buffers as invalid. */
3178 memset(ibuf->display_buffer_flags, 0, g_config->get_num_displays() * sizeof(uint));
3179 ibuf->display_buffer_flags[display_index] |= view_flag;
3180
3182 }
3183
3184 if (display_buffer) {
3185 ColormanageProcessor *cm_processor = nullptr;
3186 bool skip_transform = false;
3187
3188 /* If we only have a byte or a float buffer, and color space already
3189 * matches display, there's no need to do color transforms.
3190 * However if both float and byte buffers exist, it is likely that
3191 * some operation was performed on float buffer first, and the byte
3192 * buffer is out of date. */
3193 if (linear_buffer == nullptr && byte_buffer != nullptr) {
3194 skip_transform = is_colorspace_same_as_display(
3195 ibuf->byte_buffer.colorspace, view_settings, display_settings);
3196 }
3197 if (byte_buffer == nullptr && linear_buffer != nullptr) {
3198 skip_transform = is_colorspace_same_as_display(
3199 ibuf->float_buffer.colorspace, view_settings, display_settings);
3200 }
3201
3202 if (!skip_transform) {
3203 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
3204 }
3205
3206 threading::parallel_for(IndexRange(ymin, ymax - ymin),
3207 do_threads ? 64 : ymax - ymin,
3208 [&](const IndexRange y_range) {
3210 display_buffer,
3211 linear_buffer,
3212 byte_buffer,
3213 buffer_width,
3214 stride,
3215 offset_x,
3216 offset_y,
3217 cm_processor,
3218 xmin,
3219 y_range.first(),
3220 xmax,
3221 y_range.one_after_last());
3222 });
3223
3224 if (cm_processor) {
3226 }
3227
3228 IMB_display_buffer_release(cache_handle);
3229 }
3230}
3231
3233 const float *linear_buffer,
3234 const uchar *byte_buffer,
3235 int stride,
3236 int offset_x,
3237 int offset_y,
3238 const ColorManagedViewSettings *view_settings,
3239 const ColorManagedDisplaySettings *display_settings,
3240 int xmin,
3241 int ymin,
3242 int xmax,
3243 int ymax)
3244{
3246 linear_buffer,
3247 byte_buffer,
3248 stride,
3249 offset_x,
3250 offset_y,
3251 view_settings,
3252 display_settings,
3253 xmin,
3254 ymin,
3255 xmax,
3256 ymax,
3257 false);
3258}
3259
3261 ImBuf *ibuf,
3262 const float *linear_buffer,
3263 const uchar *byte_buffer,
3264 int stride,
3265 int offset_x,
3266 int offset_y,
3267 const ColorManagedViewSettings *view_settings,
3268 const ColorManagedDisplaySettings *display_settings,
3269 int xmin,
3270 int ymin,
3271 int xmax,
3272 int ymax)
3273{
3274 int width = xmax - xmin;
3275 int height = ymax - ymin;
3276 bool do_threads = (size_t(width) * height >= 64 * 64);
3278 linear_buffer,
3279 byte_buffer,
3280 stride,
3281 offset_x,
3282 offset_y,
3283 view_settings,
3284 display_settings,
3285 xmin,
3286 ymin,
3287 xmax,
3288 ymax,
3289 do_threads);
3290}
3291
3292void IMB_partial_display_buffer_update_delayed(ImBuf *ibuf, int xmin, int ymin, int xmax, int ymax)
3293{
3294 if (ibuf->invalid_rect.xmin == ibuf->invalid_rect.xmax) {
3295 BLI_rcti_init(&ibuf->invalid_rect, xmin, xmax, ymin, ymax);
3296 }
3297 else {
3298 rcti rect;
3299 BLI_rcti_init(&rect, xmin, xmax, ymin, ymax);
3300 BLI_rcti_union(&ibuf->invalid_rect, &rect);
3301 }
3302}
3303
3305
3306/* -------------------------------------------------------------------- */
3309
3311 const ColorManagedViewSettings *view_settings,
3312 const ColorManagedDisplaySettings *display_settings)
3313{
3314 ColormanageProcessor *cm_processor;
3315 ColorManagedViewSettings default_view_settings;
3316 const ColorManagedViewSettings *applied_view_settings;
3317 const ColorSpace *display_space;
3318
3319 cm_processor = MEM_new<ColormanageProcessor>("colormanagement processor");
3320
3321 if (view_settings) {
3322 applied_view_settings = view_settings;
3323 }
3324 else {
3325 IMB_colormanagement_init_default_view_settings(&default_view_settings, display_settings);
3326 applied_view_settings = &default_view_settings;
3327 }
3328
3329 display_space = get_display_colorspace(applied_view_settings, display_settings);
3330 if (display_space) {
3331 cm_processor->is_data_result = display_space->is_data();
3332 }
3333
3334 const bool use_white_balance = applied_view_settings->flag & COLORMANAGE_VIEW_USE_WHITE_BALANCE;
3335 cm_processor->cpu_processor = get_display_buffer_processor(applied_view_settings->look,
3336 applied_view_settings->view_transform,
3337 display_settings->display_device,
3338 applied_view_settings->exposure,
3339 applied_view_settings->gamma,
3340 applied_view_settings->temperature,
3341 applied_view_settings->tint,
3342 use_white_balance,
3344
3345 if (applied_view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) {
3346 cm_processor->curve_mapping = BKE_curvemapping_copy(applied_view_settings->curve_mapping);
3347 BKE_curvemapping_premultiply(cm_processor->curve_mapping, false);
3348 }
3349
3350 return cm_processor;
3351}
3352
3354 const char *to_colorspace)
3355{
3356 ColormanageProcessor *cm_processor;
3357
3358 cm_processor = MEM_new<ColormanageProcessor>("colormanagement processor");
3359 cm_processor->is_data_result = IMB_colormanagement_space_name_is_data(to_colorspace);
3360
3361 cm_processor->cpu_processor = g_config->get_cpu_processor(from_colorspace, to_colorspace);
3362
3363 return cm_processor;
3364}
3365
3367{
3368 if (cm_processor->curve_mapping) {
3369 /* Consider processor which has curve mapping as a non no-op.
3370 * This is mainly for the simplicity of the check, since the current cases where this
3371 * function is used the curve mapping is never assigned. */
3372 return false;
3373 }
3374
3375 if (!cm_processor->cpu_processor) {
3376 /* The CPU processor might have failed to be created, for example when the requested color
3377 * space does not exist in the configuration, or if there is a missing lookup table, or the
3378 * configuration is invalid due to other reasons.
3379 *
3380 * The actual processing checks for the cpu_processor not being null pointer, and it if is
3381 * then processing does not apply it. However, processing could still apply curve mapping.
3382 *
3383 * Hence a null-pointer here, which happens after the curve mapping check, but before
3384 * accessing cpu_processor. */
3385 return true;
3386 }
3387
3388 return cm_processor->cpu_processor->is_noop();
3389}
3390
3392{
3393 if (cm_processor->curve_mapping) {
3394 BKE_curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
3395 }
3396
3397 if (cm_processor->cpu_processor) {
3398 cm_processor->cpu_processor->apply_rgba(pixel);
3399 }
3400}
3401
3403 float pixel[4])
3404{
3405 if (cm_processor->curve_mapping) {
3406 BKE_curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
3407 }
3408
3409 if (cm_processor->cpu_processor) {
3410 cm_processor->cpu_processor->apply_rgba_predivide(pixel);
3411 ;
3412 }
3413}
3414
3416{
3417 if (cm_processor->curve_mapping) {
3418 BKE_curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
3419 }
3420
3421 if (cm_processor->cpu_processor) {
3422 cm_processor->cpu_processor->apply_rgb(pixel);
3423 }
3424}
3425
3427 float *pixel,
3428 int channels)
3429{
3430 if (channels == 4) {
3432 }
3433 else if (channels == 3) {
3434 IMB_colormanagement_processor_apply_v3(cm_processor, pixel);
3435 }
3436 else if (channels == 1) {
3437 if (cm_processor->curve_mapping) {
3438 curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, 1);
3439 }
3440 }
3441 else {
3443 false, "Incorrect number of channels passed to IMB_colormanagement_processor_apply_pixel");
3444 }
3445}
3446
3448 float *buffer,
3449 int width,
3450 int height,
3451 int channels,
3452 bool predivide)
3453{
3454 /* apply curve mapping */
3455 if (cm_processor->curve_mapping) {
3456 int x, y;
3457
3458 for (y = 0; y < height; y++) {
3459 for (x = 0; x < width; x++) {
3460 float *pixel = buffer + channels * (size_t(y) * width + x);
3461
3462 curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, channels);
3463 }
3464 }
3465 }
3466
3467 if (cm_processor->cpu_processor && channels >= 3) {
3468 /* apply OCIO processor */
3469 const ocio::PackedImage img(buffer,
3470 width,
3471 height,
3472 channels,
3474 sizeof(float),
3475 size_t(channels) * sizeof(float),
3476 size_t(channels) * sizeof(float) * width);
3477
3478 if (predivide) {
3479 cm_processor->cpu_processor->apply_predivide(img);
3480 }
3481 else {
3482 cm_processor->cpu_processor->apply(img);
3483 }
3484 }
3485}
3486
3488 ColormanageProcessor *cm_processor, uchar *buffer, int width, int height, int channels)
3489{
3490 /* TODO(sergey): Would be nice to support arbitrary channels configurations,
3491 * but for now it's not so important.
3492 */
3493 BLI_assert(channels == 4);
3494 float pixel[4];
3495 for (int y = 0; y < height; y++) {
3496 for (int x = 0; x < width; x++) {
3497 size_t offset = channels * (size_t(y) * width + x);
3498 rgba_uchar_to_float(pixel, buffer + offset);
3499 IMB_colormanagement_processor_apply_v4(cm_processor, pixel);
3500 rgba_float_to_uchar(buffer + offset, pixel);
3501 }
3502 }
3503}
3504
3506{
3507 if (cm_processor->curve_mapping) {
3508 BKE_curvemapping_free(cm_processor->curve_mapping);
3509 }
3510
3511 MEM_delete(cm_processor);
3512}
3513
3514/* **** OpenGL drawing routines using GLSL for color space transform ***** */
3515
3517{
3518 /* Using curve mapping? */
3519 const bool use_curve_mapping = (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) != 0;
3520 if (!use_curve_mapping) {
3521 return nullptr;
3522 }
3523
3524 /* Already up to date? */
3525 if (view_settings->curve_mapping->changed_timestamp ==
3526 global_gpu_state.curve_mapping_timestamp &&
3527 view_settings->curve_mapping == global_gpu_state.orig_curve_mapping)
3528 {
3529 return view_settings->curve_mapping;
3530 }
3531
3532 /* We're using curve mapping's address as a cache ID, so we need to make sure re-allocation
3533 * gives new address here. We do this by allocating new curve mapping before freeing old one.
3534 */
3535 CurveMapping *new_curve_mapping = BKE_curvemapping_copy(view_settings->curve_mapping);
3536
3537 if (global_gpu_state.curve_mapping) {
3539 global_gpu_state.curve_mapping = nullptr;
3540 }
3541
3542 /* Fill in OCIO's curve mapping settings. */
3543 global_gpu_state.curve_mapping = new_curve_mapping;
3544 global_gpu_state.curve_mapping_timestamp = view_settings->curve_mapping->changed_timestamp;
3545 global_gpu_state.orig_curve_mapping = view_settings->curve_mapping;
3546 global_gpu_state.use_curve_mapping = true;
3547
3548 return global_gpu_state.curve_mapping;
3549}
3550
3552 const ColorManagedViewSettings *view_settings,
3553 const ColorManagedDisplaySettings *display_settings,
3554 const ColorSpace *from_colorspace,
3555 float dither,
3556 bool predivide,
3557 bool do_overlay_merge)
3558{
3559 ColorManagedViewSettings default_view_settings;
3560 const ColorManagedViewSettings *applied_view_settings;
3561
3562 if (view_settings) {
3563 applied_view_settings = view_settings;
3564 }
3565 else {
3566 /* If no view settings were specified, use default ones, which will attempt not to do any
3567 * extra color correction. */
3568 IMB_colormanagement_init_default_view_settings(&default_view_settings, display_settings);
3569 applied_view_settings = &default_view_settings;
3570 }
3571
3572 /* Ensure curve mapping is up to date. */
3573 CurveMapping *applied_curve_mapping = update_glsl_curve_mapping(applied_view_settings);
3574
3575 /* GPU shader parameters. */
3576 const bool use_look = colormanage_use_look(applied_view_settings->look,
3577 applied_view_settings->view_transform);
3578 const float exposure = applied_view_settings->exposure;
3579 const float gamma = applied_view_settings->gamma;
3580
3581 /* TODO)sergey): Use designated initializer. */
3582 ocio::GPUDisplayParameters display_parameters;
3583 display_parameters.from_colorspace = from_colorspace ? from_colorspace->name().c_str() :
3585 display_parameters.view = applied_view_settings->view_transform;
3586 display_parameters.display = display_settings->display_device;
3587 display_parameters.look = (use_look) ? applied_view_settings->look : "";
3588 display_parameters.curve_mapping = applied_curve_mapping;
3589 display_parameters.scale = (exposure == 0.0f) ? 1.0f : powf(2.0f, exposure);
3590 display_parameters.exponent = (gamma == 1.0f) ? 1.0f : 1.0f / max_ff(FLT_EPSILON, gamma);
3591 display_parameters.dither = dither;
3592 display_parameters.temperature = applied_view_settings->temperature;
3593 display_parameters.tint = applied_view_settings->tint;
3594 display_parameters.use_white_balance = (applied_view_settings->flag &
3596 display_parameters.use_predivide = predivide;
3597 display_parameters.do_overlay_merge = do_overlay_merge;
3598 display_parameters.use_hdr = GPU_hdr_support() &&
3599 (applied_view_settings->flag & COLORMANAGE_VIEW_USE_HDR) != 0;
3600
3601 /* Bind shader. Internally GPU shaders are created and cached on demand. */
3602 global_gpu_state.gpu_shader_bound = g_config->get_gpu_shader_binder().display_bind(
3603 display_parameters);
3604
3605 return global_gpu_state.gpu_shader_bound;
3606}
3607
3609 const ColorManagedDisplaySettings *display_settings,
3610 float dither,
3611 bool predivide)
3612{
3614 view_settings, display_settings, nullptr, dither, predivide, false);
3615}
3616
3618 const ColorSpace *from_colorspace,
3619 float dither,
3620 bool predivide)
3621{
3622 ColorManagedViewSettings *view_settings;
3623 ColorManagedDisplaySettings *display_settings;
3624
3625 IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
3626
3628 view_settings, display_settings, from_colorspace, dither, predivide, false);
3629}
3630
3631bool IMB_colormanagement_setup_glsl_draw_ctx(const bContext *C, float dither, bool predivide)
3632{
3633 return IMB_colormanagement_setup_glsl_draw_from_space_ctx(C, nullptr, dither, predivide);
3634}
3635
3636bool IMB_colormanagement_setup_glsl_draw_to_scene_linear(const char *from_colorspace_name,
3637 const bool predivide)
3638{
3639 global_gpu_state.gpu_shader_bound = g_config->get_gpu_shader_binder().to_scene_linear_bind(
3640 from_colorspace_name, predivide);
3641 return global_gpu_state.gpu_shader_bound;
3642}
3643
3645{
3646 if (global_gpu_state.gpu_shader_bound) {
3647 g_config->get_gpu_shader_binder().unbind();
3648 global_gpu_state.gpu_shader_bound = false;
3649 }
3650}
3651
3653
3654/* -------------------------------------------------------------------- */
3657
3658/* Calculate color in range 800..12000 using an approximation
3659 * a/x+bx+c for R and G and ((at + b)t + c)t + d) for B
3660 *
3661 * The result of this can be negative to support gamut wider than
3662 * than rec.709, just needs to be clamped. */
3663
3664static const float blackbody_table_r[7][3] = {{1.61919106e+03f, -2.05010916e-03f, 5.02995757e+00f},
3665 {2.48845471e+03f, -1.11330907e-03f, 3.22621544e+00f},
3666 {3.34143193e+03f, -4.86551192e-04f, 1.76486769e+00f},
3667 {4.09461742e+03f, -1.27446582e-04f, 7.25731635e-01f},
3668 {4.67028036e+03f, 2.91258199e-05f, 1.26703442e-01f},
3669 {4.59509185e+03f, 2.87495649e-05f, 1.50345020e-01f},
3670 {3.78717450e+03f, 9.35907826e-06f, 3.99075871e-01f}};
3671
3672static const float blackbody_table_g[7][3] = {
3673 {-4.88999748e+02f, 6.04330754e-04f, -7.55807526e-02f},
3674 {-7.55994277e+02f, 3.16730098e-04f, 4.78306139e-01f},
3675 {-1.02363977e+03f, 1.20223470e-04f, 9.36662319e-01f},
3676 {-1.26571316e+03f, 4.87340896e-06f, 1.27054498e+00f},
3677 {-1.42529332e+03f, -4.01150431e-05f, 1.43972784e+00f},
3678 {-1.17554822e+03f, -2.16378048e-05f, 1.30408023e+00f},
3679 {-5.00799571e+02f, -4.59832026e-06f, 1.09098763e+00f}};
3680
3681static const float blackbody_table_b[7][4] = {
3682 {5.96945309e-11f, -4.85742887e-08f, -9.70622247e-05f, -4.07936148e-03f},
3683 {2.40430366e-11f, 5.55021075e-08f, -1.98503712e-04f, 2.89312858e-02f},
3684 {-1.40949732e-11f, 1.89878968e-07f, -3.56632824e-04f, 9.10767778e-02f},
3685 {-3.61460868e-11f, 2.84822009e-07f, -4.93211319e-04f, 1.56723440e-01f},
3686 {-1.97075738e-11f, 1.75359352e-07f, -2.50542825e-04f, -2.22783266e-02f},
3687 {-1.61997957e-13f, -1.64216008e-08f, 3.86216271e-04f, -7.38077418e-01f},
3688 {6.72650283e-13f, -2.73078809e-08f, 4.24098264e-04f, -7.52335691e-01f}};
3689
3690static void blackbody_temperature_to_rec709(float rec709[3], float t)
3691{
3692 if (t >= 12000.0f) {
3693 rec709[0] = 0.8262954810464208f;
3694 rec709[1] = 0.9945080501520986f;
3695 rec709[2] = 1.566307710274283f;
3696 }
3697 else if (t < 800.0f) {
3698 rec709[0] = 5.413294490189271f;
3699 rec709[1] = -0.20319390035873933f;
3700 rec709[2] = -0.0822535242887164f;
3701 }
3702 else {
3703 int i = (t >= 6365.0f) ? 6 :
3704 (t >= 3315.0f) ? 5 :
3705 (t >= 1902.0f) ? 4 :
3706 (t >= 1449.0f) ? 3 :
3707 (t >= 1167.0f) ? 2 :
3708 (t >= 965.0f) ? 1 :
3709 0;
3710
3711 const float *r = blackbody_table_r[i];
3712 const float *g = blackbody_table_g[i];
3713 const float *b = blackbody_table_b[i];
3714
3715 const float t_inv = 1.0f / t;
3716 rec709[0] = r[0] * t_inv + r[1] * t + r[2];
3717 rec709[1] = g[0] * t_inv + g[1] * t + g[2];
3718 rec709[2] = ((b[0] * t + b[1]) * t + b[2]) * t + b[3];
3719 }
3720}
3721
3723{
3724 float rec709[3];
3725 blackbody_temperature_to_rec709(rec709, value);
3726
3727 float rgb[3];
3729 clamp_v3(rgb, 0.0f, FLT_MAX);
3730
3731 copy_v3_v3(r_dest, rgb);
3732 r_dest[3] = 1.0f;
3733}
3734
3736 const int width,
3737 const float min,
3738 const float max)
3739{
3740 for (int i = 0; i < width; i++) {
3741 float temperature = min + (max - min) / float(width) * float(i);
3742 IMB_colormanagement_blackbody_temperature_to_rgb(&r_table[i * 4], temperature);
3743 }
3744}
3745
3757
3758static float cie_color_match[81][3] = {
3759 {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f},
3760 {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f},
3761 {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f},
3762 {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f},
3763 {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f},
3764 {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f},
3765 {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f},
3766 {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f},
3767 {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f},
3768 {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f},
3769 {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f},
3770 {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f},
3771 {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f},
3772 {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f},
3773 {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f},
3774 {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f},
3775 {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f},
3776 {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f},
3777 {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f},
3778 {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f},
3779 {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f},
3780 {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f},
3781 {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f},
3782 {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f},
3783 {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f},
3784 {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f},
3785 {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}};
3786
3787static void wavelength_to_xyz(float xyz[3], float lambda_nm)
3788{
3789 float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); /* Scaled 0..80. */
3790 int i = int(ii);
3791
3792 if (i < 0 || i >= 80) {
3793 xyz[0] = 0.0f;
3794 xyz[1] = 0.0f;
3795 xyz[2] = 0.0f;
3796 }
3797 else {
3798 ii -= float(i);
3799 const float *c = cie_color_match[i];
3800 xyz[0] = c[0] + ii * (c[3] - c[0]);
3801 xyz[1] = c[1] + ii * (c[4] - c[1]);
3802 xyz[2] = c[2] + ii * (c[5] - c[2]);
3803 }
3804}
3805
3806void IMB_colormanagement_wavelength_to_rgb(float r_dest[4], float value)
3807{
3808 float xyz[3];
3809 wavelength_to_xyz(xyz, value);
3810
3811 float rgb[3];
3813 clamp_v3(rgb, 0.0f, FLT_MAX);
3814
3815 copy_v3_v3(r_dest, rgb);
3816 r_dest[3] = 1.0f;
3817}
3818
3819void IMB_colormanagement_wavelength_to_rgb_table(float *r_table, const int width)
3820{
3821 for (int i = 0; i < width; i++) {
3822 float wavelength = 380 + 400 / float(width) * float(i);
3823 IMB_colormanagement_wavelength_to_rgb(&r_table[i * 4], wavelength);
3824 }
3825}
3826
@ BLENDER_DATAFILES
std::optional< std::string > BKE_appdir_folder_id(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT
Definition appdir.cc:717
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)
bool BKE_image_format_is_byte(const ImageFormatData *imf)
bool BKE_imtype_requires_linear_float(char imtype)
blender::ocio::Display ColorManagedDisplay
Definition BLF_api.hh:35
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH(type, var, list)
MINLINE float max_ff(float a, float b)
MINLINE void straight_to_premul_v4(float color[4])
void BLI_init_srgb_conversion(void)
void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4])
void rgba_float_to_uchar(unsigned char r_col[4], const float col_f[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])
void rgb_uchar_to_float(float r_col[3], const unsigned char col_ub[3])
void rgb_float_to_uchar(unsigned char r_col[3], const float col_f[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
#define BLI_path_join(...)
const char * BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
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:688
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
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 STREQ(a, b)
@ COLORMANAGE_VIEW_USE_WHITE_BALANCE
@ COLORMANAGE_VIEW_USE_CURVES
@ COLORMANAGE_VIEW_USE_HDR
@ IMA_VIEW_AS_RENDER
@ R_IMF_PLANES_RGBA
static AppView * view
bool GPU_hdr_support()
bool IMB_colormanagement_space_is_scene_linear(const ColorSpace *colorspace)
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])
blender::ocio::ColorSpace ColorSpace
#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:70
bool IMB_alpha_affects_rgb(const ImBuf *ibuf)
Definition conversion.cc:65
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
@ IMB_COLORMANAGE_IS_DATA
@ IB_TAKE_OWNERSHIP
@ 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.
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
BMesh const char void * data
long long int int64_t
constexpr int64_t first() const
constexpr int64_t one_after_last() const
constexpr int64_t size() const
constexpr bool is_empty() const
constexpr const char * c_str() const
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 bool is_invertible() const =0
virtual const CPUProcessor * get_to_scene_linear_cpu_processor() const =0
virtual const CPUProcessor * get_from_scene_linear_cpu_processor() const =0
virtual StringRefNull description() const =0
virtual bool is_data() const =0
virtual bool is_scene_linear() const =0
virtual bool is_srgb() const =0
virtual float3 get_default_luma_coefs() 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
virtual float3x3 get_xyz_to_scene_linear_matrix() const =0
static std::unique_ptr< Config > create_from_file(StringRefNull filename)
Definition config.cc:24
virtual StringRefNull name() const =0
virtual const CPUProcessor * get_from_scene_linear_cpu_processor() const =0
virtual const View * get_view_by_name(StringRefNull name) const =0
virtual const CPUProcessor * get_to_scene_linear_cpu_processor() const =0
virtual const View * get_default_view() const =0
virtual int get_num_views() const =0
virtual const View * get_view_by_index(int index) const =0
virtual StringRefNull view() const =0
virtual StringRefNull name() const =0
virtual StringRefNull ui_name() const =0
virtual StringRefNull process_space() const =0
virtual StringRefNull name() const =0
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)
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)
void IMB_colormanagement_pixel_to_display_space_v3(float result[3], const float pixel[3], const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
static const float blackbody_table_r[7][3]
void IMB_colormanagement_assign_byte_colorspace(ImBuf *ibuf, const char *name)
float3x3 imbuf_scene_linear_to_xyz
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)
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)
float3x3 imbuf_xyz_to_scene_linear
static float cie_color_match[81][3]
ColormanageProcessor * IMB_colormanagement_colorspace_processor_new(const char *from_colorspace, const char *to_colorspace)
static blender::VectorSet< blender::StringRefNull > g_all_view_names
void IMB_colormanagement_assign_float_colorspace(ImBuf *ibuf, const char *name)
void IMB_colormanagement_wavelength_to_rgb(float r_dest[4], float value)
static void colormanage_check_colorspace_settings(ColorManagedColorspaceSettings *colorspace_settings, const char *what)
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)
void IMB_colormanagement_pixel_to_display_space_v4(float result[4], const float pixel[4], const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
void IMB_colormanagement_transform_byte(uchar *buffer, int width, int height, int channels, const char *from_colorspace, const char *to_colorspace)
static void colormanage_cachedata_set(ImBuf *ibuf, ColormanageCacheData *data)
static void colormanagement_imbuf_make_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, bool make_byte)
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)
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()
bool IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, float dither, bool predivide)
bool IMB_colormanagement_space_name_is_srgb(const char *name)
bool IMB_colormanagement_space_name_is_scene_linear(const char *name)
void IMB_colormanagement_imbuf_make_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
const char * IMB_colormanagement_look_validate_for_view(const char *view_name, const char *look_name)
static pthread_mutex_t processor_lock
static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings, const char *what, const ocio::Display *default_display)
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 bool colormanage_compatible_look(const ocio::Look *look, const char *view_name, const bool has_explicit_look)
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)
bool IMB_colormanagement_processor_is_noop(ColormanageProcessor *cm_processor)
static void colormanage_display_buffer_process_ex(ImBuf *ibuf, float *display_buffer, uchar *display_buffer_byte, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
static MovieCache * colormanage_moviecache_get(const ImBuf *ibuf)
void IMB_colormanagement_check_file_config(Main *bmain)
static struct GlobalColorPickingState global_color_picking_state
static ColormanageCacheData * colormanage_cachedata_get(const ImBuf *ibuf)
static bool has_explicit_look_for_view(const char *view_name)
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)
ColormanageProcessor * IMB_colormanagement_display_processor_new(const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
static struct GlobalGPUState global_gpu_state
bool IMB_colormanagement_space_name_is_data(const char *name)
float3x3 imbuf_scene_linear_to_rec709
bool IMB_colormanagement_setup_glsl_draw_from_space_ctx(const bContext *C, const ColorSpace *from_colorspace, float dither, bool predivide)
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)
const char * IMB_colormanagement_display_get_indexed_name(const int index)
static bool colormanage_hashcmp(const void *av, const void *bv)
void IMB_colormanagement_init_default_view_settings(ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
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)
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_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)
static void colormanage_display_buffer_process(ImBuf *ibuf, uchar *display_buffer, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
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)
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)
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)
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)
const char * IMB_colormanagement_view_get_default_name(const char *display_name)
static void colormanage_cache_handle_release(void *cache_handle)
static void do_display_buffer_apply_no_processor(DisplayBufferThread *handle)
static const ColorSpace * get_display_colorspace(const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
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()
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]
float3x3 imbuf_rec709_to_scene_linear
float3x3 imbuf_aces_to_scene_linear
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)
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 std::shared_ptr< const ocio::CPUProcessor > get_display_buffer_processor(const char *look, const char *view_transform, const char *display, const float exposure, const float gamma, const float temperature, const float tint, const bool use_white_balance, const char *from_colorspace)
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)
static bool strip_callback(Strip *strip, void *)
void IMB_colormanagement_scene_linear_to_color_picking_v3(float color_picking[3], const float scene_linear[3])
static char global_role_scene_linear[MAX_COLORSPACE_NAME]
void IMB_colormanagement_blackbody_temperature_to_rgb(float r_dest[4], float value)
void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], const ColorManagedDisplay *display)
int IMB_colormanagement_colorspace_get_named_index(const char *name)
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)
void IMB_display_buffer_transform_apply_float(float *float_display_buffer, float *linear_buffer, int width, int height, int channels, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, bool predivide)
static char global_role_texture_painting[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 colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace)
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]
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)
const char * IMB_colormanagement_get_rect_colorspace(ImBuf *ibuf)
void IMB_partial_display_buffer_update_delayed(ImBuf *ibuf, int xmin, int ymin, int xmax, int ymax)
static void colormanage_free_config()
void IMB_colormanagement_transform_v4(float pixel[4], const char *from_colorspace, const char *to_colorspace)
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)
const char * IMB_colormanagement_get_float_colorspace(ImBuf *ibuf)
static void colormanage_check_view_settings(ColorManagedDisplaySettings *display_settings, ColorManagedViewSettings *view_settings, const char *what)
float3x3 imbuf_scene_linear_to_aces
static void wavelength_to_xyz(float xyz[3], float lambda_nm)
float3 imbuf_luma_coefficients(0.0f)
const char * IMB_colormanagement_display_get_default_name()
static MovieCache * colormanage_moviecache_ensure(ImBuf *ibuf)
bool IMB_colormanagement_space_is_srgb(const ColorSpace *colorspace)
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)
void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], const ColorManagedDisplay *display)
static void display_buffer_init_handle(DisplayBufferThread *handle, int start_line, int tot_line, DisplayBufferInitData *init_data)
#define powf(x, y)
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:559
#define in
#define out
#define printf(...)
#define MEM_SAFE_FREE(v)
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 G(x, y, z)
float3 whitepoint_from_temp_tint(float temperature, float tint)
bool whitepoint_to_temp_tint(const float3 &white, float &temperature, float &tint)
CartesianBasis invert(const CartesianBasis &basis)
static const float3x3 ACES_TO_XYZ
static const float3x3 XYZ_TO_REC709
void for_each_callback(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
VecBase< float, 3 > float3
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:623
const char * name
Definition RNA_types.hh:627
const char * description
Definition RNA_types.hh:629
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
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
ListBase scenes
Definition BKE_main.hh:245
ListBase movieclips
Definition BKE_main.hh:280
ListBase images
Definition BKE_main.hh:253
ColormanageProcessor * cm_processor
ColormanageProcessor * cm_processor
ColorManagedViewSettings view_settings
ColorManagedDisplaySettings display_settings
struct Image * image
ColorManagedColorspaceSettings colorspace_settings
StripData * data
int ymin
int ymax
int xmin
int xmax
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251