2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2012 by Blender Foundation.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): Xavier Thomas,
27 * ***** END GPL LICENSE BLOCK *****
31 /** \file blender/imbuf/intern/colormanagement.c
35 #include "IMB_colormanagement.h"
36 #include "IMB_colormanagement_intern.h"
41 #include "DNA_color_types.h"
42 #include "DNA_image_types.h"
43 #include "DNA_movieclip_types.h"
44 #include "DNA_scene_types.h"
45 #include "DNA_space_types.h"
47 #include "IMB_imbuf.h"
48 #include "IMB_imbuf_types.h"
49 #include "IMB_filetype.h"
50 #include "IMB_moviecache.h"
52 #include "MEM_guardedalloc.h"
54 #include "BLI_blenlib.h"
56 #include "BLI_math_color.h"
57 #include "BLI_string.h"
58 #include "BLI_threads.h"
61 #include "BKE_appdir.h"
62 #include "BKE_colortools.h"
63 #include "BKE_context.h"
64 #include "BKE_image.h"
67 #include "RNA_define.h"
69 #include <ocio_capi.h>
71 /*********************** Global declarations *************************/
73 #define DISPLAY_BUFFER_CHANNELS 4
75 /* ** list of all supported color spaces, displays and views */
76 static char global_role_scene_linear[MAX_COLORSPACE_NAME];
77 static char global_role_color_picking[MAX_COLORSPACE_NAME];
78 static char global_role_texture_painting[MAX_COLORSPACE_NAME];
79 static char global_role_default_byte[MAX_COLORSPACE_NAME];
80 static char global_role_default_float[MAX_COLORSPACE_NAME];
81 static char global_role_default_sequencer[MAX_COLORSPACE_NAME];
83 static ListBase global_colorspaces = {NULL, NULL};
84 static ListBase global_displays = {NULL, NULL};
85 static ListBase global_views = {NULL, NULL};
86 static ListBase global_looks = {NULL, NULL};
88 static int global_tot_colorspace = 0;
89 static int global_tot_display = 0;
90 static int global_tot_view = 0;
91 static int global_tot_looks = 0;
93 /* Set to ITU-BT.709 / sRGB primaries weight. Brute force stupid, but only
94 * option with no colormanagement in place.
96 float imbuf_luma_coefficients[3] = { 0.2126f, 0.7152f, 0.0722f };
98 /* lock used by pre-cached processors getters, so processor wouldn't
99 * be created several times
100 * LOCK_COLORMANAGE can not be used since this mutex could be needed to
101 * be locked before pre-cached processor are creating
103 static pthread_mutex_t processor_lock = BLI_MUTEX_INITIALIZER;
105 typedef struct ColormanageProcessor {
106 OCIO_ConstProcessorRcPtr *processor;
107 CurveMapping *curve_mapping;
109 } ColormanageProcessor;
111 static struct global_glsl_state {
112 /* Actual processor used for GLSL baked LUTs. */
113 OCIO_ConstProcessorRcPtr *processor;
115 /* Settings of processor for comparison. */
116 char look[MAX_COLORSPACE_NAME];
117 char view[MAX_COLORSPACE_NAME];
118 char display[MAX_COLORSPACE_NAME];
119 char input[MAX_COLORSPACE_NAME];
120 float exposure, gamma;
122 CurveMapping *curve_mapping, *orig_curve_mapping;
123 bool use_curve_mapping;
124 int curve_mapping_timestamp;
125 OCIO_CurveMappingSettings curve_mapping_settings;
127 /* Container for GLSL state needed for OCIO module. */
128 struct OCIO_GLSLDrawState *ocio_glsl_state;
129 struct OCIO_GLSLDrawState *transform_ocio_glsl_state;
132 /*********************** Color managed cache *************************/
134 /* Cache Implementation Notes
135 * ==========================
137 * All color management cache stuff is stored in two properties of
140 * 1. display_buffer_flags
142 * This is a bit field which used to mark calculated transformations
143 * for particular image buffer. Index inside of this array means index
144 * of a color managed display. Element with given index matches view
145 * transformations applied for a given display. So if bit B of array
146 * element B is set to 1, this means display buffer with display index
147 * of A and view transform of B was ever calculated for this imbuf.
149 * In contrast with indices in global lists of displays and views this
150 * indices are 0-based, not 1-based. This is needed to save some bytes
153 * 2. colormanage_cache
155 * This is a pointer to a structure which holds all data which is
156 * needed for color management cache to work.
158 * It contains two parts:
162 * Data field is used to store additional information about cached
163 * buffers which affects on whether cached buffer could be used.
164 * This data can't go to cache key because changes in this data
165 * shouldn't lead extra buffers adding to cache, it shall
166 * invalidate cached images.
168 * Currently such a data contains only exposure and gamma, but
169 * would likely extended further.
171 * data field is not null only for elements of cache, not used for
172 * original image buffers.
174 * Color management cache is using generic MovieCache implementation
175 * to make it easier to deal with memory limitation.
177 * Currently color management is using the same memory limitation
178 * pool as sequencer and clip editor are using which means color
179 * managed buffers would be removed from the cache as soon as new
180 * frames are loading for the movie clip and there's no space in
183 * Every image buffer has got own movie cache instance, which
184 * means keys for color managed buffers could be really simple
185 * and look up in this cache would be fast and independent from
186 * overall amount of color managed images.
189 /* NOTE: ColormanageCacheViewSettings and ColormanageCacheDisplaySettings are
190 * quite the same as ColorManagedViewSettings and ColorManageDisplaySettings
191 * but they holds indexes of all transformations and color spaces, not
194 * This helps avoid extra colorspace / display / view lookup without
195 * requiring to pass all variables which affects on display buffer
196 * to color management cache system and keeps calls small and nice.
198 typedef struct ColormanageCacheViewSettings {
205 CurveMapping *curve_mapping;
206 } ColormanageCacheViewSettings;
208 typedef struct ColormanageCacheDisplaySettings {
210 } ColormanageCacheDisplaySettings;
212 typedef struct ColormanageCacheKey {
213 int view; /* view transformation used for display buffer */
214 int display; /* display device name */
215 } ColormanageCacheKey;
217 typedef struct ColormnaageCacheData {
218 int flag; /* view flags of cached buffer */
219 int look; /* Additional artistics transform */
220 float exposure; /* exposure value cached buffer is calculated with */
221 float gamma; /* gamma value cached buffer is calculated with */
222 float dither; /* dither value cached buffer is calculated with */
223 CurveMapping *curve_mapping; /* curve mapping used for cached buffer */
224 int curve_mapping_timestamp; /* time stamp of curve mapping used for cached buffer */
225 } ColormnaageCacheData;
227 typedef struct ColormanageCache {
228 struct MovieCache *moviecache;
230 ColormnaageCacheData *data;
233 static struct MovieCache *colormanage_moviecache_get(const ImBuf *ibuf)
235 if (!ibuf->colormanage_cache)
238 return ibuf->colormanage_cache->moviecache;
241 static ColormnaageCacheData *colormanage_cachedata_get(const ImBuf *ibuf)
243 if (!ibuf->colormanage_cache)
246 return ibuf->colormanage_cache->data;
249 static unsigned int colormanage_hashhash(const void *key_v)
251 const ColormanageCacheKey *key = key_v;
253 unsigned int rval = (key->display << 16) | (key->view % 0xffff);
258 static bool colormanage_hashcmp(const void *av, const void *bv)
260 const ColormanageCacheKey *a = av;
261 const ColormanageCacheKey *b = bv;
263 return ((a->view != b->view) ||
264 (a->display != b->display));
267 static struct MovieCache *colormanage_moviecache_ensure(ImBuf *ibuf)
269 if (!ibuf->colormanage_cache)
270 ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage cache");
272 if (!ibuf->colormanage_cache->moviecache) {
273 struct MovieCache *moviecache;
275 moviecache = IMB_moviecache_create("colormanage cache", sizeof(ColormanageCacheKey),
276 colormanage_hashhash, colormanage_hashcmp);
278 ibuf->colormanage_cache->moviecache = moviecache;
281 return ibuf->colormanage_cache->moviecache;
284 static void colormanage_cachedata_set(ImBuf *ibuf, ColormnaageCacheData *data)
286 if (!ibuf->colormanage_cache)
287 ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage cache");
289 ibuf->colormanage_cache->data = data;
292 static void colormanage_view_settings_to_cache(ImBuf *ibuf,
293 ColormanageCacheViewSettings *cache_view_settings,
294 const ColorManagedViewSettings *view_settings)
296 int look = IMB_colormanagement_look_get_named_index(view_settings->look);
297 int view = IMB_colormanagement_view_get_named_index(view_settings->view_transform);
299 cache_view_settings->look = look;
300 cache_view_settings->view = view;
301 cache_view_settings->exposure = view_settings->exposure;
302 cache_view_settings->gamma = view_settings->gamma;
303 cache_view_settings->dither = ibuf->dither;
304 cache_view_settings->flag = view_settings->flag;
305 cache_view_settings->curve_mapping = view_settings->curve_mapping;
308 static void colormanage_display_settings_to_cache(ColormanageCacheDisplaySettings *cache_display_settings,
309 const ColorManagedDisplaySettings *display_settings)
311 int display = IMB_colormanagement_display_get_named_index(display_settings->display_device);
313 cache_display_settings->display = display;
316 static void colormanage_settings_to_key(ColormanageCacheKey *key,
317 const ColormanageCacheViewSettings *view_settings,
318 const ColormanageCacheDisplaySettings *display_settings)
320 key->view = view_settings->view;
321 key->display = display_settings->display;
324 static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf, ColormanageCacheKey *key, void **cache_handle)
327 struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
330 /* if there's no moviecache it means no color management was applied on given image buffer before */
335 *cache_handle = NULL;
337 cache_ibuf = IMB_moviecache_get(moviecache, key);
339 *cache_handle = cache_ibuf;
344 static unsigned char *colormanage_cache_get(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings,
345 const ColormanageCacheDisplaySettings *display_settings,
348 ColormanageCacheKey key;
350 int view_flag = 1 << (view_settings->view - 1);
351 CurveMapping *curve_mapping = view_settings->curve_mapping;
352 int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
354 colormanage_settings_to_key(&key, view_settings, display_settings);
356 /* check whether image was marked as dirty for requested transform */
357 if ((ibuf->display_buffer_flags[display_settings->display - 1] & view_flag) == 0) {
361 cache_ibuf = colormanage_cache_get_ibuf(ibuf, &key, cache_handle);
364 ColormnaageCacheData *cache_data;
366 BLI_assert(cache_ibuf->x == ibuf->x &&
367 cache_ibuf->y == ibuf->y);
369 /* only buffers with different color space conversions are being stored
370 * in cache separately. buffer which were used only different exposure/gamma
371 * are re-suing the same cached buffer
373 * check here which exposure/gamma/curve was used for cached buffer and if they're
374 * different from requested buffer should be re-generated
376 cache_data = colormanage_cachedata_get(cache_ibuf);
378 if (cache_data->look != view_settings->look ||
379 cache_data->exposure != view_settings->exposure ||
380 cache_data->gamma != view_settings->gamma ||
381 cache_data->dither != view_settings->dither ||
382 cache_data->flag != view_settings->flag ||
383 cache_data->curve_mapping != curve_mapping ||
384 cache_data->curve_mapping_timestamp != curve_mapping_timestamp)
386 *cache_handle = NULL;
388 IMB_freeImBuf(cache_ibuf);
393 return (unsigned char *) cache_ibuf->rect;
399 static void colormanage_cache_put(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings,
400 const ColormanageCacheDisplaySettings *display_settings,
401 unsigned char *display_buffer, void **cache_handle)
403 ColormanageCacheKey key;
405 ColormnaageCacheData *cache_data;
406 int view_flag = 1 << (view_settings->view - 1);
407 struct MovieCache *moviecache = colormanage_moviecache_ensure(ibuf);
408 CurveMapping *curve_mapping = view_settings->curve_mapping;
409 int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
411 colormanage_settings_to_key(&key, view_settings, display_settings);
413 /* mark display buffer as valid */
414 ibuf->display_buffer_flags[display_settings->display - 1] |= view_flag;
417 cache_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0);
418 cache_ibuf->rect = (unsigned int *) display_buffer;
420 cache_ibuf->mall |= IB_rect;
421 cache_ibuf->flags |= IB_rect;
423 /* store data which is needed to check whether cached buffer could be used for color managed display settings */
424 cache_data = MEM_callocN(sizeof(ColormnaageCacheData), "color manage cache imbuf data");
425 cache_data->look = view_settings->look;
426 cache_data->exposure = view_settings->exposure;
427 cache_data->gamma = view_settings->gamma;
428 cache_data->dither = view_settings->dither;
429 cache_data->flag = view_settings->flag;
430 cache_data->curve_mapping = curve_mapping;
431 cache_data->curve_mapping_timestamp = curve_mapping_timestamp;
433 colormanage_cachedata_set(cache_ibuf, cache_data);
435 *cache_handle = cache_ibuf;
437 IMB_moviecache_put(moviecache, &key, cache_ibuf);
440 static void colormanage_cache_handle_release(void *cache_handle)
442 ImBuf *cache_ibuf = cache_handle;
444 IMB_freeImBuf(cache_ibuf);
447 /*********************** Initialization / De-initialization *************************/
449 static void colormanage_role_color_space_name_get(OCIO_ConstConfigRcPtr *config, char *colorspace_name, const char *role, const char *backup_role)
451 OCIO_ConstColorSpaceRcPtr *ociocs;
453 ociocs = OCIO_configGetColorSpace(config, role);
455 if (!ociocs && backup_role)
456 ociocs = OCIO_configGetColorSpace(config, backup_role);
459 const char *name = OCIO_colorSpaceGetName(ociocs);
461 /* assume function was called with buffer properly allocated to MAX_COLORSPACE_NAME chars */
462 BLI_strncpy(colorspace_name, name, MAX_COLORSPACE_NAME);
463 OCIO_colorSpaceRelease(ociocs);
466 printf("Color management: Error could not find role %s role.\n", role);
470 static void colormanage_load_config(OCIO_ConstConfigRcPtr *config)
472 int tot_colorspace, tot_display, tot_display_view, tot_looks;
473 int index, viewindex, viewindex2;
477 colormanage_role_color_space_name_get(config, global_role_scene_linear, OCIO_ROLE_SCENE_LINEAR, NULL);
478 colormanage_role_color_space_name_get(config, global_role_color_picking, OCIO_ROLE_COLOR_PICKING, NULL);
479 colormanage_role_color_space_name_get(config, global_role_texture_painting, OCIO_ROLE_TEXTURE_PAINT, NULL);
480 colormanage_role_color_space_name_get(config, global_role_default_sequencer, OCIO_ROLE_DEFAULT_SEQUENCER, OCIO_ROLE_SCENE_LINEAR);
481 colormanage_role_color_space_name_get(config, global_role_default_byte, OCIO_ROLE_DEFAULT_BYTE, OCIO_ROLE_TEXTURE_PAINT);
482 colormanage_role_color_space_name_get(config, global_role_default_float, OCIO_ROLE_DEFAULT_FLOAT, OCIO_ROLE_SCENE_LINEAR);
484 /* load colorspaces */
485 tot_colorspace = OCIO_configGetNumColorSpaces(config);
486 for (index = 0 ; index < tot_colorspace; index++) {
487 OCIO_ConstColorSpaceRcPtr *ocio_colorspace;
488 const char *description;
489 bool is_invertible, is_data;
491 name = OCIO_configGetColorSpaceNameByIndex(config, index);
493 ocio_colorspace = OCIO_configGetColorSpace(config, name);
494 description = OCIO_colorSpaceGetDescription(ocio_colorspace);
495 is_invertible = OCIO_colorSpaceIsInvertible(ocio_colorspace);
496 is_data = OCIO_colorSpaceIsData(ocio_colorspace);
498 colormanage_colorspace_add(name, description, is_invertible, is_data);
500 OCIO_colorSpaceRelease(ocio_colorspace);
505 tot_display = OCIO_configGetNumDisplays(config);
507 for (index = 0 ; index < tot_display; index++) {
508 const char *displayname;
509 ColorManagedDisplay *display;
511 displayname = OCIO_configGetDisplay(config, index);
513 display = colormanage_display_add(displayname);
516 tot_display_view = OCIO_configGetNumViews(config, displayname);
517 for (viewindex = 0 ; viewindex < tot_display_view; viewindex++, viewindex2++) {
518 const char *viewname;
519 ColorManagedView *view;
520 LinkData *display_view;
522 viewname = OCIO_configGetView(config, displayname, viewindex);
524 /* first check if view transform with given name was already loaded */
525 view = colormanage_view_get_named(viewname);
528 view = colormanage_view_add(viewname);
531 display_view = BLI_genericNodeN(view);
533 BLI_addtail(&display->views, display_view);
537 global_tot_display = tot_display;
540 tot_looks = OCIO_configGetNumLooks(config);
541 colormanage_look_add("None", "", true);
542 for (index = 0; index < tot_looks; index++) {
543 OCIO_ConstLookRcPtr *ocio_look;
544 const char *process_space;
546 name = OCIO_configGetLookNameByIndex(config, index);
547 ocio_look = OCIO_configGetLook(config, name);
548 process_space = OCIO_lookGetProcessSpace(ocio_look);
549 OCIO_lookRelease(ocio_look);
551 colormanage_look_add(name, process_space, false);
554 /* Load luminance coefficients. */
555 OCIO_configGetDefaultLumaCoefs(config, imbuf_luma_coefficients);
558 static void colormanage_free_config(void)
560 ColorSpace *colorspace;
561 ColorManagedDisplay *display;
563 /* free color spaces */
564 colorspace = global_colorspaces.first;
566 ColorSpace *colorspace_next = colorspace->next;
568 /* free precomputer processors */
569 if (colorspace->to_scene_linear)
570 OCIO_processorRelease((OCIO_ConstProcessorRcPtr *) colorspace->to_scene_linear);
572 if (colorspace->from_scene_linear)
573 OCIO_processorRelease((OCIO_ConstProcessorRcPtr *) colorspace->from_scene_linear);
575 /* free color space itself */
576 MEM_freeN(colorspace);
578 colorspace = colorspace_next;
580 BLI_listbase_clear(&global_colorspaces);
581 global_tot_colorspace = 0;
584 display = global_displays.first;
586 ColorManagedDisplay *display_next = display->next;
588 /* free precomputer processors */
589 if (display->to_scene_linear)
590 OCIO_processorRelease((OCIO_ConstProcessorRcPtr *) display->to_scene_linear);
592 if (display->from_scene_linear)
593 OCIO_processorRelease((OCIO_ConstProcessorRcPtr *) display->from_scene_linear);
595 /* free list of views */
596 BLI_freelistN(&display->views);
599 display = display_next;
601 BLI_listbase_clear(&global_displays);
602 global_tot_display = 0;
605 BLI_freelistN(&global_views);
609 BLI_freelistN(&global_looks);
610 global_tot_looks = 0;
615 void colormanagement_init(void)
617 const char *ocio_env;
618 const char *configdir;
619 char configfile[FILE_MAX];
620 OCIO_ConstConfigRcPtr *config = NULL;
624 ocio_env = getenv("OCIO");
626 if (ocio_env && ocio_env[0] != '\0') {
627 config = OCIO_configCreateFromEnv();
628 if (config != NULL) {
629 printf("Color management: Using %s as a configuration file\n", ocio_env);
633 if (config == NULL) {
634 configdir = BKE_appdir_folder_id(BLENDER_DATAFILES, "colormanagement");
637 BLI_join_dirfile(configfile, sizeof(configfile), configdir, BCM_CONFIG_FILE);
641 /* quite a hack to support loading configuration from path with non-acii symbols */
643 char short_name[256];
644 BLI_get_short_name(short_name, configfile);
645 config = OCIO_configCreateFromFile(short_name);
648 config = OCIO_configCreateFromFile(configfile);
653 if (config == NULL) {
654 printf("Color management: using fallback mode for management\n");
656 config = OCIO_configCreateFallback();
660 OCIO_setCurrentConfig(config);
662 colormanage_load_config(config);
664 OCIO_configRelease(config);
667 /* If there're no valid display/views, use fallback mode. */
668 if (global_tot_display == 0 || global_tot_view == 0) {
669 printf("Color management: no displays/views in the config, using fallback mode instead\n");
671 /* Free old config. */
672 colormanage_free_config();
674 /* Initialize fallback config. */
675 config = OCIO_configCreateFallback();
676 colormanage_load_config(config);
679 BLI_init_srgb_conversion();
682 void colormanagement_exit(void)
684 if (global_glsl_state.processor)
685 OCIO_processorRelease(global_glsl_state.processor);
687 if (global_glsl_state.curve_mapping)
688 curvemapping_free(global_glsl_state.curve_mapping);
690 if (global_glsl_state.curve_mapping_settings.lut)
691 MEM_freeN(global_glsl_state.curve_mapping_settings.lut);
693 if (global_glsl_state.ocio_glsl_state)
694 OCIO_freeOGLState(global_glsl_state.ocio_glsl_state);
696 if (global_glsl_state.transform_ocio_glsl_state)
697 OCIO_freeOGLState(global_glsl_state.transform_ocio_glsl_state);
699 colormanage_free_config();
702 /*********************** Internal functions *************************/
704 void colormanage_cache_free(ImBuf *ibuf)
706 if (ibuf->display_buffer_flags) {
707 MEM_freeN(ibuf->display_buffer_flags);
709 ibuf->display_buffer_flags = NULL;
712 if (ibuf->colormanage_cache) {
713 ColormnaageCacheData *cache_data = colormanage_cachedata_get(ibuf);
714 struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
717 MEM_freeN(cache_data);
721 IMB_moviecache_free(moviecache);
724 MEM_freeN(ibuf->colormanage_cache);
726 ibuf->colormanage_cache = NULL;
730 void IMB_colormanagement_display_settings_from_ctx(const bContext *C,
731 ColorManagedViewSettings **view_settings_r,
732 ColorManagedDisplaySettings **display_settings_r)
734 Scene *scene = CTX_data_scene(C);
735 SpaceImage *sima = CTX_wm_space_image(C);
737 *view_settings_r = &scene->view_settings;
738 *display_settings_r = &scene->display_settings;
740 if (sima && sima->image) {
741 if ((sima->image->flag & IMA_VIEW_AS_RENDER) == 0)
742 *view_settings_r = NULL;
746 const char *IMB_colormanagement_get_display_colorspace_name(const ColorManagedViewSettings *view_settings,
747 const ColorManagedDisplaySettings *display_settings)
749 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
751 const char *display = display_settings->display_device;
752 const char *view = view_settings->view_transform;
753 const char *colorspace_name;
755 colorspace_name = OCIO_configGetDisplayColorSpaceName(config, display, view);
757 OCIO_configRelease(config);
759 return colorspace_name;
762 static ColorSpace *display_transform_get_colorspace(const ColorManagedViewSettings *view_settings,
763 const ColorManagedDisplaySettings *display_settings)
765 const char *colorspace_name = IMB_colormanagement_get_display_colorspace_name(view_settings, display_settings);
768 return colormanage_colorspace_get_named(colorspace_name);
773 static OCIO_ConstProcessorRcPtr *create_display_buffer_processor(const char *look,
774 const char *view_transform,
776 float exposure, float gamma,
777 const char *from_colorspace)
779 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
780 OCIO_DisplayTransformRcPtr *dt;
781 OCIO_ConstProcessorRcPtr *processor;
782 ColorManagedLook *look_descr = colormanage_look_get_named(look);
784 dt = OCIO_createDisplayTransform();
786 OCIO_displayTransformSetInputColorSpaceName(dt, from_colorspace);
787 OCIO_displayTransformSetView(dt, view_transform);
788 OCIO_displayTransformSetDisplay(dt, display);
790 if (look_descr->is_noop == false) {
791 OCIO_displayTransformSetLooksOverrideEnabled(dt, true);
792 OCIO_displayTransformSetLooksOverride(dt, look);
795 /* fstop exposure control */
796 if (exposure != 0.0f) {
797 OCIO_MatrixTransformRcPtr *mt;
798 float gain = powf(2.0f, exposure);
799 const float scale4f[] = {gain, gain, gain, 1.0f};
800 float m44[16], offset4[4];
802 OCIO_matrixTransformScale(m44, offset4, scale4f);
803 mt = OCIO_createMatrixTransform();
804 OCIO_matrixTransformSetValue(mt, m44, offset4);
805 OCIO_displayTransformSetLinearCC(dt, (OCIO_ConstTransformRcPtr *) mt);
807 OCIO_matrixTransformRelease(mt);
810 /* post-display gamma transform */
812 OCIO_ExponentTransformRcPtr *et;
813 float exponent = 1.0f / MAX2(FLT_EPSILON, gamma);
814 const float exponent4f[] = {exponent, exponent, exponent, exponent};
816 et = OCIO_createExponentTransform();
817 OCIO_exponentTransformSetValue(et, exponent4f);
818 OCIO_displayTransformSetDisplayCC(dt, (OCIO_ConstTransformRcPtr *) et);
820 OCIO_exponentTransformRelease(et);
823 processor = OCIO_configGetProcessor(config, (OCIO_ConstTransformRcPtr *) dt);
825 OCIO_displayTransformRelease(dt);
826 OCIO_configRelease(config);
831 static OCIO_ConstProcessorRcPtr *create_colorspace_transform_processor(const char *from_colorspace,
832 const char *to_colorspace)
834 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
835 OCIO_ConstProcessorRcPtr *processor;
837 processor = OCIO_configGetProcessorWithNames(config, from_colorspace, to_colorspace);
839 OCIO_configRelease(config);
844 static OCIO_ConstProcessorRcPtr *colorspace_to_scene_linear_processor(ColorSpace *colorspace)
846 if (colorspace->to_scene_linear == NULL) {
847 BLI_mutex_lock(&processor_lock);
849 if (colorspace->to_scene_linear == NULL) {
850 OCIO_ConstProcessorRcPtr *to_scene_linear;
851 to_scene_linear = create_colorspace_transform_processor(colorspace->name, global_role_scene_linear);
852 colorspace->to_scene_linear = (struct OCIO_ConstProcessorRcPtr *) to_scene_linear;
855 BLI_mutex_unlock(&processor_lock);
858 return (OCIO_ConstProcessorRcPtr *) colorspace->to_scene_linear;
861 static OCIO_ConstProcessorRcPtr *colorspace_from_scene_linear_processor(ColorSpace *colorspace)
863 if (colorspace->from_scene_linear == NULL) {
864 BLI_mutex_lock(&processor_lock);
866 if (colorspace->from_scene_linear == NULL) {
867 OCIO_ConstProcessorRcPtr *from_scene_linear;
868 from_scene_linear = create_colorspace_transform_processor(global_role_scene_linear, colorspace->name);
869 colorspace->from_scene_linear = (struct OCIO_ConstProcessorRcPtr *) from_scene_linear;
872 BLI_mutex_unlock(&processor_lock);
875 return (OCIO_ConstProcessorRcPtr *) colorspace->from_scene_linear;
878 static OCIO_ConstProcessorRcPtr *display_from_scene_linear_processor(ColorManagedDisplay *display)
880 if (display->from_scene_linear == NULL) {
881 BLI_mutex_lock(&processor_lock);
883 if (display->from_scene_linear == NULL) {
884 const char *view_name = colormanage_view_get_default_name(display);
885 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
886 OCIO_ConstProcessorRcPtr *processor = NULL;
888 if (view_name && config) {
889 const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(config, display->name, view_name);
890 processor = OCIO_configGetProcessorWithNames(config, global_role_scene_linear, view_colorspace);
892 OCIO_configRelease(config);
895 display->from_scene_linear = (struct OCIO_ConstProcessorRcPtr *) processor;
898 BLI_mutex_unlock(&processor_lock);
901 return (OCIO_ConstProcessorRcPtr *) display->from_scene_linear;
904 static OCIO_ConstProcessorRcPtr *display_to_scene_linear_processor(ColorManagedDisplay *display)
906 if (display->to_scene_linear == NULL) {
907 BLI_mutex_lock(&processor_lock);
909 if (display->to_scene_linear == NULL) {
910 const char *view_name = colormanage_view_get_default_name(display);
911 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
912 OCIO_ConstProcessorRcPtr *processor = NULL;
914 if (view_name && config) {
915 const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(config, display->name, view_name);
916 processor = OCIO_configGetProcessorWithNames(config, view_colorspace, global_role_scene_linear);
918 OCIO_configRelease(config);
921 display->to_scene_linear = (struct OCIO_ConstProcessorRcPtr *) processor;
924 BLI_mutex_unlock(&processor_lock);
927 return (OCIO_ConstProcessorRcPtr *) display->to_scene_linear;
930 static void init_default_view_settings(const ColorManagedDisplaySettings *display_settings,
931 ColorManagedViewSettings *view_settings)
933 ColorManagedDisplay *display;
934 ColorManagedView *default_view = NULL;
936 display = colormanage_display_get_named(display_settings->display_device);
939 default_view = colormanage_view_get_default(display);
942 BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
944 view_settings->view_transform[0] = '\0';
946 BLI_strncpy(view_settings->look, "None", sizeof(view_settings->look));
947 view_settings->flag = 0;
948 view_settings->gamma = 1.0f;
949 view_settings->exposure = 0.0f;
950 view_settings->curve_mapping = NULL;
953 static void curve_mapping_apply_pixel(CurveMapping *curve_mapping, float *pixel, int channels)
956 pixel[0] = curvemap_evaluateF(curve_mapping->cm, pixel[0]);
958 else if (channels == 2) {
959 pixel[0] = curvemap_evaluateF(curve_mapping->cm, pixel[0]);
960 pixel[1] = curvemap_evaluateF(curve_mapping->cm, pixel[1]);
963 curvemapping_evaluate_premulRGBF(curve_mapping, pixel, pixel);
967 void colorspace_set_default_role(char *colorspace, int size, int role)
969 if (colorspace && colorspace[0] == '\0') {
970 const char *role_colorspace;
972 role_colorspace = IMB_colormanagement_role_colorspace_name_get(role);
974 BLI_strncpy(colorspace, role_colorspace, size);
978 void colormanage_imbuf_set_default_spaces(ImBuf *ibuf)
980 ibuf->rect_colorspace = colormanage_colorspace_get_named(global_role_default_byte);
983 void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace)
985 ColorSpace *colorspace = colormanage_colorspace_get_named(from_colorspace);
987 if (colorspace && colorspace->is_data) {
988 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
992 if (ibuf->rect_float) {
993 const char *to_colorspace = global_role_scene_linear;
996 imb_freerectImBuf(ibuf);
998 IMB_colormanagement_transform(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels,
999 from_colorspace, to_colorspace, true);
1003 /*********************** Generic functions *************************/
1005 static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings, const char *what,
1006 const ColorManagedDisplay *default_display)
1008 if (display_settings->display_device[0] == '\0') {
1009 BLI_strncpy(display_settings->display_device, default_display->name, sizeof(display_settings->display_device));
1012 ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device);
1015 printf("Color management: display \"%s\" used by %s not found, setting to default (\"%s\").\n",
1016 display_settings->display_device, what, default_display->name);
1018 BLI_strncpy(display_settings->display_device, default_display->name,
1019 sizeof(display_settings->display_device));
1024 static void colormanage_check_view_settings(ColorManagedDisplaySettings *display_settings,
1025 ColorManagedViewSettings *view_settings, const char *what)
1027 ColorManagedDisplay *display;
1028 ColorManagedView *default_view = NULL;
1029 ColorManagedLook *default_look = (ColorManagedLook *) global_looks.first;
1031 if (view_settings->view_transform[0] == '\0') {
1032 display = colormanage_display_get_named(display_settings->display_device);
1035 default_view = colormanage_view_get_default(display);
1038 BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1041 ColorManagedView *view = colormanage_view_get_named(view_settings->view_transform);
1044 display = colormanage_display_get_named(display_settings->display_device);
1047 default_view = colormanage_view_get_default(display);
1050 printf("Color management: %s view \"%s\" not found, setting default \"%s\".\n",
1051 what, view_settings->view_transform, default_view->name);
1053 BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1058 if (view_settings->look[0] == '\0') {
1059 BLI_strncpy(view_settings->look, default_look->name, sizeof(view_settings->look));
1062 ColorManagedLook *look = colormanage_look_get_named(view_settings->look);
1064 printf("Color management: %s look \"%s\" not found, setting default \"%s\".\n",
1065 what, view_settings->look, default_look->name);
1067 BLI_strncpy(view_settings->look, default_look->name, sizeof(view_settings->look));
1071 /* OCIO_TODO: move to do_versions() */
1072 if (view_settings->exposure == 0.0f && view_settings->gamma == 0.0f) {
1073 view_settings->exposure = 0.0f;
1074 view_settings->gamma = 1.0f;
1078 static void colormanage_check_colorspace_settings(ColorManagedColorspaceSettings *colorspace_settings, const char *what)
1080 if (colorspace_settings->name[0] == '\0') {
1084 ColorSpace *colorspace = colormanage_colorspace_get_named(colorspace_settings->name);
1087 printf("Color management: %s colorspace \"%s\" not found, will use default instead.\n",
1088 what, colorspace_settings->name);
1090 BLI_strncpy(colorspace_settings->name, "", sizeof(colorspace_settings->name));
1097 void IMB_colormanagement_check_file_config(Main *bmain)
1103 ColorManagedDisplay *default_display;
1105 default_display = colormanage_display_get_default();
1107 if (!default_display) {
1108 /* happens when OCIO configuration is incorrect */
1112 for (scene = bmain->scene.first; scene; scene = scene->id.next) {
1113 ColorManagedColorspaceSettings *sequencer_colorspace_settings;
1115 colormanage_check_display_settings(&scene->display_settings, "scene", default_display);
1116 colormanage_check_view_settings(&scene->display_settings, &scene->view_settings, "scene");
1118 sequencer_colorspace_settings = &scene->sequencer_colorspace_settings;
1120 colormanage_check_colorspace_settings(sequencer_colorspace_settings, "sequencer");
1122 if (sequencer_colorspace_settings->name[0] == '\0') {
1123 BLI_strncpy(sequencer_colorspace_settings->name, global_role_default_sequencer, MAX_COLORSPACE_NAME);
1127 /* ** check input color space settings ** */
1129 for (image = bmain->image.first; image; image = image->id.next) {
1130 colormanage_check_colorspace_settings(&image->colorspace_settings, "image");
1133 for (clip = bmain->movieclip.first; clip; clip = clip->id.next) {
1134 colormanage_check_colorspace_settings(&clip->colorspace_settings, "clip");
1138 void IMB_colormanagement_validate_settings(ColorManagedDisplaySettings *display_settings,
1139 ColorManagedViewSettings *view_settings)
1141 ColorManagedDisplay *display;
1142 ColorManagedView *default_view = NULL;
1143 LinkData *view_link;
1145 display = colormanage_display_get_named(display_settings->display_device);
1147 default_view = colormanage_view_get_default(display);
1149 for (view_link = display->views.first; view_link; view_link = view_link->next) {
1150 ColorManagedView *view = view_link->data;
1152 if (STREQ(view->name, view_settings->view_transform))
1156 if (view_link == NULL && default_view)
1157 BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1160 const char *IMB_colormanagement_role_colorspace_name_get(int role)
1163 case COLOR_ROLE_SCENE_LINEAR:
1164 return global_role_scene_linear;
1165 case COLOR_ROLE_COLOR_PICKING:
1166 return global_role_color_picking;
1167 case COLOR_ROLE_TEXTURE_PAINTING:
1168 return global_role_texture_painting;
1169 case COLOR_ROLE_DEFAULT_SEQUENCER:
1170 return global_role_default_sequencer;
1171 case COLOR_ROLE_DEFAULT_FLOAT:
1172 return global_role_default_float;
1173 case COLOR_ROLE_DEFAULT_BYTE:
1174 return global_role_default_byte;
1176 printf("Unknown role was passed to %s\n", __func__);
1184 void IMB_colormanagement_check_is_data(ImBuf *ibuf, const char *name)
1186 ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1188 if (colorspace && colorspace->is_data)
1189 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1191 ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1194 void IMB_colormanagement_assign_float_colorspace(ImBuf *ibuf, const char *name)
1196 ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1198 ibuf->float_colorspace = colorspace;
1200 if (colorspace && colorspace->is_data)
1201 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1203 ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1206 void IMB_colormanagement_assign_rect_colorspace(ImBuf *ibuf, const char *name)
1208 ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1210 ibuf->rect_colorspace = colorspace;
1212 if (colorspace && colorspace->is_data)
1213 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1215 ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1218 const char *IMB_colormanagement_get_float_colorspace(ImBuf *ibuf)
1220 if (ibuf->float_colorspace) {
1221 return ibuf->float_colorspace->name;
1224 return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR);
1228 const char *IMB_colormanagement_get_rect_colorspace(ImBuf *ibuf)
1230 if (ibuf->rect_colorspace) {
1231 return ibuf->rect_colorspace->name;
1234 return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE);
1238 /*********************** Threaded display buffer transform routines *************************/
1240 typedef struct DisplayBufferThread {
1241 ColormanageProcessor *cm_processor;
1243 const float *buffer;
1244 unsigned char *byte_buffer;
1246 float *display_buffer;
1247 unsigned char *display_buffer_byte;
1257 const char *byte_colorspace;
1258 const char *float_colorspace;
1259 } DisplayBufferThread;
1261 typedef struct DisplayBufferInitData {
1263 ColormanageProcessor *cm_processor;
1264 const float *buffer;
1265 unsigned char *byte_buffer;
1267 float *display_buffer;
1268 unsigned char *display_buffer_byte;
1272 const char *byte_colorspace;
1273 const char *float_colorspace;
1274 } DisplayBufferInitData;
1276 static void display_buffer_init_handle(void *handle_v, int start_line, int tot_line, void *init_data_v)
1278 DisplayBufferThread *handle = (DisplayBufferThread *) handle_v;
1279 DisplayBufferInitData *init_data = (DisplayBufferInitData *) init_data_v;
1280 ImBuf *ibuf = init_data->ibuf;
1282 int channels = ibuf->channels;
1283 float dither = ibuf->dither;
1284 bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
1286 size_t offset = ((size_t)channels) * start_line * ibuf->x;
1287 size_t display_buffer_byte_offset = ((size_t)DISPLAY_BUFFER_CHANNELS) * start_line * ibuf->x;
1289 memset(handle, 0, sizeof(DisplayBufferThread));
1291 handle->cm_processor = init_data->cm_processor;
1293 if (init_data->buffer)
1294 handle->buffer = init_data->buffer + offset;
1296 if (init_data->byte_buffer)
1297 handle->byte_buffer = init_data->byte_buffer + offset;
1299 if (init_data->display_buffer)
1300 handle->display_buffer = init_data->display_buffer + offset;
1302 if (init_data->display_buffer_byte)
1303 handle->display_buffer_byte = init_data->display_buffer_byte + display_buffer_byte_offset;
1305 handle->width = ibuf->x;
1307 handle->start_line = start_line;
1308 handle->tot_line = tot_line;
1310 handle->channels = channels;
1311 handle->dither = dither;
1312 handle->is_data = is_data;
1314 handle->byte_colorspace = init_data->byte_colorspace;
1315 handle->float_colorspace = init_data->float_colorspace;
1318 static void display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle, int height,
1319 float *linear_buffer, bool *is_straight_alpha)
1321 int channels = handle->channels;
1322 int width = handle->width;
1324 size_t buffer_size = ((size_t)channels) * width * height;
1326 bool is_data = handle->is_data;
1327 bool is_data_display = handle->cm_processor->is_data_result;
1329 if (!handle->buffer) {
1330 unsigned char *byte_buffer = handle->byte_buffer;
1332 const char *from_colorspace = handle->byte_colorspace;
1333 const char *to_colorspace = global_role_scene_linear;
1337 const size_t i_last = ((size_t)width) * height;
1340 /* first convert byte buffer to float, keep in image space */
1341 for (i = 0, fp = linear_buffer, cp = byte_buffer;
1343 i++, fp += channels, cp += channels)
1345 if (channels == 3) {
1346 rgb_uchar_to_float(fp, cp);
1348 else if (channels == 4) {
1349 rgba_uchar_to_float(fp, cp);
1352 BLI_assert(!"Buffers of 3 or 4 channels are only supported here");
1356 if (!is_data && !is_data_display) {
1357 /* convert float buffer to scene linear space */
1358 IMB_colormanagement_transform(linear_buffer, width, height, channels,
1359 from_colorspace, to_colorspace, false);
1362 *is_straight_alpha = true;
1364 else if (handle->float_colorspace) {
1365 /* currently float is non-linear only in sequencer, which is working
1366 * in it's own color space even to handle float buffers.
1367 * This color space is the same for byte and float images.
1368 * Need to convert float buffer to linear space before applying display transform
1371 const char *from_colorspace = handle->float_colorspace;
1372 const char *to_colorspace = global_role_scene_linear;
1374 memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1376 if (!is_data && !is_data_display) {
1377 IMB_colormanagement_transform(linear_buffer, width, height, channels,
1378 from_colorspace, to_colorspace, true);
1381 *is_straight_alpha = false;
1384 /* some processors would want to modify float original buffer
1385 * before converting it into display byte buffer, so we need to
1386 * make sure original's ImBuf buffers wouldn't be modified by
1387 * using duplicated buffer here
1390 memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1392 *is_straight_alpha = false;
1396 static void *do_display_buffer_apply_thread(void *handle_v)
1398 DisplayBufferThread *handle = (DisplayBufferThread *) handle_v;
1399 ColormanageProcessor *cm_processor = handle->cm_processor;
1400 float *display_buffer = handle->display_buffer;
1401 unsigned char *display_buffer_byte = handle->display_buffer_byte;
1402 int channels = handle->channels;
1403 int width = handle->width;
1404 int height = handle->tot_line;
1405 float dither = handle->dither;
1406 bool is_data = handle->is_data;
1408 if (cm_processor == NULL) {
1409 if (display_buffer_byte) {
1410 IMB_buffer_byte_from_byte(display_buffer_byte, handle->byte_buffer, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1411 false, width, height, width, width);
1414 if (display_buffer) {
1415 IMB_buffer_float_from_byte(display_buffer, handle->byte_buffer, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1416 false, width, height, width, width);
1420 bool is_straight_alpha, predivide;
1421 float *linear_buffer = MEM_mallocN(((size_t)channels) * width * height * sizeof(float),
1422 "color conversion linear buffer");
1424 display_buffer_apply_get_linear_buffer(handle, height, linear_buffer, &is_straight_alpha);
1426 predivide = is_straight_alpha == false;
1429 /* special case for data buffers - no color space conversions,
1430 * only generate byte buffers
1434 /* apply processor */
1435 IMB_colormanagement_processor_apply(cm_processor, linear_buffer, width, height, channels,
1439 /* copy result to output buffers */
1440 if (display_buffer_byte) {
1442 IMB_buffer_byte_from_float(display_buffer_byte, linear_buffer,
1443 channels, dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1444 predivide, width, height, width, width);
1447 if (display_buffer) {
1448 memcpy(display_buffer, linear_buffer, ((size_t)width) * height * channels * sizeof(float));
1450 if (is_straight_alpha && channels == 4) {
1451 const size_t i_last = ((size_t)width) * height;
1455 for (i = 0, fp = display_buffer;
1457 i++, fp += channels)
1459 straight_to_premul_v4(fp);
1464 MEM_freeN(linear_buffer);
1470 static void display_buffer_apply_threaded(ImBuf *ibuf, float *buffer, unsigned char *byte_buffer, float *display_buffer,
1471 unsigned char *display_buffer_byte, ColormanageProcessor *cm_processor)
1473 DisplayBufferInitData init_data;
1475 init_data.ibuf = ibuf;
1476 init_data.cm_processor = cm_processor;
1477 init_data.buffer = buffer;
1478 init_data.byte_buffer = byte_buffer;
1479 init_data.display_buffer = display_buffer;
1480 init_data.display_buffer_byte = display_buffer_byte;
1482 if (ibuf->rect_colorspace != NULL) {
1483 init_data.byte_colorspace = ibuf->rect_colorspace->name;
1486 /* happens for viewer images, which are not so simple to determine where to
1487 * set image buffer's color spaces
1489 init_data.byte_colorspace = global_role_default_byte;
1492 if (ibuf->float_colorspace != NULL) {
1493 /* sequencer stores float buffers in non-linear space */
1494 init_data.float_colorspace = ibuf->float_colorspace->name;
1497 init_data.float_colorspace = NULL;
1500 IMB_processor_apply_threaded(ibuf->y, sizeof(DisplayBufferThread), &init_data,
1501 display_buffer_init_handle, do_display_buffer_apply_thread);
1504 static bool is_ibuf_rect_in_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
1505 const ColorManagedDisplaySettings *display_settings)
1507 if ((view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) == 0 &&
1508 view_settings->exposure == 0.0f &&
1509 view_settings->gamma == 1.0f)
1511 const char *from_colorspace = ibuf->rect_colorspace->name;
1512 const char *to_colorspace = IMB_colormanagement_get_display_colorspace_name(view_settings, display_settings);
1514 if (to_colorspace && STREQ(from_colorspace, to_colorspace))
1521 static void colormanage_display_buffer_process_ex(ImBuf *ibuf, float *display_buffer, unsigned char *display_buffer_byte,
1522 const ColorManagedViewSettings *view_settings,
1523 const ColorManagedDisplaySettings *display_settings)
1525 ColormanageProcessor *cm_processor = NULL;
1526 bool skip_transform = false;
1528 /* if we're going to transform byte buffer, check whether transformation would
1529 * happen to the same color space as byte buffer itself is
1530 * this would save byte -> float -> byte conversions making display buffer
1531 * computation noticeable faster
1533 if (ibuf->rect_float == NULL && ibuf->rect_colorspace) {
1534 skip_transform = is_ibuf_rect_in_display_space(ibuf, view_settings, display_settings);
1537 if (skip_transform == false)
1538 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1540 display_buffer_apply_threaded(ibuf, ibuf->rect_float, (unsigned char *) ibuf->rect,
1541 display_buffer, display_buffer_byte, cm_processor);
1544 IMB_colormanagement_processor_free(cm_processor);
1547 static void colormanage_display_buffer_process(ImBuf *ibuf, unsigned char *display_buffer,
1548 const ColorManagedViewSettings *view_settings,
1549 const ColorManagedDisplaySettings *display_settings)
1551 colormanage_display_buffer_process_ex(ibuf, NULL, display_buffer, view_settings, display_settings);
1554 /*********************** Threaded processor transform routines *************************/
1556 typedef struct ProcessorTransformThread {
1557 ColormanageProcessor *cm_processor;
1564 } ProcessorTransformThread;
1566 typedef struct ProcessorTransformInit {
1567 ColormanageProcessor *cm_processor;
1573 } ProcessorTransformInitData;
1575 static void processor_transform_init_handle(void *handle_v, int start_line, int tot_line, void *init_data_v)
1577 ProcessorTransformThread *handle = (ProcessorTransformThread *) handle_v;
1578 ProcessorTransformInitData *init_data = (ProcessorTransformInitData *) init_data_v;
1580 int channels = init_data->channels;
1581 int width = init_data->width;
1582 bool predivide = init_data->predivide;
1584 size_t offset = ((size_t)channels) * start_line * width;
1586 memset(handle, 0, sizeof(ProcessorTransformThread));
1588 handle->cm_processor = init_data->cm_processor;
1590 handle->buffer = init_data->buffer + offset;
1592 handle->width = width;
1594 handle->start_line = start_line;
1595 handle->tot_line = tot_line;
1597 handle->channels = channels;
1598 handle->predivide = predivide;
1601 static void *do_processor_transform_thread(void *handle_v)
1603 ProcessorTransformThread *handle = (ProcessorTransformThread *) handle_v;
1604 float *buffer = handle->buffer;
1605 int channels = handle->channels;
1606 int width = handle->width;
1607 int height = handle->tot_line;
1608 bool predivide = handle->predivide;
1610 IMB_colormanagement_processor_apply(handle->cm_processor, buffer, width, height, channels, predivide);
1615 static void processor_transform_apply_threaded(float *buffer, int width, int height, int channels,
1616 ColormanageProcessor *cm_processor, bool predivide)
1618 ProcessorTransformInitData init_data;
1620 init_data.cm_processor = cm_processor;
1621 init_data.buffer = buffer;
1622 init_data.width = width;
1623 init_data.height = height;
1624 init_data.channels = channels;
1625 init_data.predivide = predivide;
1627 IMB_processor_apply_threaded(height, sizeof(ProcessorTransformThread), &init_data,
1628 processor_transform_init_handle, do_processor_transform_thread);
1631 /*********************** Color space transformation functions *************************/
1633 /* convert the whole buffer from specified by name color space to another - internal implementation */
1634 static void colormanagement_transform_ex(float *buffer, int width, int height, int channels, const char *from_colorspace,
1635 const char *to_colorspace, bool predivide, bool do_threaded)
1637 ColormanageProcessor *cm_processor;
1639 if (from_colorspace[0] == '\0') {
1643 if (STREQ(from_colorspace, to_colorspace)) {
1644 /* if source and destination color spaces are identical, skip
1645 * threading overhead and simply do nothing
1650 cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
1653 processor_transform_apply_threaded(buffer, width, height, channels, cm_processor, predivide);
1655 IMB_colormanagement_processor_apply(cm_processor, buffer, width, height, channels, predivide);
1657 IMB_colormanagement_processor_free(cm_processor);
1660 /* convert the whole buffer from specified by name color space to another */
1661 void IMB_colormanagement_transform(float *buffer, int width, int height, int channels,
1662 const char *from_colorspace, const char *to_colorspace, bool predivide)
1664 colormanagement_transform_ex(buffer, width, height, channels, from_colorspace, to_colorspace, predivide, false);
1667 /* convert the whole buffer from specified by name color space to another
1668 * will do threaded conversion
1670 void IMB_colormanagement_transform_threaded(float *buffer, int width, int height, int channels,
1671 const char *from_colorspace, const char *to_colorspace, bool predivide)
1673 colormanagement_transform_ex(buffer, width, height, channels, from_colorspace, to_colorspace, predivide, true);
1676 void IMB_colormanagement_transform_v4(float pixel[4], const char *from_colorspace, const char *to_colorspace)
1678 ColormanageProcessor *cm_processor;
1680 if (from_colorspace[0] == '\0') {
1684 if (STREQ(from_colorspace, to_colorspace)) {
1685 /* if source and destination color spaces are identical, skip
1686 * threading overhead and simply do nothing
1691 cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
1693 IMB_colormanagement_processor_apply_v4(cm_processor, pixel);
1695 IMB_colormanagement_processor_free(cm_processor);
1698 /* convert pixel from specified by descriptor color space to scene linear
1699 * used by performance-critical areas such as renderer and baker
1701 void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], ColorSpace *colorspace)
1703 OCIO_ConstProcessorRcPtr *processor;
1706 /* should never happen */
1707 printf("%s: perform conversion from unknown color space\n", __func__);
1711 processor = colorspace_to_scene_linear_processor(colorspace);
1714 OCIO_processorApplyRGB(processor, pixel);
1717 /* same as above, but converts colors in opposite direction */
1718 void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], ColorSpace *colorspace)
1720 OCIO_ConstProcessorRcPtr *processor;
1723 /* should never happen */
1724 printf("%s: perform conversion from unknown color space\n", __func__);
1728 processor = colorspace_from_scene_linear_processor(colorspace);
1731 OCIO_processorApplyRGB(processor, pixel);
1734 void IMB_colormanagement_colorspace_to_scene_linear_v4(float pixel[4], bool predivide, ColorSpace *colorspace)
1736 OCIO_ConstProcessorRcPtr *processor;
1739 /* should never happen */
1740 printf("%s: perform conversion from unknown color space\n", __func__);
1744 processor = colorspace_to_scene_linear_processor(colorspace);
1748 OCIO_processorApplyRGBA_predivide(processor, pixel);
1750 OCIO_processorApplyRGBA(processor, pixel);
1754 void IMB_colormanagement_colorspace_to_scene_linear(float *buffer, int width, int height, int channels, struct ColorSpace *colorspace, bool predivide)
1756 OCIO_ConstProcessorRcPtr *processor;
1759 /* should never happen */
1760 printf("%s: perform conversion from unknown color space\n", __func__);
1764 processor = colorspace_to_scene_linear_processor(colorspace);
1767 OCIO_PackedImageDesc *img;
1769 img = OCIO_createOCIO_PackedImageDesc(
1770 buffer, width, height, channels, sizeof(float),
1771 (size_t)channels * sizeof(float),
1772 (size_t)channels * sizeof(float) * width);
1775 OCIO_processorApply_predivide(processor, img);
1777 OCIO_processorApply(processor, img);
1779 OCIO_PackedImageDescRelease(img);
1783 /* convert pixel from scene linear to display space using default view
1784 * used by performance-critical areas such as color-related widgets where we want to reduce
1785 * amount of per-widget allocations
1787 void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManagedDisplay *display)
1789 OCIO_ConstProcessorRcPtr *processor;
1791 processor = display_from_scene_linear_processor(display);
1794 OCIO_processorApplyRGB(processor, pixel);
1797 /* same as above, but converts color in opposite direction */
1798 void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], ColorManagedDisplay *display)
1800 OCIO_ConstProcessorRcPtr *processor;
1802 processor = display_to_scene_linear_processor(display);
1805 OCIO_processorApplyRGB(processor, pixel);
1808 void IMB_colormanagement_pixel_to_display_space_v4(float result[4], const float pixel[4],
1809 const ColorManagedViewSettings *view_settings,
1810 const ColorManagedDisplaySettings *display_settings)
1812 ColormanageProcessor *cm_processor;
1814 copy_v4_v4(result, pixel);
1816 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1817 IMB_colormanagement_processor_apply_v4(cm_processor, result);
1818 IMB_colormanagement_processor_free(cm_processor);
1821 void IMB_colormanagement_pixel_to_display_space_v3(float result[3], const float pixel[3],
1822 const ColorManagedViewSettings *view_settings,
1823 const ColorManagedDisplaySettings *display_settings)
1825 ColormanageProcessor *cm_processor;
1827 copy_v3_v3(result, pixel);
1829 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1830 IMB_colormanagement_processor_apply_v3(cm_processor, result);
1831 IMB_colormanagement_processor_free(cm_processor);
1834 static void colormanagement_imbuf_make_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
1835 const ColorManagedDisplaySettings *display_settings, bool make_byte)
1837 if (!ibuf->rect && make_byte)
1838 imb_addrectImBuf(ibuf);
1840 colormanage_display_buffer_process_ex(ibuf, ibuf->rect_float, (unsigned char *)ibuf->rect,
1841 view_settings, display_settings);
1844 void IMB_colormanagement_imbuf_make_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
1845 const ColorManagedDisplaySettings *display_settings)
1847 colormanagement_imbuf_make_display_space(ibuf, view_settings, display_settings, false);
1850 /* prepare image buffer to be saved on disk, applying color management if needed
1851 * color management would be applied if image is saving as render result and if
1852 * file format is not expecting float buffer to be in linear space (currently
1853 * JPEG2000 and TIFF are such formats -- they're storing image as float but
1854 * file itself stores applied color space).
1856 * Both byte and float buffers would contain applied color space, and result's
1857 * float_colorspace would be set to display color space. This should be checked
1858 * in image format write callback and if float_colorspace is not NULL, no color
1859 * space transformation should be applied on this buffer.
1861 ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, bool save_as_render, bool allocate_result, const ColorManagedViewSettings *view_settings,
1862 const ColorManagedDisplaySettings *display_settings, ImageFormatData *image_format_data)
1864 ImBuf *colormanaged_ibuf = ibuf;
1865 bool do_colormanagement;
1866 bool is_movie = BKE_imtype_is_movie(image_format_data->imtype);
1867 bool requires_linear_float = BKE_imtype_requires_linear_float(image_format_data->imtype);
1868 bool do_alpha_under = image_format_data->planes != R_IMF_PLANES_RGBA;
1870 if (ibuf->rect_float && ibuf->rect &&
1871 (ibuf->userflags & (IB_DISPLAY_BUFFER_INVALID | IB_RECT_INVALID)) != 0)
1873 IMB_rect_from_float(ibuf);
1874 ibuf->userflags &= ~(IB_RECT_INVALID | IB_DISPLAY_BUFFER_INVALID);
1877 do_colormanagement = save_as_render && (is_movie || !requires_linear_float);
1879 if (do_colormanagement || do_alpha_under) {
1880 if (allocate_result) {
1881 colormanaged_ibuf = IMB_dupImBuf(ibuf);
1884 /* render pipeline is constructing image buffer itself, but it's re-using byte and float buffers from render result
1885 * make copy of this buffers here sine this buffers would be transformed to other color space here
1888 if (ibuf->rect && (ibuf->mall & IB_rect) == 0) {
1889 ibuf->rect = MEM_dupallocN(ibuf->rect);
1890 ibuf->mall |= IB_rect;
1893 if (ibuf->rect_float && (ibuf->mall & IB_rectfloat) == 0) {
1894 ibuf->rect_float = MEM_dupallocN(ibuf->rect_float);
1895 ibuf->mall |= IB_rectfloat;
1900 /* If we're saving from RGBA to RGB buffer then it's not
1901 * so much useful to just ignore alpha -- it leads to bad
1902 * artifacts especially when saving byte images.
1904 * What we do here is we're overlaying our image on top of
1905 * background color (which is currently black).
1907 * This is quite much the same as what Gimp does and it
1908 * seems to be what artists expects from saving.
1910 * Do a conversion here, so image format writers could
1911 * happily assume all the alpha tricks were made already.
1912 * helps keep things locally here, not spreading it to
1913 * all possible image writers we've got.
1915 if (do_alpha_under) {
1916 float color[3] = {0, 0, 0};
1918 if (colormanaged_ibuf->rect_float && colormanaged_ibuf->channels == 4) {
1919 IMB_alpha_under_color_float(colormanaged_ibuf->rect_float, colormanaged_ibuf->x,
1920 colormanaged_ibuf->y, color);
1923 if (colormanaged_ibuf->rect) {
1924 IMB_alpha_under_color_byte((unsigned char *)colormanaged_ibuf->rect,
1925 colormanaged_ibuf->x, colormanaged_ibuf->y,
1930 if (do_colormanagement) {
1931 bool make_byte = false;
1932 const ImFileType *type;
1934 /* for proper check whether byte buffer is required by a format or not
1935 * should be pretty safe since this image buffer is supposed to be used for
1936 * saving only and ftype would be overwritten a bit later by BKE_imbuf_write
1938 colormanaged_ibuf->ftype = BKE_image_imtype_to_ftype(image_format_data->imtype, &colormanaged_ibuf->foptions);
1940 /* if file format isn't able to handle float buffer itself,
1941 * we need to allocate byte buffer and store color managed
1944 for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
1945 if (type->save && type->ftype(type, colormanaged_ibuf)) {
1946 if ((type->flag & IM_FTYPE_FLOAT) == 0)
1953 /* perform color space conversions */
1954 colormanagement_imbuf_make_display_space(colormanaged_ibuf, view_settings, display_settings, make_byte);
1956 if (colormanaged_ibuf->rect_float) {
1957 /* float buffer isn't linear anymore,
1958 * image format write callback should check for this flag and assume
1959 * no space conversion should happen if ibuf->float_colorspace != NULL
1961 colormanaged_ibuf->float_colorspace = display_transform_get_colorspace(view_settings, display_settings);
1965 return colormanaged_ibuf;
1968 void IMB_colormanagement_buffer_make_display_space(float *buffer, unsigned char *display_buffer,
1969 int width, int height, int channels, float dither,
1970 const ColorManagedViewSettings *view_settings,
1971 const ColorManagedDisplaySettings *display_settings)
1973 ColormanageProcessor *cm_processor;
1974 size_t float_buffer_size = ((size_t)width) * height * channels * sizeof(float);
1975 float *display_buffer_float = MEM_mallocN(float_buffer_size, "byte_buffer_make_display_space");
1977 memcpy(display_buffer_float, buffer, float_buffer_size);
1979 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1981 processor_transform_apply_threaded(display_buffer_float, width, height, channels,
1982 cm_processor, true);
1984 IMB_buffer_byte_from_float(display_buffer, display_buffer_float,
1985 channels, dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1986 true, width, height, width, width);
1988 MEM_freeN(display_buffer_float);
1989 IMB_colormanagement_processor_free(cm_processor);
1992 /*********************** Public display buffers interfaces *************************/
1994 /* acquire display buffer for given image buffer using specified view and display settings */
1995 unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
1996 const ColorManagedDisplaySettings *display_settings, void **cache_handle)
1998 unsigned char *display_buffer;
2000 ColormanageCacheViewSettings cache_view_settings;
2001 ColormanageCacheDisplaySettings cache_display_settings;
2002 ColorManagedViewSettings default_view_settings;
2003 const ColorManagedViewSettings *applied_view_settings;
2005 *cache_handle = NULL;
2007 if (!ibuf->x || !ibuf->y)
2010 if (view_settings) {
2011 applied_view_settings = view_settings;
2014 /* if no view settings were specified, use default display transformation
2015 * this happens for images which don't want to be displayed with render settings
2018 init_default_view_settings(display_settings, &default_view_settings);
2019 applied_view_settings = &default_view_settings;
2022 /* early out: no float buffer and byte buffer is already in display space,
2025 if (ibuf->rect_float == NULL && ibuf->rect_colorspace && ibuf->channels == 4) {
2026 if (is_ibuf_rect_in_display_space(ibuf, applied_view_settings, display_settings))
2027 return (unsigned char *) ibuf->rect;
2030 colormanage_view_settings_to_cache(ibuf, &cache_view_settings, applied_view_settings);
2031 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
2033 if (ibuf->invalid_rect.xmin != ibuf->invalid_rect.xmax) {
2034 if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
2035 IMB_partial_display_buffer_update_threaded(ibuf,
2037 (unsigned char *) ibuf->rect,
2040 applied_view_settings,
2042 ibuf->invalid_rect.xmin,
2043 ibuf->invalid_rect.ymin,
2044 ibuf->invalid_rect.xmax,
2045 ibuf->invalid_rect.ymax,
2049 BLI_rcti_init(&ibuf->invalid_rect, 0, 0, 0, 0);
2052 BLI_lock_thread(LOCK_COLORMANAGE);
2054 /* ensure color management bit fields exists */
2055 if (!ibuf->display_buffer_flags) {
2056 ibuf->display_buffer_flags = MEM_callocN(sizeof(unsigned int) * global_tot_display, "imbuf display_buffer_flags");
2058 else if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) {
2059 /* all display buffers were marked as invalid from other areas,
2060 * now propagate this flag to internal color management routines
2062 memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
2064 ibuf->userflags &= ~IB_DISPLAY_BUFFER_INVALID;
2067 display_buffer = colormanage_cache_get(ibuf, &cache_view_settings, &cache_display_settings, cache_handle);
2069 if (display_buffer) {
2070 BLI_unlock_thread(LOCK_COLORMANAGE);
2071 return display_buffer;
2074 buffer_size = DISPLAY_BUFFER_CHANNELS * ((size_t)ibuf->x) * ibuf->y * sizeof(char);
2075 display_buffer = MEM_callocN(buffer_size, "imbuf display buffer");
2077 colormanage_display_buffer_process(ibuf, display_buffer, applied_view_settings, display_settings);
2079 colormanage_cache_put(ibuf, &cache_view_settings, &cache_display_settings, display_buffer, cache_handle);
2081 BLI_unlock_thread(LOCK_COLORMANAGE);
2083 return display_buffer;
2086 /* same as IMB_display_buffer_acquire but gets view and display settings from context */
2087 unsigned char *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle)
2089 ColorManagedViewSettings *view_settings;
2090 ColorManagedDisplaySettings *display_settings;
2092 IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
2094 return IMB_display_buffer_acquire(ibuf, view_settings, display_settings, cache_handle);
2097 void IMB_display_buffer_transform_apply(unsigned char *display_buffer, float *linear_buffer, int width, int height,
2098 int channels, const ColorManagedViewSettings *view_settings,
2099 const ColorManagedDisplaySettings *display_settings, bool predivide)
2102 ColormanageProcessor *cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2104 buffer = MEM_mallocN((size_t)channels * width * height * sizeof(float), "display transform temp buffer");
2105 memcpy(buffer, linear_buffer, (size_t)channels * width * height * sizeof(float));
2107 IMB_colormanagement_processor_apply(cm_processor, buffer, width, height, channels, predivide);
2109 IMB_colormanagement_processor_free(cm_processor);
2111 IMB_buffer_byte_from_float(display_buffer, buffer, channels, 0.0f, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
2112 false, width, height, width, width);
2117 void IMB_display_buffer_release(void *cache_handle)
2120 BLI_lock_thread(LOCK_COLORMANAGE);
2122 colormanage_cache_handle_release(cache_handle);
2124 BLI_unlock_thread(LOCK_COLORMANAGE);
2128 /*********************** Display functions *************************/
2130 const char *colormanage_display_get_default_name(void)
2132 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
2133 const char *display_name;
2135 display_name = OCIO_configGetDefaultDisplay(config);
2137 OCIO_configRelease(config);
2139 return display_name;
2142 ColorManagedDisplay *colormanage_display_get_default(void)
2144 const char *display_name = colormanage_display_get_default_name();
2146 if (display_name[0] == '\0')
2149 return colormanage_display_get_named(display_name);
2152 ColorManagedDisplay *colormanage_display_add(const char *name)
2154 ColorManagedDisplay *display;
2157 if (global_displays.last) {
2158 ColorManagedDisplay *last_display = global_displays.last;
2160 index = last_display->index;
2163 display = MEM_callocN(sizeof(ColorManagedDisplay), "ColorManagedDisplay");
2165 display->index = index + 1;
2167 BLI_strncpy(display->name, name, sizeof(display->name));
2169 BLI_addtail(&global_displays, display);
2174 ColorManagedDisplay *colormanage_display_get_named(const char *name)
2176 ColorManagedDisplay *display;
2178 for (display = global_displays.first; display; display = display->next) {
2179 if (STREQ(display->name, name))
2186 ColorManagedDisplay *colormanage_display_get_indexed(int index)
2188 /* display indices are 1-based */
2189 return BLI_findlink(&global_displays, index - 1);
2192 int IMB_colormanagement_display_get_named_index(const char *name)
2194 ColorManagedDisplay *display;
2196 display = colormanage_display_get_named(name);
2199 return display->index;
2205 const char *IMB_colormanagement_display_get_indexed_name(int index)
2207 ColorManagedDisplay *display;
2209 display = colormanage_display_get_indexed(index);
2212 return display->name;
2218 const char *IMB_colormanagement_display_get_default_name(void)
2220 ColorManagedDisplay *display = colormanage_display_get_default();
2222 return display->name;
2225 /* used by performance-critical pixel processing areas, such as color widgets */
2226 ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name)
2228 return colormanage_display_get_named(name);
2231 const char *IMB_colormanagement_display_get_none_name(void)
2233 if (colormanage_display_get_named("None") != NULL)
2236 return colormanage_display_get_default_name();
2239 /*********************** View functions *************************/
2241 const char *colormanage_view_get_default_name(const ColorManagedDisplay *display)
2243 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
2246 name = OCIO_configGetDefaultView(config, display->name);
2248 OCIO_configRelease(config);
2253 ColorManagedView *colormanage_view_get_default(const ColorManagedDisplay *display)
2255 const char *name = colormanage_view_get_default_name(display);
2257 if (!name || name[0] == '\0')
2260 return colormanage_view_get_named(name);
2263 ColorManagedView *colormanage_view_add(const char *name)
2265 ColorManagedView *view;
2266 int index = global_tot_view;
2268 view = MEM_callocN(sizeof(ColorManagedView), "ColorManagedView");
2269 view->index = index + 1;
2270 BLI_strncpy(view->name, name, sizeof(view->name));
2272 BLI_addtail(&global_views, view);
2279 ColorManagedView *colormanage_view_get_named(const char *name)
2281 ColorManagedView *view;
2283 for (view = global_views.first; view; view = view->next) {
2284 if (STREQ(view->name, name))
2291 ColorManagedView *colormanage_view_get_indexed(int index)
2293 /* view transform indices are 1-based */
2294 return BLI_findlink(&global_views, index - 1);
2297 int IMB_colormanagement_view_get_named_index(const char *name)
2299 ColorManagedView *view = colormanage_view_get_named(name);
2308 const char *IMB_colormanagement_view_get_indexed_name(int index)
2310 ColorManagedView *view = colormanage_view_get_indexed(index);
2319 const char *IMB_colormanagement_view_get_default_name(const char *display_name)
2321 ColorManagedDisplay *display = colormanage_display_get_named(display_name);
2322 ColorManagedView *view = NULL;
2325 view = colormanage_view_get_default(display);
2333 /*********************** Color space functions *************************/
2335 static void colormanage_description_strip(char *description)
2339 for (i = (int)strlen(description) - 1; i >= 0; i--) {
2340 if (ELEM(description[i], '\r', '\n')) {
2341 description[i] = '\0';
2348 for (i = 0, n = strlen(description); i < n; i++) {
2349 if (ELEM(description[i], '\r', '\n')) {
2350 description[i] = ' ';
2355 ColorSpace *colormanage_colorspace_add(const char *name, const char *description, bool is_invertible, bool is_data)
2357 ColorSpace *colorspace, *prev_space;
2360 colorspace = MEM_callocN(sizeof(ColorSpace), "ColorSpace");
2362 BLI_strncpy(colorspace->name, name, sizeof(colorspace->name));
2365 BLI_strncpy(colorspace->description, description, sizeof(colorspace->description));
2367 colormanage_description_strip(colorspace->description);
2370 colorspace->is_invertible = is_invertible;
2371 colorspace->is_data = is_data;
2373 for (prev_space = global_colorspaces.first; prev_space; prev_space = prev_space->next) {
2374 if (BLI_strcasecmp(prev_space->name, colorspace->name) > 0)
2377 prev_space->index = counter++;
2381 BLI_addtail(&global_colorspaces, colorspace);
2383 BLI_insertlinkbefore(&global_colorspaces, prev_space, colorspace);
2385 colorspace->index = counter++;
2386 for (; prev_space; prev_space = prev_space->next) {
2387 prev_space->index = counter++;
2390 global_tot_colorspace++;
2395 ColorSpace *colormanage_colorspace_get_named(const char *name)
2397 ColorSpace *colorspace;
2399 for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) {
2400 if (STREQ(colorspace->name, name))
2407 ColorSpace *colormanage_colorspace_get_roled(int role)
2409 const char *role_colorspace = IMB_colormanagement_role_colorspace_name_get(role);
2411 return colormanage_colorspace_get_named(role_colorspace);
2414 ColorSpace *colormanage_colorspace_get_indexed(int index)
2416 /* color space indices are 1-based */
2417 return BLI_findlink(&global_colorspaces, index - 1);
2420 int IMB_colormanagement_colorspace_get_named_index(const char *name)
2422 ColorSpace *colorspace;
2424 colorspace = colormanage_colorspace_get_named(name);
2427 return colorspace->index;
2433 const char *IMB_colormanagement_colorspace_get_indexed_name(int index)
2435 ColorSpace *colorspace;
2437 colorspace = colormanage_colorspace_get_indexed(index);
2440 return colorspace->name;
2446 void IMB_colormanagment_colorspace_from_ibuf_ftype(ColorManagedColorspaceSettings *colorspace_settings, ImBuf *ibuf)
2448 const ImFileType *type;
2450 for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
2451 if (type->save && type->ftype(type, ibuf)) {
2452 const char *role_colorspace;
2454 role_colorspace = IMB_colormanagement_role_colorspace_name_get(type->default_save_role);
2456 BLI_strncpy(colorspace_settings->name, role_colorspace, sizeof(colorspace_settings->name));
2461 /*********************** Looks functions *************************/
2463 ColorManagedLook *colormanage_look_add(const char *name, const char *process_space, bool is_noop)
2465 ColorManagedLook *look;
2466 int index = global_tot_looks;
2468 look = MEM_callocN(sizeof(ColorManagedLook), "ColorManagedLook");
2469 look->index = index + 1;
2470 BLI_strncpy(look->name, name, sizeof(look->name));
2471 BLI_strncpy(look->process_space, process_space, sizeof(look->process_space));
2472 look->is_noop = is_noop;
2474 BLI_addtail(&global_looks, look);
2481 ColorManagedLook *colormanage_look_get_named(const char *name)
2483 ColorManagedLook *look;
2485 for (look = global_looks.first; look; look = look->next) {
2486 if (STREQ(look->name, name)) {
2494 ColorManagedLook *colormanage_look_get_indexed(int index)
2496 /* look indices are 1-based */
2497 return BLI_findlink(&global_looks, index - 1);
2500 int IMB_colormanagement_look_get_named_index(const char *name)
2502 ColorManagedLook *look;
2504 look = colormanage_look_get_named(name);
2513 const char *IMB_colormanagement_look_get_indexed_name(int index)
2515 ColorManagedLook *look;
2517 look = colormanage_look_get_indexed(index);
2526 /*********************** RNA helper functions *************************/
2528 void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totitem)
2530 ColorManagedDisplay *display;
2532 for (display = global_displays.first; display; display = display->next) {
2533 EnumPropertyItem item;
2535 item.value = display->index;
2536 item.name = display->name;
2537 item.identifier = display->name;
2539 item.description = "";
2541 RNA_enum_item_add(items, totitem, &item);
2545 static void colormanagement_view_item_add(EnumPropertyItem **items, int *totitem, ColorManagedView *view)
2547 EnumPropertyItem item;
2549 item.value = view->index;
2550 item.name = view->name;
2551 item.identifier = view->name;
2553 item.description = "";
2555 RNA_enum_item_add(items, totitem, &item);
2558 void IMB_colormanagement_view_items_add(EnumPropertyItem **items, int *totitem, const char *display_name)
2560 ColorManagedDisplay *display = colormanage_display_get_named(display_name);
2561 ColorManagedView *view;
2564 LinkData *display_view;
2566 for (display_view = display->views.first; display_view; display_view = display_view->next) {
2567 view = display_view->data;
2569 colormanagement_view_item_add(items, totitem, view);
2574 void IMB_colormanagement_look_items_add(struct EnumPropertyItem **items, int *totitem)
2576 ColorManagedLook *look;
2578 for (look = global_looks.first; look; look = look->next) {
2579 EnumPropertyItem item;
2581 item.value = look->index;
2582 item.name = look->name;
2583 item.identifier = look->name;
2585 item.description = "";
2587 RNA_enum_item_add(items, totitem, &item);
2591 void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *totitem)
2593 ColorSpace *colorspace;
2595 for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) {
2596 EnumPropertyItem item;
2598 if (!colorspace->is_invertible)
2601 item.value = colorspace->index;
2602 item.name = colorspace->name;
2603 item.identifier = colorspace->name;
2605 item.description = colorspace->description;
2607 RNA_enum_item_add(items, totitem, &item);
2611 /*********************** Partial display buffer update *************************/
2614 * Partial display update is supposed to be used by such areas as
2615 * compositor and renderer, This areas are calculating tiles of the
2616 * images and because of performance reasons only this tiles should
2618 * This gives nice visual feedback without slowing things down.
2620 * Updating happens for active display transformation only, all
2621 * the rest buffers would be marked as dirty
2624 static void partial_buffer_update_rect(ImBuf *ibuf,
2625 unsigned char *display_buffer,
2626 const float *linear_buffer,
2627 const unsigned char *byte_buffer,
2630 int linear_offset_x, int linear_offset_y,
2631 ColormanageProcessor *cm_processor,
2632 const int xmin, const int ymin,
2633 const int xmax, const int ymax)
2636 int channels = ibuf->channels;
2637 float dither = ibuf->dither;
2638 ColorSpace *rect_colorspace = ibuf->rect_colorspace;
2639 float *display_buffer_float = NULL;
2640 const int width = xmax - xmin;
2641 const int height = ymax - ymin;
2642 bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
2644 if (dither != 0.0f) {
2645 /* cm_processor is NULL in cases byte_buffer's space matches display
2647 * in this case we could skip extra transform and only apply dither
2648 * use 4 channels for easier byte->float->byte conversion here so
2649 * (this is only needed to apply dither, in other cases we'll convert
2650 * byte buffer to display directly)
2655 display_buffer_float = MEM_callocN((size_t)channels * width * height * sizeof(float), "display buffer for dither");
2659 for (y = ymin; y < ymax; y++) {
2660 for (x = xmin; x < xmax; x++) {
2661 size_t display_index = ((size_t)y * display_stride + x) * 4;
2662 size_t linear_index = ((size_t)(y - linear_offset_y) * linear_stride + (x - linear_offset_x)) * channels;
2665 if (linear_buffer) {
2666 if (channels == 4) {
2667 copy_v4_v4(pixel, (float *) linear_buffer + linear_index);
2669 else if (channels == 3) {
2670 copy_v3_v3(pixel, (float *) linear_buffer + linear_index);
2673 else if (channels == 1) {
2674 pixel[0] = linear_buffer[linear_index];
2677 BLI_assert(!"Unsupported number of channels in partial buffer update");
2680 else if (byte_buffer) {
2681 rgba_uchar_to_float(pixel, byte_buffer + linear_index);
2682 IMB_colormanagement_colorspace_to_scene_linear_v3(pixel, rect_colorspace);
2683 straight_to_premul_v4(pixel);
2687 IMB_colormanagement_processor_apply_pixel(cm_processor, pixel, channels);
2690 if (display_buffer_float) {
2691 size_t index = ((size_t)(y - ymin) * width + (x - xmin)) * channels;
2693 if (channels == 4) {
2694 copy_v4_v4(display_buffer_float + index, pixel);
2696 else if (channels == 3) {
2697 copy_v3_v3(display_buffer_float + index, pixel);
2699 else /* if (channels == 1) */ {
2700 display_buffer_float[index] = pixel[0];
2704 if (channels == 4) {
2705 float pixel_straight[4];
2706 premul_to_straight_v4_v4(pixel_straight, pixel);
2707 rgba_float_to_uchar(display_buffer + display_index, pixel_straight);
2709 else if (channels == 3) {
2710 rgb_float_to_uchar(display_buffer + display_index, pixel);
2711 display_buffer[display_index + 3] = 255;
2713 else /* if (channels == 1) */ {
2714 display_buffer[display_index] =
2715 display_buffer[display_index + 1] =
2716 display_buffer[display_index + 2] =
2717 display_buffer[display_index + 3] = FTOCHAR(pixel[0]);
2724 if (display_buffer_float) {
2725 /* huh, for dither we need float buffer first, no cheaper way. currently */
2726 IMB_buffer_float_from_byte(display_buffer_float, byte_buffer,
2727 IB_PROFILE_SRGB, IB_PROFILE_SRGB, true,
2728 width, height, width, display_stride);
2733 for (i = ymin; i < ymax; i++) {
2734 size_t byte_offset = ((size_t)linear_stride * i + xmin) * 4;
2735 size_t display_offset = ((size_t)display_stride * i + xmin) * 4;
2737 memcpy(display_buffer + display_offset, byte_buffer + byte_offset, 4 * sizeof(char) * width);
2742 if (display_buffer_float) {
2743 size_t display_index = ((size_t)ymin * display_stride + xmin) * channels;
2745 IMB_buffer_byte_from_float(display_buffer + display_index, display_buffer_float, channels, dither,
2746 IB_PROFILE_SRGB, IB_PROFILE_SRGB, true, width, height, display_stride, width);
2748 MEM_freeN(display_buffer_float);
2752 typedef struct PartialThreadData {
2754 unsigned char *display_buffer;
2755 const float *linear_buffer;
2756 const unsigned char *byte_buffer;
2759 int linear_offset_x, linear_offset_y;
2760 ColormanageProcessor *cm_processor;
2761 int xmin, ymin, xmax;
2762 } PartialThreadData;
2764 static void partial_buffer_update_rect_thread_do(void *data_v,
2768 PartialThreadData *data = (PartialThreadData *)data_v;
2769 int ymin = data->ymin + start_scanline;
2770 partial_buffer_update_rect(data->ibuf,
2771 data->display_buffer,
2772 data->linear_buffer,
2774 data->display_stride,
2775 data->linear_stride,
2776 data->linear_offset_x,
2777 data->linear_offset_y,
2782 ymin + num_scanlines);
2785 static void imb_partial_display_buffer_update_ex(ImBuf *ibuf,
2786 const float *linear_buffer,
2787 const unsigned char *byte_buffer,
2789 int offset_x, int offset_y,
2790 const ColorManagedViewSettings *view_settings,
2791 const ColorManagedDisplaySettings *display_settings,
2794 bool copy_display_to_byte_buffer,
2797 ColormanageCacheViewSettings cache_view_settings;
2798 ColormanageCacheDisplaySettings cache_display_settings;
2799 void *cache_handle = NULL;
2800 unsigned char *display_buffer = NULL;
2801 int buffer_width = ibuf->x;
2803 if (ibuf->display_buffer_flags) {
2804 int view_flag, display_index;
2806 colormanage_view_settings_to_cache(ibuf, &cache_view_settings, view_settings);
2807 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
2809 view_flag = 1 << (cache_view_settings.view - 1);
2810 display_index = cache_display_settings.display - 1;
2812 BLI_lock_thread(LOCK_COLORMANAGE);
2814 if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
2815 display_buffer = colormanage_cache_get(ibuf,
2816 &cache_view_settings,
2817 &cache_display_settings,
2821 /* In some rare cases buffer's dimension could be changing directly from
2823 * this i.e. happens when image editor acquires render result
2825 buffer_width = ibuf->x;
2827 /* Mark all other buffers as invalid. */
2828 memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
2829 ibuf->display_buffer_flags[display_index] |= view_flag;
2831 BLI_unlock_thread(LOCK_COLORMANAGE);
2834 if (display_buffer == NULL) {
2835 if (copy_display_to_byte_buffer) {
2836 display_buffer = (unsigned char *) ibuf->rect;
2840 if (display_buffer) {
2841 ColormanageProcessor *cm_processor = NULL;
2842 bool skip_transform = false;
2844 /* Byte buffer is assumed to be in imbuf's rect space, so if byte buffer
2845 * is known we could skip display->linear->display conversion in case
2846 * display color space matches imbuf's rect space.
2848 * But if there's a float buffer it's likely operation was performed on
2849 * it first and byte buffer is likely to be out of date here.
2851 if (linear_buffer == NULL && byte_buffer != NULL) {
2852 skip_transform = is_ibuf_rect_in_display_space(ibuf,
2857 if (!skip_transform) {
2858 cm_processor = IMB_colormanagement_display_processor_new(
2859 view_settings, display_settings);
2863 PartialThreadData data;
2865 data.display_buffer = display_buffer;
2866 data.linear_buffer = linear_buffer;
2867 data.byte_buffer = byte_buffer;
2868 data.display_stride = buffer_width;
2869 data.linear_stride = stride;
2870 data.linear_offset_x = offset_x;
2871 data.linear_offset_y = offset_y;
2872 data.cm_processor = cm_processor;
2876 IMB_processor_apply_threaded_scanlines(
2877 ymax - ymin, partial_buffer_update_rect_thread_do, &data);
2880 partial_buffer_update_rect(ibuf,
2881 display_buffer, linear_buffer, byte_buffer,
2886 xmin, ymin, xmax, ymax);
2890 IMB_colormanagement_processor_free(cm_processor);
2893 IMB_display_buffer_release(cache_handle);
2896 if (copy_display_to_byte_buffer && (unsigned char *) ibuf->rect != display_buffer) {
2898 for (y = ymin; y < ymax; y++) {
2899 size_t index = (size_t)y * buffer_width * 4;
2900 memcpy((unsigned char *)ibuf->rect + index,
2901 display_buffer + index,
2902 (size_t)(xmax - xmin) * 4);
2907 void IMB_partial_display_buffer_update(ImBuf *ibuf,
2908 const float *linear_buffer,
2909 const unsigned char *byte_buffer,
2911 int offset_x, int offset_y,
2912 const ColorManagedViewSettings *view_settings,
2913 const ColorManagedDisplaySettings *display_settings,
2916 bool copy_display_to_byte_buffer)
2918 imb_partial_display_buffer_update_ex(ibuf,
2927 copy_display_to_byte_buffer,
2932 void IMB_partial_display_buffer_update_threaded(struct ImBuf *ibuf,
2933 const float *linear_buffer,
2934 const unsigned char *byte_buffer,
2936 int offset_x, int offset_y,
2937 const struct ColorManagedViewSettings *view_settings,
2938 const struct ColorManagedDisplaySettings *display_settings,
2939 int xmin, int ymin, int xmax, int ymax,
2940 bool copy_display_to_byte_buffer)
2942 int width = xmax - xmin;
2943 int height = ymax - ymin;
2944 bool do_threads = (((size_t)width) * height >= 64 * 64);
2945 imb_partial_display_buffer_update_ex(ibuf,
2954 copy_display_to_byte_buffer,
2958 void IMB_partial_display_buffer_update_delayed(ImBuf *ibuf, int xmin, int ymin, int xmax, int ymax)
2960 if (ibuf->invalid_rect.xmin == ibuf->invalid_rect.xmax) {
2961 BLI_rcti_init(&ibuf->invalid_rect, xmin, xmax, ymin, ymax);
2965 BLI_rcti_init(&rect, xmin, xmax, ymin, ymax);
2966 BLI_rcti_union(&ibuf->invalid_rect, &rect);
2970 /*********************** Pixel processor functions *************************/
2972 ColormanageProcessor *IMB_colormanagement_display_processor_new(const ColorManagedViewSettings *view_settings,
2973 const ColorManagedDisplaySettings *display_settings)
2975 ColormanageProcessor *cm_processor;
2976 ColorManagedViewSettings default_view_settings;
2977 const ColorManagedViewSettings *applied_view_settings;
2978 ColorSpace *display_space;
2980 cm_processor = MEM_callocN(sizeof(ColormanageProcessor), "colormanagement processor");
2982 if (view_settings) {
2983 applied_view_settings = view_settings;
2986 init_default_view_settings(display_settings, &default_view_settings);
2987 applied_view_settings = &default_view_settings;
2990 display_space = display_transform_get_colorspace(applied_view_settings, display_settings);
2992 cm_processor->is_data_result = display_space->is_data;
2994 cm_processor->processor = create_display_buffer_processor(applied_view_settings->look,
2995 applied_view_settings->view_transform,
2996 display_settings->display_device,
2997 applied_view_settings->exposure,
2998 applied_view_settings->gamma,
2999 global_role_scene_linear);
3001 if (applied_view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) {
3002 cm_processor->curve_mapping = curvemapping_copy(applied_view_settings->curve_mapping);
3003 curvemapping_premultiply(cm_processor->curve_mapping, false);
3006 return cm_processor;
3009 ColormanageProcessor *IMB_colormanagement_colorspace_processor_new(const char *from_colorspace, const char *to_colorspace)
3011 ColormanageProcessor *cm_processor;
3012 ColorSpace *color_space;
3014 cm_processor = MEM_callocN(sizeof(ColormanageProcessor), "colormanagement processor");
3016 color_space = colormanage_colorspace_get_named(to_colorspace);
3017 cm_processor->is_data_result = color_space->is_data;
3019 cm_processor->processor = create_colorspace_transform_processor(from_colorspace, to_colorspace);
3021 return cm_processor;
3024 void IMB_colormanagement_processor_apply_v4(ColormanageProcessor *cm_processor, float pixel[4])
3026 if (cm_processor->curve_mapping)
3027 curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
3029 if (cm_processor->processor)
3030 OCIO_processorApplyRGBA(cm_processor->processor, pixel);
3033 void IMB_colormanagement_processor_apply_v4_predivide(ColormanageProcessor *cm_processor, float pixel[4])
3035 if (cm_processor->curve_mapping)
3036 curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
3038 if (cm_processor->processor)
3039 OCIO_processorApplyRGBA_predivide(cm_processor->processor, pixel);
3042 void IMB_colormanagement_processor_apply_v3(ColormanageProcessor *cm_processor, float pixel[3])
3044 if (cm_processor->curve_mapping)
3045 curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
3047 if (cm_processor->processor)
3048 OCIO_processorApplyRGB(cm_processor->processor, pixel);
3051 void IMB_colormanagement_processor_apply_pixel(struct ColormanageProcessor *cm_processor, float *pixel, int channels)
3053 if (channels == 4) {
3054 IMB_colormanagement_processor_apply_v4_predivide(cm_processor, pixel);
3056 else if (channels == 3) {
3057 IMB_colormanagement_processor_apply_v3(cm_processor, pixel);
3059 else if (channels == 1) {
3060 if (cm_processor->curve_mapping) {
3061 curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, 1);
3065 BLI_assert(!"Incorrect number of channels passed to IMB_colormanagement_processor_apply_pixel");
3069 void IMB_colormanagement_processor_apply(ColormanageProcessor *cm_processor, float *buffer, int width, int height,
3070 int channels, bool predivide)
3072 /* apply curve mapping */
3073 if (cm_processor->curve_mapping) {
3076 for (y = 0; y < height; y++) {
3077 for (x = 0; x < width; x++) {
3078 float *pixel = buffer + channels * (((size_t)y) * width + x);
3080 curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, channels);
3085 if (cm_processor->processor && channels >= 3) {
3086 OCIO_PackedImageDesc *img;
3088 /* apply OCIO processor */
3089 img = OCIO_createOCIO_PackedImageDesc(
3090 buffer, width, height, channels, sizeof(float),
3091 (size_t)channels * sizeof(float),
3092 (size_t)channels * sizeof(float) * width);
3095 OCIO_processorApply_predivide(cm_processor->processor, img);
3097 OCIO_processorApply(cm_processor->processor, img);
3099 OCIO_PackedImageDescRelease(img);
3103 void IMB_colormanagement_processor_free(ColormanageProcessor *cm_processor)
3105 if (cm_processor->curve_mapping)
3106 curvemapping_free(cm_processor->curve_mapping);
3107 if (cm_processor->processor)
3108 OCIO_processorRelease(cm_processor->processor);
3110 MEM_freeN(cm_processor);
3113 /* **** OpenGL drawing routines using GLSL for color space transform ***** */
3115 static bool check_glsl_display_processor_changed(const ColorManagedViewSettings *view_settings,
3116 const ColorManagedDisplaySettings *display_settings,
3117 const char *from_colorspace)
3119 return !(global_glsl_state.exposure == view_settings->exposure &&
3120 global_glsl_state.gamma == view_settings->gamma &&
3121 STREQ(global_glsl_state.look, view_settings->look) &&
3122 STREQ(global_glsl_state.view, view_settings->view_transform) &&
3123 STREQ(global_glsl_state.display, display_settings->display_device) &&
3124 STREQ(global_glsl_state.input, from_colorspace));
3127 static void curve_mapping_to_ocio_settings(CurveMapping *curve_mapping,
3128 OCIO_CurveMappingSettings *curve_mapping_settings)
3132 curvemapping_initialize(curve_mapping);
3133 curvemapping_premultiply(curve_mapping, false);
3134 curvemapping_table_RGBA(curve_mapping,
3135 &curve_mapping_settings->lut,
3136 &curve_mapping_settings->lut_size);
3138 for (i = 0; i < 4; i++) {
3139 CurveMap *cuma = curve_mapping->cm + i;
3140 curve_mapping_settings->use_extend_extrapolate[i] = (cuma->flag & CUMA_EXTEND_EXTRAPOLATE) != 0;
3141 curve_mapping_settings->range[i] = cuma->range;
3142 curve_mapping_settings->mintable[i] = cuma->mintable;
3143 curve_mapping_settings->ext_in_x[i] = cuma->ext_in[0];
3144 curve_mapping_settings->ext_in_y[i] = cuma->ext_in[1];
3145 curve_mapping_settings->ext_out_x[i] = cuma->ext_out[0];
3146 curve_mapping_settings->ext_out_y[i] = cuma->ext_out[1];
3147 curve_mapping_settings->first_x[i] = cuma->table[0].x;
3148 curve_mapping_settings->first_y[i] = cuma->table[0].y;
3149 curve_mapping_settings->last_x[i] = cuma->table[CM_TABLE].x;
3150 curve_mapping_settings->last_y[i] = cuma->table[CM_TABLE].y;
3153 copy_v3_v3(curve_mapping_settings->black, curve_mapping->black);
3154 copy_v3_v3(curve_mapping_settings->bwmul, curve_mapping->bwmul);
3156 curve_mapping_settings->cache_id = (size_t) curve_mapping;
3159 static void update_glsl_display_processor(const ColorManagedViewSettings *view_settings,
3160 const ColorManagedDisplaySettings *display_settings,
3161 const char *from_colorspace)
3163 bool use_curve_mapping = (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) != 0;
3164 bool need_update = false;
3166 need_update = global_glsl_state.processor == NULL ||
3167 check_glsl_display_processor_changed(view_settings, display_settings, from_colorspace) ||
3168 use_curve_mapping != global_glsl_state.use_curve_mapping;
3170 if (use_curve_mapping && need_update == false) {
3171 need_update |= view_settings->curve_mapping->changed_timestamp != global_glsl_state.curve_mapping_timestamp ||
3172 view_settings->curve_mapping != global_glsl_state.orig_curve_mapping;
3175 /* Update state if there's no processor yet or
3176 * processor settings has been changed.
3179 OCIO_CurveMappingSettings *curve_mapping_settings = &global_glsl_state.curve_mapping_settings;
3180 CurveMapping *new_curve_mapping = NULL;
3182 /* Store settings of processor for further comparison. */
3183 BLI_strncpy(global_glsl_state.look, view_settings->look, MAX_COLORSPACE_NAME);
3184 BLI_strncpy(global_glsl_state.view, view_settings->view_transform, MAX_COLORSPACE_NAME);
3185 BLI_strncpy(global_glsl_state.display, display_settings->display_device, MAX_COLORSPACE_NAME);
3186 BLI_strncpy(global_glsl_state.input, from_colorspace, MAX_COLORSPACE_NAME);
3187 global_glsl_state.exposure = view_settings->exposure;
3188 global_glsl_state.gamma = view_settings->gamma;
3190 /* We're using curve mapping's address as a cache ID,
3191 * so we need to make sure re-allocation gives new address here.
3192 * We do this by allocating new curve mapping before freeing ol one.
3194 if (use_curve_mapping) {
3195 new_curve_mapping = curvemapping_copy(view_settings->curve_mapping);
3198 if (global_glsl_state.curve_mapping) {
3199 curvemapping_free(global_glsl_state.curve_mapping);
3200 MEM_freeN(curve_mapping_settings->lut);
3201 global_glsl_state.curve_mapping = NULL;
3202 curve_mapping_settings->lut = NULL;
3205 /* Fill in OCIO's curve mapping settings. */
3206 if (use_curve_mapping) {
3207 curve_mapping_to_ocio_settings(new_curve_mapping, &global_glsl_state.curve_mapping_settings);
3209 global_glsl_state.curve_mapping = new_curve_mapping;
3210 global_glsl_state.curve_mapping_timestamp = view_settings->curve_mapping->changed_timestamp;
3211 global_glsl_state.orig_curve_mapping = view_settings->curve_mapping;
3212 global_glsl_state.use_curve_mapping = true;
3215 global_glsl_state.orig_curve_mapping = NULL;
3216 global_glsl_state.use_curve_mapping = false;
3219 /* Free old processor, if any. */
3220 if (global_glsl_state.processor)
3221 OCIO_processorRelease(global_glsl_state.processor);
3223 /* We're using display OCIO processor, no RGB curves yet. */
3224 global_glsl_state.processor =
3225 create_display_buffer_processor(global_glsl_state.look,
3226 global_glsl_state.view,
3227 global_glsl_state.display,