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 #include "IMB_colormanagement.h"
32 #include "IMB_colormanagement_intern.h"
37 #include "DNA_color_types.h"
38 #include "DNA_image_types.h"
39 #include "DNA_movieclip_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_screen_types.h"
42 #include "DNA_space_types.h"
43 #include "DNA_windowmanager_types.h"
45 #include "IMB_filter.h"
46 #include "IMB_imbuf.h"
47 #include "IMB_imbuf_types.h"
48 #include "IMB_filetype.h"
49 #include "IMB_moviecache.h"
51 #include "MEM_guardedalloc.h"
53 #include "BLI_blenlib.h"
54 #include "BLI_fileops.h"
56 #include "BLI_math_color.h"
57 #include "BLI_path_util.h"
58 #include "BLI_string.h"
59 #include "BLI_threads.h"
61 #include "BKE_colortools.h"
62 #include "BKE_context.h"
63 #include "BKE_image.h"
64 #include "BKE_utildefines.h"
67 #include "RNA_define.h"
69 #include <ocio_capi.h>
71 /*********************** Global declarations *************************/
73 #define MAX_COLORSPACE_NAME 64
74 #define DISPLAY_BUFFER_CHANNELS 4
76 /* ** list of all supported color spaces, displays and views */
77 static char global_role_scene_linear[MAX_COLORSPACE_NAME];
78 static char global_role_color_picking[MAX_COLORSPACE_NAME];
79 static char global_role_texture_painting[MAX_COLORSPACE_NAME];
80 static char global_role_default_byte[MAX_COLORSPACE_NAME];
81 static char global_role_default_float[MAX_COLORSPACE_NAME];
82 static char global_role_default_sequencer[MAX_COLORSPACE_NAME];
84 static ListBase global_colorspaces = {NULL};
85 static ListBase global_displays = {NULL};
86 static ListBase global_views = {NULL};
88 static int global_tot_colorspace = 0;
89 static int global_tot_display = 0;
90 static int global_tot_view = 0;
92 /* lock used by pre-cached processors getters, so processor wouldn't
93 * be created several times
94 * LOCK_COLORMANAGE can not be used since this mutex could be needed to
95 * be locked before pre-cached processor are creating
97 static pthread_mutex_t processor_lock = BLI_MUTEX_INITIALIZER;
99 typedef struct ColormanageProcessor {
100 OCIO_ConstProcessorRcPtr *processor;
101 CurveMapping *curve_mapping;
103 } ColormanageProcessor;
105 /*********************** Color managed cache *************************/
107 /* Cache Implementation Notes
108 * ==========================
110 * All color management cache stuff is stored in two properties of
113 * 1. display_buffer_flags
115 * This is a bit field which used to mark calculated transformations
116 * for particular image buffer. Index inside of this array means index
117 * of a color managed display. Element with given index matches view
118 * transformations applied for a given display. So if bit B of array
119 * element B is set to 1, this means display buffer with display index
120 * of A and view transform of B was ever calculated for this imbuf.
122 * In contrast with indices in global lists of displays and views this
123 * indices are 0-based, not 1-based. This is needed to save some bytes
126 * 2. colormanage_cache
128 * This is a pointer to a structure which holds all data which is
129 * needed for color management cache to work.
131 * It contains two parts:
135 * Data field is used to store additional information about cached
136 * buffers which affects on whether cached buffer could be used.
137 * This data can't go to cache key because changes in this data
138 * shouldn't lead extra buffers adding to cache, it shall
139 * invalidate cached images.
141 * Currently such a data contains only exposure and gamma, but
142 * would likely extended further.
144 * data field is not null only for elements of cache, not used for
145 * original image buffers.
147 * Color management cache is using generic MovieCache implementation
148 * to make it easier to deal with memory limitation.
150 * Currently color management is using the same memory limitation
151 * pool as sequencer and clip editor are using which means color
152 * managed buffers would be removed from the cache as soon as new
153 * frames are loading for the movie clip and there's no space in
156 * Every image buffer has got own movie cache instance, which
157 * means keys for color managed buffers could be really simple
158 * and look up in this cache would be fast and independent from
159 * overall amount of color managed images.
162 /* NOTE: ColormanageCacheViewSettings and ColormanageCacheDisplaySettings are
163 * quite the same as ColorManagedViewSettings and ColorManageDisplaySettings
164 * but they holds indexes of all transformations and color spaces, not
167 * This helps avoid extra colorspace / display / view lookup without
168 * requiring to pass all variables which affects on display buffer
169 * to color management cache system and keeps calls small and nice.
171 typedef struct ColormanageCacheViewSettings {
176 CurveMapping *curve_mapping;
177 } ColormanageCacheViewSettings;
179 typedef struct ColormanageCacheDisplaySettings {
181 } ColormanageCacheDisplaySettings;
183 typedef struct ColormanageCacheKey {
184 int view; /* view transformation used for display buffer */
185 int display; /* display device name */
186 } ColormanageCacheKey;
188 typedef struct ColormnaageCacheData {
189 int flag; /* view flags of cached buffer */
190 float exposure; /* exposure value cached buffer is calculated with */
191 float gamma; /* gamma value cached buffer is calculated with */
192 CurveMapping *curve_mapping; /* curve mapping used for cached buffer */
193 int curve_mapping_timestamp; /* time stamp of curve mapping used for cached buffer */
194 } ColormnaageCacheData;
196 typedef struct ColormanageCache {
197 struct MovieCache *moviecache;
199 ColormnaageCacheData *data;
202 static struct MovieCache *colormanage_moviecache_get(const ImBuf *ibuf)
204 if (!ibuf->colormanage_cache)
207 return ibuf->colormanage_cache->moviecache;
210 static ColormnaageCacheData *colormanage_cachedata_get(const ImBuf *ibuf)
212 if (!ibuf->colormanage_cache)
215 return ibuf->colormanage_cache->data;
218 static unsigned int colormanage_hashhash(const void *key_v)
220 ColormanageCacheKey *key = (ColormanageCacheKey *)key_v;
222 unsigned int rval = (key->display << 16) | (key->view % 0xffff);
227 static int colormanage_hashcmp(const void *av, const void *bv)
229 const ColormanageCacheKey *a = (ColormanageCacheKey *) av;
230 const ColormanageCacheKey *b = (ColormanageCacheKey *) bv;
232 if (a->view < b->view)
234 else if (a->view > b->view)
237 if (a->display < b->display)
239 else if (a->display > b->display)
245 static struct MovieCache *colormanage_moviecache_ensure(ImBuf *ibuf)
247 if (!ibuf->colormanage_cache)
248 ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage cache");
250 if (!ibuf->colormanage_cache->moviecache) {
251 struct MovieCache *moviecache;
253 moviecache = IMB_moviecache_create("colormanage cache", sizeof(ColormanageCacheKey),
254 colormanage_hashhash, colormanage_hashcmp);
256 ibuf->colormanage_cache->moviecache = moviecache;
259 return ibuf->colormanage_cache->moviecache;
262 static void colormanage_cachedata_set(ImBuf *ibuf, ColormnaageCacheData *data)
264 if (!ibuf->colormanage_cache)
265 ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage cache");
267 ibuf->colormanage_cache->data = data;
270 static void colormanage_view_settings_to_cache(ColormanageCacheViewSettings *cache_view_settings,
271 const ColorManagedViewSettings *view_settings)
273 int view = IMB_colormanagement_view_get_named_index(view_settings->view_transform);
275 cache_view_settings->view = view;
276 cache_view_settings->exposure = view_settings->exposure;
277 cache_view_settings->gamma = view_settings->gamma;
278 cache_view_settings->flag = view_settings->flag;
279 cache_view_settings->curve_mapping = view_settings->curve_mapping;
282 static void colormanage_display_settings_to_cache(ColormanageCacheDisplaySettings *cache_display_settings,
283 const ColorManagedDisplaySettings *display_settings)
285 int display = IMB_colormanagement_display_get_named_index(display_settings->display_device);
287 cache_display_settings->display = display;
290 static void colormanage_settings_to_key(ColormanageCacheKey *key,
291 const ColormanageCacheViewSettings *view_settings,
292 const ColormanageCacheDisplaySettings *display_settings)
294 key->view = view_settings->view;
295 key->display = display_settings->display;
298 static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf, ColormanageCacheKey *key, void **cache_handle)
301 struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
304 /* if there's no moviecache it means no color management was applied on given image buffer before */
309 *cache_handle = NULL;
311 cache_ibuf = IMB_moviecache_get(moviecache, key);
313 *cache_handle = cache_ibuf;
318 static unsigned char *colormanage_cache_get(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings,
319 const ColormanageCacheDisplaySettings *display_settings,
322 ColormanageCacheKey key;
324 int view_flag = 1 << (view_settings->view - 1);
325 CurveMapping *curve_mapping = view_settings->curve_mapping;
326 int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
328 colormanage_settings_to_key(&key, view_settings, display_settings);
330 /* check whether image was marked as dirty for requested transform */
331 if ((ibuf->display_buffer_flags[display_settings->display - 1] & view_flag) == 0) {
335 cache_ibuf = colormanage_cache_get_ibuf(ibuf, &key, cache_handle);
338 ColormnaageCacheData *cache_data;
340 BLI_assert(cache_ibuf->x == ibuf->x &&
341 cache_ibuf->y == ibuf->y);
343 /* only buffers with different color space conversions are being stored
344 * in cache separately. buffer which were used only different exposure/gamma
345 * are re-suing the same cached buffer
347 * check here which exposure/gamma/curve was used for cached buffer and if they're
348 * different from requested buffer should be re-generated
350 cache_data = colormanage_cachedata_get(cache_ibuf);
352 if (cache_data->exposure != view_settings->exposure ||
353 cache_data->gamma != view_settings->gamma ||
354 cache_data->flag != view_settings->flag ||
355 cache_data->curve_mapping != curve_mapping ||
356 cache_data->curve_mapping_timestamp != curve_mapping_timestamp)
358 *cache_handle = NULL;
360 IMB_freeImBuf(cache_ibuf);
365 return (unsigned char *) cache_ibuf->rect;
371 static void colormanage_cache_put(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings,
372 const ColormanageCacheDisplaySettings *display_settings,
373 unsigned char *display_buffer, void **cache_handle)
375 ColormanageCacheKey key;
377 ColormnaageCacheData *cache_data;
378 int view_flag = 1 << (view_settings->view - 1);
379 struct MovieCache *moviecache = colormanage_moviecache_ensure(ibuf);
380 CurveMapping *curve_mapping = view_settings->curve_mapping;
381 int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
383 colormanage_settings_to_key(&key, view_settings, display_settings);
385 /* mark display buffer as valid */
386 ibuf->display_buffer_flags[display_settings->display - 1] |= view_flag;
389 cache_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0);
390 cache_ibuf->rect = (unsigned int *) display_buffer;
392 cache_ibuf->mall |= IB_rect;
393 cache_ibuf->flags |= IB_rect;
395 /* store data which is needed to check whether cached buffer could be used for color managed display settings */
396 cache_data = MEM_callocN(sizeof(ColormnaageCacheData), "color manage cache imbuf data");
397 cache_data->exposure = view_settings->exposure;
398 cache_data->gamma = view_settings->gamma;
399 cache_data->flag = view_settings->flag;
400 cache_data->curve_mapping = curve_mapping;
401 cache_data->curve_mapping_timestamp = curve_mapping_timestamp;
403 colormanage_cachedata_set(cache_ibuf, cache_data);
405 *cache_handle = cache_ibuf;
407 IMB_moviecache_put(moviecache, &key, cache_ibuf);
410 static void colormanage_cache_handle_release(void *cache_handle)
412 ImBuf *cache_ibuf = cache_handle;
414 IMB_freeImBuf(cache_ibuf);
417 /*********************** Initialization / De-initialization *************************/
419 static void colormanage_role_color_space_name_get(OCIO_ConstConfigRcPtr *config, char *colorspace_name, const char *role, const char *backup_role)
421 OCIO_ConstColorSpaceRcPtr *ociocs;
423 ociocs = OCIO_configGetColorSpace(config, role);
425 if (!ociocs && backup_role)
426 ociocs = OCIO_configGetColorSpace(config, backup_role);
429 const char *name = OCIO_colorSpaceGetName(ociocs);
431 /* assume function was called with buffer properly allocated to MAX_COLORSPACE_NAME chars */
432 BLI_strncpy(colorspace_name, name, MAX_COLORSPACE_NAME);
433 OCIO_colorSpaceRelease(ociocs);
436 printf("Color management: Error could not find role %s role.\n", role);
440 static void colormanage_load_config(OCIO_ConstConfigRcPtr *config)
442 int tot_colorspace, tot_display, tot_display_view, index, viewindex, viewindex2;
446 colormanage_role_color_space_name_get(config, global_role_scene_linear, OCIO_ROLE_SCENE_LINEAR, NULL);
447 colormanage_role_color_space_name_get(config, global_role_color_picking, OCIO_ROLE_COLOR_PICKING, NULL);
448 colormanage_role_color_space_name_get(config, global_role_texture_painting, OCIO_ROLE_TEXTURE_PAINT, NULL);
449 colormanage_role_color_space_name_get(config, global_role_default_sequencer, OCIO_ROLE_DEFAULT_SEQUENCER, OCIO_ROLE_SCENE_LINEAR);
450 colormanage_role_color_space_name_get(config, global_role_default_byte, OCIO_ROLE_DEFAULT_BYTE, OCIO_ROLE_TEXTURE_PAINT);
451 colormanage_role_color_space_name_get(config, global_role_default_float, OCIO_ROLE_DEFAULT_FLOAT, OCIO_ROLE_SCENE_LINEAR);
453 /* load colorspaces */
454 tot_colorspace = OCIO_configGetNumColorSpaces(config);
455 for (index = 0 ; index < tot_colorspace; index++) {
456 OCIO_ConstColorSpaceRcPtr *ocio_colorspace;
457 const char *description;
458 int is_invertible, is_data;
460 name = OCIO_configGetColorSpaceNameByIndex(config, index);
462 ocio_colorspace = OCIO_configGetColorSpace(config, name);
463 description = OCIO_colorSpaceGetDescription(ocio_colorspace);
464 is_invertible = OCIO_colorSpaceIsInvertible(ocio_colorspace);
465 is_data = OCIO_colorSpaceIsData(ocio_colorspace);
467 colormanage_colorspace_add(name, description, is_invertible, is_data);
469 OCIO_colorSpaceRelease(ocio_colorspace);
474 tot_display = OCIO_configGetNumDisplays(config);
476 for (index = 0 ; index < tot_display; index++) {
477 const char *displayname;
478 ColorManagedDisplay *display;
480 displayname = OCIO_configGetDisplay(config, index);
482 display = colormanage_display_add(displayname);
485 tot_display_view = OCIO_configGetNumViews(config, displayname);
486 for (viewindex = 0 ; viewindex < tot_display_view; viewindex++, viewindex2++) {
487 const char *viewname;
488 ColorManagedView *view;
489 LinkData *display_view;
491 viewname = OCIO_configGetView(config, displayname, viewindex);
493 /* first check if view transform with given name was already loaded */
494 view = colormanage_view_get_named(viewname);
497 view = colormanage_view_add(viewname);
500 display_view = BLI_genericNodeN(view);
502 BLI_addtail(&display->views, display_view);
506 global_tot_display = tot_display;
509 static void colormanage_free_config(void)
511 ColorSpace *colorspace;
512 ColorManagedDisplay *display;
514 /* free color spaces */
515 colorspace = global_colorspaces.first;
517 ColorSpace *colorspace_next = colorspace->next;
519 /* free precomputer processors */
520 if (colorspace->to_scene_linear)
521 OCIO_processorRelease((OCIO_ConstProcessorRcPtr *) colorspace->to_scene_linear);
523 if (colorspace->from_scene_linear)
524 OCIO_processorRelease((OCIO_ConstProcessorRcPtr *) colorspace->from_scene_linear);
526 /* free color space itself */
527 MEM_freeN(colorspace);
529 colorspace = colorspace_next;
533 display = global_displays.first;
535 ColorManagedDisplay *display_next = display->next;
537 /* free precomputer processors */
538 if (display->to_scene_linear)
539 OCIO_processorRelease((OCIO_ConstProcessorRcPtr *) display->to_scene_linear);
541 if (display->from_scene_linear)
542 OCIO_processorRelease((OCIO_ConstProcessorRcPtr *) display->from_scene_linear);
544 /* free list of views */
545 BLI_freelistN(&display->views);
548 display = display_next;
552 BLI_freelistN(&global_views);
557 void colormanagement_init(void)
559 const char *ocio_env;
560 const char *configdir;
561 char configfile[FILE_MAX];
562 OCIO_ConstConfigRcPtr *config = NULL;
566 ocio_env = getenv("OCIO");
568 if (ocio_env && ocio_env[0] != '\0')
569 config = OCIO_configCreateFromEnv();
571 if (config == NULL) {
572 configdir = BLI_get_folder(BLENDER_DATAFILES, "colormanagement");
575 BLI_join_dirfile(configfile, sizeof(configfile), configdir, BCM_CONFIG_FILE);
579 /* quite a hack to support loading configuration from path with non-acii symbols */
581 char short_name[256];
582 BLI_get_short_name(short_name, configfile);
583 config = OCIO_configCreateFromFile(short_name);
586 config = OCIO_configCreateFromFile(configfile);
591 if (config == NULL) {
592 printf("Color management: using fallback mode for management\n");
594 config = OCIO_configCreateFallback();
598 OCIO_setCurrentConfig(config);
600 colormanage_load_config(config);
602 OCIO_configRelease(config);
605 BLI_init_srgb_conversion();
608 void colormanagement_exit(void)
610 colormanage_free_config();
613 /*********************** Internal functions *************************/
615 void colormanage_cache_free(ImBuf *ibuf)
617 if (ibuf->display_buffer_flags) {
618 MEM_freeN(ibuf->display_buffer_flags);
620 ibuf->display_buffer_flags = NULL;
623 if (ibuf->colormanage_cache) {
624 ColormnaageCacheData *cache_data = colormanage_cachedata_get(ibuf);
625 struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
628 MEM_freeN(cache_data);
632 IMB_moviecache_free(moviecache);
635 MEM_freeN(ibuf->colormanage_cache);
637 ibuf->colormanage_cache = NULL;
641 static void display_transform_get_from_ctx(const bContext *C, ColorManagedViewSettings **view_settings_r,
642 ColorManagedDisplaySettings **display_settings_r)
644 Scene *scene = CTX_data_scene(C);
645 SpaceImage *sima = CTX_wm_space_image(C);
647 *view_settings_r = &scene->view_settings;
648 *display_settings_r = &scene->display_settings;
651 if ((sima->image->flag & IMA_VIEW_AS_RENDER) == 0)
652 *view_settings_r = NULL;
656 static const char *display_transform_get_colorspace_name(const ColorManagedViewSettings *view_settings,
657 const ColorManagedDisplaySettings *display_settings)
659 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
661 const char *display = display_settings->display_device;
662 const char *view = view_settings->view_transform;
663 const char *colorspace_name;
665 colorspace_name = OCIO_configGetDisplayColorSpaceName(config, display, view);
667 OCIO_configRelease(config);
669 return colorspace_name;
672 static ColorSpace *display_transform_get_colorspace(const ColorManagedViewSettings *view_settings,
673 const ColorManagedDisplaySettings *display_settings)
675 const char *colorspace_name = display_transform_get_colorspace_name(view_settings, display_settings);
678 return colormanage_colorspace_get_named(colorspace_name);
683 static OCIO_ConstProcessorRcPtr *create_display_buffer_processor(const char *view_transform, const char *display,
684 float exposure, float gamma)
686 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
687 OCIO_DisplayTransformRcPtr *dt;
688 OCIO_ConstProcessorRcPtr *processor;
690 dt = OCIO_createDisplayTransform();
692 /* assuming handling buffer was already converted to scene linear space */
693 OCIO_displayTransformSetInputColorSpaceName(dt, global_role_scene_linear);
694 OCIO_displayTransformSetView(dt, view_transform);
695 OCIO_displayTransformSetDisplay(dt, display);
697 /* fstop exposure control */
698 if (exposure != 0.0f) {
699 OCIO_MatrixTransformRcPtr *mt;
700 float gain = powf(2.0f, exposure);
701 const float scale4f[] = {gain, gain, gain, 1.0f};
702 float m44[16], offset4[4];
704 OCIO_matrixTransformScale(m44, offset4, scale4f);
705 mt = OCIO_createMatrixTransform();
706 OCIO_matrixTransformSetValue(mt, m44, offset4);
707 OCIO_displayTransformSetLinearCC(dt, (OCIO_ConstTransformRcPtr *) mt);
709 OCIO_matrixTransformRelease(mt);
712 /* post-display gamma transform */
714 OCIO_ExponentTransformRcPtr *et;
715 float exponent = 1.0f / MAX2(FLT_EPSILON, gamma);
716 const float exponent4f[] = {exponent, exponent, exponent, exponent};
718 et = OCIO_createExponentTransform();
719 OCIO_exponentTransformSetValue(et, exponent4f);
720 OCIO_displayTransformSetDisplayCC(dt, (OCIO_ConstTransformRcPtr *) et);
722 OCIO_exponentTransformRelease(et);
725 processor = OCIO_configGetProcessor(config, (OCIO_ConstTransformRcPtr *) dt);
727 OCIO_displayTransformRelease(dt);
728 OCIO_configRelease(config);
733 static OCIO_ConstProcessorRcPtr *create_colorspace_transform_processor(const char *from_colorspace,
734 const char *to_colorspace)
736 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
737 OCIO_ConstProcessorRcPtr *processor;
739 processor = OCIO_configGetProcessorWithNames(config, from_colorspace, to_colorspace);
741 OCIO_configRelease(config);
746 static OCIO_ConstProcessorRcPtr *colorspace_to_scene_linear_processor(ColorSpace *colorspace)
748 if (colorspace->to_scene_linear == NULL) {
749 BLI_mutex_lock(&processor_lock);
751 if (colorspace->to_scene_linear == NULL) {
752 OCIO_ConstProcessorRcPtr *to_scene_linear;
753 to_scene_linear = create_colorspace_transform_processor(colorspace->name, global_role_scene_linear);
754 colorspace->to_scene_linear = (struct OCIO_ConstProcessorRcPtr *) to_scene_linear;
757 BLI_mutex_unlock(&processor_lock);
760 return (OCIO_ConstProcessorRcPtr *) colorspace->to_scene_linear;
763 static OCIO_ConstProcessorRcPtr *colorspace_from_scene_linear_processor(ColorSpace *colorspace)
765 if (colorspace->from_scene_linear == NULL) {
766 BLI_mutex_lock(&processor_lock);
768 if (colorspace->from_scene_linear == NULL) {
769 OCIO_ConstProcessorRcPtr *from_scene_linear;
770 from_scene_linear = create_colorspace_transform_processor(global_role_scene_linear, colorspace->name);
771 colorspace->from_scene_linear = (struct OCIO_ConstProcessorRcPtr *) from_scene_linear;
774 BLI_mutex_unlock(&processor_lock);
777 return (OCIO_ConstProcessorRcPtr *) colorspace->from_scene_linear;
780 static OCIO_ConstProcessorRcPtr *display_from_scene_linear_processor(ColorManagedDisplay *display)
782 if (display->from_scene_linear == NULL) {
783 BLI_mutex_lock(&processor_lock);
785 if (display->from_scene_linear == NULL) {
786 const char *view_name = colormanage_view_get_default_name(display);
787 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
788 OCIO_ConstProcessorRcPtr *processor = NULL;
790 if (view_name && config) {
791 const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(config, display->name, view_name);
792 processor = OCIO_configGetProcessorWithNames(config, global_role_scene_linear, view_colorspace);
794 OCIO_configRelease(config);
797 display->from_scene_linear = (struct OCIO_ConstProcessorRcPtr *) processor;
800 BLI_mutex_unlock(&processor_lock);
803 return (OCIO_ConstProcessorRcPtr *) display->from_scene_linear;
806 static OCIO_ConstProcessorRcPtr *display_to_scene_linear_processor(ColorManagedDisplay *display)
808 if (display->to_scene_linear == NULL) {
809 BLI_mutex_lock(&processor_lock);
811 if (display->to_scene_linear == NULL) {
812 const char *view_name = colormanage_view_get_default_name(display);
813 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
814 OCIO_ConstProcessorRcPtr *processor = NULL;
816 if (view_name && config) {
817 const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(config, display->name, view_name);
818 processor = OCIO_configGetProcessorWithNames(config, view_colorspace, global_role_scene_linear);
820 OCIO_configRelease(config);
823 display->to_scene_linear = (struct OCIO_ConstProcessorRcPtr *) processor;
826 BLI_mutex_unlock(&processor_lock);
829 return (OCIO_ConstProcessorRcPtr *) display->to_scene_linear;
832 static void init_default_view_settings(const ColorManagedDisplaySettings *display_settings,
833 ColorManagedViewSettings *view_settings)
835 ColorManagedDisplay *display;
836 ColorManagedView *default_view = NULL;
838 display = colormanage_display_get_named(display_settings->display_device);
841 default_view = colormanage_view_get_default(display);
844 BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
846 view_settings->view_transform[0] = '\0';
848 view_settings->flag = 0;
849 view_settings->gamma = 1.0f;
850 view_settings->exposure = 0.0f;
851 view_settings->curve_mapping = NULL;
854 static void curve_mapping_apply_pixel(CurveMapping *curve_mapping, float *pixel, int channels)
857 pixel[0] = curvemap_evaluateF(curve_mapping->cm, pixel[0]);
859 else if (channels == 2) {
860 pixel[0] = curvemap_evaluateF(curve_mapping->cm, pixel[0]);
861 pixel[1] = curvemap_evaluateF(curve_mapping->cm, pixel[1]);
864 curvemapping_evaluate_premulRGBF(curve_mapping, pixel, pixel);
868 void colorspace_set_default_role(char *colorspace, int size, int role)
870 if (colorspace && colorspace[0] == '\0') {
871 const char *role_colorspace;
873 role_colorspace = IMB_colormanagement_role_colorspace_name_get(role);
875 BLI_strncpy(colorspace, role_colorspace, size);
879 void colormanage_imbuf_set_default_spaces(ImBuf *ibuf)
881 ibuf->rect_colorspace = colormanage_colorspace_get_named(global_role_default_byte);
884 void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace)
886 ColorSpace *colorspace = colormanage_colorspace_get_named(from_colorspace);
888 if (colorspace && colorspace->is_data) {
889 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
893 if (ibuf->rect_float) {
894 const char *to_colorspace = global_role_scene_linear;
897 imb_freerectImBuf(ibuf);
899 IMB_colormanagement_transform(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels,
900 from_colorspace, to_colorspace, TRUE);
904 /*********************** Generic functions *************************/
906 static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings, const char *what,
907 const ColorManagedDisplay *default_display)
909 if (display_settings->display_device[0] == '\0') {
910 BLI_strncpy(display_settings->display_device, default_display->name, sizeof(display_settings->display_device));
913 ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device);
916 printf("Color management: display \"%s\" used by %s not found, setting to default (\"%s\").\n",
917 display_settings->display_device, what, default_display->name);
919 BLI_strncpy(display_settings->display_device, default_display->name,
920 sizeof(display_settings->display_device));
925 static void colormanage_check_view_settings(ColorManagedDisplaySettings *display_settings,
926 ColorManagedViewSettings *view_settings, const char *what)
928 ColorManagedDisplay *display;
929 ColorManagedView *default_view = NULL;
931 if (view_settings->view_transform[0] == '\0') {
932 display = colormanage_display_get_named(display_settings->display_device);
935 default_view = colormanage_view_get_default(display);
938 BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
941 ColorManagedView *view = colormanage_view_get_named(view_settings->view_transform);
944 display = colormanage_display_get_named(display_settings->display_device);
947 default_view = colormanage_view_get_default(display);
950 printf("Color management: %s view \"%s\" not found, setting default \"%s\".\n",
951 what, view_settings->view_transform, default_view->name);
953 BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
958 /* OCIO_TODO: move to do_versions() */
959 if (view_settings->exposure == 0.0f && view_settings->gamma == 0.0f) {
960 view_settings->exposure = 0.0f;
961 view_settings->gamma = 1.0f;
965 static void colormanage_check_colorspace_settings(ColorManagedColorspaceSettings *colorspace_settings, const char *what)
967 if (colorspace_settings->name[0] == '\0') {
971 ColorSpace *colorspace = colormanage_colorspace_get_named(colorspace_settings->name);
974 printf("Color management: %s colorspace \"%s\" not found, will use default instead.\n",
975 what, colorspace_settings->name);
977 BLI_strncpy(colorspace_settings->name, "", sizeof(colorspace_settings->name));
984 void IMB_colormanagement_check_file_config(Main *bmain)
990 ColorManagedDisplay *default_display;
992 default_display = colormanage_display_get_default();
994 if (!default_display) {
995 /* happens when OCIO configuration is incorrect */
999 for (scene = bmain->scene.first; scene; scene = scene->id.next) {
1000 ColorManagedColorspaceSettings *sequencer_colorspace_settings;
1002 colormanage_check_display_settings(&scene->display_settings, "scene", default_display);
1003 colormanage_check_view_settings(&scene->display_settings, &scene->view_settings, "scene");
1005 sequencer_colorspace_settings = &scene->sequencer_colorspace_settings;
1007 colormanage_check_colorspace_settings(sequencer_colorspace_settings, "sequencer");
1009 if (sequencer_colorspace_settings->name[0] == '\0') {
1010 BLI_strncpy(sequencer_colorspace_settings->name, global_role_default_sequencer, MAX_COLORSPACE_NAME);
1014 /* ** check input color space settings ** */
1016 for (image = bmain->image.first; image; image = image->id.next) {
1017 colormanage_check_colorspace_settings(&image->colorspace_settings, "image");
1020 for (clip = bmain->movieclip.first; clip; clip = clip->id.next) {
1021 colormanage_check_colorspace_settings(&clip->colorspace_settings, "clip");
1025 void IMB_colormanagement_validate_settings(ColorManagedDisplaySettings *display_settings,
1026 ColorManagedViewSettings *view_settings)
1028 ColorManagedDisplay *display;
1029 ColorManagedView *default_view = NULL;
1030 LinkData *view_link;
1032 display = colormanage_display_get_named(display_settings->display_device);
1034 default_view = colormanage_view_get_default(display);
1036 for (view_link = display->views.first; view_link; view_link = view_link->next) {
1037 ColorManagedView *view = view_link->data;
1039 if (!strcmp(view->name, view_settings->view_transform))
1043 if (view_link == NULL && default_view)
1044 BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1047 const char *IMB_colormanagement_role_colorspace_name_get(int role)
1050 case COLOR_ROLE_SCENE_LINEAR:
1051 return global_role_scene_linear;
1053 case COLOR_ROLE_COLOR_PICKING:
1054 return global_role_color_picking;
1056 case COLOR_ROLE_TEXTURE_PAINTING:
1057 return global_role_texture_painting;
1059 case COLOR_ROLE_DEFAULT_SEQUENCER:
1060 return global_role_default_sequencer;
1062 case COLOR_ROLE_DEFAULT_FLOAT:
1063 return global_role_default_float;
1065 case COLOR_ROLE_DEFAULT_BYTE:
1066 return global_role_default_byte;
1069 printf("Unknown role was passed to %s\n", __func__);
1076 void IMB_colormanagement_check_is_data(ImBuf *ibuf, const char *name)
1078 ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1080 if (colorspace && colorspace->is_data)
1081 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1083 ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1086 void IMB_colormanagement_assign_float_colorspace(ImBuf *ibuf, const char *name)
1088 ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1090 ibuf->float_colorspace = colorspace;
1092 if (colorspace && colorspace->is_data)
1093 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1095 ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1098 void IMB_colormanagement_assign_rect_colorspace(ImBuf *ibuf, const char *name)
1100 ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1102 ibuf->rect_colorspace = colorspace;
1104 if (colorspace && colorspace->is_data)
1105 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1107 ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1110 /*********************** Threaded display buffer transform routines *************************/
1112 typedef struct DisplayBufferThread {
1113 ColormanageProcessor *cm_processor;
1116 unsigned char *byte_buffer;
1118 float *display_buffer;
1119 unsigned char *display_buffer_byte;
1129 const char *byte_colorspace;
1130 const char *float_colorspace;
1131 } DisplayBufferThread;
1133 typedef struct DisplayBufferInitData {
1135 ColormanageProcessor *cm_processor;
1137 unsigned char *byte_buffer;
1139 float *display_buffer;
1140 unsigned char *display_buffer_byte;
1144 const char *byte_colorspace;
1145 const char *float_colorspace;
1146 } DisplayBufferInitData;
1148 static void display_buffer_init_handle(void *handle_v, int start_line, int tot_line, void *init_data_v)
1150 DisplayBufferThread *handle = (DisplayBufferThread *) handle_v;
1151 DisplayBufferInitData *init_data = (DisplayBufferInitData *) init_data_v;
1152 ImBuf *ibuf = init_data->ibuf;
1154 int channels = ibuf->channels;
1155 float dither = ibuf->dither;
1156 int is_data = ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA;
1158 int offset = channels * start_line * ibuf->x;
1159 int display_buffer_byte_offset = DISPLAY_BUFFER_CHANNELS * start_line * ibuf->x;
1161 memset(handle, 0, sizeof(DisplayBufferThread));
1163 handle->cm_processor = init_data->cm_processor;
1165 if (init_data->buffer)
1166 handle->buffer = init_data->buffer + offset;
1168 if (init_data->byte_buffer)
1169 handle->byte_buffer = init_data->byte_buffer + offset;
1171 if (init_data->display_buffer)
1172 handle->display_buffer = init_data->display_buffer + offset;
1174 if (init_data->display_buffer_byte)
1175 handle->display_buffer_byte = init_data->display_buffer_byte + display_buffer_byte_offset;
1177 handle->width = ibuf->x;
1179 handle->start_line = start_line;
1180 handle->tot_line = tot_line;
1182 handle->channels = channels;
1183 handle->dither = dither;
1184 handle->is_data = is_data;
1186 handle->byte_colorspace = init_data->byte_colorspace;
1187 handle->float_colorspace = init_data->float_colorspace;
1190 static void *display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle)
1192 float *linear_buffer = NULL;
1194 int channels = handle->channels;
1195 int width = handle->width;
1196 int height = handle->tot_line;
1198 int buffer_size = channels * width * height;
1200 int is_data = handle->is_data;
1201 int is_data_display = handle->cm_processor->is_data_result;
1203 linear_buffer = MEM_callocN(buffer_size * sizeof(float), "color conversion linear buffer");
1205 if (!handle->buffer) {
1206 unsigned char *byte_buffer = handle->byte_buffer;
1208 const char *from_colorspace = handle->byte_colorspace;
1209 const char *to_colorspace = global_role_scene_linear;
1215 /* first convert byte buffer to float, keep in image space */
1216 for (i = 0, fp = linear_buffer, cp = byte_buffer;
1218 i++, fp += channels, cp += channels)
1220 if (channels == 3) {
1221 rgb_uchar_to_float(fp, cp);
1223 else if (channels == 4) {
1224 rgba_uchar_to_float(fp, cp);
1225 straight_to_premul_v4(fp);
1228 BLI_assert(!"Buffers of 3 or 4 channels are only supported here");
1232 if (!is_data && !is_data_display) {
1233 /* convert float buffer to scene linear space */
1234 IMB_colormanagement_transform(linear_buffer, width, height, channels,
1235 from_colorspace, to_colorspace, TRUE);
1238 else if (handle->float_colorspace) {
1239 /* currently float is non-linear only in sequencer, which is working
1240 * in it's own color space even to handle float buffers.
1241 * This color space is the same for byte and float images.
1242 * Need to convert float buffer to linear space before applying display transform
1245 const char *from_colorspace = handle->float_colorspace;
1246 const char *to_colorspace = global_role_scene_linear;
1248 memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1250 IMB_colormanagement_transform(linear_buffer, width, height, channels,
1251 from_colorspace, to_colorspace, TRUE);
1254 /* some processors would want to modify float original buffer
1255 * before converting it into display byte buffer, so we need to
1256 * make sure original's ImBuf buffers wouldn't be modified by
1257 * using duplicated buffer here
1259 * NOTE: MEM_dupallocN can't be used because buffer could be
1260 * specified as an offset inside allocated buffer
1263 memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1266 return linear_buffer;
1269 static void *do_display_buffer_apply_thread(void *handle_v)
1271 DisplayBufferThread *handle = (DisplayBufferThread *) handle_v;
1272 ColormanageProcessor *cm_processor = handle->cm_processor;
1273 float *display_buffer = handle->display_buffer;
1274 unsigned char *display_buffer_byte = handle->display_buffer_byte;
1275 int channels = handle->channels;
1276 int width = handle->width;
1277 int height = handle->tot_line;
1278 float dither = handle->dither;
1279 int is_data = handle->is_data;
1281 if (cm_processor == NULL) {
1282 if (display_buffer_byte) {
1283 IMB_buffer_byte_from_byte(display_buffer_byte, handle->byte_buffer, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1284 FALSE, width, height, width, width);
1287 if (display_buffer) {
1288 IMB_buffer_float_from_byte(display_buffer, handle->byte_buffer, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1289 FALSE, width, height, width, width);
1293 float *linear_buffer = display_buffer_apply_get_linear_buffer(handle);
1296 /* special case for data buffers - no color space conversions,
1297 * only generate byte buffers
1301 /* apply processor */
1302 IMB_colormanagement_processor_apply(cm_processor, linear_buffer, width, height, channels, TRUE);
1305 /* copy result to output buffers */
1306 if (display_buffer_byte) {
1308 IMB_buffer_byte_from_float(display_buffer_byte, linear_buffer,
1309 channels, dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1310 TRUE, width, height, width, width);
1314 memcpy(display_buffer, linear_buffer, width * height * channels * sizeof(float));
1316 MEM_freeN(linear_buffer);
1322 static void display_buffer_apply_threaded(ImBuf *ibuf, float *buffer, unsigned char *byte_buffer, float *display_buffer,
1323 unsigned char *display_buffer_byte, ColormanageProcessor *cm_processor)
1325 DisplayBufferInitData init_data;
1327 init_data.ibuf = ibuf;
1328 init_data.cm_processor = cm_processor;
1329 init_data.buffer = buffer;
1330 init_data.byte_buffer = byte_buffer;
1331 init_data.display_buffer = display_buffer;
1332 init_data.display_buffer_byte = display_buffer_byte;
1334 if (ibuf->rect_colorspace != NULL) {
1335 init_data.byte_colorspace = ibuf->rect_colorspace->name;
1338 /* happens for viewer images, which are not so simple to determine where to
1339 * set image buffer's color spaces
1341 init_data.byte_colorspace = global_role_default_byte;
1344 if (ibuf->float_colorspace != NULL) {
1345 /* sequencer stores float buffers in non-linear space */
1346 init_data.float_colorspace = ibuf->float_colorspace->name;
1349 init_data.float_colorspace = NULL;
1352 IMB_processor_apply_threaded(ibuf->y, sizeof(DisplayBufferThread), &init_data,
1353 display_buffer_init_handle, do_display_buffer_apply_thread);
1356 static int is_ibuf_rect_in_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
1357 const ColorManagedDisplaySettings *display_settings)
1359 if ((view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) == 0 &&
1360 view_settings->exposure == 0.0f &&
1361 view_settings->gamma == 1.0f)
1363 const char *from_colorspace = ibuf->rect_colorspace->name;
1364 const char *to_colorspace = display_transform_get_colorspace_name(view_settings, display_settings);
1366 if (to_colorspace && !strcmp(from_colorspace, to_colorspace))
1373 static void colormanage_display_buffer_process_ex(ImBuf *ibuf, float *display_buffer, unsigned char *display_buffer_byte,
1374 const ColorManagedViewSettings *view_settings,
1375 const ColorManagedDisplaySettings *display_settings)
1377 ColormanageProcessor *cm_processor = NULL;
1378 int skip_transform = FALSE;
1380 /* if we're going to transform byte buffer, check whether transformation would
1381 * happen to the same color space as byte buffer itself is
1382 * this would save byte -> float -> byte conversions making display buffer
1383 * computation noticeable faster
1385 if (ibuf->rect_float == NULL && ibuf->rect_colorspace) {
1386 skip_transform = is_ibuf_rect_in_display_space(ibuf, view_settings, display_settings);
1389 if (skip_transform == FALSE)
1390 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1392 display_buffer_apply_threaded(ibuf, ibuf->rect_float, (unsigned char *) ibuf->rect,
1393 display_buffer, display_buffer_byte, cm_processor);
1396 IMB_colormanagement_processor_free(cm_processor);
1399 static void colormanage_display_buffer_process(ImBuf *ibuf, unsigned char *display_buffer,
1400 const ColorManagedViewSettings *view_settings,
1401 const ColorManagedDisplaySettings *display_settings)
1403 colormanage_display_buffer_process_ex(ibuf, NULL, display_buffer, view_settings, display_settings);
1406 /*********************** Threaded processor transform routines *************************/
1408 typedef struct ProcessorTransformThread {
1409 ColormanageProcessor *cm_processor;
1416 } ProcessorTransformThread;
1418 typedef struct ProcessorTransformInit {
1419 ColormanageProcessor *cm_processor;
1425 } ProcessorTransformInitData;
1427 static void processor_transform_init_handle(void *handle_v, int start_line, int tot_line, void *init_data_v)
1429 ProcessorTransformThread *handle = (ProcessorTransformThread *) handle_v;
1430 ProcessorTransformInitData *init_data = (ProcessorTransformInitData *) init_data_v;
1432 int channels = init_data->channels;
1433 int width = init_data->width;
1434 int predivide = init_data->predivide;
1436 int offset = channels * start_line * width;
1438 memset(handle, 0, sizeof(ProcessorTransformThread));
1440 handle->cm_processor = init_data->cm_processor;
1442 handle->buffer = init_data->buffer + offset;
1444 handle->width = width;
1446 handle->start_line = start_line;
1447 handle->tot_line = tot_line;
1449 handle->channels = channels;
1450 handle->predivide = predivide;
1453 static void *do_processor_transform_thread(void *handle_v)
1455 ProcessorTransformThread *handle = (ProcessorTransformThread *) handle_v;
1456 float *buffer = handle->buffer;
1457 int channels = handle->channels;
1458 int width = handle->width;
1459 int height = handle->tot_line;
1460 int predivide = handle->predivide;
1462 IMB_colormanagement_processor_apply(handle->cm_processor, buffer, width, height, channels, predivide);
1467 static void processor_transform_apply_threaded(float *buffer, int width, int height, int channels,
1468 ColormanageProcessor *cm_processor, int predivide)
1470 ProcessorTransformInitData init_data;
1472 init_data.cm_processor = cm_processor;
1473 init_data.buffer = buffer;
1474 init_data.width = width;
1475 init_data.height = height;
1476 init_data.channels = channels;
1477 init_data.predivide = predivide;
1479 IMB_processor_apply_threaded(height, sizeof(ProcessorTransformThread), &init_data,
1480 processor_transform_init_handle, do_processor_transform_thread);
1483 /*********************** Color space transformation functions *************************/
1485 /* convert the whole buffer from specified by name color space to another - internal implementation */
1486 static void colormanagement_transform_ex(float *buffer, int width, int height, int channels, const char *from_colorspace,
1487 const char *to_colorspace, int predivide, int do_threaded)
1489 ColormanageProcessor *cm_processor;
1491 if (from_colorspace[0] == '\0') {
1495 if (!strcmp(from_colorspace, to_colorspace)) {
1496 /* if source and destination color spaces are identical, skip
1497 * threading overhead and simply do nothing
1502 cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
1505 processor_transform_apply_threaded(buffer, width, height, channels, cm_processor, predivide);
1507 IMB_colormanagement_processor_apply(cm_processor, buffer, width, height, channels, predivide);
1509 IMB_colormanagement_processor_free(cm_processor);
1512 /* convert the whole buffer from specified by name color space to another */
1513 void IMB_colormanagement_transform(float *buffer, int width, int height, int channels,
1514 const char *from_colorspace, const char *to_colorspace, int predivide)
1516 colormanagement_transform_ex(buffer, width, height, channels, from_colorspace, to_colorspace, predivide, FALSE);
1519 /* convert the whole buffer from specified by name color space to another
1520 * will do threaded conversion
1522 void IMB_colormanagement_transform_threaded(float *buffer, int width, int height, int channels,
1523 const char *from_colorspace, const char *to_colorspace, int predivide)
1525 colormanagement_transform_ex(buffer, width, height, channels, from_colorspace, to_colorspace, predivide, TRUE);
1528 void IMB_colormanagement_transform_v4(float pixel[4], const char *from_colorspace, const char *to_colorspace)
1530 ColormanageProcessor *cm_processor;
1532 if (from_colorspace[0] == '\0') {
1536 if (!strcmp(from_colorspace, to_colorspace)) {
1537 /* if source and destination color spaces are identical, skip
1538 * threading overhead and simply do nothing
1543 cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
1545 IMB_colormanagement_processor_apply_v4(cm_processor, pixel);
1547 IMB_colormanagement_processor_free(cm_processor);
1550 /* convert pixel from specified by descriptor color space to scene linear
1551 * used by performance-critical areas such as renderer and baker
1553 void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], ColorSpace *colorspace)
1555 OCIO_ConstProcessorRcPtr *processor;
1558 /* should never happen */
1559 printf("%s: perform conversion from unknown color space\n", __func__);
1563 processor = colorspace_to_scene_linear_processor(colorspace);
1566 OCIO_processorApplyRGB(processor, pixel);
1569 /* same as above, but converts colors in opposite direction */
1570 void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], ColorSpace *colorspace)
1572 OCIO_ConstProcessorRcPtr *processor;
1575 /* should never happen */
1576 printf("%s: perform conversion from unknown color space\n", __func__);
1580 processor = colorspace_from_scene_linear_processor(colorspace);
1583 OCIO_processorApplyRGB(processor, pixel);
1586 void IMB_colormanagement_colorspace_to_scene_linear(float *buffer, int width, int height, int channels, struct ColorSpace *colorspace, int predivide)
1588 OCIO_ConstProcessorRcPtr *processor;
1591 /* should never happen */
1592 printf("%s: perform conversion from unknown color space\n", __func__);
1596 processor = colorspace_to_scene_linear_processor(colorspace);
1599 OCIO_PackedImageDesc *img;
1601 img = OCIO_createOCIO_PackedImageDesc(buffer, width, height, channels, sizeof(float),
1602 channels * sizeof(float), channels * sizeof(float) * width);
1605 OCIO_processorApply_predivide(processor, img);
1607 OCIO_processorApply(processor, img);
1609 OCIO_PackedImageDescRelease(img);
1613 /* convert pixel from scene linear to display space using default view
1614 * used by performance-critical areas such as color-related widgets where we want to reduce
1615 * amount of per-widget allocations
1617 void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManagedDisplay *display)
1619 OCIO_ConstProcessorRcPtr *processor;
1621 processor = display_from_scene_linear_processor(display);
1624 OCIO_processorApplyRGB(processor, pixel);
1627 /* same as above, but converts color in opposite direction */
1628 void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], ColorManagedDisplay *display)
1630 OCIO_ConstProcessorRcPtr *processor;
1632 processor = display_to_scene_linear_processor(display);
1635 OCIO_processorApplyRGB(processor, pixel);
1638 void IMB_colormanagement_pixel_to_display_space_v4(float result[4], const float pixel[4],
1639 const ColorManagedViewSettings *view_settings,
1640 const ColorManagedDisplaySettings *display_settings)
1642 ColormanageProcessor *cm_processor;
1644 copy_v4_v4(result, pixel);
1646 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1647 IMB_colormanagement_processor_apply_v4(cm_processor, result);
1648 IMB_colormanagement_processor_free(cm_processor);
1651 void IMB_colormanagement_pixel_to_display_space_v3(float result[3], const float pixel[3],
1652 const ColorManagedViewSettings *view_settings,
1653 const ColorManagedDisplaySettings *display_settings)
1655 ColormanageProcessor *cm_processor;
1657 copy_v3_v3(result, pixel);
1659 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1660 IMB_colormanagement_processor_apply_v3(cm_processor, result);
1661 IMB_colormanagement_processor_free(cm_processor);
1664 static void colormanagement_imbuf_make_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
1665 const ColorManagedDisplaySettings *display_settings, int make_byte)
1667 if (!ibuf->rect && make_byte)
1668 imb_addrectImBuf(ibuf);
1670 if (global_tot_display == 0 || global_tot_view == 0) {
1671 IMB_buffer_float_from_float(ibuf->rect_float, ibuf->rect_float, ibuf->channels, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB,
1672 TRUE, ibuf->x, ibuf->y, ibuf->x, ibuf->x);
1675 colormanage_display_buffer_process_ex(ibuf, ibuf->rect_float, (unsigned char *)ibuf->rect,
1676 view_settings, display_settings);
1680 void IMB_colormanagement_imbuf_make_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
1681 const ColorManagedDisplaySettings *display_settings)
1683 colormanagement_imbuf_make_display_space(ibuf, view_settings, display_settings, FALSE);
1686 /* prepare image buffer to be saved on disk, applying color management if needed
1687 * color management would be applied if image is saving as render result and if
1688 * file format is not expecting float buffer to be in linear space (currently
1689 * JPEG2000 and TIFF are such formats -- they're storing image as float but
1690 * file itself stores applied color space).
1692 * Both byte and float buffers would contain applied color space, and result's
1693 * float_colorspace would be set to display color space. This should be checked
1694 * in image format write callback and if float_colorspace is not NULL, no color
1695 * space transformation should be applied on this buffer.
1697 ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, int save_as_render, int allocate_result, const ColorManagedViewSettings *view_settings,
1698 const ColorManagedDisplaySettings *display_settings, ImageFormatData *image_format_data)
1700 ImBuf *colormanaged_ibuf = ibuf;
1701 int do_colormanagement;
1702 int is_movie = BKE_imtype_is_movie(image_format_data->imtype);
1703 int requires_linear_float = BKE_imtype_requires_linear_float(image_format_data->imtype);
1705 do_colormanagement = save_as_render && (is_movie || !requires_linear_float);
1707 if (do_colormanagement) {
1708 int make_byte = FALSE;
1711 if (allocate_result) {
1712 colormanaged_ibuf = IMB_dupImBuf(ibuf);
1715 /* render pipeline is constructing image buffer itself, but it's re-using byte and float buffers from render result
1716 * make copy of this buffers here sine this buffers would be transformed to other color space here
1719 if (ibuf->rect && (ibuf->mall & IB_rect) == 0) {
1720 ibuf->rect = MEM_dupallocN(ibuf->rect);
1721 ibuf->mall |= IB_rect;
1724 if (ibuf->rect_float && (ibuf->mall & IB_rectfloat) == 0) {
1725 ibuf->rect_float = MEM_dupallocN(ibuf->rect_float);
1726 ibuf->mall |= IB_rectfloat;
1730 /* for proper check whether byte buffer is required by a format or not
1731 * should be pretty safe since this image buffer is supposed to be used for
1732 * saving only and ftype would be overwritten a bit later by BKE_imbuf_write
1734 colormanaged_ibuf->ftype = BKE_imtype_to_ftype(image_format_data->imtype);
1736 /* if file format isn't able to handle float buffer itself,
1737 * we need to allocate byte buffer and store color managed
1740 for (type = IMB_FILE_TYPES; type->is_a; type++) {
1741 if (type->save && type->ftype(type, colormanaged_ibuf)) {
1742 if ((type->flag & IM_FTYPE_FLOAT) == 0)
1749 /* perform color space conversions */
1750 colormanagement_imbuf_make_display_space(colormanaged_ibuf, view_settings, display_settings, make_byte);
1752 if (colormanaged_ibuf->rect_float) {
1753 /* float buffer isn't linear anymore,
1754 * image format write callback should check for this flag and assume
1755 * no space conversion should happen if ibuf->float_colorspace != NULL
1757 colormanaged_ibuf->float_colorspace = display_transform_get_colorspace(view_settings, display_settings);
1761 return colormanaged_ibuf;
1764 static void imbuf_verify_float(ImBuf *ibuf)
1766 /* multiple threads could request for display buffer at once and in case
1767 * view transform is not used it'll lead to display buffer calculated
1769 * it is harmless, but would take much more time (assuming thread lock
1770 * happens faster than running float->byte conversion for average image)
1772 BLI_lock_thread(LOCK_COLORMANAGE);
1774 if (ibuf->rect_float && (ibuf->rect == NULL || (ibuf->userflags & IB_RECT_INVALID))) {
1775 IMB_rect_from_float(ibuf);
1777 ibuf->userflags &= ~IB_RECT_INVALID;
1780 BLI_unlock_thread(LOCK_COLORMANAGE);
1783 /*********************** Public display buffers interfaces *************************/
1785 /* acquire display buffer for given image buffer using specified view and display settings */
1786 unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
1787 const ColorManagedDisplaySettings *display_settings, void **cache_handle)
1789 *cache_handle = NULL;
1791 if (!ibuf->x || !ibuf->y)
1794 if (global_tot_display == 0 || global_tot_view == 0) {
1795 /* if there's no view transform or display transforms, fallback to standard sRGB/linear conversion
1796 * the same logic would be used if OCIO is disabled
1799 imbuf_verify_float(ibuf);
1801 return (unsigned char *) ibuf->rect;
1804 unsigned char *display_buffer;
1806 ColormanageCacheViewSettings cache_view_settings;
1807 ColormanageCacheDisplaySettings cache_display_settings;
1808 ColorManagedViewSettings default_view_settings;
1809 const ColorManagedViewSettings *applied_view_settings;
1811 if (view_settings) {
1812 applied_view_settings = view_settings;
1815 /* if no view settings were specified, use default display transformation
1816 * this happens for images which don't want to be displayed with render settings
1819 init_default_view_settings(display_settings, &default_view_settings);
1820 applied_view_settings = &default_view_settings;
1823 colormanage_view_settings_to_cache(&cache_view_settings, applied_view_settings);
1824 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
1826 BLI_lock_thread(LOCK_COLORMANAGE);
1828 /* ensure color management bit fields exists */
1829 if (!ibuf->display_buffer_flags) {
1830 if (global_tot_display)
1831 ibuf->display_buffer_flags = MEM_callocN(sizeof(unsigned int) * global_tot_display, "imbuf display_buffer_flags");
1833 else if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) {
1834 /* all display buffers were marked as invalid from other areas,
1835 * now propagate this flag to internal color management routines
1837 memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
1839 ibuf->userflags &= ~IB_DISPLAY_BUFFER_INVALID;
1842 display_buffer = colormanage_cache_get(ibuf, &cache_view_settings, &cache_display_settings, cache_handle);
1844 if (display_buffer) {
1845 BLI_unlock_thread(LOCK_COLORMANAGE);
1846 return display_buffer;
1849 buffer_size = DISPLAY_BUFFER_CHANNELS * ibuf->x * ibuf->y * sizeof(char);
1850 display_buffer = MEM_callocN(buffer_size, "imbuf display buffer");
1852 colormanage_display_buffer_process(ibuf, display_buffer, applied_view_settings, display_settings);
1854 colormanage_cache_put(ibuf, &cache_view_settings, &cache_display_settings, display_buffer, cache_handle);
1856 BLI_unlock_thread(LOCK_COLORMANAGE);
1858 return display_buffer;
1862 /* same as IMB_display_buffer_acquire but gets view and display settings from context */
1863 unsigned char *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle)
1865 ColorManagedViewSettings *view_settings;
1866 ColorManagedDisplaySettings *display_settings;
1868 display_transform_get_from_ctx(C, &view_settings, &display_settings);
1870 return IMB_display_buffer_acquire(ibuf, view_settings, display_settings, cache_handle);
1873 void IMB_display_buffer_transform_apply(unsigned char *display_buffer, float *linear_buffer, int width, int height,
1874 int channels, const ColorManagedViewSettings *view_settings,
1875 const ColorManagedDisplaySettings *display_settings, int predivide)
1877 if (global_tot_display == 0 || global_tot_view == 0) {
1878 IMB_buffer_byte_from_float(display_buffer, linear_buffer, channels, 0.0f, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB, FALSE,
1879 width, height, width, width);
1883 ColormanageProcessor *cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1885 buffer = MEM_callocN(channels * width * height * sizeof(float), "display transform temp buffer");
1886 memcpy(buffer, linear_buffer, channels * width * height * sizeof(float));
1888 IMB_colormanagement_processor_apply(cm_processor, buffer, width, height, channels, predivide);
1890 IMB_colormanagement_processor_free(cm_processor);
1892 IMB_buffer_byte_from_float(display_buffer, buffer, channels, 0.0f, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1893 FALSE, width, height, width, width);
1899 void IMB_display_buffer_release(void *cache_handle)
1902 BLI_lock_thread(LOCK_COLORMANAGE);
1904 colormanage_cache_handle_release(cache_handle);
1906 BLI_unlock_thread(LOCK_COLORMANAGE);
1910 /*********************** Display functions *************************/
1912 const char *colormanage_display_get_default_name(void)
1914 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
1915 const char *display_name;
1917 display_name = OCIO_configGetDefaultDisplay(config);
1919 OCIO_configRelease(config);
1921 return display_name;
1924 ColorManagedDisplay *colormanage_display_get_default(void)
1926 const char *display_name = colormanage_display_get_default_name();
1928 if (display_name[0] == '\0')
1931 return colormanage_display_get_named(display_name);
1934 ColorManagedDisplay *colormanage_display_add(const char *name)
1936 ColorManagedDisplay *display;
1939 if (global_displays.last) {
1940 ColorManagedDisplay *last_display = global_displays.last;
1942 index = last_display->index;
1945 display = MEM_callocN(sizeof(ColorManagedDisplay), "ColorManagedDisplay");
1947 display->index = index + 1;
1949 BLI_strncpy(display->name, name, sizeof(display->name));
1951 BLI_addtail(&global_displays, display);
1956 ColorManagedDisplay *colormanage_display_get_named(const char *name)
1958 ColorManagedDisplay *display;
1960 for (display = global_displays.first; display; display = display->next) {
1961 if (!strcmp(display->name, name))
1968 ColorManagedDisplay *colormanage_display_get_indexed(int index)
1970 /* display indices are 1-based */
1971 return BLI_findlink(&global_displays, index - 1);
1974 int IMB_colormanagement_display_get_named_index(const char *name)
1976 ColorManagedDisplay *display;
1978 display = colormanage_display_get_named(name);
1981 return display->index;
1987 const char *IMB_colormanagement_display_get_indexed_name(int index)
1989 ColorManagedDisplay *display;
1991 display = colormanage_display_get_indexed(index);
1994 return display->name;
2000 const char *IMB_colormanagement_display_get_default_name(void)
2002 ColorManagedDisplay *display = colormanage_display_get_default();
2004 return display->name;
2007 /* used by performance-critical pixel processing areas, such as color widgets */
2008 ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name)
2010 return colormanage_display_get_named(name);
2013 const char *IMB_colormanagement_display_get_none_name(void)
2015 if (colormanage_display_get_named("None") != NULL)
2018 return colormanage_display_get_default_name();
2021 /*********************** View functions *************************/
2023 const char *colormanage_view_get_default_name(const ColorManagedDisplay *display)
2025 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
2028 name = OCIO_configGetDefaultView(config, display->name);
2030 OCIO_configRelease(config);
2035 ColorManagedView *colormanage_view_get_default(const ColorManagedDisplay *display)
2037 const char *name = colormanage_view_get_default_name(display);
2039 if (!name || name[0] == '\0')
2042 return colormanage_view_get_named(name);
2045 ColorManagedView *colormanage_view_add(const char *name)
2047 ColorManagedView *view;
2048 int index = global_tot_view;
2050 view = MEM_callocN(sizeof(ColorManagedView), "ColorManagedView");
2051 view->index = index + 1;
2052 BLI_strncpy(view->name, name, sizeof(view->name));
2054 BLI_addtail(&global_views, view);
2061 ColorManagedView *colormanage_view_get_named(const char *name)
2063 ColorManagedView *view;
2065 for (view = global_views.first; view; view = view->next) {
2066 if (!strcmp(view->name, name))
2073 ColorManagedView *colormanage_view_get_indexed(int index)
2075 /* view transform indices are 1-based */
2076 return BLI_findlink(&global_views, index - 1);
2079 int IMB_colormanagement_view_get_named_index(const char *name)
2081 ColorManagedView *view = colormanage_view_get_named(name);
2090 const char *IMB_colormanagement_view_get_indexed_name(int index)
2092 ColorManagedView *view = colormanage_view_get_indexed(index);
2101 const char *IMB_colormanagement_view_get_default_name(const char *display_name)
2103 ColorManagedDisplay *display = colormanage_display_get_named(display_name);
2104 ColorManagedView *view = NULL;
2107 view = colormanage_view_get_default(display);
2115 /*********************** Color space functions *************************/
2117 static void colormanage_description_strip(char *description)
2121 for (i = (int)strlen(description) - 1; i >= 0; i--) {
2122 if (ELEM(description[i], '\r', '\n')) {
2123 description[i] = '\0';
2130 for (i = 0, n = strlen(description); i < n; i++) {
2131 if (ELEM(description[i], '\r', '\n')) {
2132 description[i] = ' ';
2137 ColorSpace *colormanage_colorspace_add(const char *name, const char *description, int is_invertible, int is_data)
2139 ColorSpace *colorspace, *prev_space;
2142 colorspace = MEM_callocN(sizeof(ColorSpace), "ColorSpace");
2144 BLI_strncpy(colorspace->name, name, sizeof(colorspace->name));
2147 BLI_strncpy(colorspace->description, description, sizeof(colorspace->description));
2149 colormanage_description_strip(colorspace->description);
2152 colorspace->is_invertible = is_invertible;
2153 colorspace->is_data = is_data;
2155 for (prev_space = global_colorspaces.first; prev_space; prev_space = prev_space->next) {
2156 if (BLI_strcasecmp(prev_space->name, colorspace->name) > 0)
2159 prev_space->index = counter++;
2163 BLI_addtail(&global_colorspaces, colorspace);
2165 BLI_insertlinkbefore(&global_colorspaces, prev_space, colorspace);
2167 colorspace->index = counter++;
2168 for (; prev_space; prev_space = prev_space->next) {
2169 prev_space->index = counter++;
2172 global_tot_colorspace++;
2177 ColorSpace *colormanage_colorspace_get_named(const char *name)
2179 ColorSpace *colorspace;
2181 for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) {
2182 if (!strcmp(colorspace->name, name))
2189 ColorSpace *colormanage_colorspace_get_roled(int role)
2191 const char *role_colorspace = IMB_colormanagement_role_colorspace_name_get(role);
2193 return colormanage_colorspace_get_named(role_colorspace);
2196 ColorSpace *colormanage_colorspace_get_indexed(int index)
2198 /* display indices are 1-based */
2199 return BLI_findlink(&global_colorspaces, index - 1);
2202 int IMB_colormanagement_colorspace_get_named_index(const char *name)
2204 ColorSpace *colorspace;
2206 colorspace = colormanage_colorspace_get_named(name);
2209 return colorspace->index;
2215 const char *IMB_colormanagement_colorspace_get_indexed_name(int index)
2217 ColorSpace *colorspace;
2219 colorspace = colormanage_colorspace_get_indexed(index);
2222 return colorspace->name;
2228 void IMB_colormanagment_colorspace_from_ibuf_ftype(ColorManagedColorspaceSettings *colorspace_settings, ImBuf *ibuf)
2232 for (type = IMB_FILE_TYPES; type->is_a; type++) {
2233 if (type->save && type->ftype(type, ibuf)) {
2234 const char *role_colorspace;
2236 role_colorspace = IMB_colormanagement_role_colorspace_name_get(type->default_save_role);
2238 BLI_strncpy(colorspace_settings->name, role_colorspace, sizeof(colorspace_settings->name));
2243 /*********************** RNA helper functions *************************/
2245 void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totitem)
2247 ColorManagedDisplay *display;
2249 for (display = global_displays.first; display; display = display->next) {
2250 EnumPropertyItem item;
2252 item.value = display->index;
2253 item.name = display->name;
2254 item.identifier = display->name;
2256 item.description = "";
2258 RNA_enum_item_add(items, totitem, &item);
2262 static void colormanagement_view_item_add(EnumPropertyItem **items, int *totitem, ColorManagedView *view)
2264 EnumPropertyItem item;
2266 item.value = view->index;
2267 item.name = view->name;
2268 item.identifier = view->name;
2270 item.description = "";
2272 RNA_enum_item_add(items, totitem, &item);
2275 void IMB_colormanagement_view_items_add(EnumPropertyItem **items, int *totitem, const char *display_name)
2277 ColorManagedDisplay *display = colormanage_display_get_named(display_name);
2278 ColorManagedView *view;
2281 LinkData *display_view;
2283 for (display_view = display->views.first; display_view; display_view = display_view->next) {
2284 view = display_view->data;
2286 colormanagement_view_item_add(items, totitem, view);
2291 void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *totitem)
2293 ColorSpace *colorspace;
2295 for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) {
2296 EnumPropertyItem item;
2298 if (!colorspace->is_invertible)
2301 item.value = colorspace->index;
2302 item.name = colorspace->name;
2303 item.identifier = colorspace->name;
2306 if (colorspace->description)
2307 item.description = colorspace->description;
2309 item.description = "";
2311 RNA_enum_item_add(items, totitem, &item);
2315 /*********************** Partial display buffer update *************************/
2318 * Partial display update is supposed to be used by such areas as
2319 * compositor and renderer, This areas are calculating tiles of the
2320 * images and because of performance reasons only this tiles should
2322 * This gives nice visual feedback without slowing things down.
2324 * Updating happens for active display transformation only, all
2325 * the rest buffers would be marked as dirty
2328 static void partial_buffer_update_rect(ImBuf *ibuf, unsigned char *display_buffer, const float *linear_buffer,
2329 const unsigned char *byte_buffer, int display_stride, int linear_stride,
2330 int linear_offset_x, int linear_offset_y, ColormanageProcessor *cm_processor,
2331 int xmin, int ymin, int xmax, int ymax)
2334 int channels = ibuf->channels;
2335 float dither = ibuf->dither;
2336 ColorSpace *rect_colorspace = ibuf->rect_colorspace;
2337 float *display_buffer_float = NULL;
2338 int width = xmax - xmin;
2339 int height = ymax - ymin;
2340 int is_data = ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA;
2342 if (dither != 0.0f) {
2343 /* cm_processor is NULL in cases byte_buffer's space matches display
2345 * in this case we could skip extra transform and only apply dither
2346 * use 4 channels for easier byte->float->byte conversion here so
2347 * (this is only needed to apply dither, in other cases we'll convert
2348 * byte buffer to display directly)
2353 display_buffer_float = MEM_callocN(channels * width * height * sizeof(float), "display buffer for dither");
2357 for (y = ymin; y < ymax; y++) {
2358 for (x = xmin; x < xmax; x++) {
2359 int display_index = (y * display_stride + x) * channels;
2360 int linear_index = ((y - linear_offset_y) * linear_stride + (x - linear_offset_x)) * channels;
2363 if (linear_buffer) {
2364 copy_v4_v4(pixel, (float *) linear_buffer + linear_index);
2366 else if (byte_buffer) {
2367 rgba_uchar_to_float(pixel, byte_buffer + linear_index);
2368 IMB_colormanagement_colorspace_to_scene_linear_v3(pixel, rect_colorspace);
2369 straight_to_premul_v4(pixel);
2373 IMB_colormanagement_processor_apply_v4_predivide(cm_processor, pixel);
2376 if (display_buffer_float) {
2377 int index = ((y - ymin) * width + (x - xmin)) * channels;
2379 copy_v4_v4(display_buffer_float + index, pixel);
2382 float pixel_straight[4];
2383 premul_to_straight_v4_v4(pixel_straight, pixel);
2384 rgba_float_to_uchar(display_buffer + display_index, pixel_straight);
2390 if (display_buffer_float) {
2391 /* huh, for dither we need float buffer first, no cheaper way. currently */
2392 IMB_buffer_float_from_byte(display_buffer_float, byte_buffer,
2393 IB_PROFILE_SRGB, IB_PROFILE_SRGB, TRUE,
2394 width, height, width, display_stride);
2397 int i, width = xmax - xmin;
2399 for (i = ymin; i < ymax; i++) {
2400 int byte_offset = (linear_stride * i + xmin) * 4;
2401 int display_offset = (display_stride * i + xmin) * 4;
2403 memcpy(display_buffer + display_offset, byte_buffer + byte_offset, 4 * sizeof(char) * width);
2408 if (display_buffer_float) {
2409 int display_index = (ymin * display_stride + xmin) * channels;
2411 IMB_buffer_byte_from_float(display_buffer + display_index, display_buffer_float, channels, dither,
2412 IB_PROFILE_SRGB, IB_PROFILE_SRGB, TRUE, width, height, display_stride, width);
2414 MEM_freeN(display_buffer_float);
2418 void IMB_partial_display_buffer_update(ImBuf *ibuf, const float *linear_buffer, const unsigned char *byte_buffer,
2419 int stride, int offset_x, int offset_y, const ColorManagedViewSettings *view_settings,
2420 const ColorManagedDisplaySettings *display_settings,
2421 int xmin, int ymin, int xmax, int ymax, int update_orig_byte_buffer)
2423 if ((ibuf->rect && ibuf->rect_float) || update_orig_byte_buffer) {
2424 /* update byte buffer created by legacy color management */
2426 unsigned char *rect = (unsigned char *) ibuf->rect;
2427 int channels = ibuf->channels;
2428 int width = xmax - xmin;
2429 int height = ymax - ymin;
2430 int rect_index = (ymin * ibuf->x + xmin) * channels;
2431 int linear_index = ((ymin - offset_y) * stride + (xmin - offset_x)) * channels;
2433 IMB_buffer_byte_from_float(rect + rect_index, linear_buffer + linear_index, channels, ibuf->dither,
2434 IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB, TRUE, width, height, ibuf->x, stride);
2437 if (ibuf->display_buffer_flags) {
2438 ColormanageCacheViewSettings cache_view_settings;
2439 ColormanageCacheDisplaySettings cache_display_settings;
2440 void *cache_handle = NULL;
2441 unsigned char *display_buffer = NULL;
2442 int view_flag, display_index, buffer_width;
2444 colormanage_view_settings_to_cache(&cache_view_settings, view_settings);
2445 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
2447 view_flag = 1 << (cache_view_settings.view - 1);
2448 display_index = cache_display_settings.display - 1;
2450 BLI_lock_thread(LOCK_COLORMANAGE);
2451 if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0)
2452 display_buffer = colormanage_cache_get(ibuf, &cache_view_settings, &cache_display_settings, &cache_handle);
2454 /* in some rare cases buffer's dimension could be changing directly from
2456 * this i.e. happens when image editor acquires render result
2458 buffer_width = ibuf->x;
2460 /* mark all other buffers as invalid */
2461 memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
2462 ibuf->display_buffer_flags[display_index] |= view_flag;
2464 BLI_unlock_thread(LOCK_COLORMANAGE);
2466 if (display_buffer) {
2467 ColormanageProcessor *cm_processor = NULL;
2468 int skip_transform = 0;
2470 /* byte buffer is assumed to be in imbuf's rect space, so if byte buffer
2471 * is known we could skip display->linear->display conversion in case
2472 * display color space matches imbuf's rect space
2474 if (byte_buffer != NULL)
2475 skip_transform = is_ibuf_rect_in_display_space(ibuf, view_settings, display_settings);
2477 if (!skip_transform)
2478 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2480 partial_buffer_update_rect(ibuf, display_buffer, linear_buffer, byte_buffer, buffer_width, stride,
2481 offset_x, offset_y, cm_processor, xmin, ymin, xmax, ymax);
2484 IMB_colormanagement_processor_free(cm_processor);
2486 IMB_display_buffer_release(cache_handle);
2491 /*********************** Pixel processor functions *************************/
2493 ColormanageProcessor *IMB_colormanagement_display_processor_new(const ColorManagedViewSettings *view_settings,
2494 const ColorManagedDisplaySettings *display_settings)
2496 ColormanageProcessor *cm_processor;
2497 ColorManagedViewSettings default_view_settings;
2498 const ColorManagedViewSettings *applied_view_settings;
2499 ColorSpace *display_space;
2501 cm_processor = MEM_callocN(sizeof(ColormanageProcessor), "colormanagement processor");
2503 if (view_settings) {
2504 applied_view_settings = view_settings;
2507 init_default_view_settings(display_settings, &default_view_settings);
2508 applied_view_settings = &default_view_settings;
2511 display_space = display_transform_get_colorspace(applied_view_settings, display_settings);
2513 cm_processor->is_data_result = display_space->is_data;
2515 cm_processor->processor = create_display_buffer_processor(applied_view_settings->view_transform, display_settings->display_device,
2516 applied_view_settings->exposure, applied_view_settings->gamma);
2518 if (applied_view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) {
2519 cm_processor->curve_mapping = curvemapping_copy(applied_view_settings->curve_mapping);
2520 curvemapping_premultiply(cm_processor->curve_mapping, FALSE);
2523 return cm_processor;
2526 ColormanageProcessor *IMB_colormanagement_colorspace_processor_new(const char *from_colorspace, const char *to_colorspace)
2528 ColormanageProcessor *cm_processor;
2529 ColorSpace *color_space;
2531 cm_processor = MEM_callocN(sizeof(ColormanageProcessor), "colormanagement processor");
2533 color_space = colormanage_colorspace_get_named(to_colorspace);
2534 cm_processor->is_data_result = color_space->is_data;
2536 cm_processor->processor = create_colorspace_transform_processor(from_colorspace, to_colorspace);
2538 return cm_processor;
2541 void IMB_colormanagement_processor_apply_v4(ColormanageProcessor *cm_processor, float pixel[4])
2543 if (cm_processor->curve_mapping)
2544 curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
2546 if (cm_processor->processor)
2547 OCIO_processorApplyRGBA(cm_processor->processor, pixel);
2550 void IMB_colormanagement_processor_apply_v4_predivide(ColormanageProcessor *cm_processor, float pixel[4])
2552 if (cm_processor->curve_mapping)
2553 curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
2555 if (cm_processor->processor)
2556 OCIO_processorApplyRGBA_predivide(cm_processor->processor, pixel);
2559 void IMB_colormanagement_processor_apply_v3(ColormanageProcessor *cm_processor, float pixel[3])
2561 if (cm_processor->curve_mapping)
2562 curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
2564 if (cm_processor->processor)
2565 OCIO_processorApplyRGB(cm_processor->processor, pixel);
2568 void IMB_colormanagement_processor_apply(ColormanageProcessor *cm_processor, float *buffer, int width, int height,
2569 int channels, int predivide)
2571 /* apply curve mapping */
2572 if (cm_processor->curve_mapping) {
2575 for (y = 0; y < height; y++) {
2576 for (x = 0; x < width; x++) {
2577 float *pixel = buffer + channels * (y * width + x);
2579 curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, channels);
2584 if (cm_processor->processor && channels >= 3) {
2585 OCIO_PackedImageDesc *img;
2587 /* apply OCIO processor */
2588 img = OCIO_createOCIO_PackedImageDesc(buffer, width, height, channels, sizeof(float),
2589 channels * sizeof(float), channels * sizeof(float) * width);
2592 OCIO_processorApply_predivide(cm_processor->processor, img);
2594 OCIO_processorApply(cm_processor->processor, img);
2596 OCIO_PackedImageDescRelease(img);
2600 void IMB_colormanagement_processor_free(ColormanageProcessor *cm_processor)
2602 if (cm_processor->curve_mapping)
2603 curvemapping_free(cm_processor->curve_mapping);
2604 if (cm_processor->processor)
2605 OCIO_processorRelease(cm_processor->processor);
2607 MEM_freeN(cm_processor);