Color management: change view transform for color pickers and display modes.
[blender.git] / source / blender / imbuf / intern / colormanagement.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2012 by Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file blender/imbuf/intern/colormanagement.c
21  *  \ingroup imbuf
22  */
23
24 #include "IMB_colormanagement.h"
25 #include "IMB_colormanagement_intern.h"
26
27 #include <string.h>
28 #include <math.h>
29
30 #include "DNA_color_types.h"
31 #include "DNA_image_types.h"
32 #include "DNA_movieclip_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_space_types.h"
35
36 #include "IMB_imbuf.h"
37 #include "IMB_imbuf_types.h"
38 #include "IMB_filetype.h"
39 #include "IMB_filter.h"
40 #include "IMB_moviecache.h"
41 #include "IMB_metadata.h"
42
43 #include "MEM_guardedalloc.h"
44
45 #include "BLI_blenlib.h"
46 #include "BLI_math.h"
47 #include "BLI_math_color.h"
48 #include "BLI_string.h"
49 #include "BLI_threads.h"
50 #include "BLI_rect.h"
51
52 #include "BKE_appdir.h"
53 #include "BKE_colortools.h"
54 #include "BKE_context.h"
55 #include "BKE_image.h"
56 #include "BKE_main.h"
57 #include "BKE_sequencer.h"
58
59 #include "RNA_define.h"
60
61 #include <ocio_capi.h>
62
63 /*********************** Global declarations *************************/
64
65 #define DISPLAY_BUFFER_CHANNELS 4
66
67 /* ** list of all supported color spaces, displays and views */
68 static char global_role_scene_linear[MAX_COLORSPACE_NAME];
69 static char global_role_color_picking[MAX_COLORSPACE_NAME];
70 static char global_role_texture_painting[MAX_COLORSPACE_NAME];
71 static char global_role_default_byte[MAX_COLORSPACE_NAME];
72 static char global_role_default_float[MAX_COLORSPACE_NAME];
73 static char global_role_default_sequencer[MAX_COLORSPACE_NAME];
74
75 static ListBase global_colorspaces = {NULL, NULL};
76 static ListBase global_displays = {NULL, NULL};
77 static ListBase global_views = {NULL, NULL};
78 static ListBase global_looks = {NULL, NULL};
79
80 static int global_tot_colorspace = 0;
81 static int global_tot_display = 0;
82 static int global_tot_view = 0;
83 static int global_tot_looks = 0;
84
85 /* Luma coefficients and XYZ to RGB to be initialized by OCIO. */
86 float imbuf_luma_coefficients[3] = {0.0f};
87 float imbuf_xyz_to_rgb[3][3] = {{0.0f}};
88 float imbuf_rgb_to_xyz[3][3] = {{0.0f}};
89 static float imbuf_xyz_to_linear_srgb[3][3] = {{0.0f}};
90 static float imbuf_linear_srgb_to_xyz[3][3] = {{0.0f}};
91
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
96  */
97 static pthread_mutex_t processor_lock = BLI_MUTEX_INITIALIZER;
98
99 typedef struct ColormanageProcessor {
100         OCIO_ConstProcessorRcPtr *processor;
101         CurveMapping *curve_mapping;
102         bool is_data_result;
103 } ColormanageProcessor;
104
105 static struct global_glsl_state {
106         /* Actual processor used for GLSL baked LUTs. */
107         OCIO_ConstProcessorRcPtr *processor;
108
109         /* Settings of processor for comparison. */
110         char look[MAX_COLORSPACE_NAME];
111         char view[MAX_COLORSPACE_NAME];
112         char display[MAX_COLORSPACE_NAME];
113         char input[MAX_COLORSPACE_NAME];
114         float exposure, gamma;
115
116         CurveMapping *curve_mapping, *orig_curve_mapping;
117         bool use_curve_mapping;
118         int curve_mapping_timestamp;
119         OCIO_CurveMappingSettings curve_mapping_settings;
120
121         /* Container for GLSL state needed for OCIO module. */
122         struct OCIO_GLSLDrawState *ocio_glsl_state;
123         struct OCIO_GLSLDrawState *transform_ocio_glsl_state;
124 } global_glsl_state = {NULL};
125
126 static struct global_color_picking_state {
127         /* Cached processor for color picking conversion. */
128         OCIO_ConstProcessorRcPtr *processor_to;
129         OCIO_ConstProcessorRcPtr *processor_from;
130         bool failed;
131 } global_color_picking_state = {NULL};
132
133 /*********************** Color managed cache *************************/
134
135 /* Cache Implementation Notes
136  * ==========================
137  *
138  * All color management cache stuff is stored in two properties of
139  * image buffers:
140  *
141  *   1. display_buffer_flags
142  *
143  *      This is a bit field which used to mark calculated transformations
144  *      for particular image buffer. Index inside of this array means index
145  *      of a color managed display. Element with given index matches view
146  *      transformations applied for a given display. So if bit B of array
147  *      element B is set to 1, this means display buffer with display index
148  *      of A and view transform of B was ever calculated for this imbuf.
149  *
150  *      In contrast with indices in global lists of displays and views this
151  *      indices are 0-based, not 1-based. This is needed to save some bytes
152  *      of memory.
153  *
154  *   2. colormanage_cache
155  *
156  *      This is a pointer to a structure which holds all data which is
157  *      needed for color management cache to work.
158  *
159  *      It contains two parts:
160  *        - data
161  *        - moviecache
162  *
163  *      Data field is used to store additional information about cached
164  *      buffers which affects on whether cached buffer could be used.
165  *      This data can't go to cache key because changes in this data
166  *      shouldn't lead extra buffers adding to cache, it shall
167  *      invalidate cached images.
168  *
169  *      Currently such a data contains only exposure and gamma, but
170  *      would likely extended further.
171  *
172  *      data field is not null only for elements of cache, not used for
173  *      original image buffers.
174  *
175  *      Color management cache is using generic MovieCache implementation
176  *      to make it easier to deal with memory limitation.
177  *
178  *      Currently color management is using the same memory limitation
179  *      pool as sequencer and clip editor are using which means color
180  *      managed buffers would be removed from the cache as soon as new
181  *      frames are loading for the movie clip and there's no space in
182  *      cache.
183  *
184  *      Every image buffer has got own movie cache instance, which
185  *      means keys for color managed buffers could be really simple
186  *      and look up in this cache would be fast and independent from
187  *      overall amount of color managed images.
188  */
189
190 /* NOTE: ColormanageCacheViewSettings and ColormanageCacheDisplaySettings are
191  *       quite the same as ColorManagedViewSettings and ColorManageDisplaySettings
192  *       but they holds indexes of all transformations and color spaces, not
193  *       their names.
194  *
195  *       This helps avoid extra colorspace / display / view lookup without
196  *       requiring to pass all variables which affects on display buffer
197  *       to color management cache system and keeps calls small and nice.
198  */
199 typedef struct ColormanageCacheViewSettings {
200         int flag;
201         int look;
202         int view;
203         float exposure;
204         float gamma;
205         float dither;
206         CurveMapping *curve_mapping;
207 } ColormanageCacheViewSettings;
208
209 typedef struct ColormanageCacheDisplaySettings {
210         int display;
211 } ColormanageCacheDisplaySettings;
212
213 typedef struct ColormanageCacheKey {
214         int view;            /* view transformation used for display buffer */
215         int display;         /* display device name */
216 } ColormanageCacheKey;
217
218 typedef struct ColormanageCacheData {
219         int flag;        /* view flags of cached buffer */
220         int look;        /* Additional artistics transform */
221         float exposure;  /* exposure value cached buffer is calculated with */
222         float gamma;     /* gamma value cached buffer is calculated with */
223         float dither;    /* dither value cached buffer is calculated with */
224         CurveMapping *curve_mapping;  /* curve mapping used for cached buffer */
225         int curve_mapping_timestamp;  /* time stamp of curve mapping used for cached buffer */
226 } ColormanageCacheData;
227
228 typedef struct ColormanageCache {
229         struct MovieCache *moviecache;
230
231         ColormanageCacheData *data;
232 } ColormanageCache;
233
234 static struct MovieCache *colormanage_moviecache_get(const ImBuf *ibuf)
235 {
236         if (!ibuf->colormanage_cache)
237                 return NULL;
238
239         return ibuf->colormanage_cache->moviecache;
240 }
241
242 static ColormanageCacheData *colormanage_cachedata_get(const ImBuf *ibuf)
243 {
244         if (!ibuf->colormanage_cache)
245                 return NULL;
246
247         return ibuf->colormanage_cache->data;
248 }
249
250 static unsigned int colormanage_hashhash(const void *key_v)
251 {
252         const ColormanageCacheKey *key = key_v;
253
254         unsigned int rval = (key->display << 16) | (key->view % 0xffff);
255
256         return rval;
257 }
258
259 static bool colormanage_hashcmp(const void *av, const void *bv)
260 {
261         const ColormanageCacheKey *a = av;
262         const ColormanageCacheKey *b = bv;
263
264         return ((a->view != b->view) ||
265                 (a->display != b->display));
266 }
267
268 static struct MovieCache *colormanage_moviecache_ensure(ImBuf *ibuf)
269 {
270         if (!ibuf->colormanage_cache)
271                 ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage cache");
272
273         if (!ibuf->colormanage_cache->moviecache) {
274                 struct MovieCache *moviecache;
275
276                 moviecache = IMB_moviecache_create("colormanage cache", sizeof(ColormanageCacheKey),
277                                                    colormanage_hashhash, colormanage_hashcmp);
278
279                 ibuf->colormanage_cache->moviecache = moviecache;
280         }
281
282         return ibuf->colormanage_cache->moviecache;
283 }
284
285 static void colormanage_cachedata_set(ImBuf *ibuf, ColormanageCacheData *data)
286 {
287         if (!ibuf->colormanage_cache)
288                 ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage cache");
289
290         ibuf->colormanage_cache->data = data;
291 }
292
293 static void colormanage_view_settings_to_cache(ImBuf *ibuf,
294                                                ColormanageCacheViewSettings *cache_view_settings,
295                                                const ColorManagedViewSettings *view_settings)
296 {
297         int look = IMB_colormanagement_look_get_named_index(view_settings->look);
298         int view = IMB_colormanagement_view_get_named_index(view_settings->view_transform);
299
300         cache_view_settings->look = look;
301         cache_view_settings->view = view;
302         cache_view_settings->exposure = view_settings->exposure;
303         cache_view_settings->gamma = view_settings->gamma;
304         cache_view_settings->dither = ibuf->dither;
305         cache_view_settings->flag = view_settings->flag;
306         cache_view_settings->curve_mapping = view_settings->curve_mapping;
307 }
308
309 static void colormanage_display_settings_to_cache(ColormanageCacheDisplaySettings *cache_display_settings,
310                                                   const ColorManagedDisplaySettings *display_settings)
311 {
312         int display = IMB_colormanagement_display_get_named_index(display_settings->display_device);
313
314         cache_display_settings->display = display;
315 }
316
317 static void colormanage_settings_to_key(ColormanageCacheKey *key,
318                                         const ColormanageCacheViewSettings *view_settings,
319                                         const ColormanageCacheDisplaySettings *display_settings)
320 {
321         key->view = view_settings->view;
322         key->display = display_settings->display;
323 }
324
325 static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf, ColormanageCacheKey *key, void **cache_handle)
326 {
327         ImBuf *cache_ibuf;
328         struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
329
330         if (!moviecache) {
331                 /* if there's no moviecache it means no color management was applied on given image buffer before */
332
333                 return NULL;
334         }
335
336         *cache_handle = NULL;
337
338         cache_ibuf = IMB_moviecache_get(moviecache, key);
339
340         *cache_handle = cache_ibuf;
341
342         return cache_ibuf;
343 }
344
345 static unsigned char *colormanage_cache_get(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings,
346                                             const ColormanageCacheDisplaySettings *display_settings,
347                                             void **cache_handle)
348 {
349         ColormanageCacheKey key;
350         ImBuf *cache_ibuf;
351         int view_flag = 1 << (view_settings->view - 1);
352         CurveMapping *curve_mapping = view_settings->curve_mapping;
353         int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
354
355         colormanage_settings_to_key(&key, view_settings, display_settings);
356
357         /* check whether image was marked as dirty for requested transform */
358         if ((ibuf->display_buffer_flags[display_settings->display - 1] & view_flag) == 0) {
359                 return NULL;
360         }
361
362         cache_ibuf = colormanage_cache_get_ibuf(ibuf, &key, cache_handle);
363
364         if (cache_ibuf) {
365                 ColormanageCacheData *cache_data;
366
367                 BLI_assert(cache_ibuf->x == ibuf->x &&
368                            cache_ibuf->y == ibuf->y);
369
370                 /* only buffers with different color space conversions are being stored
371                  * in cache separately. buffer which were used only different exposure/gamma
372                  * are re-suing the same cached buffer
373                  *
374                  * check here which exposure/gamma/curve was used for cached buffer and if they're
375                  * different from requested buffer should be re-generated
376                  */
377                 cache_data = colormanage_cachedata_get(cache_ibuf);
378
379                 if (cache_data->look != view_settings->look ||
380                     cache_data->exposure != view_settings->exposure ||
381                     cache_data->gamma != view_settings->gamma ||
382                     cache_data->dither != view_settings->dither ||
383                     cache_data->flag != view_settings->flag ||
384                     cache_data->curve_mapping != curve_mapping ||
385                     cache_data->curve_mapping_timestamp != curve_mapping_timestamp)
386                 {
387                         *cache_handle = NULL;
388
389                         IMB_freeImBuf(cache_ibuf);
390
391                         return NULL;
392                 }
393
394                 return (unsigned char *) cache_ibuf->rect;
395         }
396
397         return NULL;
398 }
399
400 static void colormanage_cache_put(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings,
401                                   const ColormanageCacheDisplaySettings *display_settings,
402                                   unsigned char *display_buffer, void **cache_handle)
403 {
404         ColormanageCacheKey key;
405         ImBuf *cache_ibuf;
406         ColormanageCacheData *cache_data;
407         int view_flag = 1 << (view_settings->view - 1);
408         struct MovieCache *moviecache = colormanage_moviecache_ensure(ibuf);
409         CurveMapping *curve_mapping = view_settings->curve_mapping;
410         int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
411
412         colormanage_settings_to_key(&key, view_settings, display_settings);
413
414         /* mark display buffer as valid */
415         ibuf->display_buffer_flags[display_settings->display - 1] |= view_flag;
416
417         /* buffer itself */
418         cache_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0);
419         cache_ibuf->rect = (unsigned int *) display_buffer;
420
421         cache_ibuf->mall |= IB_rect;
422         cache_ibuf->flags |= IB_rect;
423
424         /* store data which is needed to check whether cached buffer could be used for color managed display settings */
425         cache_data = MEM_callocN(sizeof(ColormanageCacheData), "color manage cache imbuf data");
426         cache_data->look = view_settings->look;
427         cache_data->exposure = view_settings->exposure;
428         cache_data->gamma = view_settings->gamma;
429         cache_data->dither = view_settings->dither;
430         cache_data->flag = view_settings->flag;
431         cache_data->curve_mapping = curve_mapping;
432         cache_data->curve_mapping_timestamp = curve_mapping_timestamp;
433
434         colormanage_cachedata_set(cache_ibuf, cache_data);
435
436         *cache_handle = cache_ibuf;
437
438         IMB_moviecache_put(moviecache, &key, cache_ibuf);
439 }
440
441 static void colormanage_cache_handle_release(void *cache_handle)
442 {
443         ImBuf *cache_ibuf = cache_handle;
444
445         IMB_freeImBuf(cache_ibuf);
446 }
447
448 /*********************** Initialization / De-initialization *************************/
449
450 static void colormanage_role_color_space_name_get(OCIO_ConstConfigRcPtr *config, char *colorspace_name, const char *role, const char *backup_role)
451 {
452         OCIO_ConstColorSpaceRcPtr *ociocs;
453
454         ociocs = OCIO_configGetColorSpace(config, role);
455
456         if (!ociocs && backup_role)
457                 ociocs = OCIO_configGetColorSpace(config, backup_role);
458
459         if (ociocs) {
460                 const char *name = OCIO_colorSpaceGetName(ociocs);
461
462                 /* assume function was called with buffer properly allocated to MAX_COLORSPACE_NAME chars */
463                 BLI_strncpy(colorspace_name, name, MAX_COLORSPACE_NAME);
464                 OCIO_colorSpaceRelease(ociocs);
465         }
466         else {
467                 printf("Color management: Error could not find role %s role.\n", role);
468         }
469 }
470
471 static void colormanage_load_config(OCIO_ConstConfigRcPtr *config)
472 {
473         int tot_colorspace, tot_display, tot_display_view, tot_looks;
474         int index, viewindex, viewindex2;
475         const char *name;
476
477         /* get roles */
478         colormanage_role_color_space_name_get(config, global_role_scene_linear, OCIO_ROLE_SCENE_LINEAR, NULL);
479         colormanage_role_color_space_name_get(config, global_role_color_picking, OCIO_ROLE_COLOR_PICKING, NULL);
480         colormanage_role_color_space_name_get(config, global_role_texture_painting, OCIO_ROLE_TEXTURE_PAINT, NULL);
481         colormanage_role_color_space_name_get(config, global_role_default_sequencer, OCIO_ROLE_DEFAULT_SEQUENCER, OCIO_ROLE_SCENE_LINEAR);
482         colormanage_role_color_space_name_get(config, global_role_default_byte, OCIO_ROLE_DEFAULT_BYTE, OCIO_ROLE_TEXTURE_PAINT);
483         colormanage_role_color_space_name_get(config, global_role_default_float, OCIO_ROLE_DEFAULT_FLOAT, OCIO_ROLE_SCENE_LINEAR);
484
485         /* load colorspaces */
486         tot_colorspace = OCIO_configGetNumColorSpaces(config);
487         for (index = 0 ; index < tot_colorspace; index++) {
488                 OCIO_ConstColorSpaceRcPtr *ocio_colorspace;
489                 const char *description;
490                 bool is_invertible, is_data;
491
492                 name = OCIO_configGetColorSpaceNameByIndex(config, index);
493
494                 ocio_colorspace = OCIO_configGetColorSpace(config, name);
495                 description = OCIO_colorSpaceGetDescription(ocio_colorspace);
496                 is_invertible = OCIO_colorSpaceIsInvertible(ocio_colorspace);
497                 is_data = OCIO_colorSpaceIsData(ocio_colorspace);
498
499                 colormanage_colorspace_add(name, description, is_invertible, is_data);
500
501                 OCIO_colorSpaceRelease(ocio_colorspace);
502         }
503
504         /* load displays */
505         viewindex2 = 0;
506         tot_display = OCIO_configGetNumDisplays(config);
507
508         for (index = 0 ; index < tot_display; index++) {
509                 const char *displayname;
510                 ColorManagedDisplay *display;
511
512                 displayname = OCIO_configGetDisplay(config, index);
513
514                 display = colormanage_display_add(displayname);
515
516                 /* load views */
517                 tot_display_view = OCIO_configGetNumViews(config, displayname);
518                 for (viewindex = 0 ; viewindex < tot_display_view; viewindex++, viewindex2++) {
519                         const char *viewname;
520                         ColorManagedView *view;
521                         LinkData *display_view;
522
523                         viewname = OCIO_configGetView(config, displayname, viewindex);
524
525                         /* first check if view transform with given name was already loaded */
526                         view = colormanage_view_get_named(viewname);
527
528                         if (!view) {
529                                 view = colormanage_view_add(viewname);
530                         }
531
532                         display_view = BLI_genericNodeN(view);
533
534                         BLI_addtail(&display->views, display_view);
535                 }
536         }
537
538         global_tot_display = tot_display;
539
540         /* load looks */
541         tot_looks = OCIO_configGetNumLooks(config);
542         colormanage_look_add("None", "", true);
543         for (index = 0; index < tot_looks; index++) {
544                 OCIO_ConstLookRcPtr *ocio_look;
545                 const char *process_space;
546
547                 name = OCIO_configGetLookNameByIndex(config, index);
548                 ocio_look = OCIO_configGetLook(config, name);
549                 process_space = OCIO_lookGetProcessSpace(ocio_look);
550                 OCIO_lookRelease(ocio_look);
551
552                 colormanage_look_add(name, process_space, false);
553         }
554
555         /* Load luminance coefficients. */
556         OCIO_configGetDefaultLumaCoefs(config, imbuf_luma_coefficients);
557         OCIO_configGetXYZtoRGB(config, imbuf_xyz_to_rgb);
558         invert_m3_m3(imbuf_rgb_to_xyz, imbuf_xyz_to_rgb);
559         copy_m3_m3(imbuf_xyz_to_linear_srgb, OCIO_XYZ_TO_LINEAR_SRGB);
560         invert_m3_m3(imbuf_linear_srgb_to_xyz, imbuf_xyz_to_linear_srgb);
561 }
562
563 static void colormanage_free_config(void)
564 {
565         ColorSpace *colorspace;
566         ColorManagedDisplay *display;
567
568         /* free color spaces */
569         colorspace = global_colorspaces.first;
570         while (colorspace) {
571                 ColorSpace *colorspace_next = colorspace->next;
572
573                 /* free precomputer processors */
574                 if (colorspace->to_scene_linear)
575                         OCIO_processorRelease((OCIO_ConstProcessorRcPtr *) colorspace->to_scene_linear);
576
577                 if (colorspace->from_scene_linear)
578                         OCIO_processorRelease((OCIO_ConstProcessorRcPtr *) colorspace->from_scene_linear);
579
580                 /* free color space itself */
581                 MEM_freeN(colorspace);
582
583                 colorspace = colorspace_next;
584         }
585         BLI_listbase_clear(&global_colorspaces);
586         global_tot_colorspace = 0;
587
588         /* free displays */
589         display = global_displays.first;
590         while (display) {
591                 ColorManagedDisplay *display_next = display->next;
592
593                 /* free precomputer processors */
594                 if (display->to_scene_linear)
595                         OCIO_processorRelease((OCIO_ConstProcessorRcPtr *) display->to_scene_linear);
596
597                 if (display->from_scene_linear)
598                         OCIO_processorRelease((OCIO_ConstProcessorRcPtr *) display->from_scene_linear);
599
600                 /* free list of views */
601                 BLI_freelistN(&display->views);
602
603                 MEM_freeN(display);
604                 display = display_next;
605         }
606         BLI_listbase_clear(&global_displays);
607         global_tot_display = 0;
608
609         /* free views */
610         BLI_freelistN(&global_views);
611         global_tot_view = 0;
612
613         /* free looks */
614         BLI_freelistN(&global_looks);
615         global_tot_looks = 0;
616
617         OCIO_exit();
618 }
619
620 void colormanagement_init(void)
621 {
622         const char *ocio_env;
623         const char *configdir;
624         char configfile[FILE_MAX];
625         OCIO_ConstConfigRcPtr *config = NULL;
626
627         OCIO_init();
628
629         ocio_env = BLI_getenv("OCIO");
630
631         if (ocio_env && ocio_env[0] != '\0') {
632                 config = OCIO_configCreateFromEnv();
633                 if (config != NULL) {
634                         printf("Color management: Using %s as a configuration file\n", ocio_env);
635                 }
636         }
637
638         if (config == NULL) {
639                 configdir = BKE_appdir_folder_id(BLENDER_DATAFILES, "colormanagement");
640
641                 if (configdir) {
642                         BLI_join_dirfile(configfile, sizeof(configfile), configdir, BCM_CONFIG_FILE);
643
644 #ifdef WIN32
645                         {
646                                 /* quite a hack to support loading configuration from path with non-acii symbols */
647
648                                 char short_name[256];
649                                 BLI_get_short_name(short_name, configfile);
650                                 config = OCIO_configCreateFromFile(short_name);
651                         }
652 #else
653                         config = OCIO_configCreateFromFile(configfile);
654 #endif
655                 }
656         }
657
658         if (config == NULL) {
659                 printf("Color management: using fallback mode for management\n");
660
661                 config = OCIO_configCreateFallback();
662         }
663
664         if (config) {
665                 OCIO_setCurrentConfig(config);
666
667                 colormanage_load_config(config);
668
669                 OCIO_configRelease(config);
670         }
671
672         /* If there're no valid display/views, use fallback mode. */
673         if (global_tot_display == 0 || global_tot_view == 0) {
674                 printf("Color management: no displays/views in the config, using fallback mode instead\n");
675
676                 /* Free old config. */
677                 colormanage_free_config();
678
679                 /* Initialize fallback config. */
680                 config = OCIO_configCreateFallback();
681                 colormanage_load_config(config);
682         }
683
684         BLI_init_srgb_conversion();
685 }
686
687 void colormanagement_exit(void)
688 {
689         if (global_glsl_state.processor)
690                 OCIO_processorRelease(global_glsl_state.processor);
691
692         if (global_glsl_state.curve_mapping)
693                 curvemapping_free(global_glsl_state.curve_mapping);
694
695         if (global_glsl_state.curve_mapping_settings.lut)
696                 MEM_freeN(global_glsl_state.curve_mapping_settings.lut);
697
698         if (global_glsl_state.ocio_glsl_state)
699                 OCIO_freeOGLState(global_glsl_state.ocio_glsl_state);
700
701         if (global_glsl_state.transform_ocio_glsl_state)
702                 OCIO_freeOGLState(global_glsl_state.transform_ocio_glsl_state);
703
704         if (global_color_picking_state.processor_to)
705                 OCIO_processorRelease(global_color_picking_state.processor_to);
706
707         if (global_color_picking_state.processor_from)
708                 OCIO_processorRelease(global_color_picking_state.processor_from);
709
710         memset(&global_glsl_state, 0, sizeof(global_glsl_state));
711         memset(&global_color_picking_state, 0, sizeof(global_color_picking_state));
712
713         colormanage_free_config();
714 }
715
716 /*********************** Internal functions *************************/
717
718 void colormanage_cache_free(ImBuf *ibuf)
719 {
720         if (ibuf->display_buffer_flags) {
721                 MEM_freeN(ibuf->display_buffer_flags);
722
723                 ibuf->display_buffer_flags = NULL;
724         }
725
726         if (ibuf->colormanage_cache) {
727                 ColormanageCacheData *cache_data = colormanage_cachedata_get(ibuf);
728                 struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
729
730                 if (cache_data) {
731                         MEM_freeN(cache_data);
732                 }
733
734                 if (moviecache) {
735                         IMB_moviecache_free(moviecache);
736                 }
737
738                 MEM_freeN(ibuf->colormanage_cache);
739
740                 ibuf->colormanage_cache = NULL;
741         }
742 }
743
744 void IMB_colormanagement_display_settings_from_ctx(const bContext *C,
745                                                    ColorManagedViewSettings **view_settings_r,
746                                                    ColorManagedDisplaySettings **display_settings_r)
747 {
748         Scene *scene = CTX_data_scene(C);
749         SpaceImage *sima = CTX_wm_space_image(C);
750
751         *view_settings_r = &scene->view_settings;
752         *display_settings_r = &scene->display_settings;
753
754         if (sima && sima->image) {
755                 if ((sima->image->flag & IMA_VIEW_AS_RENDER) == 0)
756                         *view_settings_r = NULL;
757         }
758 }
759
760 const char *IMB_colormanagement_get_display_colorspace_name(const ColorManagedViewSettings *view_settings,
761                                                             const ColorManagedDisplaySettings *display_settings)
762 {
763         OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
764
765         const char *display = display_settings->display_device;
766         const char *view = view_settings->view_transform;
767         const char *colorspace_name;
768
769         colorspace_name = OCIO_configGetDisplayColorSpaceName(config, display, view);
770
771         OCIO_configRelease(config);
772
773         return colorspace_name;
774 }
775
776 static ColorSpace *display_transform_get_colorspace(const ColorManagedViewSettings *view_settings,
777                                                     const ColorManagedDisplaySettings *display_settings)
778 {
779         const char *colorspace_name = IMB_colormanagement_get_display_colorspace_name(view_settings, display_settings);
780
781         if (colorspace_name)
782                 return colormanage_colorspace_get_named(colorspace_name);
783
784         return NULL;
785 }
786
787 static OCIO_ConstProcessorRcPtr *create_display_buffer_processor(const char *look,
788                                                                  const char *view_transform,
789                                                                  const char *display,
790                                                                  float exposure, float gamma,
791                                                                  const char *from_colorspace)
792 {
793         OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
794         OCIO_DisplayTransformRcPtr *dt;
795         OCIO_ConstProcessorRcPtr *processor;
796         ColorManagedLook *look_descr = colormanage_look_get_named(look);
797
798         dt = OCIO_createDisplayTransform();
799
800         OCIO_displayTransformSetInputColorSpaceName(dt, from_colorspace);
801         OCIO_displayTransformSetView(dt, view_transform);
802         OCIO_displayTransformSetDisplay(dt, display);
803
804         if (look_descr->is_noop == false) {
805                 OCIO_displayTransformSetLooksOverrideEnabled(dt, true);
806                 OCIO_displayTransformSetLooksOverride(dt, look);
807         }
808
809         /* fstop exposure control */
810         if (exposure != 0.0f) {
811                 OCIO_MatrixTransformRcPtr *mt;
812                 float gain = powf(2.0f, exposure);
813                 const float scale4f[] = {gain, gain, gain, 1.0f};
814                 float m44[16], offset4[4];
815
816                 OCIO_matrixTransformScale(m44, offset4, scale4f);
817                 mt = OCIO_createMatrixTransform();
818                 OCIO_matrixTransformSetValue(mt, m44, offset4);
819                 OCIO_displayTransformSetLinearCC(dt, (OCIO_ConstTransformRcPtr *) mt);
820
821                 OCIO_matrixTransformRelease(mt);
822         }
823
824         /* post-display gamma transform */
825         if (gamma != 1.0f) {
826                 OCIO_ExponentTransformRcPtr *et;
827                 float exponent = 1.0f / MAX2(FLT_EPSILON, gamma);
828                 const float exponent4f[] = {exponent, exponent, exponent, exponent};
829
830                 et = OCIO_createExponentTransform();
831                 OCIO_exponentTransformSetValue(et, exponent4f);
832                 OCIO_displayTransformSetDisplayCC(dt, (OCIO_ConstTransformRcPtr *) et);
833
834                 OCIO_exponentTransformRelease(et);
835         }
836
837         processor = OCIO_configGetProcessor(config, (OCIO_ConstTransformRcPtr *) dt);
838
839         OCIO_displayTransformRelease(dt);
840         OCIO_configRelease(config);
841
842         return processor;
843 }
844
845 static OCIO_ConstProcessorRcPtr *create_colorspace_transform_processor(const char *from_colorspace,
846                                                                        const char *to_colorspace)
847 {
848         OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
849         OCIO_ConstProcessorRcPtr *processor;
850
851         processor = OCIO_configGetProcessorWithNames(config, from_colorspace, to_colorspace);
852
853         OCIO_configRelease(config);
854
855         return processor;
856 }
857
858 static OCIO_ConstProcessorRcPtr *colorspace_to_scene_linear_processor(ColorSpace *colorspace)
859 {
860         if (colorspace->to_scene_linear == NULL) {
861                 BLI_mutex_lock(&processor_lock);
862
863                 if (colorspace->to_scene_linear == NULL) {
864                         OCIO_ConstProcessorRcPtr *to_scene_linear;
865                         to_scene_linear = create_colorspace_transform_processor(colorspace->name, global_role_scene_linear);
866                         colorspace->to_scene_linear = (struct OCIO_ConstProcessorRcPtr *) to_scene_linear;
867                 }
868
869                 BLI_mutex_unlock(&processor_lock);
870         }
871
872         return (OCIO_ConstProcessorRcPtr *) colorspace->to_scene_linear;
873 }
874
875 static OCIO_ConstProcessorRcPtr *colorspace_from_scene_linear_processor(ColorSpace *colorspace)
876 {
877         if (colorspace->from_scene_linear == NULL) {
878                 BLI_mutex_lock(&processor_lock);
879
880                 if (colorspace->from_scene_linear == NULL) {
881                         OCIO_ConstProcessorRcPtr *from_scene_linear;
882                         from_scene_linear = create_colorspace_transform_processor(global_role_scene_linear, colorspace->name);
883                         colorspace->from_scene_linear = (struct OCIO_ConstProcessorRcPtr *) from_scene_linear;
884                 }
885
886                 BLI_mutex_unlock(&processor_lock);
887         }
888
889         return (OCIO_ConstProcessorRcPtr *) colorspace->from_scene_linear;
890 }
891
892 static OCIO_ConstProcessorRcPtr *display_from_scene_linear_processor(ColorManagedDisplay *display)
893 {
894         if (display->from_scene_linear == NULL) {
895                 BLI_mutex_lock(&processor_lock);
896
897                 if (display->from_scene_linear == NULL) {
898                         const char *view_name = colormanage_view_get_default_name(display);
899                         OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
900                         OCIO_ConstProcessorRcPtr *processor = NULL;
901
902                         if (view_name && config) {
903                                 const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(config, display->name, view_name);
904                                 processor = OCIO_configGetProcessorWithNames(config, global_role_scene_linear, view_colorspace);
905
906                                 OCIO_configRelease(config);
907                         }
908
909                         display->from_scene_linear = (struct OCIO_ConstProcessorRcPtr *) processor;
910                 }
911
912                 BLI_mutex_unlock(&processor_lock);
913         }
914
915         return (OCIO_ConstProcessorRcPtr *) display->from_scene_linear;
916 }
917
918 static OCIO_ConstProcessorRcPtr *display_to_scene_linear_processor(ColorManagedDisplay *display)
919 {
920         if (display->to_scene_linear == NULL) {
921                 BLI_mutex_lock(&processor_lock);
922
923                 if (display->to_scene_linear == NULL) {
924                         const char *view_name = colormanage_view_get_default_name(display);
925                         OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
926                         OCIO_ConstProcessorRcPtr *processor = NULL;
927
928                         if (view_name && config) {
929                                 const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(config, display->name, view_name);
930                                 processor = OCIO_configGetProcessorWithNames(config, view_colorspace, global_role_scene_linear);
931
932                                 OCIO_configRelease(config);
933                         }
934
935                         display->to_scene_linear = (struct OCIO_ConstProcessorRcPtr *) processor;
936                 }
937
938                 BLI_mutex_unlock(&processor_lock);
939         }
940
941         return (OCIO_ConstProcessorRcPtr *) display->to_scene_linear;
942 }
943
944 void IMB_colormanagement_init_default_view_settings(
945         ColorManagedViewSettings *view_settings,
946         const ColorManagedDisplaySettings *display_settings)
947 {
948         /* First, try use "Default" view transform of the requested device. */
949         ColorManagedView *default_view = colormanage_view_get_named_for_display(
950                 display_settings->display_device, "Default");
951         /* If that fails, we fall back to the default view transform of the display
952          * as per OCIO configuration. */
953         if (default_view == NULL) {
954                 ColorManagedDisplay *display = colormanage_display_get_named(
955                         display_settings->display_device);
956                 if (display != NULL) {
957                         default_view = colormanage_view_get_default(display);
958                 }
959         }
960         if (default_view != NULL) {
961                 BLI_strncpy(view_settings->view_transform,
962                             default_view->name,
963                             sizeof(view_settings->view_transform));
964         }
965         else {
966                 view_settings->view_transform[0] = '\0';
967         }
968         /* TODO(sergey): Find a way to safely/reliable un-hardcode this. */
969         BLI_strncpy(view_settings->look, "None", sizeof(view_settings->look));
970         /* Initialize rest of the settings. */
971         view_settings->flag = 0;
972         view_settings->gamma = 1.0f;
973         view_settings->exposure = 0.0f;
974         view_settings->curve_mapping = NULL;
975 }
976
977 static void curve_mapping_apply_pixel(CurveMapping *curve_mapping, float *pixel, int channels)
978 {
979         if (channels == 1) {
980                 pixel[0] = curvemap_evaluateF(curve_mapping->cm, pixel[0]);
981         }
982         else if (channels == 2) {
983                 pixel[0] = curvemap_evaluateF(curve_mapping->cm, pixel[0]);
984                 pixel[1] = curvemap_evaluateF(curve_mapping->cm, pixel[1]);
985         }
986         else {
987                 curvemapping_evaluate_premulRGBF(curve_mapping, pixel, pixel);
988         }
989 }
990
991 void colorspace_set_default_role(char *colorspace, int size, int role)
992 {
993         if (colorspace && colorspace[0] == '\0') {
994                 const char *role_colorspace;
995
996                 role_colorspace = IMB_colormanagement_role_colorspace_name_get(role);
997
998                 BLI_strncpy(colorspace, role_colorspace, size);
999         }
1000 }
1001
1002 void colormanage_imbuf_set_default_spaces(ImBuf *ibuf)
1003 {
1004         ibuf->rect_colorspace = colormanage_colorspace_get_named(global_role_default_byte);
1005 }
1006
1007 void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace)
1008 {
1009         ColorSpace *colorspace = colormanage_colorspace_get_named(from_colorspace);
1010
1011         if (colorspace && colorspace->is_data) {
1012                 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1013                 return;
1014         }
1015
1016         if (ibuf->rect_float) {
1017                 const char *to_colorspace = global_role_scene_linear;
1018
1019                 if (ibuf->rect)
1020                         imb_freerectImBuf(ibuf);
1021
1022                 IMB_colormanagement_transform(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels,
1023                                               from_colorspace, to_colorspace, true);
1024         }
1025 }
1026
1027 /*********************** Generic functions *************************/
1028
1029 static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings, const char *what,
1030                                                const ColorManagedDisplay *default_display)
1031 {
1032         if (display_settings->display_device[0] == '\0') {
1033                 BLI_strncpy(display_settings->display_device, default_display->name, sizeof(display_settings->display_device));
1034         }
1035         else {
1036                 ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device);
1037
1038                 if (!display) {
1039                         printf("Color management: display \"%s\" used by %s not found, setting to default (\"%s\").\n",
1040                                display_settings->display_device, what, default_display->name);
1041
1042                         BLI_strncpy(display_settings->display_device, default_display->name,
1043                                     sizeof(display_settings->display_device));
1044                 }
1045         }
1046 }
1047
1048 static void colormanage_check_view_settings(ColorManagedDisplaySettings *display_settings,
1049                                             ColorManagedViewSettings *view_settings, const char *what)
1050 {
1051         ColorManagedDisplay *display;
1052         ColorManagedView *default_view = NULL;
1053         ColorManagedLook *default_look = (ColorManagedLook *) global_looks.first;
1054
1055         if (view_settings->view_transform[0] == '\0') {
1056                 display = colormanage_display_get_named(display_settings->display_device);
1057
1058                 if (display)
1059                         default_view = colormanage_view_get_default(display);
1060
1061                 if (default_view)
1062                         BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1063         }
1064         else {
1065                 ColorManagedView *view = colormanage_view_get_named(view_settings->view_transform);
1066
1067                 if (!view) {
1068                         display = colormanage_display_get_named(display_settings->display_device);
1069
1070                         if (display)
1071                                 default_view = colormanage_view_get_default(display);
1072
1073                         if (default_view) {
1074                                 printf("Color management: %s view \"%s\" not found, setting default \"%s\".\n",
1075                                        what, view_settings->view_transform, default_view->name);
1076
1077                                 BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1078                         }
1079                 }
1080         }
1081
1082         if (view_settings->look[0] == '\0') {
1083                 BLI_strncpy(view_settings->look, default_look->name, sizeof(view_settings->look));
1084         }
1085         else {
1086                 ColorManagedLook *look = colormanage_look_get_named(view_settings->look);
1087                 if (look == NULL) {
1088                         printf("Color management: %s look \"%s\" not found, setting default \"%s\".\n",
1089                                what, view_settings->look, default_look->name);
1090
1091                         BLI_strncpy(view_settings->look, default_look->name, sizeof(view_settings->look));
1092                 }
1093         }
1094
1095         /* OCIO_TODO: move to do_versions() */
1096         if (view_settings->exposure == 0.0f && view_settings->gamma == 0.0f) {
1097                 view_settings->exposure = 0.0f;
1098                 view_settings->gamma = 1.0f;
1099         }
1100 }
1101
1102 static void colormanage_check_colorspace_settings(ColorManagedColorspaceSettings *colorspace_settings, const char *what)
1103 {
1104         if (colorspace_settings->name[0] == '\0') {
1105                 /* pass */
1106         }
1107         else {
1108                 ColorSpace *colorspace = colormanage_colorspace_get_named(colorspace_settings->name);
1109
1110                 if (!colorspace) {
1111                         printf("Color management: %s colorspace \"%s\" not found, will use default instead.\n",
1112                                what, colorspace_settings->name);
1113
1114                         BLI_strncpy(colorspace_settings->name, "", sizeof(colorspace_settings->name));
1115                 }
1116         }
1117
1118         (void) what;
1119 }
1120
1121 void IMB_colormanagement_check_file_config(Main *bmain)
1122 {
1123         Scene *scene;
1124         Image *image;
1125         MovieClip *clip;
1126
1127         ColorManagedDisplay *default_display;
1128
1129         default_display = colormanage_display_get_default();
1130
1131         if (!default_display) {
1132                 /* happens when OCIO configuration is incorrect */
1133                 return;
1134         }
1135
1136         for (scene = bmain->scene.first; scene; scene = scene->id.next) {
1137                 ColorManagedColorspaceSettings *sequencer_colorspace_settings;
1138
1139                 /* check scene color management settings */
1140                 colormanage_check_display_settings(&scene->display_settings, "scene", default_display);
1141                 colormanage_check_view_settings(&scene->display_settings, &scene->view_settings, "scene");
1142
1143                 sequencer_colorspace_settings = &scene->sequencer_colorspace_settings;
1144
1145                 colormanage_check_colorspace_settings(sequencer_colorspace_settings, "sequencer");
1146
1147                 if (sequencer_colorspace_settings->name[0] == '\0') {
1148                         BLI_strncpy(sequencer_colorspace_settings->name, global_role_default_sequencer, MAX_COLORSPACE_NAME);
1149                 }
1150
1151                 /* check sequencer strip input color space settings */
1152                 Sequence *seq;
1153                 SEQ_BEGIN (scene->ed, seq) {
1154                         if (seq->strip) {
1155                                 colormanage_check_colorspace_settings(&seq->strip->colorspace_settings, "sequencer strip");
1156                         }
1157                 } SEQ_END;
1158         }
1159
1160         /* ** check input color space settings ** */
1161
1162         for (image = bmain->image.first; image; image = image->id.next) {
1163                 colormanage_check_colorspace_settings(&image->colorspace_settings, "image");
1164         }
1165
1166         for (clip = bmain->movieclip.first; clip; clip = clip->id.next) {
1167                 colormanage_check_colorspace_settings(&clip->colorspace_settings, "clip");
1168         }
1169 }
1170
1171 void IMB_colormanagement_validate_settings(const ColorManagedDisplaySettings *display_settings,
1172                                            ColorManagedViewSettings *view_settings)
1173 {
1174         ColorManagedDisplay *display;
1175         ColorManagedView *default_view = NULL;
1176         LinkData *view_link;
1177
1178         display = colormanage_display_get_named(display_settings->display_device);
1179
1180         default_view = colormanage_view_get_default(display);
1181
1182         for (view_link = display->views.first; view_link; view_link = view_link->next) {
1183                 ColorManagedView *view = view_link->data;
1184
1185                 if (STREQ(view->name, view_settings->view_transform))
1186                         break;
1187         }
1188
1189         if (view_link == NULL && default_view)
1190                 BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1191 }
1192
1193 const char *IMB_colormanagement_role_colorspace_name_get(int role)
1194 {
1195         switch (role) {
1196                 case COLOR_ROLE_SCENE_LINEAR:
1197                         return global_role_scene_linear;
1198                 case COLOR_ROLE_COLOR_PICKING:
1199                         return global_role_color_picking;
1200                 case COLOR_ROLE_TEXTURE_PAINTING:
1201                         return global_role_texture_painting;
1202                 case COLOR_ROLE_DEFAULT_SEQUENCER:
1203                         return global_role_default_sequencer;
1204                 case COLOR_ROLE_DEFAULT_FLOAT:
1205                         return global_role_default_float;
1206                 case COLOR_ROLE_DEFAULT_BYTE:
1207                         return global_role_default_byte;
1208                 default:
1209                         printf("Unknown role was passed to %s\n", __func__);
1210                         BLI_assert(0);
1211                         break;
1212         }
1213
1214         return NULL;
1215 }
1216
1217 void IMB_colormanagement_check_is_data(ImBuf *ibuf, const char *name)
1218 {
1219         ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1220
1221         if (colorspace && colorspace->is_data)
1222                 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1223         else
1224                 ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1225 }
1226
1227 void IMB_colormanagement_assign_float_colorspace(ImBuf *ibuf, const char *name)
1228 {
1229         ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1230
1231         ibuf->float_colorspace = colorspace;
1232
1233         if (colorspace && colorspace->is_data)
1234                 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1235         else
1236                 ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1237 }
1238
1239 void IMB_colormanagement_assign_rect_colorspace(ImBuf *ibuf, const char *name)
1240 {
1241         ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1242
1243         ibuf->rect_colorspace = colorspace;
1244
1245         if (colorspace && colorspace->is_data)
1246                 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1247         else
1248                 ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1249 }
1250
1251 const char *IMB_colormanagement_get_float_colorspace(ImBuf *ibuf)
1252 {
1253         if (ibuf->float_colorspace) {
1254                 return ibuf->float_colorspace->name;
1255         }
1256         else {
1257                 return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR);
1258         }
1259 }
1260
1261 const char *IMB_colormanagement_get_rect_colorspace(ImBuf *ibuf)
1262 {
1263         if (ibuf->rect_colorspace) {
1264                 return ibuf->rect_colorspace->name;
1265         }
1266         else {
1267                 return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE);
1268         }
1269 }
1270
1271 /*********************** Threaded display buffer transform routines *************************/
1272
1273 typedef struct DisplayBufferThread {
1274         ColormanageProcessor *cm_processor;
1275
1276         const float *buffer;
1277         unsigned char *byte_buffer;
1278
1279         float *display_buffer;
1280         unsigned char *display_buffer_byte;
1281
1282         int width;
1283         int start_line;
1284         int tot_line;
1285
1286         int channels;
1287         float dither;
1288         bool is_data;
1289
1290         const char *byte_colorspace;
1291         const char *float_colorspace;
1292 } DisplayBufferThread;
1293
1294 typedef struct DisplayBufferInitData {
1295         ImBuf *ibuf;
1296         ColormanageProcessor *cm_processor;
1297         const float *buffer;
1298         unsigned char *byte_buffer;
1299
1300         float *display_buffer;
1301         unsigned char *display_buffer_byte;
1302
1303         int width;
1304
1305         const char *byte_colorspace;
1306         const char *float_colorspace;
1307 } DisplayBufferInitData;
1308
1309 static void display_buffer_init_handle(void *handle_v, int start_line, int tot_line, void *init_data_v)
1310 {
1311         DisplayBufferThread *handle = (DisplayBufferThread *) handle_v;
1312         DisplayBufferInitData *init_data = (DisplayBufferInitData *) init_data_v;
1313         ImBuf *ibuf = init_data->ibuf;
1314
1315         int channels = ibuf->channels;
1316         float dither = ibuf->dither;
1317         bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
1318
1319         size_t offset = ((size_t)channels) * start_line * ibuf->x;
1320         size_t display_buffer_byte_offset = ((size_t)DISPLAY_BUFFER_CHANNELS) * start_line * ibuf->x;
1321
1322         memset(handle, 0, sizeof(DisplayBufferThread));
1323
1324         handle->cm_processor = init_data->cm_processor;
1325
1326         if (init_data->buffer)
1327                 handle->buffer = init_data->buffer + offset;
1328
1329         if (init_data->byte_buffer)
1330                 handle->byte_buffer = init_data->byte_buffer + offset;
1331
1332         if (init_data->display_buffer)
1333                 handle->display_buffer = init_data->display_buffer + offset;
1334
1335         if (init_data->display_buffer_byte)
1336                 handle->display_buffer_byte = init_data->display_buffer_byte + display_buffer_byte_offset;
1337
1338         handle->width = ibuf->x;
1339
1340         handle->start_line = start_line;
1341         handle->tot_line = tot_line;
1342
1343         handle->channels = channels;
1344         handle->dither = dither;
1345         handle->is_data = is_data;
1346
1347         handle->byte_colorspace = init_data->byte_colorspace;
1348         handle->float_colorspace = init_data->float_colorspace;
1349 }
1350
1351 static void display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle, int height,
1352                                                    float *linear_buffer, bool *is_straight_alpha)
1353 {
1354         int channels = handle->channels;
1355         int width = handle->width;
1356
1357         size_t buffer_size = ((size_t)channels) * width * height;
1358
1359         bool is_data = handle->is_data;
1360         bool is_data_display = handle->cm_processor->is_data_result;
1361
1362         if (!handle->buffer) {
1363                 unsigned char *byte_buffer = handle->byte_buffer;
1364
1365                 const char *from_colorspace = handle->byte_colorspace;
1366                 const char *to_colorspace = global_role_scene_linear;
1367
1368                 float *fp;
1369                 unsigned char *cp;
1370                 const size_t i_last = ((size_t)width) * height;
1371                 size_t i;
1372
1373                 /* first convert byte buffer to float, keep in image space */
1374                 for (i = 0, fp = linear_buffer, cp = byte_buffer;
1375                      i != i_last;
1376                      i++, fp += channels, cp += channels)
1377                 {
1378                         if (channels == 3) {
1379                                 rgb_uchar_to_float(fp, cp);
1380                         }
1381                         else if (channels == 4) {
1382                                 rgba_uchar_to_float(fp, cp);
1383                         }
1384                         else {
1385                                 BLI_assert(!"Buffers of 3 or 4 channels are only supported here");
1386                         }
1387                 }
1388
1389                 if (!is_data && !is_data_display) {
1390                         /* convert float buffer to scene linear space */
1391                         IMB_colormanagement_transform(linear_buffer, width, height, channels,
1392                                                       from_colorspace, to_colorspace, false);
1393                 }
1394
1395                 *is_straight_alpha = true;
1396         }
1397         else if (handle->float_colorspace) {
1398                 /* currently float is non-linear only in sequencer, which is working
1399                  * in it's own color space even to handle float buffers.
1400                  * This color space is the same for byte and float images.
1401                  * Need to convert float buffer to linear space before applying display transform
1402                  */
1403
1404                 const char *from_colorspace = handle->float_colorspace;
1405                 const char *to_colorspace = global_role_scene_linear;
1406
1407                 memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1408
1409                 if (!is_data && !is_data_display) {
1410                         IMB_colormanagement_transform(linear_buffer, width, height, channels,
1411                                                       from_colorspace, to_colorspace, true);
1412                 }
1413
1414                 *is_straight_alpha = false;
1415         }
1416         else {
1417                 /* some processors would want to modify float original buffer
1418                  * before converting it into display byte buffer, so we need to
1419                  * make sure original's ImBuf buffers wouldn't be modified by
1420                  * using duplicated buffer here
1421                  */
1422
1423                 memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1424
1425                 *is_straight_alpha = false;
1426         }
1427 }
1428
1429 static void *do_display_buffer_apply_thread(void *handle_v)
1430 {
1431         DisplayBufferThread *handle = (DisplayBufferThread *) handle_v;
1432         ColormanageProcessor *cm_processor = handle->cm_processor;
1433         float *display_buffer = handle->display_buffer;
1434         unsigned char *display_buffer_byte = handle->display_buffer_byte;
1435         int channels = handle->channels;
1436         int width = handle->width;
1437         int height = handle->tot_line;
1438         float dither = handle->dither;
1439         bool is_data = handle->is_data;
1440
1441         if (cm_processor == NULL) {
1442                 if (display_buffer_byte && display_buffer_byte != handle->byte_buffer) {
1443                         IMB_buffer_byte_from_byte(display_buffer_byte, handle->byte_buffer, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1444                                                   false, width, height, width, width);
1445                 }
1446
1447                 if (display_buffer) {
1448                         IMB_buffer_float_from_byte(display_buffer, handle->byte_buffer, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1449                                                    false, width, height, width, width);
1450                 }
1451         }
1452         else {
1453                 bool is_straight_alpha, predivide;
1454                 float *linear_buffer = MEM_mallocN(((size_t)channels) * width * height * sizeof(float),
1455                                                    "color conversion linear buffer");
1456
1457                 display_buffer_apply_get_linear_buffer(handle, height, linear_buffer, &is_straight_alpha);
1458
1459                 predivide = is_straight_alpha == false;
1460
1461                 if (is_data) {
1462                         /* special case for data buffers - no color space conversions,
1463                          * only generate byte buffers
1464                          */
1465                 }
1466                 else {
1467                         /* apply processor */
1468                         IMB_colormanagement_processor_apply(cm_processor, linear_buffer, width, height, channels,
1469                                                             predivide);
1470                 }
1471
1472                 /* copy result to output buffers */
1473                 if (display_buffer_byte) {
1474                         /* do conversion */
1475                         IMB_buffer_byte_from_float(display_buffer_byte, linear_buffer,
1476                                                    channels, dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1477                                                    predivide, width, height, width, width);
1478                 }
1479
1480                 if (display_buffer) {
1481                         memcpy(display_buffer, linear_buffer, ((size_t)width) * height * channels * sizeof(float));
1482
1483                         if (is_straight_alpha && channels == 4) {
1484                                 const size_t i_last = ((size_t)width) * height;
1485                                 size_t i;
1486                                 float *fp;
1487
1488                                 for (i = 0, fp = display_buffer;
1489                                      i != i_last;
1490                                      i++, fp += channels)
1491                                 {
1492                                         straight_to_premul_v4(fp);
1493                                 }
1494                         }
1495                 }
1496
1497                 MEM_freeN(linear_buffer);
1498         }
1499
1500         return NULL;
1501 }
1502
1503 static void display_buffer_apply_threaded(ImBuf *ibuf, float *buffer, unsigned char *byte_buffer, float *display_buffer,
1504                                           unsigned char *display_buffer_byte, ColormanageProcessor *cm_processor)
1505 {
1506         DisplayBufferInitData init_data;
1507
1508         init_data.ibuf = ibuf;
1509         init_data.cm_processor = cm_processor;
1510         init_data.buffer = buffer;
1511         init_data.byte_buffer = byte_buffer;
1512         init_data.display_buffer = display_buffer;
1513         init_data.display_buffer_byte = display_buffer_byte;
1514
1515         if (ibuf->rect_colorspace != NULL) {
1516                 init_data.byte_colorspace = ibuf->rect_colorspace->name;
1517         }
1518         else {
1519                 /* happens for viewer images, which are not so simple to determine where to
1520                  * set image buffer's color spaces
1521                  */
1522                 init_data.byte_colorspace = global_role_default_byte;
1523         }
1524
1525         if (ibuf->float_colorspace != NULL) {
1526                 /* sequencer stores float buffers in non-linear space */
1527                 init_data.float_colorspace = ibuf->float_colorspace->name;
1528         }
1529         else {
1530                 init_data.float_colorspace = NULL;
1531         }
1532
1533         IMB_processor_apply_threaded(ibuf->y, sizeof(DisplayBufferThread), &init_data,
1534                                      display_buffer_init_handle, do_display_buffer_apply_thread);
1535 }
1536
1537 static bool is_ibuf_rect_in_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
1538                                           const ColorManagedDisplaySettings *display_settings)
1539 {
1540         if ((view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) == 0 &&
1541             view_settings->exposure == 0.0f &&
1542             view_settings->gamma == 1.0f)
1543         {
1544                 const char *from_colorspace = ibuf->rect_colorspace->name;
1545                 const char *to_colorspace = IMB_colormanagement_get_display_colorspace_name(view_settings, display_settings);
1546                 ColorManagedLook *look_descr = colormanage_look_get_named(view_settings->look);
1547                 if (look_descr != NULL && !STREQ(look_descr->process_space, "")) {
1548                         return false;
1549                 }
1550
1551                 if (to_colorspace && STREQ(from_colorspace, to_colorspace))
1552                         return true;
1553         }
1554
1555         return false;
1556 }
1557
1558 static void colormanage_display_buffer_process_ex(ImBuf *ibuf, float *display_buffer, unsigned char *display_buffer_byte,
1559                                                   const ColorManagedViewSettings *view_settings,
1560                                                   const ColorManagedDisplaySettings *display_settings)
1561 {
1562         ColormanageProcessor *cm_processor = NULL;
1563         bool skip_transform = false;
1564
1565         /* if we're going to transform byte buffer, check whether transformation would
1566          * happen to the same color space as byte buffer itself is
1567          * this would save byte -> float -> byte conversions making display buffer
1568          * computation noticeable faster
1569          */
1570         if (ibuf->rect_float == NULL && ibuf->rect_colorspace) {
1571                 skip_transform = is_ibuf_rect_in_display_space(ibuf, view_settings, display_settings);
1572         }
1573
1574         if (skip_transform == false)
1575                 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1576
1577         display_buffer_apply_threaded(ibuf, ibuf->rect_float, (unsigned char *) ibuf->rect,
1578                                       display_buffer, display_buffer_byte, cm_processor);
1579
1580         if (cm_processor)
1581                 IMB_colormanagement_processor_free(cm_processor);
1582 }
1583
1584 static void colormanage_display_buffer_process(ImBuf *ibuf, unsigned char *display_buffer,
1585                                                const ColorManagedViewSettings *view_settings,
1586                                                const ColorManagedDisplaySettings *display_settings)
1587 {
1588         colormanage_display_buffer_process_ex(ibuf, NULL, display_buffer, view_settings, display_settings);
1589 }
1590
1591 /*********************** Threaded processor transform routines *************************/
1592
1593 typedef struct ProcessorTransformThread {
1594         ColormanageProcessor *cm_processor;
1595         unsigned char *byte_buffer;
1596         float *float_buffer;
1597         int width;
1598         int start_line;
1599         int tot_line;
1600         int channels;
1601         bool predivide;
1602         bool float_from_byte;
1603 } ProcessorTransformThread;
1604
1605 typedef struct ProcessorTransformInit {
1606         ColormanageProcessor *cm_processor;
1607         unsigned char *byte_buffer;
1608         float *float_buffer;
1609         int width;
1610         int height;
1611         int channels;
1612         bool predivide;
1613         bool float_from_byte;
1614 } ProcessorTransformInitData;
1615
1616 static void processor_transform_init_handle(void *handle_v, int start_line, int tot_line, void *init_data_v)
1617 {
1618         ProcessorTransformThread *handle = (ProcessorTransformThread *) handle_v;
1619         ProcessorTransformInitData *init_data = (ProcessorTransformInitData *) init_data_v;
1620
1621         const int channels = init_data->channels;
1622         const int width = init_data->width;
1623         const bool predivide = init_data->predivide;
1624         const bool float_from_byte = init_data->float_from_byte;
1625
1626         const size_t offset = ((size_t)channels) * start_line * width;
1627
1628         memset(handle, 0, sizeof(ProcessorTransformThread));
1629
1630         handle->cm_processor = init_data->cm_processor;
1631
1632         if (init_data->byte_buffer != NULL) {
1633                 /* TODO(serge): Offset might be different for byte and float buffers. */
1634                 handle->byte_buffer = init_data->byte_buffer + offset;
1635         }
1636         if (init_data->float_buffer != NULL) {
1637                 handle->float_buffer = init_data->float_buffer + offset;
1638         }
1639
1640         handle->width = width;
1641
1642         handle->start_line = start_line;
1643         handle->tot_line = tot_line;
1644
1645         handle->channels = channels;
1646         handle->predivide = predivide;
1647         handle->float_from_byte = float_from_byte;
1648 }
1649
1650 static void *do_processor_transform_thread(void *handle_v)
1651 {
1652         ProcessorTransformThread *handle = (ProcessorTransformThread *) handle_v;
1653         unsigned char *byte_buffer = handle->byte_buffer;
1654         float *float_buffer = handle->float_buffer;
1655         const int channels = handle->channels;
1656         const int width = handle->width;
1657         const int height = handle->tot_line;
1658         const bool predivide = handle->predivide;
1659         const bool float_from_byte = handle->float_from_byte;
1660
1661         if (float_from_byte) {
1662                 IMB_buffer_float_from_byte(float_buffer, byte_buffer,
1663                                            IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1664                                            false,
1665                                            width, height, width, width);
1666                 IMB_colormanagement_processor_apply(handle->cm_processor,
1667                                                     float_buffer,
1668                                                     width, height, channels,
1669                                                     predivide);
1670                 IMB_premultiply_rect_float(float_buffer, 4, width, height);
1671         }
1672         else {
1673                 if (byte_buffer != NULL) {
1674                         IMB_colormanagement_processor_apply_byte(handle->cm_processor,
1675                                                                  byte_buffer,
1676                                                                  width, height, channels);
1677                 }
1678                 if (float_buffer != NULL) {
1679                         IMB_colormanagement_processor_apply(handle->cm_processor,
1680                                                             float_buffer,
1681                                                             width, height, channels,
1682                                                             predivide);
1683                 }
1684         }
1685
1686         return NULL;
1687 }
1688
1689 static void processor_transform_apply_threaded(unsigned char *byte_buffer, float *float_buffer,
1690                                                const int width, const int height, const int channels,
1691                                                ColormanageProcessor *cm_processor,
1692                                                const bool predivide, const bool float_from_byte)
1693 {
1694         ProcessorTransformInitData init_data;
1695
1696         init_data.cm_processor = cm_processor;
1697         init_data.byte_buffer = byte_buffer;
1698         init_data.float_buffer = float_buffer;
1699         init_data.width = width;
1700         init_data.height = height;
1701         init_data.channels = channels;
1702         init_data.predivide = predivide;
1703         init_data.float_from_byte = float_from_byte;
1704
1705         IMB_processor_apply_threaded(height, sizeof(ProcessorTransformThread), &init_data,
1706                                      processor_transform_init_handle, do_processor_transform_thread);
1707 }
1708
1709 /*********************** Color space transformation functions *************************/
1710
1711 /* convert the whole buffer from specified by name color space to another - internal implementation */
1712 static void colormanagement_transform_ex(unsigned char *byte_buffer, float *float_buffer,
1713                                          int width, int height, int channels,
1714                                          const char *from_colorspace, const char *to_colorspace,
1715                                          bool predivide, bool do_threaded)
1716 {
1717         ColormanageProcessor *cm_processor;
1718
1719         if (from_colorspace[0] == '\0') {
1720                 return;
1721         }
1722
1723         if (STREQ(from_colorspace, to_colorspace)) {
1724                 /* if source and destination color spaces are identical, skip
1725                  * threading overhead and simply do nothing
1726                  */
1727                 return;
1728         }
1729
1730         cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
1731
1732         if (do_threaded) {
1733                 processor_transform_apply_threaded(byte_buffer, float_buffer,
1734                                                    width, height, channels,
1735                                                    cm_processor, predivide, false);
1736         }
1737         else {
1738                 if (byte_buffer != NULL) {
1739                         IMB_colormanagement_processor_apply_byte(cm_processor, byte_buffer, width, height, channels);
1740                 }
1741                 if (float_buffer != NULL) {
1742                         IMB_colormanagement_processor_apply(cm_processor, float_buffer, width, height, channels, predivide);
1743                 }
1744         }
1745
1746         IMB_colormanagement_processor_free(cm_processor);
1747 }
1748
1749 /* convert the whole buffer from specified by name color space to another */
1750 void IMB_colormanagement_transform(float *buffer, int width, int height, int channels,
1751                                    const char *from_colorspace, const char *to_colorspace, bool predivide)
1752 {
1753         colormanagement_transform_ex(NULL, buffer, width, height, channels, from_colorspace, to_colorspace, predivide, false);
1754 }
1755
1756 /* convert the whole buffer from specified by name color space to another
1757  * will do threaded conversion
1758  */
1759 void IMB_colormanagement_transform_threaded(float *buffer, int width, int height, int channels,
1760                                             const char *from_colorspace, const char *to_colorspace, bool predivide)
1761 {
1762         colormanagement_transform_ex(NULL, buffer, width, height, channels, from_colorspace, to_colorspace, predivide, true);
1763 }
1764
1765 /* Similar to functions above, but operates on byte buffer. */
1766 void IMB_colormanagement_transform_byte(unsigned char *buffer, int width, int height, int channels,
1767                                         const char *from_colorspace, const char *to_colorspace)
1768 {
1769         colormanagement_transform_ex(buffer, NULL, width, height, channels, from_colorspace, to_colorspace, false, false);
1770 }
1771 void IMB_colormanagement_transform_byte_threaded(unsigned char *buffer, int width, int height, int channels,
1772                                                  const char *from_colorspace, const char *to_colorspace)
1773 {
1774         colormanagement_transform_ex(buffer, NULL, width, height, channels, from_colorspace, to_colorspace, false, true);
1775 }
1776
1777 /* Similar to above, but gets float buffer from display one. */
1778 void IMB_colormanagement_transform_from_byte(float *float_buffer, unsigned char *byte_buffer,
1779                                              int width, int height, int channels,
1780                                              const char *from_colorspace, const char *to_colorspace)
1781 {
1782         IMB_buffer_float_from_byte(float_buffer, byte_buffer,
1783                                    IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1784                                    true,
1785                                    width, height, width, width);
1786         IMB_colormanagement_transform(float_buffer,
1787                                       width, height, channels,
1788                                       from_colorspace, to_colorspace,
1789                                       true);
1790 }
1791 void IMB_colormanagement_transform_from_byte_threaded(float *float_buffer, unsigned char *byte_buffer,
1792                                                       int width, int height, int channels,
1793                                                       const char *from_colorspace, const char *to_colorspace)
1794 {
1795         ColormanageProcessor *cm_processor;
1796         if (from_colorspace == NULL || from_colorspace[0] == '\0') {
1797                 return;
1798         }
1799         if (STREQ(from_colorspace, to_colorspace)) {
1800                 /* Because this function always takes a byte buffer and returns a float buffer, it must
1801                  * always do byte-to-float conversion of some kind. To avoid threading overhead
1802                  * IMB_buffer_float_from_byte is used when color spaces are identical. See T51002.
1803                  */
1804                 IMB_buffer_float_from_byte(float_buffer, byte_buffer,
1805                                            IB_PROFILE_SRGB, IB_PROFILE_SRGB,
1806                                            false,
1807                                            width, height, width, width);
1808                 IMB_premultiply_rect_float(float_buffer, 4, width, height);
1809                 return;
1810         }
1811         cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
1812         processor_transform_apply_threaded(byte_buffer, float_buffer,
1813                                            width, height, channels,
1814                                            cm_processor, false, true);
1815         IMB_colormanagement_processor_free(cm_processor);
1816 }
1817
1818 void IMB_colormanagement_transform_v4(float pixel[4], const char *from_colorspace, const char *to_colorspace)
1819 {
1820         ColormanageProcessor *cm_processor;
1821
1822         if (from_colorspace[0] == '\0') {
1823                 return;
1824         }
1825
1826         if (STREQ(from_colorspace, to_colorspace)) {
1827                 /* if source and destination color spaces are identical, skip
1828                  * threading overhead and simply do nothing
1829                  */
1830                 return;
1831         }
1832
1833         cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
1834
1835         IMB_colormanagement_processor_apply_v4(cm_processor, pixel);
1836
1837         IMB_colormanagement_processor_free(cm_processor);
1838 }
1839
1840 /* convert pixel from specified by descriptor color space to scene linear
1841  * used by performance-critical areas such as renderer and baker
1842  */
1843 void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], ColorSpace *colorspace)
1844 {
1845         OCIO_ConstProcessorRcPtr *processor;
1846
1847         if (!colorspace) {
1848                 /* should never happen */
1849                 printf("%s: perform conversion from unknown color space\n", __func__);
1850                 return;
1851         }
1852
1853         processor = colorspace_to_scene_linear_processor(colorspace);
1854
1855         if (processor)
1856                 OCIO_processorApplyRGB(processor, pixel);
1857 }
1858
1859 /* same as above, but converts colors in opposite direction */
1860 void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], ColorSpace *colorspace)
1861 {
1862         OCIO_ConstProcessorRcPtr *processor;
1863
1864         if (!colorspace) {
1865                 /* should never happen */
1866                 printf("%s: perform conversion from unknown color space\n", __func__);
1867                 return;
1868         }
1869
1870         processor = colorspace_from_scene_linear_processor(colorspace);
1871
1872         if (processor)
1873                 OCIO_processorApplyRGB(processor, pixel);
1874 }
1875
1876 void IMB_colormanagement_colorspace_to_scene_linear_v4(float pixel[4], bool predivide, ColorSpace *colorspace)
1877 {
1878         OCIO_ConstProcessorRcPtr *processor;
1879
1880         if (!colorspace) {
1881                 /* should never happen */
1882                 printf("%s: perform conversion from unknown color space\n", __func__);
1883                 return;
1884         }
1885
1886         processor = colorspace_to_scene_linear_processor(colorspace);
1887
1888         if (processor) {
1889                 if (predivide)
1890                         OCIO_processorApplyRGBA_predivide(processor, pixel);
1891                 else
1892                         OCIO_processorApplyRGBA(processor, pixel);
1893         }
1894 }
1895
1896 void IMB_colormanagement_colorspace_to_scene_linear(float *buffer, int width, int height, int channels, struct ColorSpace *colorspace, bool predivide)
1897 {
1898         OCIO_ConstProcessorRcPtr *processor;
1899
1900         if (!colorspace) {
1901                 /* should never happen */
1902                 printf("%s: perform conversion from unknown color space\n", __func__);
1903                 return;
1904         }
1905
1906         processor = colorspace_to_scene_linear_processor(colorspace);
1907
1908         if (processor) {
1909                 OCIO_PackedImageDesc *img;
1910
1911                 img = OCIO_createOCIO_PackedImageDesc(
1912                         buffer, width, height, channels, sizeof(float),
1913                         (size_t)channels * sizeof(float),
1914                         (size_t)channels * sizeof(float) * width);
1915
1916                 if (predivide)
1917                         OCIO_processorApply_predivide(processor, img);
1918                 else
1919                         OCIO_processorApply(processor, img);
1920
1921                 OCIO_PackedImageDescRelease(img);
1922         }
1923 }
1924
1925 /* Conversion between color picking role. Typically we would expect such a
1926  * requirements:
1927  * - It is approximately perceptually linear, so that the HSV numbers and
1928  *   the HSV cube/circle have an intuitive distribution.
1929  * - It has the same gamut as the scene linear color space.
1930  * - Color picking values 0..1 map to scene linear values in the 0..1 range,
1931  *   so that picked albedo values are energy conserving.
1932  */
1933 void IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3])
1934 {
1935         if (!global_color_picking_state.processor_to && !global_color_picking_state.failed) {
1936                 /* Create processor if none exists. */
1937                 BLI_mutex_lock(&processor_lock);
1938
1939                 if (!global_color_picking_state.processor_to && !global_color_picking_state.failed) {
1940                         global_color_picking_state.processor_to =
1941                                 create_colorspace_transform_processor(global_role_scene_linear,
1942                                                                       global_role_color_picking);
1943
1944                         if (!global_color_picking_state.processor_to) {
1945                                 global_color_picking_state.failed = true;
1946                         }
1947                 }
1948
1949                 BLI_mutex_unlock(&processor_lock);
1950         }
1951
1952         if (global_color_picking_state.processor_to) {
1953                 OCIO_processorApplyRGB(global_color_picking_state.processor_to, pixel);
1954         }
1955 }
1956
1957 void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3])
1958 {
1959         if (!global_color_picking_state.processor_from && !global_color_picking_state.failed) {
1960                 /* Create processor if none exists. */
1961                 BLI_mutex_lock(&processor_lock);
1962
1963                 if (!global_color_picking_state.processor_from && !global_color_picking_state.failed) {
1964                         global_color_picking_state.processor_from =
1965                                 create_colorspace_transform_processor(global_role_color_picking,
1966                                                                       global_role_scene_linear);
1967
1968                         if (!global_color_picking_state.processor_from) {
1969                                 global_color_picking_state.failed = true;
1970                         }
1971                 }
1972
1973                 BLI_mutex_unlock(&processor_lock);
1974         }
1975
1976         if (global_color_picking_state.processor_from) {
1977                 OCIO_processorApplyRGB(global_color_picking_state.processor_from, pixel);
1978         }
1979 }
1980
1981 /* Conversion between sRGB, for rare cases like hex color or copy/pasting
1982  * between UI theme and scene linear colors. */
1983 void IMB_colormanagement_scene_linear_to_srgb_v3(float pixel[3])
1984 {
1985         mul_m3_v3(imbuf_rgb_to_xyz, pixel);
1986         mul_m3_v3(imbuf_xyz_to_linear_srgb, pixel);
1987         linearrgb_to_srgb_v3_v3(pixel, pixel);
1988 }
1989
1990 void IMB_colormanagement_srgb_to_scene_linear_v3(float pixel[3])
1991 {
1992         srgb_to_linearrgb_v3_v3(pixel, pixel);
1993         mul_m3_v3(imbuf_linear_srgb_to_xyz, pixel);
1994         mul_m3_v3(imbuf_xyz_to_rgb, pixel);
1995 }
1996
1997 /* convert pixel from scene linear to display space using default view
1998  * used by performance-critical areas such as color-related widgets where we want to reduce
1999  * amount of per-widget allocations
2000  */
2001 void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManagedDisplay *display)
2002 {
2003         OCIO_ConstProcessorRcPtr *processor;
2004
2005         processor = display_from_scene_linear_processor(display);
2006
2007         if (processor)
2008                 OCIO_processorApplyRGB(processor, pixel);
2009 }
2010
2011 /* same as above, but converts color in opposite direction */
2012 void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], ColorManagedDisplay *display)
2013 {
2014         OCIO_ConstProcessorRcPtr *processor;
2015
2016         processor = display_to_scene_linear_processor(display);
2017
2018         if (processor)
2019                 OCIO_processorApplyRGB(processor, pixel);
2020 }
2021
2022 void IMB_colormanagement_pixel_to_display_space_v4(float result[4], const float pixel[4],
2023                                                    const ColorManagedViewSettings *view_settings,
2024                                                    const ColorManagedDisplaySettings *display_settings)
2025 {
2026         ColormanageProcessor *cm_processor;
2027
2028         copy_v4_v4(result, pixel);
2029
2030         cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2031         IMB_colormanagement_processor_apply_v4(cm_processor, result);
2032         IMB_colormanagement_processor_free(cm_processor);
2033 }
2034
2035 void IMB_colormanagement_pixel_to_display_space_v3(float result[3], const float pixel[3],
2036                                                    const ColorManagedViewSettings *view_settings,
2037                                                    const ColorManagedDisplaySettings *display_settings)
2038 {
2039         ColormanageProcessor *cm_processor;
2040
2041         copy_v3_v3(result, pixel);
2042
2043         cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2044         IMB_colormanagement_processor_apply_v3(cm_processor, result);
2045         IMB_colormanagement_processor_free(cm_processor);
2046 }
2047
2048 static void colormanagement_imbuf_make_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
2049                                                      const ColorManagedDisplaySettings *display_settings, bool make_byte)
2050 {
2051         if (!ibuf->rect && make_byte)
2052                 imb_addrectImBuf(ibuf);
2053
2054         colormanage_display_buffer_process_ex(ibuf, ibuf->rect_float, (unsigned char *)ibuf->rect,
2055                                               view_settings, display_settings);
2056 }
2057
2058 void IMB_colormanagement_imbuf_make_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
2059                                                   const ColorManagedDisplaySettings *display_settings)
2060 {
2061         colormanagement_imbuf_make_display_space(ibuf, view_settings, display_settings, false);
2062 }
2063
2064 /* prepare image buffer to be saved on disk, applying color management if needed
2065  * color management would be applied if image is saving as render result and if
2066  * file format is not expecting float buffer to be in linear space (currently
2067  * JPEG2000 and TIFF are such formats -- they're storing image as float but
2068  * file itself stores applied color space).
2069  *
2070  * Both byte and float buffers would contain applied color space, and result's
2071  * float_colorspace would be set to display color space. This should be checked
2072  * in image format write callback and if float_colorspace is not NULL, no color
2073  * space transformation should be applied on this buffer.
2074  */
2075 ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, bool save_as_render, bool allocate_result, const ColorManagedViewSettings *view_settings,
2076                                            const ColorManagedDisplaySettings *display_settings, ImageFormatData *image_format_data)
2077 {
2078         ImBuf *colormanaged_ibuf = ibuf;
2079         bool do_colormanagement;
2080         bool is_movie = BKE_imtype_is_movie(image_format_data->imtype);
2081         bool requires_linear_float = BKE_imtype_requires_linear_float(image_format_data->imtype);
2082         bool do_alpha_under = image_format_data->planes != R_IMF_PLANES_RGBA;
2083
2084         if (ibuf->rect_float && ibuf->rect &&
2085             (ibuf->userflags & (IB_DISPLAY_BUFFER_INVALID | IB_RECT_INVALID)) != 0)
2086         {
2087                 IMB_rect_from_float(ibuf);
2088                 ibuf->userflags &= ~(IB_RECT_INVALID | IB_DISPLAY_BUFFER_INVALID);
2089         }
2090
2091         do_colormanagement = save_as_render && (is_movie || !requires_linear_float);
2092
2093         if (do_colormanagement || do_alpha_under) {
2094                 if (allocate_result) {
2095                         colormanaged_ibuf = IMB_dupImBuf(ibuf);
2096                 }
2097                 else {
2098                         /* render pipeline is constructing image buffer itself, but it's re-using byte and float buffers from render result
2099                          * make copy of this buffers here sine this buffers would be transformed to other color space here
2100                          */
2101
2102                         if (ibuf->rect && (ibuf->mall & IB_rect) == 0) {
2103                                 ibuf->rect = MEM_dupallocN(ibuf->rect);
2104                                 ibuf->mall |= IB_rect;
2105                         }
2106
2107                         if (ibuf->rect_float && (ibuf->mall & IB_rectfloat) == 0) {
2108                                 ibuf->rect_float = MEM_dupallocN(ibuf->rect_float);
2109                                 ibuf->mall |= IB_rectfloat;
2110                         }
2111                 }
2112         }
2113
2114         /* If we're saving from RGBA to RGB buffer then it's not
2115          * so much useful to just ignore alpha -- it leads to bad
2116          * artifacts especially when saving byte images.
2117          *
2118          * What we do here is we're overlaying our image on top of
2119          * background color (which is currently black).
2120          *
2121          * This is quite much the same as what Gimp does and it
2122          * seems to be what artists expects from saving.
2123          *
2124          * Do a conversion here, so image format writers could
2125          * happily assume all the alpha tricks were made already.
2126          * helps keep things locally here, not spreading it to
2127          * all possible image writers we've got.
2128          */
2129         if (do_alpha_under) {
2130                 float color[3] = {0, 0, 0};
2131
2132                 if (colormanaged_ibuf->rect_float && colormanaged_ibuf->channels == 4) {
2133                         IMB_alpha_under_color_float(colormanaged_ibuf->rect_float, colormanaged_ibuf->x,
2134                                                     colormanaged_ibuf->y, color);
2135                 }
2136
2137                 if (colormanaged_ibuf->rect) {
2138                         IMB_alpha_under_color_byte((unsigned char *)colormanaged_ibuf->rect,
2139                                                    colormanaged_ibuf->x, colormanaged_ibuf->y,
2140                                                    color);
2141                 }
2142         }
2143
2144         if (do_colormanagement) {
2145                 bool make_byte = false;
2146                 const ImFileType *type;
2147
2148                 /* for proper check whether byte buffer is required by a format or not
2149                  * should be pretty safe since this image buffer is supposed to be used for
2150                  * saving only and ftype would be overwritten a bit later by BKE_imbuf_write
2151                  */
2152                 colormanaged_ibuf->ftype = BKE_image_imtype_to_ftype(image_format_data->imtype, &colormanaged_ibuf->foptions);
2153
2154                 /* if file format isn't able to handle float buffer itself,
2155                  * we need to allocate byte buffer and store color managed
2156                  * image there
2157                  */
2158                 for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
2159                         if (type->save && type->ftype(type, colormanaged_ibuf)) {
2160                                 if ((type->flag & IM_FTYPE_FLOAT) == 0)
2161                                         make_byte = true;
2162
2163                                 break;
2164                         }
2165                 }
2166
2167                 /* perform color space conversions */
2168                 colormanagement_imbuf_make_display_space(colormanaged_ibuf, view_settings, display_settings, make_byte);
2169
2170                 if (colormanaged_ibuf->rect_float) {
2171                         /* float buffer isn't linear anymore,
2172                          * image format write callback should check for this flag and assume
2173                          * no space conversion should happen if ibuf->float_colorspace != NULL
2174                          */
2175                         colormanaged_ibuf->float_colorspace = display_transform_get_colorspace(view_settings, display_settings);
2176                 }
2177         }
2178
2179         if (colormanaged_ibuf != ibuf) {
2180                 IMB_metadata_copy(colormanaged_ibuf, ibuf);
2181         }
2182
2183         return colormanaged_ibuf;
2184 }
2185
2186 void IMB_colormanagement_buffer_make_display_space(float *buffer, unsigned char *display_buffer,
2187                                                    int width, int height, int channels, float dither,
2188                                                    const ColorManagedViewSettings *view_settings,
2189                                                    const ColorManagedDisplaySettings *display_settings)
2190 {
2191         ColormanageProcessor *cm_processor;
2192         size_t float_buffer_size = ((size_t)width) * height * channels * sizeof(float);
2193         float *display_buffer_float = MEM_mallocN(float_buffer_size, "byte_buffer_make_display_space");
2194
2195         /* TODO(sergey): Convert float directly to byte buffer. */
2196
2197         memcpy(display_buffer_float, buffer, float_buffer_size);
2198
2199         cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2200
2201         processor_transform_apply_threaded(NULL, display_buffer_float, width, height, channels,
2202                                            cm_processor, true, false);
2203
2204         IMB_buffer_byte_from_float(display_buffer, display_buffer_float,
2205                                    channels, dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
2206                                    true, width, height, width, width);
2207
2208         MEM_freeN(display_buffer_float);
2209         IMB_colormanagement_processor_free(cm_processor);
2210 }
2211
2212 /*********************** Public display buffers interfaces *************************/
2213
2214 /* acquire display buffer for given image buffer using specified view and display settings */
2215 unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
2216                                           const ColorManagedDisplaySettings *display_settings, void **cache_handle)
2217 {
2218         unsigned char *display_buffer;
2219         size_t buffer_size;
2220         ColormanageCacheViewSettings cache_view_settings;
2221         ColormanageCacheDisplaySettings cache_display_settings;
2222         ColorManagedViewSettings default_view_settings;
2223         const ColorManagedViewSettings *applied_view_settings;
2224
2225         *cache_handle = NULL;
2226
2227         if (!ibuf->x || !ibuf->y)
2228                 return NULL;
2229
2230         if (view_settings) {
2231                 applied_view_settings = view_settings;
2232         }
2233         else {
2234                 /* If no view settings were specified, use default ones, which will
2235                  * attempt not to do any extra color correction. */
2236                 IMB_colormanagement_init_default_view_settings(
2237                         &default_view_settings, display_settings);
2238                 applied_view_settings = &default_view_settings;
2239         }
2240
2241         /* early out: no float buffer and byte buffer is already in display space,
2242          * let's just use if
2243          */
2244         if (ibuf->rect_float == NULL && ibuf->rect_colorspace && ibuf->channels == 4) {
2245                 if (is_ibuf_rect_in_display_space(ibuf, applied_view_settings, display_settings))
2246                         return (unsigned char *) ibuf->rect;
2247         }
2248
2249         colormanage_view_settings_to_cache(ibuf, &cache_view_settings, applied_view_settings);
2250         colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
2251
2252         if (ibuf->invalid_rect.xmin != ibuf->invalid_rect.xmax) {
2253                 if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
2254                         IMB_partial_display_buffer_update_threaded(ibuf,
2255                                                                    ibuf->rect_float,
2256                                                                    (unsigned char *) ibuf->rect,
2257                                                                    ibuf->x,
2258                                                                    0, 0,
2259                                                                    applied_view_settings,
2260                                                                    display_settings,
2261                                                                    ibuf->invalid_rect.xmin,
2262                                                                    ibuf->invalid_rect.ymin,
2263                                                                    ibuf->invalid_rect.xmax,
2264                                                                    ibuf->invalid_rect.ymax,
2265                                                                    false);
2266                 }
2267
2268                 BLI_rcti_init(&ibuf->invalid_rect, 0, 0, 0, 0);
2269         }
2270
2271         BLI_thread_lock(LOCK_COLORMANAGE);
2272
2273         /* ensure color management bit fields exists */
2274         if (!ibuf->display_buffer_flags) {
2275                 ibuf->display_buffer_flags = MEM_callocN(sizeof(unsigned int) * global_tot_display, "imbuf display_buffer_flags");
2276         }
2277         else if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) {
2278                 /* all display buffers were marked as invalid from other areas,
2279                  * now propagate this flag to internal color management routines
2280                  */
2281                 memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
2282
2283                 ibuf->userflags &= ~IB_DISPLAY_BUFFER_INVALID;
2284         }
2285
2286         display_buffer = colormanage_cache_get(ibuf, &cache_view_settings, &cache_display_settings, cache_handle);
2287
2288         if (display_buffer) {
2289                 BLI_thread_unlock(LOCK_COLORMANAGE);
2290                 return display_buffer;
2291         }
2292
2293         buffer_size = DISPLAY_BUFFER_CHANNELS * ((size_t)ibuf->x) * ibuf->y * sizeof(char);
2294         display_buffer = MEM_callocN(buffer_size, "imbuf display buffer");
2295
2296         colormanage_display_buffer_process(ibuf, display_buffer, applied_view_settings, display_settings);
2297
2298         colormanage_cache_put(ibuf, &cache_view_settings, &cache_display_settings, display_buffer, cache_handle);
2299
2300         BLI_thread_unlock(LOCK_COLORMANAGE);
2301
2302         return display_buffer;
2303 }
2304
2305 /* same as IMB_display_buffer_acquire but gets view and display settings from context */
2306 unsigned char *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle)
2307 {
2308         ColorManagedViewSettings *view_settings;
2309         ColorManagedDisplaySettings *display_settings;
2310
2311         IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
2312
2313         return IMB_display_buffer_acquire(ibuf, view_settings, display_settings, cache_handle);
2314 }
2315
2316 void IMB_display_buffer_transform_apply(unsigned char *display_buffer, float *linear_buffer, int width, int height,
2317                                         int channels, const ColorManagedViewSettings *view_settings,
2318                                         const ColorManagedDisplaySettings *display_settings, bool predivide)
2319 {
2320         float *buffer;
2321         ColormanageProcessor *cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2322
2323         buffer = MEM_mallocN((size_t)channels * width * height * sizeof(float), "display transform temp buffer");
2324         memcpy(buffer, linear_buffer, (size_t)channels * width * height * sizeof(float));
2325
2326         IMB_colormanagement_processor_apply(cm_processor, buffer, width, height, channels, predivide);
2327
2328         IMB_colormanagement_processor_free(cm_processor);
2329
2330         IMB_buffer_byte_from_float(display_buffer, buffer, channels, 0.0f, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
2331                                    false, width, height, width, width);
2332
2333         MEM_freeN(buffer);
2334 }
2335
2336 void IMB_display_buffer_release(void *cache_handle)
2337 {
2338         if (cache_handle) {
2339                 BLI_thread_lock(LOCK_COLORMANAGE);
2340
2341                 colormanage_cache_handle_release(cache_handle);
2342
2343                 BLI_thread_unlock(LOCK_COLORMANAGE);
2344         }
2345 }
2346
2347 /*********************** Display functions *************************/
2348
2349 const char *colormanage_display_get_default_name(void)
2350 {
2351         OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
2352         const char *display_name;
2353
2354         display_name = OCIO_configGetDefaultDisplay(config);
2355
2356         OCIO_configRelease(config);
2357
2358         return display_name;
2359 }
2360
2361 ColorManagedDisplay *colormanage_display_get_default(void)
2362 {
2363         const char *display_name = colormanage_display_get_default_name();
2364
2365         if (display_name[0] == '\0')
2366                 return NULL;
2367
2368         return colormanage_display_get_named(display_name);
2369 }
2370
2371 ColorManagedDisplay *colormanage_display_add(const char *name)
2372 {
2373         ColorManagedDisplay *display;
2374         int index = 0;
2375
2376         if (global_displays.last) {
2377                 ColorManagedDisplay *last_display = global_displays.last;
2378
2379                 index = last_display->index;
2380         }
2381
2382         display = MEM_callocN(sizeof(ColorManagedDisplay), "ColorManagedDisplay");
2383
2384         display->index = index + 1;
2385
2386         BLI_strncpy(display->name, name, sizeof(display->name));
2387
2388         BLI_addtail(&global_displays, display);
2389
2390         return display;
2391 }
2392
2393 ColorManagedDisplay *colormanage_display_get_named(const char *name)
2394 {
2395         ColorManagedDisplay *display;
2396
2397         for (display = global_displays.first; display; display = display->next) {
2398                 if (STREQ(display->name, name))
2399                         return display;
2400         }
2401
2402         return NULL;
2403 }
2404
2405 ColorManagedDisplay *colormanage_display_get_indexed(int index)
2406 {
2407         /* display indices are 1-based */
2408         return BLI_findlink(&global_displays, index - 1);
2409 }
2410
2411 int IMB_colormanagement_display_get_named_index(const char *name)
2412 {
2413         ColorManagedDisplay *display;
2414
2415         display = colormanage_display_get_named(name);
2416
2417         if (display) {
2418                 return display->index;
2419         }
2420
2421         return 0;
2422 }
2423
2424 const char *IMB_colormanagement_display_get_indexed_name(int index)
2425 {
2426         ColorManagedDisplay *display;
2427
2428         display = colormanage_display_get_indexed(index);
2429
2430         if (display) {
2431                 return display->name;
2432         }
2433
2434         return NULL;
2435 }
2436
2437 const char *IMB_colormanagement_display_get_default_name(void)
2438 {
2439         ColorManagedDisplay *display = colormanage_display_get_default();
2440
2441         return display->name;
2442 }
2443
2444 /* used by performance-critical pixel processing areas, such as color widgets */
2445 ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name)
2446 {
2447         return colormanage_display_get_named(name);
2448 }
2449
2450 const char *IMB_colormanagement_display_get_none_name(void)
2451 {
2452         if (colormanage_display_get_named("None") != NULL)
2453                 return "None";
2454
2455         return colormanage_display_get_default_name();
2456 }
2457
2458 const char *IMB_colormanagement_display_get_default_view_transform_name(
2459         struct ColorManagedDisplay *display)
2460 {
2461         return colormanage_view_get_default_name(display);
2462 }
2463
2464 /*********************** View functions *************************/
2465
2466 const char *colormanage_view_get_default_name(const ColorManagedDisplay *display)
2467 {
2468         OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
2469         const char *name;
2470
2471         name = OCIO_configGetDefaultView(config, display->name);
2472
2473         OCIO_configRelease(config);
2474
2475         return name;
2476 }
2477
2478 ColorManagedView *colormanage_view_get_default(const ColorManagedDisplay *display)
2479 {
2480         const char *name = colormanage_view_get_default_name(display);
2481
2482         if (!name || name[0] == '\0')
2483                 return NULL;
2484
2485         return colormanage_view_get_named(name);
2486 }
2487
2488 ColorManagedView *colormanage_view_add(const char *name)
2489 {
2490         ColorManagedView *view;
2491         int index = global_tot_view;
2492
2493         view = MEM_callocN(sizeof(ColorManagedView), "ColorManagedView");
2494         view->index = index + 1;
2495         BLI_strncpy(view->name, name, sizeof(view->name));
2496
2497         BLI_addtail(&global_views, view);
2498
2499         global_tot_view++;
2500
2501         return view;
2502 }
2503
2504 ColorManagedView *colormanage_view_get_named(const char *name)
2505 {
2506         ColorManagedView *view;
2507
2508         for (view = global_views.first; view; view = view->next) {
2509                 if (STREQ(view->name, name))
2510                         return view;
2511         }
2512
2513         return NULL;
2514 }
2515
2516 ColorManagedView *colormanage_view_get_indexed(int index)
2517 {
2518         /* view transform indices are 1-based */
2519         return BLI_findlink(&global_views, index - 1);
2520 }
2521
2522 ColorManagedView *colormanage_view_get_named_for_display(
2523         const char *display_name, const char *name)
2524 {
2525         ColorManagedDisplay *display = colormanage_display_get_named(display_name);
2526         if (display == NULL) {
2527                 return NULL;
2528         }
2529         LISTBASE_FOREACH(LinkData *, view_link, &display->views) {
2530                 ColorManagedView *view = view_link->data;
2531                 if (STRCASEEQ(name, view->name)) {
2532                         return view;
2533                 }
2534         }
2535         return NULL;
2536 }
2537
2538 int IMB_colormanagement_view_get_named_index(const char *name)
2539 {
2540         ColorManagedView *view = colormanage_view_get_named(name);
2541
2542         if (view) {
2543                 return view->index;
2544         }
2545
2546         return 0;
2547 }
2548
2549 const char *IMB_colormanagement_view_get_indexed_name(int index)
2550 {
2551         ColorManagedView *view = colormanage_view_get_indexed(index);
2552
2553         if (view) {
2554                 return view->name;
2555         }
2556
2557         return NULL;
2558 }
2559
2560 const char *IMB_colormanagement_view_get_default_name(const char *display_name)
2561 {
2562         ColorManagedDisplay *display = colormanage_display_get_named(display_name);
2563         ColorManagedView *view = NULL;
2564
2565         if (display)
2566                 view = colormanage_view_get_default(display);
2567
2568         if (view)
2569                 return view->name;
2570
2571         return NULL;
2572 }
2573
2574 /*********************** Color space functions *************************/
2575
2576 static void colormanage_description_strip(char *description)
2577 {
2578         int i, n;
2579
2580         for (i = (int)strlen(description) - 1; i >= 0; i--) {
2581                 if (ELEM(description[i], '\r', '\n')) {
2582                         description[i] = '\0';
2583                 }
2584                 else {
2585                         break;
2586                 }
2587         }
2588
2589         for (i = 0, n = strlen(description); i < n; i++) {
2590                 if (ELEM(description[i], '\r', '\n')) {
2591                         description[i] = ' ';
2592                 }
2593         }
2594 }
2595
2596 ColorSpace *colormanage_colorspace_add(const char *name, const char *description, bool is_invertible, bool is_data)
2597 {
2598         ColorSpace *colorspace, *prev_space;
2599         int counter = 1;
2600
2601         colorspace = MEM_callocN(sizeof(ColorSpace), "ColorSpace");
2602
2603         BLI_strncpy(colorspace->name, name, sizeof(colorspace->name));
2604
2605         if (description) {
2606                 BLI_strncpy(colorspace->description, description, sizeof(colorspace->description));
2607
2608                 colormanage_description_strip(colorspace->description);
2609         }
2610
2611         colorspace->is_invertible = is_invertible;
2612         colorspace->is_data = is_data;
2613
2614         for (prev_space = global_colorspaces.first; prev_space; prev_space = prev_space->next) {
2615                 if (BLI_strcasecmp(prev_space->name, colorspace->name) > 0)
2616                         break;
2617
2618                 prev_space->index = counter++;
2619         }
2620
2621         if (!prev_space)
2622                 BLI_addtail(&global_colorspaces, colorspace);
2623         else
2624                 BLI_insertlinkbefore(&global_colorspaces, prev_space, colorspace);
2625
2626         colorspace->index = counter++;
2627         for (; prev_space; prev_space = prev_space->next) {
2628                 prev_space->index = counter++;
2629         }
2630
2631         global_tot_colorspace++;
2632
2633         return colorspace;
2634 }
2635
2636 ColorSpace *colormanage_colorspace_get_named(const char *name)
2637 {
2638         ColorSpace *colorspace;
2639
2640         for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) {
2641                 if (STREQ(colorspace->name, name))
2642                         return colorspace;
2643         }
2644
2645         return NULL;
2646 }
2647
2648 ColorSpace *colormanage_colorspace_get_roled(int role)
2649 {
2650         const char *role_colorspace = IMB_colormanagement_role_colorspace_name_get(role);
2651
2652         return colormanage_colorspace_get_named(role_colorspace);
2653 }
2654
2655 ColorSpace *colormanage_colorspace_get_indexed(int index)
2656 {
2657         /* color space indices are 1-based */
2658         return BLI_findlink(&global_colorspaces, index - 1);
2659 }
2660
2661 int IMB_colormanagement_colorspace_get_named_index(const char *name)
2662 {
2663         ColorSpace *colorspace;
2664
2665         colorspace = colormanage_colorspace_get_named(name);
2666
2667         if (colorspace) {
2668                 return colorspace->index;
2669         }
2670
2671         return 0;
2672 }
2673
2674 const char *IMB_colormanagement_colorspace_get_indexed_name(int index)
2675 {
2676         ColorSpace *colorspace;
2677
2678         colorspace = colormanage_colorspace_get_indexed(index);
2679
2680         if (colorspace) {
2681                 return colorspace->name;
2682         }
2683
2684         return "";
2685 }
2686
2687 void IMB_colormanagement_colorspace_from_ibuf_ftype(ColorManagedColorspaceSettings *colorspace_settings, ImBuf *ibuf)
2688 {
2689         /* Don't modify non-color data space, it does not change with file type. */
2690         ColorSpace *colorspace = colormanage_colorspace_get_named(colorspace_settings->name);
2691
2692         if (colorspace && colorspace->is_data) {
2693                 return;
2694         }
2695
2696         /* Get color space from file type. */
2697         const ImFileType *type;
2698
2699         for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
2700                 if (type->save && type->ftype(type, ibuf)) {
2701                         const char *role_colorspace;
2702
2703                         role_colorspace = IMB_colormanagement_role_colorspace_name_get(type->default_save_role);
2704
2705                         BLI_strncpy(colorspace_settings->name, role_colorspace, sizeof(colorspace_settings->name));
2706                 }
2707         }
2708 }
2709
2710 /*********************** Looks functions *************************/
2711
2712 ColorManagedLook *colormanage_look_add(const char *name, const char *process_space, bool is_noop)
2713 {
2714         ColorManagedLook *look;
2715         int index = global_tot_looks;
2716
2717         look = MEM_callocN(sizeof(ColorManagedLook), "ColorManagedLook");
2718         look->index = index + 1;
2719         BLI_strncpy(look->name, name, sizeof(look->name));
2720         BLI_strncpy(look->ui_name, name, sizeof(look->ui_name));
2721         BLI_strncpy(look->process_space, process_space, sizeof(look->process_space));
2722         look->is_noop = is_noop;
2723
2724         /* Detect view specific looks. */
2725         const char *separator_offset = strstr(look->name, " - ");
2726         if (separator_offset) {
2727                 BLI_strncpy(look->view, look->name, separator_offset - look->name + 1);
2728                 BLI_strncpy(look->ui_name, separator_offset + strlen(" - "), sizeof(look->ui_name));
2729         }
2730
2731         BLI_addtail(&global_looks, look);
2732
2733         global_tot_looks++;
2734
2735         return look;
2736 }
2737
2738 ColorManagedLook *colormanage_look_get_named(const char *name)
2739 {
2740         ColorManagedLook *look;
2741
2742         for (look = global_looks.first; look; look = look->next) {
2743                 if (STREQ(look->name, name)) {
2744                         return look;
2745                 }
2746         }
2747
2748         return NULL;
2749 }
2750
2751 ColorManagedLook *colormanage_look_get_indexed(int index)
2752 {
2753         /* look indices are 1-based */
2754         return BLI_findlink(&global_looks, index - 1);
2755 }
2756
2757 int IMB_colormanagement_look_get_named_index(const char *name)
2758 {
2759         ColorManagedLook *look;
2760
2761         look = colormanage_look_get_named(name);
2762
2763         if (look) {
2764                 return look->index;
2765         }
2766
2767         return 0;
2768 }
2769
2770 const char *IMB_colormanagement_look_get_indexed_name(int index)
2771 {
2772         ColorManagedLook *look;
2773
2774         look = colormanage_look_get_indexed(index);
2775
2776         if (look) {
2777                 return look->name;
2778         }
2779
2780         return NULL;
2781 }
2782
2783 /*********************** RNA helper functions *************************/
2784
2785 void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totitem)
2786 {
2787         ColorManagedDisplay *display;
2788
2789         for (display = global_displays.first; display; display = display->next) {
2790                 EnumPropertyItem item;
2791
2792                 item.value = display->index;
2793                 item.name = display->name;
2794                 item.identifier = display->name;
2795                 item.icon = 0;
2796                 item.description = "";
2797
2798                 RNA_enum_item_add(items, totitem, &item);
2799         }
2800 }
2801
2802 static void colormanagement_view_item_add(EnumPropertyItem **items, int *totitem, ColorManagedView *view)
2803 {
2804         EnumPropertyItem item;
2805
2806         item.value = view->index;
2807         item.name = view->name;
2808         item.identifier = view->name;
2809         item.icon = 0;
2810         item.description = "";
2811
2812         RNA_enum_item_add(items, totitem, &item);
2813 }
2814
2815 void IMB_colormanagement_view_items_add(EnumPropertyItem **items, int *totitem, const char *display_name)
2816 {
2817         ColorManagedDisplay *display = colormanage_display_get_named(display_name);
2818         ColorManagedView *view;
2819
2820         if (display) {
2821                 LinkData *display_view;
2822
2823                 for (display_view = display->views.first; display_view; display_view = display_view->next) {
2824                         view = display_view->data;
2825
2826                         colormanagement_view_item_add(items, totitem, view);
2827                 }
2828         }
2829 }
2830
2831 void IMB_colormanagement_look_items_add(struct EnumPropertyItem **items, int *totitem, const char *view_name)
2832 {
2833         ColorManagedLook *look;
2834         const char *view_filter = NULL;
2835
2836         /* Test if this view transform is limited to specific looks. */
2837         for (look = global_looks.first; look; look = look->next) {
2838                 if (STREQ(look->view, view_name)) {
2839                         view_filter = view_name;
2840                 }
2841         }
2842
2843         for (look = global_looks.first; look; look = look->next) {
2844                 if (!look->is_noop && view_filter && !STREQ(look->view, view_filter)) {
2845                         continue;
2846                 }
2847
2848                 EnumPropertyItem item;
2849
2850                 item.value = look->index;
2851                 item.name = look->ui_name;
2852                 item.identifier = look->name;
2853                 item.icon = 0;
2854                 item.description = "";
2855
2856                 RNA_enum_item_add(items, totitem, &item);
2857         }
2858 }
2859
2860 void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *totitem)
2861 {
2862         ColorSpace *colorspace;
2863
2864         for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) {
2865                 EnumPropertyItem item;
2866
2867                 if (!colorspace->is_invertible)
2868                         continue;
2869
2870                 item.value = colorspace->index;
2871                 item.name = colorspace->name;
2872                 item.identifier = colorspace->name;
2873                 item.icon = 0;
2874                 item.description = colorspace->description;
2875
2876                 RNA_enum_item_add(items, totitem, &item);
2877         }
2878 }
2879
2880 /*********************** Partial display buffer update  *************************/
2881
2882 /*
2883  * Partial display update is supposed to be used by such areas as
2884  * compositor and renderer, This areas are calculating tiles of the
2885  * images and because of performance reasons only this tiles should
2886  * be color managed.
2887  * This gives nice visual feedback without slowing things down.
2888  *
2889  * Updating happens for active display transformation only, all
2890  * the rest buffers would be marked as dirty
2891  */
2892
2893 static void partial_buffer_update_rect(ImBuf *ibuf,
2894                                        unsigned char *display_buffer,
2895                                        const float *linear_buffer,
2896                                        const unsigned char *byte_buffer,
2897                                        int display_stride,
2898                                        int linear_stride,
2899                                        int linear_offset_x, int linear_offset_y,
2900                                        ColormanageProcessor *cm_processor,
2901                                        const int xmin, const int ymin,
2902                                        const int xmax, const int ymax)
2903 {
2904         int x, y;
2905         int channels = ibuf->channels;
2906         float dither = ibuf->dither;
2907         ColorSpace *rect_colorspace = ibuf->rect_colorspace;
2908         float *display_buffer_float = NULL;
2909         const int width = xmax - xmin;
2910         const int height = ymax - ymin;
2911         bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
2912
2913         if (dither != 0.0f) {
2914                 /* cm_processor is NULL in cases byte_buffer's space matches display
2915                  * buffer's space
2916                  * in this case we could skip extra transform and only apply dither
2917                  * use 4 channels for easier byte->float->byte conversion here so
2918                  * (this is only needed to apply dither, in other cases we'll convert
2919                  * byte buffer to display directly)
2920                  */
2921                 if (!cm_processor)
2922                         channels = 4;
2923
2924                 display_buffer_float = MEM_callocN((size_t)channels * width * height * sizeof(float), "display buffer for dither");
2925         }
2926
2927         if (cm_processor) {
2928                 for (y = ymin; y < ymax; y++) {
2929                         for (x = xmin; x < xmax; x++) {
2930                                 size_t display_index = ((size_t)y * display_stride + x) * 4;
2931                                 size_t linear_index = ((size_t)(y - linear_offset_y) * linear_stride + (x - linear_offset_x)) * channels;
2932                                 float pixel[4];
2933
2934                                 if (linear_buffer) {
2935                                         if (channels == 4) {
2936                                                 copy_v4_v4(pixel, (float *) linear_buffer + linear_index);
2937                                         }
2938                                         else if (channels == 3) {
2939                                                 copy_v3_v3(pixel, (float *) linear_buffer + linear_index);
2940                                                 pixel[3] = 1.0f;
2941                                         }
2942                                         else if (channels == 1) {
2943                                                 pixel[0] = linear_buffer[linear_index];
2944                                         }
2945                                         else {
2946                                                 BLI_assert(!"Unsupported number of channels in partial buffer update");
2947                                         }
2948                                 }
2949                                 else if (byte_buffer) {
2950                                         rgba_uchar_to_float(pixel, byte_buffer + linear_index);
2951                                         IMB_colormanagement_colorspace_to_scene_linear_v3(pixel, rect_colorspace);
2952                                         straight_to_premul_v4(pixel);
2953                                 }
2954
2955                                 if (!is_data) {
2956                                         IMB_colormanagement_processor_apply_pixel(cm_processor, pixel, channels);
2957                                 }
2958
2959                                 if (display_buffer_float) {
2960                                         size_t index = ((size_t)(y - ymin) * width + (x - xmin)) * channels;
2961
2962                                         if (channels == 4) {
2963                                                 copy_v4_v4(display_buffer_float + index, pixel);
2964                                         }
2965                                         else if (channels == 3) {
2966                                                 copy_v3_v3(display_buffer_float + index, pixel);
2967                                         }
2968                                         else /* if (channels == 1) */ {
2969                                                 display_buffer_float[index] = pixel[0];
2970                                         }
2971                                 }
2972                                 else {
2973                                         if (channels == 4) {
2974                                                 float pixel_straight[4];
2975                                                 premul_to_straight_v4_v4(pixel_straight, pixel);
2976                                                 rgba_float_to_uchar(display_buffer + display_index, pixel_straight);
2977                                         }
2978                                         else if (channels == 3) {
2979                                                 rgb_float_to_uchar(display_buffer + display_index, pixel);
2980                                                 display_buffer[display_index + 3] = 255;
2981                                         }
2982                                         else /* if (channels == 1) */ {
2983                                                 display_buffer[display_index] =
2984                                                         display_buffer[display_index + 1] =
2985                                                         display_buffer[display_index + 2] =
2986                                                         display_buffer[display_index + 3] = unit_float_to_uchar_clamp(pixel[0]);
2987                                         }
2988                                 }
2989                         }
2990                 }
2991         }
2992         else {
2993                 if (display_buffer_float) {
2994                         /* huh, for dither we need float buffer first, no cheaper way. currently */
2995                         IMB_buffer_float_from_byte(display_buffer_float, byte_buffer,
2996                                                    IB_PROFILE_SRGB, IB_PROFILE_SRGB, true,
2997                                                    width, height, width, display_stride);
2998                 }
2999                 else {
3000                         int i;
3001
3002                         for (i = ymin; i < ymax; i++) {
3003                                 size_t byte_offset = ((size_t)linear_stride * i + xmin) * 4;
3004                                 size_t display_offset = ((size_t)display_stride * i + xmin) * 4;
3005
3006                                 memcpy(display_buffer + display_offset, byte_buffer + byte_offset, 4 * sizeof(char) * width);
3007                         }
3008                 }
3009         }
3010
3011         if (display_buffer_float) {
3012                 size_t display_index = ((size_t)ymin * display_stride + xmin) * channels;
3013
3014                 IMB_buffer_byte_from_float(display_buffer + display_index, display_buffer_float, channels, dither,
3015                                            IB_PROFILE_SRGB, IB_PROFILE_SRGB, true, width, height, display_stride, width);
3016
3017                 MEM_freeN(display_buffer_float);
3018         }
3019 }
3020
3021 typedef struct PartialThreadData {
3022         ImBuf *ibuf;
3023         unsigned char *display_buffer;
3024         const float *linear_buffer;
3025         const unsigned char *byte_buffer;
3026         int display_stride;
3027         int linear_stride;
3028         int linear_offset_x, linear_offset_y;
3029         ColormanageProcessor *cm_processor;
3030         int xmin, ymin, xmax;
3031 } PartialThreadData;
3032
3033 static void partial_buffer_update_rect_thread_do(void *data_v,
3034                                                  int start_scanline,
3035                                                  int num_scanlines)
3036 {
3037         PartialThreadData *data = (PartialThreadData *)data_v;
3038         int ymin = data->ymin + start_scanline;
3039         partial_buffer_update_rect(data->ibuf,
3040                                    data->display_buffer,
3041                                    data->linear_buffer,
3042                                    data->byte_buffer,
3043                                    data->display_stride,
3044                                    data->linear_stride,
3045                                    data->linear_offset_x,
3046                                    data->linear_offset_y,
3047                                    data->cm_processor,
3048                                    data->xmin,
3049                                    ymin,
3050                                    data->xmax,
3051                                    ymin + num_scanlines);
3052 }
3053
3054 static void imb_partial_display_buffer_update_ex(ImBuf *ibuf,
3055                                                  const float *linear_buffer,
3056                                                  const unsigned char *byte_buffer,
3057                                                  int stride,
3058                                                  int offset_x, int offset_y,
3059                                                  const ColorManagedViewSettings *view_settings,
3060                                                  const ColorManagedDisplaySettings *display_settings,
3061                                                  int xmin, int ymin,
3062                                                  int xmax, int ymax,
3063                                                  bool copy_display_to_byte_buffer,
3064                                                  bool do_threads)
3065 {
3066         ColormanageCacheViewSettings cache_view_settings;
3067         ColormanageCacheDisplaySettings cache_display_settings;
3068         void *cache_handle = NULL;
3069         unsigned char *display_buffer = NULL;
3070         int buffer_width = ibuf->x;
3071
3072         if (ibuf->display_buffer_flags) {
3073                 int view_flag, display_index;
3074
3075                 colormanage_view_settings_to_cache(ibuf, &cache_view_settings, view_settings);
3076                 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
3077
3078                 view_flag = 1 << (cache_view_settings.view - 1);
3079                 display_index = cache_display_settings.display - 1;
3080
3081                 BLI_thread_lock(LOCK_COLORMANAGE);
3082
3083                 if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
3084                         display_buffer = colormanage_cache_get(ibuf,
3085                                                                &cache_view_settings,
3086                                                                &cache_display_settings,
3087                                                                &cache_handle);
3088                 }
3089
3090                 /* In some rare cases buffer's dimension could be changing directly from
3091                  * different thread
3092                  * this i.e. happens when image editor acquires render result
3093                  */
3094                 buffer_width = ibuf->x;
3095
3096                 /* Mark all other buffers as invalid. */
3097                 memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
3098                 ibuf->display_buffer_flags[display_index] |= view_flag;
3099
3100                 BLI_thread_unlock(LOCK_COLORMANAGE);
3101         }
3102
3103         if (display_buffer == NULL) {
3104                 if (copy_display_to_byte_buffer) {
3105                         display_buffer = (unsigned char *) ibuf->rect;
3106                 }
3107         }
3108
3109         if (display_buffer) {
3110                 ColormanageProcessor *cm_processor = NULL;
3111                 bool skip_transform = false;
3112
3113                 /* Byte buffer is assumed to be in imbuf's rect space, so if byte buffer
3114                  * is known we could skip display->linear->display conversion in case
3115                  * display color space matches imbuf's rect space.
3116                  *
3117                  * But if there's a float buffer it's likely operation was performed on
3118                  * it first and byte buffer is likely to be out of date here.
3119                  */
3120                 if (linear_buffer == NULL && byte_buffer != NULL) {
3121                         skip_transform = is_ibuf_rect_in_display_space(ibuf,
3122                                                                        view_settings,
3123                                                                        display_settings);
3124                 }
3125
3126                 if (!skip_transform) {
3127                         cm_processor = IMB_colormanagement_display_processor_new(
3128                                 view_settings, display_settings);
3129                 }
3130
3131                 if (do_threads) {
3132                         PartialThreadData data;
3133                         data.ibuf = ibuf;
3134                         data.display_buffer = display_buffer;
3135                         data.linear_buffer = linear_buffer;
3136                         data.byte_buffer = byte_buffer;
3137                         data.display_stride = buffer_width;
3138                         data.linear_stride = stride;
3139                         data.linear_offset_x = offset_x;
3140                         data.linear_offset_y = offset_y;
3141                         data.cm_processor = cm_processor;
3142                         data.xmin = xmin;
3143                         data.ymin = ymin;
3144                         data.xmax = xmax;
3145                         IMB_processor_apply_threaded_scanlines(
3146                             ymax - ymin, partial_buffer_update_rect_thread_do, &data);
3147                 }
3148                 else {
3149                         partial_buffer_update_rect(ibuf,
3150                                                    display_buffer, linear_buffer, byte_buffer,
3151                                                    buffer_width,
3152                                                    stride,
3153                                                    offset_x, offset_y,
3154                                                    cm_processor,
3155                                                    xmin, ymin, xmax, ymax);
3156                 }
3157
3158                 if (cm_processor) {
3159                         IMB_colormanagement_processor_free(cm_processor);
3160                 }
3161
3162                 IMB_display_buffer_release(cache_handle);
3163         }
3164
3165         if (copy_display_to_byte_buffer && (unsigned char *) ibuf->rect != display_buffer) {
3166                 int y;
3167                 for (y = ymin; y < ymax; y++) {
3168                         size_t index = (size_t)y * buffer_width * 4;
3169                         memcpy((unsigned char *)ibuf->rect + index,
3170                                display_buffer + index,
3171                                (size_t)(xmax - xmin) * 4);
3172                 }
3173         }
3174 }
3175
3176 void IMB_partial_display_buffer_update(ImBuf *ibuf,
3177                                        const float *linear_buffer,
3178                                        const unsigned char *byte_buffer,
3179                                        int stride,
3180                                        int offset_x, int offset_y,
3181                                        const ColorManagedViewSettings *view_settings,
3182                                        const ColorManagedDisplaySettings *display_settings,
3183                                        int xmin, int ymin,
3184                                        int xmax, int ymax,
3185                                        bool copy_display_to_byte_buffer)
3186 {
3187         imb_partial_display_buffer_update_ex(ibuf,
3188                                              linear_buffer,
3189                                              byte_buffer,
3190                                              stride,
3191                                              offset_x, offset_y,
3192                                              view_settings,
3193                                              display_settings,
3194                                              xmin, ymin,
3195                                              xmax, ymax,
3196                                              copy_display_to_byte_buffer,
3197                                              false);
3198
3199 }
3200
3201 void IMB_partial_display_buffer_update_threaded(struct ImBuf *ibuf,
3202                                                 const float *linear_buffer,
3203                                                 const unsigned char *byte_buffer,
3204                                                 int stride,
3205                                                 int offset_x, int offset_y,
3206                                                 const struct ColorManagedViewSettings *view_settings,
3207                                                 const struct ColorManagedDisplaySettings *display_settings,
3208                                                 int xmin, int ymin, int xmax, int ymax,
3209                                                 bool copy_display_to_byte_buffer)
3210 {
3211         int width = xmax - xmin;
3212         int height = ymax - ymin;
3213         bool do_threads = (((size_t)width) * height >= 64 * 64);
3214         imb_partial_display_buffer_update_ex(ibuf,
3215                                              linear_buffer,
3216                                              byte_buffer,