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