1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2012 by Blender Foundation.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup imbuf
22  */
23 
24 #include "IMB_colormanagement.h"
25 #include "IMB_colormanagement_intern.h"
26 
27 #include <math.h>
28 #include <string.h>
29 
30 #include "DNA_color_types.h"
31 #include "DNA_image_types.h"
32 #include "DNA_movieclip_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_space_types.h"
35 
36 #include "IMB_filetype.h"
37 #include "IMB_filter.h"
38 #include "IMB_imbuf.h"
39 #include "IMB_imbuf_types.h"
40 #include "IMB_metadata.h"
41 #include "IMB_moviecache.h"
42 
43 #include "MEM_guardedalloc.h"
44 
45 #include "BLI_blenlib.h"
46 #include "BLI_math.h"
47 #include "BLI_math_color.h"
48 #include "BLI_rect.h"
49 #include "BLI_string.h"
50 #include "BLI_threads.h"
51 
52 #include "BKE_appdir.h"
53 #include "BKE_colortools.h"
54 #include "BKE_context.h"
55 #include "BKE_image.h"
56 #include "BKE_main.h"
57 #include "BKE_sequencer.h"
58 
59 #include "RNA_define.h"
60 
61 #include <ocio_capi.h>
62 
63 /* -------------------------------------------------------------------- */
64 /** \name Global declarations
65  * \{ */
66 
67 #define DISPLAY_BUFFER_CHANNELS 4
68 
69 /* ** list of all supported color spaces, displays and views */
70 static char global_role_data[MAX_COLORSPACE_NAME];
71 static char global_role_scene_linear[MAX_COLORSPACE_NAME];
72 static char global_role_color_picking[MAX_COLORSPACE_NAME];
73 static char global_role_texture_painting[MAX_COLORSPACE_NAME];
74 static char global_role_default_byte[MAX_COLORSPACE_NAME];
75 static char global_role_default_float[MAX_COLORSPACE_NAME];
76 static char global_role_default_sequencer[MAX_COLORSPACE_NAME];
77 
78 static ListBase global_colorspaces = {NULL, NULL};
79 static ListBase global_displays = {NULL, NULL};
80 static ListBase global_views = {NULL, NULL};
81 static ListBase global_looks = {NULL, NULL};
82 
83 static int global_tot_colorspace = 0;
84 static int global_tot_display = 0;
85 static int global_tot_view = 0;
86 static int global_tot_looks = 0;
87 
88 /* Luma coefficients and XYZ to RGB to be initialized by OCIO. */
89 float imbuf_luma_coefficients[3] = {0.0f};
90 float imbuf_xyz_to_rgb[3][3] = {{0.0f}};
91 float imbuf_rgb_to_xyz[3][3] = {{0.0f}};
92 static float imbuf_xyz_to_linear_srgb[3][3] = {{0.0f}};
93 static float imbuf_linear_srgb_to_xyz[3][3] = {{0.0f}};
94 
95 /* lock used by pre-cached processors getters, so processor wouldn't
96  * be created several times
97  * LOCK_COLORMANAGE can not be used since this mutex could be needed to
98  * be locked before pre-cached processor are creating
99  */
100 static pthread_mutex_t processor_lock = BLI_MUTEX_INITIALIZER;
101 
102 typedef struct ColormanageProcessor {
103   OCIO_ConstProcessorRcPtr *processor;
104   CurveMapping *curve_mapping;
105   bool is_data_result;
106 } ColormanageProcessor;
107 
108 static struct global_glsl_state {
109   /* Actual processor used for GLSL baked LUTs. */
110   /* UI colorspace here refers to the display linear color space,
111    * i.e: The linear color space w.r.t. display chromaticity and radiometry.
112    * We separate the colormanagement process into two steps to be able to
113    * merge UI using alpha blending in the correct color space. */
114   OCIO_ConstProcessorRcPtr *processor_scene_to_ui;
115   OCIO_ConstProcessorRcPtr *processor_ui_to_display;
116 
117   /* Settings of processor for comparison. */
118   char look[MAX_COLORSPACE_NAME];
119   char view[MAX_COLORSPACE_NAME];
120   char display[MAX_COLORSPACE_NAME];
121   char input[MAX_COLORSPACE_NAME];
122   float exposure, gamma;
123 
124   CurveMapping *curve_mapping, *orig_curve_mapping;
125   bool use_curve_mapping;
126   int curve_mapping_timestamp;
127   OCIO_CurveMappingSettings curve_mapping_settings;
128 
129   /* Container for GLSL state needed for OCIO module. */
130   struct OCIO_GLSLDrawState *ocio_glsl_state;
131 } global_glsl_state = {NULL};
132 
133 static struct global_color_picking_state {
134   /* Cached processor for color picking conversion. */
135   OCIO_ConstProcessorRcPtr *processor_to;
136   OCIO_ConstProcessorRcPtr *processor_from;
137   bool failed;
138 } global_color_picking_state = {NULL};
139 
140 /** \} */
141 
142 /* -------------------------------------------------------------------- */
143 /** \name Color Managed Cache
144  * \{ */
145 
146 /**
147  * Cache Implementation Notes
148  * ==========================
149  *
150  * All color management cache stuff is stored in two properties of
151  * image buffers:
152  *
153  *   1. display_buffer_flags
154  *
155  *      This is a bit field which used to mark calculated transformations
156  *      for particular image buffer. Index inside of this array means index
157  *      of a color managed display. Element with given index matches view
158  *      transformations applied for a given display. So if bit B of array
159  *      element B is set to 1, this means display buffer with display index
160  *      of A and view transform of B was ever calculated for this imbuf.
161  *
162  *      In contrast with indices in global lists of displays and views this
163  *      indices are 0-based, not 1-based. This is needed to save some bytes
164  *      of memory.
165  *
166  *   2. colormanage_cache
167  *
168  *      This is a pointer to a structure which holds all data which is
169  *      needed for color management cache to work.
170  *
171  *      It contains two parts:
172  *        - data
173  *        - moviecache
174  *
175  *      Data field is used to store additional information about cached
176  *      buffers which affects on whether cached buffer could be used.
177  *      This data can't go to cache key because changes in this data
178  *      shouldn't lead extra buffers adding to cache, it shall
179  *      invalidate cached images.
180  *
181  *      Currently such a data contains only exposure and gamma, but
182  *      would likely extended further.
183  *
184  *      data field is not null only for elements of cache, not used for
185  *      original image buffers.
186  *
187  *      Color management cache is using generic MovieCache implementation
188  *      to make it easier to deal with memory limitation.
189  *
190  *      Currently color management is using the same memory limitation
191  *      pool as sequencer and clip editor are using which means color
192  *      managed buffers would be removed from the cache as soon as new
193  *      frames are loading for the movie clip and there's no space in
194  *      cache.
195  *
196  *      Every image buffer has got own movie cache instance, which
197  *      means keys for color managed buffers could be really simple
198  *      and look up in this cache would be fast and independent from
199  *      overall amount of color managed images.
200  */
201 
202 /* NOTE: ColormanageCacheViewSettings and ColormanageCacheDisplaySettings are
203  *       quite the same as ColorManagedViewSettings and ColorManageDisplaySettings
204  *       but they holds indexes of all transformations and color spaces, not
205  *       their names.
206  *
207  *       This helps avoid extra colorspace / display / view lookup without
208  *       requiring to pass all variables which affects on display buffer
209  *       to color management cache system and keeps calls small and nice.
210  */
211 typedef struct ColormanageCacheViewSettings {
212   int flag;
213   int look;
214   int view;
215   float exposure;
216   float gamma;
217   float dither;
218   CurveMapping *curve_mapping;
219 } ColormanageCacheViewSettings;
220 
221 typedef struct ColormanageCacheDisplaySettings {
222   int display;
223 } ColormanageCacheDisplaySettings;
224 
225 typedef struct ColormanageCacheKey {
226   int view;    /* view transformation used for display buffer */
227   int display; /* display device name */
228 } ColormanageCacheKey;
229 
230 typedef struct ColormanageCacheData {
231   int flag;                    /* view flags of cached buffer */
232   int look;                    /* Additional artistics transform */
233   float exposure;              /* exposure value cached buffer is calculated with */
234   float gamma;                 /* gamma value cached buffer is calculated with */
235   float dither;                /* dither value cached buffer is calculated with */
236   CurveMapping *curve_mapping; /* curve mapping used for cached buffer */
237   int curve_mapping_timestamp; /* time stamp of curve mapping used for cached buffer */
238 } ColormanageCacheData;
239 
240 typedef struct ColormanageCache {
241   struct MovieCache *moviecache;
242 
243   ColormanageCacheData *data;
244 } ColormanageCache;
245 
colormanage_moviecache_get(const ImBuf * ibuf)246 static struct MovieCache *colormanage_moviecache_get(const ImBuf *ibuf)
247 {
248   if (!ibuf->colormanage_cache) {
249     return NULL;
250   }
251 
252   return ibuf->colormanage_cache->moviecache;
253 }
254 
colormanage_cachedata_get(const ImBuf * ibuf)255 static ColormanageCacheData *colormanage_cachedata_get(const ImBuf *ibuf)
256 {
257   if (!ibuf->colormanage_cache) {
258     return NULL;
259   }
260 
261   return ibuf->colormanage_cache->data;
262 }
263 
colormanage_hashhash(const void * key_v)264 static unsigned int colormanage_hashhash(const void *key_v)
265 {
266   const ColormanageCacheKey *key = key_v;
267 
268   unsigned int rval = (key->display << 16) | (key->view % 0xffff);
269 
270   return rval;
271 }
272 
colormanage_hashcmp(const void * av,const void * bv)273 static bool colormanage_hashcmp(const void *av, const void *bv)
274 {
275   const ColormanageCacheKey *a = av;
276   const ColormanageCacheKey *b = bv;
277 
278   return ((a->view != b->view) || (a->display != b->display));
279 }
280 
colormanage_moviecache_ensure(ImBuf * ibuf)281 static struct MovieCache *colormanage_moviecache_ensure(ImBuf *ibuf)
282 {
283   if (!ibuf->colormanage_cache) {
284     ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage cache");
285   }
286 
287   if (!ibuf->colormanage_cache->moviecache) {
288     struct MovieCache *moviecache;
289 
290     moviecache = IMB_moviecache_create("colormanage cache",
291                                        sizeof(ColormanageCacheKey),
292                                        colormanage_hashhash,
293                                        colormanage_hashcmp);
294 
295     ibuf->colormanage_cache->moviecache = moviecache;
296   }
297 
298   return ibuf->colormanage_cache->moviecache;
299 }
300 
colormanage_cachedata_set(ImBuf * ibuf,ColormanageCacheData * data)301 static void colormanage_cachedata_set(ImBuf *ibuf, ColormanageCacheData *data)
302 {
303   if (!ibuf->colormanage_cache) {
304     ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage cache");
305   }
306 
307   ibuf->colormanage_cache->data = data;
308 }
309 
colormanage_view_settings_to_cache(ImBuf * ibuf,ColormanageCacheViewSettings * cache_view_settings,const ColorManagedViewSettings * view_settings)310 static void colormanage_view_settings_to_cache(ImBuf *ibuf,
311                                                ColormanageCacheViewSettings *cache_view_settings,
312                                                const ColorManagedViewSettings *view_settings)
313 {
314   int look = IMB_colormanagement_look_get_named_index(view_settings->look);
315   int view = IMB_colormanagement_view_get_named_index(view_settings->view_transform);
316 
317   cache_view_settings->look = look;
318   cache_view_settings->view = view;
319   cache_view_settings->exposure = view_settings->exposure;
320   cache_view_settings->gamma = view_settings->gamma;
321   cache_view_settings->dither = ibuf->dither;
322   cache_view_settings->flag = view_settings->flag;
323   cache_view_settings->curve_mapping = view_settings->curve_mapping;
324 }
325 
colormanage_display_settings_to_cache(ColormanageCacheDisplaySettings * cache_display_settings,const ColorManagedDisplaySettings * display_settings)326 static void colormanage_display_settings_to_cache(
327     ColormanageCacheDisplaySettings *cache_display_settings,
328     const ColorManagedDisplaySettings *display_settings)
329 {
330   int display = IMB_colormanagement_display_get_named_index(display_settings->display_device);
331 
332   cache_display_settings->display = display;
333 }
334 
colormanage_settings_to_key(ColormanageCacheKey * key,const ColormanageCacheViewSettings * view_settings,const ColormanageCacheDisplaySettings * display_settings)335 static void colormanage_settings_to_key(ColormanageCacheKey *key,
336                                         const ColormanageCacheViewSettings *view_settings,
337                                         const ColormanageCacheDisplaySettings *display_settings)
338 {
339   key->view = view_settings->view;
340   key->display = display_settings->display;
341 }
342 
colormanage_cache_get_ibuf(ImBuf * ibuf,ColormanageCacheKey * key,void ** cache_handle)343 static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf,
344                                          ColormanageCacheKey *key,
345                                          void **cache_handle)
346 {
347   ImBuf *cache_ibuf;
348   struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
349 
350   if (!moviecache) {
351     /* If there's no moviecache it means no color management was applied
352      * on given image buffer before. */
353     return NULL;
354   }
355 
356   *cache_handle = NULL;
357 
358   cache_ibuf = IMB_moviecache_get(moviecache, key);
359 
360   *cache_handle = cache_ibuf;
361 
362   return cache_ibuf;
363 }
364 
colormanage_cache_get(ImBuf * ibuf,const ColormanageCacheViewSettings * view_settings,const ColormanageCacheDisplaySettings * display_settings,void ** cache_handle)365 static unsigned char *colormanage_cache_get(
366     ImBuf *ibuf,
367     const ColormanageCacheViewSettings *view_settings,
368     const ColormanageCacheDisplaySettings *display_settings,
369     void **cache_handle)
370 {
371   ColormanageCacheKey key;
372   ImBuf *cache_ibuf;
373   int view_flag = 1 << (view_settings->view - 1);
374   CurveMapping *curve_mapping = view_settings->curve_mapping;
375   int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
376 
377   colormanage_settings_to_key(&key, view_settings, display_settings);
378 
379   /* check whether image was marked as dirty for requested transform */
380   if ((ibuf->display_buffer_flags[display_settings->display - 1] & view_flag) == 0) {
381     return NULL;
382   }
383 
384   cache_ibuf = colormanage_cache_get_ibuf(ibuf, &key, cache_handle);
385 
386   if (cache_ibuf) {
387     ColormanageCacheData *cache_data;
388 
389     BLI_assert(cache_ibuf->x == ibuf->x && cache_ibuf->y == ibuf->y);
390 
391     /* only buffers with different color space conversions are being stored
392      * in cache separately. buffer which were used only different exposure/gamma
393      * are re-suing the same cached buffer
394      *
395      * check here which exposure/gamma/curve was used for cached buffer and if they're
396      * different from requested buffer should be re-generated
397      */
398     cache_data = colormanage_cachedata_get(cache_ibuf);
399 
400     if (cache_data->look != view_settings->look ||
401         cache_data->exposure != view_settings->exposure ||
402         cache_data->gamma != view_settings->gamma || cache_data->dither != view_settings->dither ||
403         cache_data->flag != view_settings->flag || cache_data->curve_mapping != curve_mapping ||
404         cache_data->curve_mapping_timestamp != curve_mapping_timestamp) {
405       *cache_handle = NULL;
406 
407       IMB_freeImBuf(cache_ibuf);
408 
409       return NULL;
410     }
411 
412     return (unsigned char *)cache_ibuf->rect;
413   }
414 
415   return NULL;
416 }
417 
colormanage_cache_put(ImBuf * ibuf,const ColormanageCacheViewSettings * view_settings,const ColormanageCacheDisplaySettings * display_settings,unsigned char * display_buffer,void ** cache_handle)418 static void colormanage_cache_put(ImBuf *ibuf,
419                                   const ColormanageCacheViewSettings *view_settings,
420                                   const ColormanageCacheDisplaySettings *display_settings,
421                                   unsigned char *display_buffer,
422                                   void **cache_handle)
423 {
424   ColormanageCacheKey key;
425   ImBuf *cache_ibuf;
426   ColormanageCacheData *cache_data;
427   int view_flag = 1 << (view_settings->view - 1);
428   struct MovieCache *moviecache = colormanage_moviecache_ensure(ibuf);
429   CurveMapping *curve_mapping = view_settings->curve_mapping;
430   int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
431 
432   colormanage_settings_to_key(&key, view_settings, display_settings);
433 
434   /* mark display buffer as valid */
435   ibuf->display_buffer_flags[display_settings->display - 1] |= view_flag;
436 
437   /* buffer itself */
438   cache_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0);
439   cache_ibuf->rect = (unsigned int *)display_buffer;
440 
441   cache_ibuf->mall |= IB_rect;
442   cache_ibuf->flags |= IB_rect;
443 
444   /* Store data which is needed to check whether cached buffer
445    * could be used for color managed display settings. */
446   cache_data = MEM_callocN(sizeof(ColormanageCacheData), "color manage cache imbuf data");
447   cache_data->look = view_settings->look;
448   cache_data->exposure = view_settings->exposure;
449   cache_data->gamma = view_settings->gamma;
450   cache_data->dither = view_settings->dither;
451   cache_data->flag = view_settings->flag;
452   cache_data->curve_mapping = curve_mapping;
453   cache_data->curve_mapping_timestamp = curve_mapping_timestamp;
454 
455   colormanage_cachedata_set(cache_ibuf, cache_data);
456 
457   *cache_handle = cache_ibuf;
458 
459   IMB_moviecache_put(moviecache, &key, cache_ibuf);
460 }
461 
colormanage_cache_handle_release(void * cache_handle)462 static void colormanage_cache_handle_release(void *cache_handle)
463 {
464   ImBuf *cache_ibuf = cache_handle;
465 
466   IMB_freeImBuf(cache_ibuf);
467 }
468 
469 /** \} */
470 
471 /* -------------------------------------------------------------------- */
472 /** \name Initialization / De-initialization
473  * \{ */
474 
colormanage_role_color_space_name_get(OCIO_ConstConfigRcPtr * config,char * colorspace_name,const char * role,const char * backup_role)475 static void colormanage_role_color_space_name_get(OCIO_ConstConfigRcPtr *config,
476                                                   char *colorspace_name,
477                                                   const char *role,
478                                                   const char *backup_role)
479 {
480   OCIO_ConstColorSpaceRcPtr *ociocs;
481 
482   ociocs = OCIO_configGetColorSpace(config, role);
483 
484   if (!ociocs && backup_role) {
485     ociocs = OCIO_configGetColorSpace(config, backup_role);
486   }
487 
488   if (ociocs) {
489     const char *name = OCIO_colorSpaceGetName(ociocs);
490 
491     /* assume function was called with buffer properly allocated to MAX_COLORSPACE_NAME chars */
492     BLI_strncpy(colorspace_name, name, MAX_COLORSPACE_NAME);
493     OCIO_colorSpaceRelease(ociocs);
494   }
495   else {
496     printf("Color management: Error could not find role %s role.\n", role);
497   }
498 }
499 
colormanage_load_config(OCIO_ConstConfigRcPtr * config)500 static void colormanage_load_config(OCIO_ConstConfigRcPtr *config)
501 {
502   int tot_colorspace, tot_display, tot_display_view, tot_looks;
503   int index, viewindex, viewindex2;
504   const char *name;
505 
506   /* get roles */
507   colormanage_role_color_space_name_get(config, global_role_data, OCIO_ROLE_DATA, NULL);
508   colormanage_role_color_space_name_get(
509       config, global_role_scene_linear, OCIO_ROLE_SCENE_LINEAR, NULL);
510   colormanage_role_color_space_name_get(
511       config, global_role_color_picking, OCIO_ROLE_COLOR_PICKING, NULL);
512   colormanage_role_color_space_name_get(
513       config, global_role_texture_painting, OCIO_ROLE_TEXTURE_PAINT, NULL);
514   colormanage_role_color_space_name_get(
515       config, global_role_default_sequencer, OCIO_ROLE_DEFAULT_SEQUENCER, OCIO_ROLE_SCENE_LINEAR);
516   colormanage_role_color_space_name_get(
517       config, global_role_default_byte, OCIO_ROLE_DEFAULT_BYTE, OCIO_ROLE_TEXTURE_PAINT);
518   colormanage_role_color_space_name_get(
519       config, global_role_default_float, OCIO_ROLE_DEFAULT_FLOAT, OCIO_ROLE_SCENE_LINEAR);
520 
521   /* load colorspaces */
522   tot_colorspace = OCIO_configGetNumColorSpaces(config);
523   for (index = 0; index < tot_colorspace; index++) {
524     OCIO_ConstColorSpaceRcPtr *ocio_colorspace;
525     const char *description;
526     bool is_invertible, is_data;
527 
528     name = OCIO_configGetColorSpaceNameByIndex(config, index);
529 
530     ocio_colorspace = OCIO_configGetColorSpace(config, name);
531     description = OCIO_colorSpaceGetDescription(ocio_colorspace);
532     is_invertible = OCIO_colorSpaceIsInvertible(ocio_colorspace);
533     is_data = OCIO_colorSpaceIsData(ocio_colorspace);
534 
535     colormanage_colorspace_add(name, description, is_invertible, is_data);
536 
537     OCIO_colorSpaceRelease(ocio_colorspace);
538   }
539 
540   /* load displays */
541   viewindex2 = 0;
542   tot_display = OCIO_configGetNumDisplays(config);
543 
544   for (index = 0; index < tot_display; index++) {
545     const char *displayname;
546     ColorManagedDisplay *display;
547 
548     displayname = OCIO_configGetDisplay(config, index);
549 
550     display = colormanage_display_add(displayname);
551 
552     /* load views */
553     tot_display_view = OCIO_configGetNumViews(config, displayname);
554     for (viewindex = 0; viewindex < tot_display_view; viewindex++, viewindex2++) {
555       const char *viewname;
556       ColorManagedView *view;
557       LinkData *display_view;
558 
559       viewname = OCIO_configGetView(config, displayname, viewindex);
560 
561       /* first check if view transform with given name was already loaded */
562       view = colormanage_view_get_named(viewname);
563 
564       if (!view) {
565         view = colormanage_view_add(viewname);
566       }
567 
568       display_view = BLI_genericNodeN(view);
569 
570       BLI_addtail(&display->views, display_view);
571     }
572   }
573 
574   global_tot_display = tot_display;
575 
576   /* load looks */
577   tot_looks = OCIO_configGetNumLooks(config);
578   colormanage_look_add("None", "", true);
579   for (index = 0; index < tot_looks; index++) {
580     OCIO_ConstLookRcPtr *ocio_look;
581     const char *process_space;
582 
583     name = OCIO_configGetLookNameByIndex(config, index);
584     ocio_look = OCIO_configGetLook(config, name);
585     process_space = OCIO_lookGetProcessSpace(ocio_look);
586     OCIO_lookRelease(ocio_look);
587 
588     colormanage_look_add(name, process_space, false);
589   }
590 
591   /* Load luminance coefficients. */
592   OCIO_configGetDefaultLumaCoefs(config, imbuf_luma_coefficients);
593   OCIO_configGetXYZtoRGB(config, imbuf_xyz_to_rgb);
594   invert_m3_m3(imbuf_rgb_to_xyz, imbuf_xyz_to_rgb);
595   copy_m3_m3(imbuf_xyz_to_linear_srgb, OCIO_XYZ_TO_LINEAR_SRGB);
596   invert_m3_m3(imbuf_linear_srgb_to_xyz, imbuf_xyz_to_linear_srgb);
597 }
598 
colormanage_free_config(void)599 static void colormanage_free_config(void)
600 {
601   ColorSpace *colorspace;
602   ColorManagedDisplay *display;
603 
604   /* free color spaces */
605   colorspace = global_colorspaces.first;
606   while (colorspace) {
607     ColorSpace *colorspace_next = colorspace->next;
608 
609     /* free precomputer processors */
610     if (colorspace->to_scene_linear) {
611       OCIO_processorRelease((OCIO_ConstProcessorRcPtr *)colorspace->to_scene_linear);
612     }
613 
614     if (colorspace->from_scene_linear) {
615       OCIO_processorRelease((OCIO_ConstProcessorRcPtr *)colorspace->from_scene_linear);
616     }
617 
618     /* free color space itself */
619     MEM_freeN(colorspace);
620 
621     colorspace = colorspace_next;
622   }
623   BLI_listbase_clear(&global_colorspaces);
624   global_tot_colorspace = 0;
625 
626   /* free displays */
627   display = global_displays.first;
628   while (display) {
629     ColorManagedDisplay *display_next = display->next;
630 
631     /* free precomputer processors */
632     if (display->to_scene_linear) {
633       OCIO_processorRelease((OCIO_ConstProcessorRcPtr *)display->to_scene_linear);
634     }
635 
636     if (display->from_scene_linear) {
637       OCIO_processorRelease((OCIO_ConstProcessorRcPtr *)display->from_scene_linear);
638     }
639 
640     /* free list of views */
641     BLI_freelistN(&display->views);
642 
643     MEM_freeN(display);
644     display = display_next;
645   }
646   BLI_listbase_clear(&global_displays);
647   global_tot_display = 0;
648 
649   /* free views */
650   BLI_freelistN(&global_views);
651   global_tot_view = 0;
652 
653   /* free looks */
654   BLI_freelistN(&global_looks);
655   global_tot_looks = 0;
656 
657   OCIO_exit();
658 }
659 
colormanagement_init(void)660 void colormanagement_init(void)
661 {
662   const char *ocio_env;
663   const char *configdir;
664   char configfile[FILE_MAX];
665   OCIO_ConstConfigRcPtr *config = NULL;
666 
667   OCIO_init();
668 
669   ocio_env = BLI_getenv("OCIO");
670 
671   if (ocio_env && ocio_env[0] != '\0') {
672     config = OCIO_configCreateFromEnv();
673     if (config != NULL) {
674       printf("Color management: Using %s as a configuration file\n", ocio_env);
675     }
676   }
677 
678   if (config == NULL) {
679     configdir = BKE_appdir_folder_id(BLENDER_DATAFILES, "colormanagement");
680 
681     if (configdir) {
682       BLI_join_dirfile(configfile, sizeof(configfile), configdir, BCM_CONFIG_FILE);
683 
684 #ifdef WIN32
685       {
686         /* quite a hack to support loading configuration from path with non-acii symbols */
687 
688         char short_name[256];
689         BLI_get_short_name(short_name, configfile);
690         config = OCIO_configCreateFromFile(short_name);
691       }
692 #else
693       config = OCIO_configCreateFromFile(configfile);
694 #endif
695     }
696   }
697 
698   if (config == NULL) {
699     printf("Color management: using fallback mode for management\n");
700 
701     config = OCIO_configCreateFallback();
702   }
703 
704   if (config) {
705     OCIO_setCurrentConfig(config);
706 
707     colormanage_load_config(config);
708 
709     OCIO_configRelease(config);
710   }
711 
712   /* If there are no valid display/views, use fallback mode. */
713   if (global_tot_display == 0 || global_tot_view == 0) {
714     printf("Color management: no displays/views in the config, using fallback mode instead\n");
715 
716     /* Free old config. */
717     colormanage_free_config();
718 
719     /* Initialize fallback config. */
720     config = OCIO_configCreateFallback();
721     colormanage_load_config(config);
722   }
723 
724   BLI_init_srgb_conversion();
725 }
726 
colormanagement_exit(void)727 void colormanagement_exit(void)
728 {
729   if (global_glsl_state.processor_scene_to_ui) {
730     OCIO_processorRelease(global_glsl_state.processor_scene_to_ui);
731   }
732 
733   if (global_glsl_state.processor_ui_to_display) {
734     OCIO_processorRelease(global_glsl_state.processor_ui_to_display);
735   }
736 
737   if (global_glsl_state.curve_mapping) {
738     BKE_curvemapping_free(global_glsl_state.curve_mapping);
739   }
740 
741   if (global_glsl_state.curve_mapping_settings.lut) {
742     MEM_freeN(global_glsl_state.curve_mapping_settings.lut);
743   }
744 
745   if (global_glsl_state.ocio_glsl_state) {
746     OCIO_freeOGLState(global_glsl_state.ocio_glsl_state);
747   }
748 
749   if (global_color_picking_state.processor_to) {
750     OCIO_processorRelease(global_color_picking_state.processor_to);
751   }
752 
753   if (global_color_picking_state.processor_from) {
754     OCIO_processorRelease(global_color_picking_state.processor_from);
755   }
756 
757   memset(&global_glsl_state, 0, sizeof(global_glsl_state));
758   memset(&global_color_picking_state, 0, sizeof(global_color_picking_state));
759 
760   colormanage_free_config();
761 }
762 
763 /** \} */
764 
765 /* -------------------------------------------------------------------- */
766 /** \name Internal functions
767  * \{ */
768 
colormanage_compatible_look(ColorManagedLook * look,const char * view_name)769 static bool colormanage_compatible_look(ColorManagedLook *look, const char *view_name)
770 {
771   if (look->is_noop) {
772     return true;
773   }
774 
775   /* Skip looks only relevant to specific view transforms. */
776   return (look->view[0] == 0 || (view_name && STREQ(look->view, view_name)));
777 }
778 
colormanage_cache_free(ImBuf * ibuf)779 void colormanage_cache_free(ImBuf *ibuf)
780 {
781   if (ibuf->display_buffer_flags) {
782     MEM_freeN(ibuf->display_buffer_flags);
783 
784     ibuf->display_buffer_flags = NULL;
785   }
786 
787   if (ibuf->colormanage_cache) {
788     ColormanageCacheData *cache_data = colormanage_cachedata_get(ibuf);
789     struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
790 
791     if (cache_data) {
792       MEM_freeN(cache_data);
793     }
794 
795     if (moviecache) {
796       IMB_moviecache_free(moviecache);
797     }
798 
799     MEM_freeN(ibuf->colormanage_cache);
800 
801     ibuf->colormanage_cache = NULL;
802   }
803 }
804 
IMB_colormanagement_display_settings_from_ctx(const bContext * C,ColorManagedViewSettings ** r_view_settings,ColorManagedDisplaySettings ** r_display_settings)805 void IMB_colormanagement_display_settings_from_ctx(
806     const bContext *C,
807     ColorManagedViewSettings **r_view_settings,
808     ColorManagedDisplaySettings **r_display_settings)
809 {
810   Scene *scene = CTX_data_scene(C);
811   SpaceImage *sima = CTX_wm_space_image(C);
812 
813   *r_view_settings = &scene->view_settings;
814   *r_display_settings = &scene->display_settings;
815 
816   if (sima && sima->image) {
817     if ((sima->image->flag & IMA_VIEW_AS_RENDER) == 0) {
818       *r_view_settings = NULL;
819     }
820   }
821 }
822 
IMB_colormanagement_get_display_colorspace_name(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)823 const char *IMB_colormanagement_get_display_colorspace_name(
824     const ColorManagedViewSettings *view_settings,
825     const ColorManagedDisplaySettings *display_settings)
826 {
827   OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
828 
829   const char *display = display_settings->display_device;
830   const char *view = view_settings->view_transform;
831   const char *colorspace_name;
832 
833   colorspace_name = OCIO_configGetDisplayColorSpaceName(config, display, view);
834 
835   OCIO_configRelease(config);
836 
837   return colorspace_name;
838 }
839 
display_transform_get_colorspace(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)840 static ColorSpace *display_transform_get_colorspace(
841     const ColorManagedViewSettings *view_settings,
842     const ColorManagedDisplaySettings *display_settings)
843 {
844   const char *colorspace_name = IMB_colormanagement_get_display_colorspace_name(view_settings,
845                                                                                 display_settings);
846 
847   if (colorspace_name) {
848     return colormanage_colorspace_get_named(colorspace_name);
849   }
850 
851   return NULL;
852 }
853 
create_display_buffer_processor(const char * look,const char * view_transform,const char * display,float exposure,float gamma,const char * from_colorspace,const bool linear_output)854 static OCIO_ConstProcessorRcPtr *create_display_buffer_processor(const char *look,
855                                                                  const char *view_transform,
856                                                                  const char *display,
857                                                                  float exposure,
858                                                                  float gamma,
859                                                                  const char *from_colorspace,
860                                                                  const bool linear_output)
861 {
862   OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
863   OCIO_DisplayTransformRcPtr *dt;
864   OCIO_ConstProcessorRcPtr *processor;
865   ColorManagedLook *look_descr = colormanage_look_get_named(look);
866 
867   dt = OCIO_createDisplayTransform();
868 
869   OCIO_displayTransformSetInputColorSpaceName(dt, from_colorspace);
870   OCIO_displayTransformSetView(dt, view_transform);
871   OCIO_displayTransformSetDisplay(dt, display);
872 
873   if (look_descr->is_noop == false && colormanage_compatible_look(look_descr, view_transform)) {
874     OCIO_displayTransformSetLooksOverrideEnabled(dt, true);
875     OCIO_displayTransformSetLooksOverride(dt, look);
876   }
877 
878   /* fstop exposure control */
879   if (exposure != 0.0f) {
880     OCIO_MatrixTransformRcPtr *mt;
881     float gain = powf(2.0f, exposure);
882     const float scale4f[] = {gain, gain, gain, 1.0f};
883     float m44[16], offset4[4];
884 
885     OCIO_matrixTransformScale(m44, offset4, scale4f);
886     mt = OCIO_createMatrixTransform();
887     OCIO_matrixTransformSetValue(mt, m44, offset4);
888     OCIO_displayTransformSetLinearCC(dt, (OCIO_ConstTransformRcPtr *)mt);
889 
890     OCIO_matrixTransformRelease(mt);
891   }
892 
893   /* post-display gamma transform */
894   if (gamma != 1.0f) {
895     OCIO_ExponentTransformRcPtr *et;
896     float exponent = 1.0f / MAX2(FLT_EPSILON, gamma);
897     const float exponent4f[] = {exponent, exponent, exponent, exponent};
898 
899     et = OCIO_createExponentTransform();
900     OCIO_exponentTransformSetValue(et, exponent4f);
901     OCIO_displayTransformSetDisplayCC(dt, (OCIO_ConstTransformRcPtr *)et);
902 
903     OCIO_exponentTransformRelease(et);
904   }
905 
906   OCIO_GroupTransformRcPtr *gt = OCIO_createGroupTransform();
907   OCIO_groupTransformSetDirection(gt, true);
908   OCIO_groupTransformPushBack(gt, (OCIO_ConstTransformRcPtr *)dt);
909 
910   if (linear_output) {
911     /* TODO use correct function display. */
912     OCIO_ExponentTransformRcPtr *et = OCIO_createExponentTransform();
913     OCIO_exponentTransformSetValue(et, (float[4]){2.2f, 2.2f, 2.2f, 1.0f});
914     OCIO_groupTransformPushBack(gt, (OCIO_ConstTransformRcPtr *)et);
915     OCIO_exponentTransformRelease(et);
916   }
917 
918   processor = OCIO_configGetProcessor(config, (OCIO_ConstTransformRcPtr *)gt);
919 
920   OCIO_groupTransformRelease(gt);
921   OCIO_displayTransformRelease(dt);
922   OCIO_configRelease(config);
923 
924   return processor;
925 }
926 
create_display_encoded_buffer_processor(const char * UNUSED (display))927 static OCIO_ConstProcessorRcPtr *create_display_encoded_buffer_processor(
928     const char *UNUSED(display))
929 {
930   OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
931   OCIO_ConstProcessorRcPtr *processor;
932 
933   /* TODO use correct function display. */
934   OCIO_ExponentTransformRcPtr *et = OCIO_createExponentTransform();
935   OCIO_exponentTransformSetValue(et, (float[4]){1.0f / 2.2f, 1.0f / 2.2f, 1.0f / 2.2f, 1.0f});
936 
937   processor = OCIO_configGetProcessor(config, (OCIO_ConstTransformRcPtr *)et);
938 
939   OCIO_exponentTransformRelease(et);
940   OCIO_configRelease(config);
941 
942   return processor;
943 }
944 
create_colorspace_transform_processor(const char * from_colorspace,const char * to_colorspace)945 static OCIO_ConstProcessorRcPtr *create_colorspace_transform_processor(const char *from_colorspace,
946                                                                        const char *to_colorspace)
947 {
948   OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
949   OCIO_ConstProcessorRcPtr *processor;
950 
951   processor = OCIO_configGetProcessorWithNames(config, from_colorspace, to_colorspace);
952 
953   OCIO_configRelease(config);
954 
955   return processor;
956 }
957 
colorspace_to_scene_linear_processor(ColorSpace * colorspace)958 static OCIO_ConstProcessorRcPtr *colorspace_to_scene_linear_processor(ColorSpace *colorspace)
959 {
960   if (colorspace->to_scene_linear == NULL) {
961     BLI_mutex_lock(&processor_lock);
962 
963     if (colorspace->to_scene_linear == NULL) {
964       OCIO_ConstProcessorRcPtr *to_scene_linear;
965       to_scene_linear = create_colorspace_transform_processor(colorspace->name,
966                                                               global_role_scene_linear);
967       colorspace->to_scene_linear = (struct OCIO_ConstProcessorRcPtr *)to_scene_linear;
968     }
969 
970     BLI_mutex_unlock(&processor_lock);
971   }
972 
973   return (OCIO_ConstProcessorRcPtr *)colorspace->to_scene_linear;
974 }
975 
colorspace_from_scene_linear_processor(ColorSpace * colorspace)976 static OCIO_ConstProcessorRcPtr *colorspace_from_scene_linear_processor(ColorSpace *colorspace)
977 {
978   if (colorspace->from_scene_linear == NULL) {
979     BLI_mutex_lock(&processor_lock);
980 
981     if (colorspace->from_scene_linear == NULL) {
982       OCIO_ConstProcessorRcPtr *from_scene_linear;
983       from_scene_linear = create_colorspace_transform_processor(global_role_scene_linear,
984                                                                 colorspace->name);
985       colorspace->from_scene_linear = (struct OCIO_ConstProcessorRcPtr *)from_scene_linear;
986     }
987 
988     BLI_mutex_unlock(&processor_lock);
989   }
990 
991   return (OCIO_ConstProcessorRcPtr *)colorspace->from_scene_linear;
992 }
993 
display_from_scene_linear_processor(ColorManagedDisplay * display)994 static OCIO_ConstProcessorRcPtr *display_from_scene_linear_processor(ColorManagedDisplay *display)
995 {
996   if (display->from_scene_linear == NULL) {
997     BLI_mutex_lock(&processor_lock);
998 
999     if (display->from_scene_linear == NULL) {
1000       const char *view_name = colormanage_view_get_default_name(display);
1001       OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
1002       OCIO_ConstProcessorRcPtr *processor = NULL;
1003 
1004       if (view_name && config) {
1005         const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(
1006             config, display->name, view_name);
1007         processor = OCIO_configGetProcessorWithNames(
1008             config, global_role_scene_linear, view_colorspace);
1009 
1010         OCIO_configRelease(config);
1011       }
1012 
1013       display->from_scene_linear = (struct OCIO_ConstProcessorRcPtr *)processor;
1014     }
1015 
1016     BLI_mutex_unlock(&processor_lock);
1017   }
1018 
1019   return (OCIO_ConstProcessorRcPtr *)display->from_scene_linear;
1020 }
1021 
display_to_scene_linear_processor(ColorManagedDisplay * display)1022 static OCIO_ConstProcessorRcPtr *display_to_scene_linear_processor(ColorManagedDisplay *display)
1023 {
1024   if (display->to_scene_linear == NULL) {
1025     BLI_mutex_lock(&processor_lock);
1026 
1027     if (display->to_scene_linear == NULL) {
1028       const char *view_name = colormanage_view_get_default_name(display);
1029       OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
1030       OCIO_ConstProcessorRcPtr *processor = NULL;
1031 
1032       if (view_name && config) {
1033         const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(
1034             config, display->name, view_name);
1035         processor = OCIO_configGetProcessorWithNames(
1036             config, view_colorspace, global_role_scene_linear);
1037 
1038         OCIO_configRelease(config);
1039       }
1040 
1041       display->to_scene_linear = (struct OCIO_ConstProcessorRcPtr *)processor;
1042     }
1043 
1044     BLI_mutex_unlock(&processor_lock);
1045   }
1046 
1047   return (OCIO_ConstProcessorRcPtr *)display->to_scene_linear;
1048 }
1049 
IMB_colormanagement_init_default_view_settings(ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)1050 void IMB_colormanagement_init_default_view_settings(
1051     ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
1052 {
1053   /* First, try use "Standard" view transform of the requested device. */
1054   ColorManagedView *default_view = colormanage_view_get_named_for_display(
1055       display_settings->display_device, "Standard");
1056   /* If that fails, we fall back to the default view transform of the display
1057    * as per OCIO configuration. */
1058   if (default_view == NULL) {
1059     ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device);
1060     if (display != NULL) {
1061       default_view = colormanage_view_get_default(display);
1062     }
1063   }
1064   if (default_view != NULL) {
1065     BLI_strncpy(
1066         view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1067   }
1068   else {
1069     view_settings->view_transform[0] = '\0';
1070   }
1071   /* TODO(sergey): Find a way to safely/reliable un-hardcode this. */
1072   BLI_strncpy(view_settings->look, "None", sizeof(view_settings->look));
1073   /* Initialize rest of the settings. */
1074   view_settings->flag = 0;
1075   view_settings->gamma = 1.0f;
1076   view_settings->exposure = 0.0f;
1077   view_settings->curve_mapping = NULL;
1078 }
1079 
curve_mapping_apply_pixel(CurveMapping * curve_mapping,float * pixel,int channels)1080 static void curve_mapping_apply_pixel(CurveMapping *curve_mapping, float *pixel, int channels)
1081 {
1082   if (channels == 1) {
1083     pixel[0] = BKE_curvemap_evaluateF(curve_mapping, curve_mapping->cm, pixel[0]);
1084   }
1085   else if (channels == 2) {
1086     pixel[0] = BKE_curvemap_evaluateF(curve_mapping, curve_mapping->cm, pixel[0]);
1087     pixel[1] = BKE_curvemap_evaluateF(curve_mapping, curve_mapping->cm, pixel[1]);
1088   }
1089   else {
1090     BKE_curvemapping_evaluate_premulRGBF(curve_mapping, pixel, pixel);
1091   }
1092 }
1093 
colorspace_set_default_role(char * colorspace,int size,int role)1094 void colorspace_set_default_role(char *colorspace, int size, int role)
1095 {
1096   if (colorspace && colorspace[0] == '\0') {
1097     const char *role_colorspace;
1098 
1099     role_colorspace = IMB_colormanagement_role_colorspace_name_get(role);
1100 
1101     BLI_strncpy(colorspace, role_colorspace, size);
1102   }
1103 }
1104 
colormanage_imbuf_set_default_spaces(ImBuf * ibuf)1105 void colormanage_imbuf_set_default_spaces(ImBuf *ibuf)
1106 {
1107   ibuf->rect_colorspace = colormanage_colorspace_get_named(global_role_default_byte);
1108 }
1109 
colormanage_imbuf_make_linear(ImBuf * ibuf,const char * from_colorspace)1110 void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace)
1111 {
1112   ColorSpace *colorspace = colormanage_colorspace_get_named(from_colorspace);
1113 
1114   if (colorspace && colorspace->is_data) {
1115     ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1116     return;
1117   }
1118 
1119   if (ibuf->rect_float) {
1120     const char *to_colorspace = global_role_scene_linear;
1121     const bool predivide = IMB_alpha_affects_rgb(ibuf);
1122 
1123     if (ibuf->rect) {
1124       imb_freerectImBuf(ibuf);
1125     }
1126 
1127     IMB_colormanagement_transform(ibuf->rect_float,
1128                                   ibuf->x,
1129                                   ibuf->y,
1130                                   ibuf->channels,
1131                                   from_colorspace,
1132                                   to_colorspace,
1133                                   predivide);
1134   }
1135 }
1136 
1137 /** \} */
1138 
1139 /* -------------------------------------------------------------------- */
1140 /** \name Generic Functions
1141  * \{ */
1142 
colormanage_check_display_settings(ColorManagedDisplaySettings * display_settings,const char * what,const ColorManagedDisplay * default_display)1143 static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings,
1144                                                const char *what,
1145                                                const ColorManagedDisplay *default_display)
1146 {
1147   if (display_settings->display_device[0] == '\0') {
1148     BLI_strncpy(display_settings->display_device,
1149                 default_display->name,
1150                 sizeof(display_settings->display_device));
1151   }
1152   else {
1153     ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device);
1154 
1155     if (!display) {
1156       printf(
1157           "Color management: display \"%s\" used by %s not found, setting to default (\"%s\").\n",
1158           display_settings->display_device,
1159           what,
1160           default_display->name);
1161 
1162       BLI_strncpy(display_settings->display_device,
1163                   default_display->name,
1164                   sizeof(display_settings->display_device));
1165     }
1166   }
1167 }
1168 
colormanage_check_view_settings(ColorManagedDisplaySettings * display_settings,ColorManagedViewSettings * view_settings,const char * what)1169 static void colormanage_check_view_settings(ColorManagedDisplaySettings *display_settings,
1170                                             ColorManagedViewSettings *view_settings,
1171                                             const char *what)
1172 {
1173   ColorManagedDisplay *display;
1174   ColorManagedView *default_view = NULL;
1175   ColorManagedLook *default_look = (ColorManagedLook *)global_looks.first;
1176 
1177   if (view_settings->view_transform[0] == '\0') {
1178     display = colormanage_display_get_named(display_settings->display_device);
1179 
1180     if (display) {
1181       default_view = colormanage_view_get_default(display);
1182     }
1183 
1184     if (default_view) {
1185       BLI_strncpy(view_settings->view_transform,
1186                   default_view->name,
1187                   sizeof(view_settings->view_transform));
1188     }
1189   }
1190   else {
1191     ColorManagedView *view = colormanage_view_get_named(view_settings->view_transform);
1192 
1193     if (!view) {
1194       display = colormanage_display_get_named(display_settings->display_device);
1195 
1196       if (display) {
1197         default_view = colormanage_view_get_default(display);
1198       }
1199 
1200       if (default_view) {
1201         printf("Color management: %s view \"%s\" not found, setting default \"%s\".\n",
1202                what,
1203                view_settings->view_transform,
1204                default_view->name);
1205 
1206         BLI_strncpy(view_settings->view_transform,
1207                     default_view->name,
1208                     sizeof(view_settings->view_transform));
1209       }
1210     }
1211   }
1212 
1213   if (view_settings->look[0] == '\0') {
1214     BLI_strncpy(view_settings->look, default_look->name, sizeof(view_settings->look));
1215   }
1216   else {
1217     ColorManagedLook *look = colormanage_look_get_named(view_settings->look);
1218     if (look == NULL) {
1219       printf("Color management: %s look \"%s\" not found, setting default \"%s\".\n",
1220              what,
1221              view_settings->look,
1222              default_look->name);
1223 
1224       BLI_strncpy(view_settings->look, default_look->name, sizeof(view_settings->look));
1225     }
1226   }
1227 
1228   /* OCIO_TODO: move to do_versions() */
1229   if (view_settings->exposure == 0.0f && view_settings->gamma == 0.0f) {
1230     view_settings->exposure = 0.0f;
1231     view_settings->gamma = 1.0f;
1232   }
1233 }
1234 
colormanage_check_colorspace_settings(ColorManagedColorspaceSettings * colorspace_settings,const char * what)1235 static void colormanage_check_colorspace_settings(
1236     ColorManagedColorspaceSettings *colorspace_settings, const char *what)
1237 {
1238   if (colorspace_settings->name[0] == '\0') {
1239     /* pass */
1240   }
1241   else {
1242     ColorSpace *colorspace = colormanage_colorspace_get_named(colorspace_settings->name);
1243 
1244     if (!colorspace) {
1245       printf("Color management: %s colorspace \"%s\" not found, will use default instead.\n",
1246              what,
1247              colorspace_settings->name);
1248 
1249       BLI_strncpy(colorspace_settings->name, "", sizeof(colorspace_settings->name));
1250     }
1251   }
1252 
1253   (void)what;
1254 }
1255 
IMB_colormanagement_check_file_config(Main * bmain)1256 void IMB_colormanagement_check_file_config(Main *bmain)
1257 {
1258   Scene *scene;
1259   Image *image;
1260   MovieClip *clip;
1261 
1262   ColorManagedDisplay *default_display;
1263 
1264   default_display = colormanage_display_get_default();
1265 
1266   if (!default_display) {
1267     /* happens when OCIO configuration is incorrect */
1268     return;
1269   }
1270 
1271   for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
1272     ColorManagedColorspaceSettings *sequencer_colorspace_settings;
1273 
1274     /* check scene color management settings */
1275     colormanage_check_display_settings(&scene->display_settings, "scene", default_display);
1276     colormanage_check_view_settings(&scene->display_settings, &scene->view_settings, "scene");
1277 
1278     sequencer_colorspace_settings = &scene->sequencer_colorspace_settings;
1279 
1280     colormanage_check_colorspace_settings(sequencer_colorspace_settings, "sequencer");
1281 
1282     if (sequencer_colorspace_settings->name[0] == '\0') {
1283       BLI_strncpy(
1284           sequencer_colorspace_settings->name, global_role_default_sequencer, MAX_COLORSPACE_NAME);
1285     }
1286 
1287     /* check sequencer strip input color space settings */
1288     Sequence *seq;
1289     SEQ_ALL_BEGIN (scene->ed, seq) {
1290       if (seq->strip) {
1291         colormanage_check_colorspace_settings(&seq->strip->colorspace_settings, "sequencer strip");
1292       }
1293     }
1294     SEQ_ALL_END;
1295   }
1296 
1297   /* ** check input color space settings ** */
1298 
1299   for (image = bmain->images.first; image; image = image->id.next) {
1300     colormanage_check_colorspace_settings(&image->colorspace_settings, "image");
1301   }
1302 
1303   for (clip = bmain->movieclips.first; clip; clip = clip->id.next) {
1304     colormanage_check_colorspace_settings(&clip->colorspace_settings, "clip");
1305   }
1306 }
1307 
IMB_colormanagement_validate_settings(const ColorManagedDisplaySettings * display_settings,ColorManagedViewSettings * view_settings)1308 void IMB_colormanagement_validate_settings(const ColorManagedDisplaySettings *display_settings,
1309                                            ColorManagedViewSettings *view_settings)
1310 {
1311   ColorManagedDisplay *display;
1312   ColorManagedView *default_view = NULL;
1313   LinkData *view_link;
1314 
1315   display = colormanage_display_get_named(display_settings->display_device);
1316 
1317   default_view = colormanage_view_get_default(display);
1318 
1319   for (view_link = display->views.first; view_link; view_link = view_link->next) {
1320     ColorManagedView *view = view_link->data;
1321 
1322     if (STREQ(view->name, view_settings->view_transform)) {
1323       break;
1324     }
1325   }
1326 
1327   if (view_link == NULL && default_view) {
1328     BLI_strncpy(
1329         view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1330   }
1331 }
1332 
IMB_colormanagement_role_colorspace_name_get(int role)1333 const char *IMB_colormanagement_role_colorspace_name_get(int role)
1334 {
1335   switch (role) {
1336     case COLOR_ROLE_DATA:
1337       return global_role_data;
1338     case COLOR_ROLE_SCENE_LINEAR:
1339       return global_role_scene_linear;
1340     case COLOR_ROLE_COLOR_PICKING:
1341       return global_role_color_picking;
1342     case COLOR_ROLE_TEXTURE_PAINTING:
1343       return global_role_texture_painting;
1344     case COLOR_ROLE_DEFAULT_SEQUENCER:
1345       return global_role_default_sequencer;
1346     case COLOR_ROLE_DEFAULT_FLOAT:
1347       return global_role_default_float;
1348     case COLOR_ROLE_DEFAULT_BYTE:
1349       return global_role_default_byte;
1350     default:
1351       printf("Unknown role was passed to %s\n", __func__);
1352       BLI_assert(0);
1353       break;
1354   }
1355 
1356   return NULL;
1357 }
1358 
IMB_colormanagement_check_is_data(ImBuf * ibuf,const char * name)1359 void IMB_colormanagement_check_is_data(ImBuf *ibuf, const char *name)
1360 {
1361   ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1362 
1363   if (colorspace && colorspace->is_data) {
1364     ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1365   }
1366   else {
1367     ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1368   }
1369 }
1370 
IMB_colormanagegent_copy_settings(ImBuf * ibuf_src,ImBuf * ibuf_dst)1371 void IMB_colormanagegent_copy_settings(ImBuf *ibuf_src, ImBuf *ibuf_dst)
1372 {
1373   IMB_colormanagement_assign_rect_colorspace(ibuf_dst,
1374                                              IMB_colormanagement_get_rect_colorspace(ibuf_src));
1375   IMB_colormanagement_assign_float_colorspace(ibuf_dst,
1376                                               IMB_colormanagement_get_float_colorspace(ibuf_src));
1377   if (ibuf_src->flags & IB_alphamode_premul) {
1378     ibuf_dst->flags |= IB_alphamode_premul;
1379   }
1380   else if (ibuf_src->flags & IB_alphamode_channel_packed) {
1381     ibuf_dst->flags |= IB_alphamode_channel_packed;
1382   }
1383   else if (ibuf_src->flags & IB_alphamode_ignore) {
1384     ibuf_dst->flags |= IB_alphamode_ignore;
1385   }
1386 }
1387 
IMB_colormanagement_assign_float_colorspace(ImBuf * ibuf,const char * name)1388 void IMB_colormanagement_assign_float_colorspace(ImBuf *ibuf, const char *name)
1389 {
1390   ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1391 
1392   ibuf->float_colorspace = colorspace;
1393 
1394   if (colorspace && colorspace->is_data) {
1395     ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1396   }
1397   else {
1398     ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1399   }
1400 }
1401 
IMB_colormanagement_assign_rect_colorspace(ImBuf * ibuf,const char * name)1402 void IMB_colormanagement_assign_rect_colorspace(ImBuf *ibuf, const char *name)
1403 {
1404   ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1405 
1406   ibuf->rect_colorspace = colorspace;
1407 
1408   if (colorspace && colorspace->is_data) {
1409     ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1410   }
1411   else {
1412     ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1413   }
1414 }
1415 
IMB_colormanagement_get_float_colorspace(ImBuf * ibuf)1416 const char *IMB_colormanagement_get_float_colorspace(ImBuf *ibuf)
1417 {
1418   if (ibuf->float_colorspace) {
1419     return ibuf->float_colorspace->name;
1420   }
1421 
1422   return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR);
1423 }
1424 
IMB_colormanagement_get_rect_colorspace(ImBuf * ibuf)1425 const char *IMB_colormanagement_get_rect_colorspace(ImBuf *ibuf)
1426 {
1427   if (ibuf->rect_colorspace) {
1428     return ibuf->rect_colorspace->name;
1429   }
1430 
1431   return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE);
1432 }
1433 
IMB_colormanagement_space_is_data(ColorSpace * colorspace)1434 bool IMB_colormanagement_space_is_data(ColorSpace *colorspace)
1435 {
1436   return (colorspace && colorspace->is_data);
1437 }
1438 
colormanage_ensure_srgb_scene_linear_info(ColorSpace * colorspace)1439 static void colormanage_ensure_srgb_scene_linear_info(ColorSpace *colorspace)
1440 {
1441   if (!colorspace->info.cached) {
1442     OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
1443     OCIO_ConstColorSpaceRcPtr *ocio_colorspace = OCIO_configGetColorSpace(config,
1444                                                                           colorspace->name);
1445 
1446     bool is_scene_linear, is_srgb;
1447     OCIO_colorSpaceIsBuiltin(config, ocio_colorspace, &is_scene_linear, &is_srgb);
1448 
1449     OCIO_colorSpaceRelease(ocio_colorspace);
1450     OCIO_configRelease(config);
1451 
1452     colorspace->info.is_scene_linear = is_scene_linear;
1453     colorspace->info.is_srgb = is_srgb;
1454     colorspace->info.cached = true;
1455   }
1456 }
1457 
IMB_colormanagement_space_is_scene_linear(ColorSpace * colorspace)1458 bool IMB_colormanagement_space_is_scene_linear(ColorSpace *colorspace)
1459 {
1460   colormanage_ensure_srgb_scene_linear_info(colorspace);
1461   return (colorspace && colorspace->info.is_scene_linear);
1462 }
1463 
IMB_colormanagement_space_is_srgb(ColorSpace * colorspace)1464 bool IMB_colormanagement_space_is_srgb(ColorSpace *colorspace)
1465 {
1466   colormanage_ensure_srgb_scene_linear_info(colorspace);
1467   return (colorspace && colorspace->info.is_srgb);
1468 }
1469 
IMB_colormanagement_space_name_is_data(const char * name)1470 bool IMB_colormanagement_space_name_is_data(const char *name)
1471 {
1472   ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1473   return (colorspace && colorspace->is_data);
1474 }
1475 
IMB_colormangement_get_xyz_to_rgb()1476 const float *IMB_colormangement_get_xyz_to_rgb()
1477 {
1478   return &imbuf_xyz_to_rgb[0][0];
1479 }
1480 
1481 /** \} */
1482 
1483 /* -------------------------------------------------------------------- */
1484 /** \name Threaded Display Buffer Transform Routines
1485  * \{ */
1486 
1487 typedef struct DisplayBufferThread {
1488   ColormanageProcessor *cm_processor;
1489 
1490   const float *buffer;
1491   unsigned char *byte_buffer;
1492 
1493   float *display_buffer;
1494   unsigned char *display_buffer_byte;
1495 
1496   int width;
1497   int start_line;
1498   int tot_line;
1499 
1500   int channels;
1501   float dither;
1502   bool is_data;
1503   bool predivide;
1504 
1505   const char *byte_colorspace;
1506   const char *float_colorspace;
1507 } DisplayBufferThread;
1508 
1509 typedef struct DisplayBufferInitData {
1510   ImBuf *ibuf;
1511   ColormanageProcessor *cm_processor;
1512   const float *buffer;
1513   unsigned char *byte_buffer;
1514 
1515   float *display_buffer;
1516   unsigned char *display_buffer_byte;
1517 
1518   int width;
1519 
1520   const char *byte_colorspace;
1521   const char *float_colorspace;
1522 } DisplayBufferInitData;
1523 
display_buffer_init_handle(void * handle_v,int start_line,int tot_line,void * init_data_v)1524 static void display_buffer_init_handle(void *handle_v,
1525                                        int start_line,
1526                                        int tot_line,
1527                                        void *init_data_v)
1528 {
1529   DisplayBufferThread *handle = (DisplayBufferThread *)handle_v;
1530   DisplayBufferInitData *init_data = (DisplayBufferInitData *)init_data_v;
1531   ImBuf *ibuf = init_data->ibuf;
1532 
1533   int channels = ibuf->channels;
1534   float dither = ibuf->dither;
1535   bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
1536 
1537   size_t offset = ((size_t)channels) * start_line * ibuf->x;
1538   size_t display_buffer_byte_offset = ((size_t)DISPLAY_BUFFER_CHANNELS) * start_line * ibuf->x;
1539 
1540   memset(handle, 0, sizeof(DisplayBufferThread));
1541 
1542   handle->cm_processor = init_data->cm_processor;
1543 
1544   if (init_data->buffer) {
1545     handle->buffer = init_data->buffer + offset;
1546   }
1547 
1548   if (init_data->byte_buffer) {
1549     handle->byte_buffer = init_data->byte_buffer + offset;
1550   }
1551 
1552   if (init_data->display_buffer) {
1553     handle->display_buffer = init_data->display_buffer + offset;
1554   }
1555 
1556   if (init_data->display_buffer_byte) {
1557     handle->display_buffer_byte = init_data->display_buffer_byte + display_buffer_byte_offset;
1558   }
1559 
1560   handle->width = ibuf->x;
1561 
1562   handle->start_line = start_line;
1563   handle->tot_line = tot_line;
1564 
1565   handle->channels = channels;
1566   handle->dither = dither;
1567   handle->is_data = is_data;
1568   handle->predivide = IMB_alpha_affects_rgb(ibuf);
1569 
1570   handle->byte_colorspace = init_data->byte_colorspace;
1571   handle->float_colorspace = init_data->float_colorspace;
1572 }
1573 
display_buffer_apply_get_linear_buffer(DisplayBufferThread * handle,int height,float * linear_buffer,bool * is_straight_alpha)1574 static void display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle,
1575                                                    int height,
1576                                                    float *linear_buffer,
1577                                                    bool *is_straight_alpha)
1578 {
1579   int channels = handle->channels;
1580   int width = handle->width;
1581 
1582   size_t buffer_size = ((size_t)channels) * width * height;
1583 
1584   bool is_data = handle->is_data;
1585   bool is_data_display = handle->cm_processor->is_data_result;
1586   bool predivide = handle->predivide;
1587 
1588   if (!handle->buffer) {
1589     unsigned char *byte_buffer = handle->byte_buffer;
1590 
1591     const char *from_colorspace = handle->byte_colorspace;
1592     const char *to_colorspace = global_role_scene_linear;
1593 
1594     float *fp;
1595     unsigned char *cp;
1596     const size_t i_last = ((size_t)width) * height;
1597     size_t i;
1598 
1599     /* first convert byte buffer to float, keep in image space */
1600     for (i = 0, fp = linear_buffer, cp = byte_buffer; i != i_last;
1601          i++, fp += channels, cp += channels) {
1602       if (channels == 3) {
1603         rgb_uchar_to_float(fp, cp);
1604       }
1605       else if (channels == 4) {
1606         rgba_uchar_to_float(fp, cp);
1607       }
1608       else {
1609         BLI_assert(!"Buffers of 3 or 4 channels are only supported here");
1610       }
1611     }
1612 
1613     if (!is_data && !is_data_display) {
1614       /* convert float buffer to scene linear space */
1615       IMB_colormanagement_transform(
1616           linear_buffer, width, height, channels, from_colorspace, to_colorspace, false);
1617     }
1618 
1619     *is_straight_alpha = true;
1620   }
1621   else if (handle->float_colorspace) {
1622     /* currently float is non-linear only in sequencer, which is working
1623      * in its own color space even to handle float buffers.
1624      * This color space is the same for byte and float images.
1625      * Need to convert float buffer to linear space before applying display transform
1626      */
1627 
1628     const char *from_colorspace = handle->float_colorspace;
1629     const char *to_colorspace = global_role_scene_linear;
1630 
1631     memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1632 
1633     if (!is_data && !is_data_display) {
1634       IMB_colormanagement_transform(
1635           linear_buffer, width, height, channels, from_colorspace, to_colorspace, predivide);
1636     }
1637 
1638     *is_straight_alpha = false;
1639   }
1640   else {
1641     /* some processors would want to modify float original buffer
1642      * before converting it into display byte buffer, so we need to
1643      * make sure original's ImBuf buffers wouldn't be modified by
1644      * using duplicated buffer here
1645      */
1646 
1647     memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1648 
1649     *is_straight_alpha = false;
1650   }
1651 }
1652 
do_display_buffer_apply_thread(void * handle_v)1653 static void *do_display_buffer_apply_thread(void *handle_v)
1654 {
1655   DisplayBufferThread *handle = (DisplayBufferThread *)handle_v;
1656   ColormanageProcessor *cm_processor = handle->cm_processor;
1657   float *display_buffer = handle->display_buffer;
1658   unsigned char *display_buffer_byte = handle->display_buffer_byte;
1659   int channels = handle->channels;
1660   int width = handle->width;
1661   int height = handle->tot_line;
1662   float dither = handle->dither;
1663   bool is_data = handle->is_data;
1664 
1665   if (cm_processor == NULL) {
1666     if (display_buffer_byte && display_buffer_byte != handle->byte_buffer) {
1667       IMB_buffer_byte_from_byte(display_buffer_byte,
1668                                 handle->byte_buffer,
1669                                 IB_PROFILE_SRGB,
1670                                 IB_PROFILE_SRGB,
1671                                 false,
1672                                 width,
1673                                 height,
1674                                 width,
1675                                 width);
1676     }
1677 
1678     if (display_buffer) {
1679       IMB_buffer_float_from_byte(display_buffer,
1680                                  handle->byte_buffer,
1681                                  IB_PROFILE_SRGB,
1682                                  IB_PROFILE_SRGB,
1683                                  false,
1684                                  width,
1685                                  height,
1686                                  width,
1687                                  width);
1688     }
1689   }
1690   else {
1691     bool is_straight_alpha;
1692     float *linear_buffer = MEM_mallocN(((size_t)channels) * width * height * sizeof(float),
1693                                        "color conversion linear buffer");
1694 
1695     display_buffer_apply_get_linear_buffer(handle, height, linear_buffer, &is_straight_alpha);
1696 
1697     bool predivide = handle->predivide && (is_straight_alpha == false);
1698 
1699     if (is_data) {
1700       /* special case for data buffers - no color space conversions,
1701        * only generate byte buffers
1702        */
1703     }
1704     else {
1705       /* apply processor */
1706       IMB_colormanagement_processor_apply(
1707           cm_processor, linear_buffer, width, height, channels, predivide);
1708     }
1709 
1710     /* copy result to output buffers */
1711     if (display_buffer_byte) {
1712       /* do conversion */
1713       IMB_buffer_byte_from_float(display_buffer_byte,
1714                                  linear_buffer,
1715                                  channels,
1716                                  dither,
1717                                  IB_PROFILE_SRGB,
1718                                  IB_PROFILE_SRGB,
1719                                  predivide,
1720                                  width,
1721                                  height,
1722                                  width,
1723                                  width);
1724     }
1725 
1726     if (display_buffer) {
1727       memcpy(display_buffer, linear_buffer, ((size_t)width) * height * channels * sizeof(float));
1728 
1729       if (is_straight_alpha && channels == 4) {
1730         const size_t i_last = ((size_t)width) * height;
1731         size_t i;
1732         float *fp;
1733 
1734         for (i = 0, fp = display_buffer; i != i_last; i++, fp += channels) {
1735           straight_to_premul_v4(fp);
1736         }
1737       }
1738     }
1739 
1740     MEM_freeN(linear_buffer);
1741   }
1742 
1743   return NULL;
1744 }
1745 
display_buffer_apply_threaded(ImBuf * ibuf,const float * buffer,unsigned char * byte_buffer,float * display_buffer,unsigned char * display_buffer_byte,ColormanageProcessor * cm_processor)1746 static void display_buffer_apply_threaded(ImBuf *ibuf,
1747                                           const float *buffer,
1748                                           unsigned char *byte_buffer,
1749                                           float *display_buffer,
1750                                           unsigned char *display_buffer_byte,
1751                                           ColormanageProcessor *cm_processor)
1752 {
1753   DisplayBufferInitData init_data;
1754 
1755   init_data.ibuf = ibuf;
1756   init_data.cm_processor = cm_processor;
1757   init_data.buffer = buffer;
1758   init_data.byte_buffer = byte_buffer;
1759   init_data.display_buffer = display_buffer;
1760   init_data.display_buffer_byte = display_buffer_byte;
1761 
1762   if (ibuf->rect_colorspace != NULL) {
1763     init_data.byte_colorspace = ibuf->rect_colorspace->name;
1764   }
1765   else {
1766     /* happens for viewer images, which are not so simple to determine where to
1767      * set image buffer's color spaces
1768      */
1769     init_data.byte_colorspace = global_role_default_byte;
1770   }
1771 
1772   if (ibuf->float_colorspace != NULL) {
1773     /* sequencer stores float buffers in non-linear space */
1774     init_data.float_colorspace = ibuf->float_colorspace->name;
1775   }
1776   else {
1777     init_data.float_colorspace = NULL;
1778   }
1779 
1780   IMB_processor_apply_threaded(ibuf->y,
1781                                sizeof(DisplayBufferThread),
1782                                &init_data,
1783                                display_buffer_init_handle,
1784                                do_display_buffer_apply_thread);
1785 }
1786 
is_ibuf_rect_in_display_space(ImBuf * ibuf,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)1787 static bool is_ibuf_rect_in_display_space(ImBuf *ibuf,
1788                                           const ColorManagedViewSettings *view_settings,
1789                                           const ColorManagedDisplaySettings *display_settings)
1790 {
1791   if ((view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) == 0 &&
1792       view_settings->exposure == 0.0f && view_settings->gamma == 1.0f) {
1793     const char *from_colorspace = ibuf->rect_colorspace->name;
1794     const char *to_colorspace = IMB_colormanagement_get_display_colorspace_name(view_settings,
1795                                                                                 display_settings);
1796     ColorManagedLook *look_descr = colormanage_look_get_named(view_settings->look);
1797     if (look_descr != NULL && !STREQ(look_descr->process_space, "")) {
1798       return false;
1799     }
1800 
1801     if (to_colorspace && STREQ(from_colorspace, to_colorspace)) {
1802       return true;
1803     }
1804   }
1805 
1806   return false;
1807 }
1808 
colormanage_display_buffer_process_ex(ImBuf * ibuf,float * display_buffer,unsigned char * display_buffer_byte,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)1809 static void colormanage_display_buffer_process_ex(
1810     ImBuf *ibuf,
1811     float *display_buffer,
1812     unsigned char *display_buffer_byte,
1813     const ColorManagedViewSettings *view_settings,
1814     const ColorManagedDisplaySettings *display_settings)
1815 {
1816   ColormanageProcessor *cm_processor = NULL;
1817   bool skip_transform = false;
1818 
1819   /* if we're going to transform byte buffer, check whether transformation would
1820    * happen to the same color space as byte buffer itself is
1821    * this would save byte -> float -> byte conversions making display buffer
1822    * computation noticeable faster
1823    */
1824   if (ibuf->rect_float == NULL && ibuf->rect_colorspace) {
1825     skip_transform = is_ibuf_rect_in_display_space(ibuf, view_settings, display_settings);
1826   }
1827 
1828   if (skip_transform == false) {
1829     cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1830   }
1831 
1832   display_buffer_apply_threaded(ibuf,
1833                                 ibuf->rect_float,
1834                                 (unsigned char *)ibuf->rect,
1835                                 display_buffer,
1836                                 display_buffer_byte,
1837                                 cm_processor);
1838 
1839   if (cm_processor) {
1840     IMB_colormanagement_processor_free(cm_processor);
1841   }
1842 }
1843 
colormanage_display_buffer_process(ImBuf * ibuf,unsigned char * display_buffer,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)1844 static void colormanage_display_buffer_process(ImBuf *ibuf,
1845                                                unsigned char *display_buffer,
1846                                                const ColorManagedViewSettings *view_settings,
1847                                                const ColorManagedDisplaySettings *display_settings)
1848 {
1849   colormanage_display_buffer_process_ex(
1850       ibuf, NULL, display_buffer, view_settings, display_settings);
1851 }
1852 
1853 /** \} */
1854 
1855 /* -------------------------------------------------------------------- */
1856 /** \name Threaded Processor Transform Routines
1857  * \{ */
1858 
1859 typedef struct ProcessorTransformThread {
1860   ColormanageProcessor *cm_processor;
1861   unsigned char *byte_buffer;
1862   float *float_buffer;
1863   int width;
1864   int start_line;
1865   int tot_line;
1866   int channels;
1867   bool predivide;
1868   bool float_from_byte;
1869 } ProcessorTransformThread;
1870 
1871 typedef struct ProcessorTransformInit {
1872   ColormanageProcessor *cm_processor;
1873   unsigned char *byte_buffer;
1874   float *float_buffer;
1875   int width;
1876   int height;
1877   int channels;
1878   bool predivide;
1879   bool float_from_byte;
1880 } ProcessorTransformInitData;
1881 
processor_transform_init_handle(void * handle_v,int start_line,int tot_line,void * init_data_v)1882 static void processor_transform_init_handle(void *handle_v,
1883                                             int start_line,
1884                                             int tot_line,
1885                                             void *init_data_v)
1886 {
1887   ProcessorTransformThread *handle = (ProcessorTransformThread *)handle_v;
1888   ProcessorTransformInitData *init_data = (ProcessorTransformInitData *)init_data_v;
1889 
1890   const int channels = init_data->channels;
1891   const int width = init_data->width;
1892   const bool predivide = init_data->predivide;
1893   const bool float_from_byte = init_data->float_from_byte;
1894 
1895   const size_t offset = ((size_t)channels) * start_line * width;
1896 
1897   memset(handle, 0, sizeof(ProcessorTransformThread));
1898 
1899   handle->cm_processor = init_data->cm_processor;
1900 
1901   if (init_data->byte_buffer != NULL) {
1902     /* TODO(serge): Offset might be different for byte and float buffers. */
1903     handle->byte_buffer = init_data->byte_buffer + offset;
1904   }
1905   if (init_data->float_buffer != NULL) {
1906     handle->float_buffer = init_data->float_buffer + offset;
1907   }
1908 
1909   handle->width = width;
1910 
1911   handle->start_line = start_line;
1912   handle->tot_line = tot_line;
1913 
1914   handle->channels = channels;
1915   handle->predivide = predivide;
1916   handle->float_from_byte = float_from_byte;
1917 }
1918 
do_processor_transform_thread(void * handle_v)1919 static void *do_processor_transform_thread(void *handle_v)
1920 {
1921   ProcessorTransformThread *handle = (ProcessorTransformThread *)handle_v;
1922   unsigned char *byte_buffer = handle->byte_buffer;
1923   float *float_buffer = handle->float_buffer;
1924   const int channels = handle->channels;
1925   const int width = handle->width;
1926   const int height = handle->tot_line;
1927   const bool predivide = handle->predivide;
1928   const bool float_from_byte = handle->float_from_byte;
1929 
1930   if (float_from_byte) {
1931     IMB_buffer_float_from_byte(float_buffer,
1932                                byte_buffer,
1933                                IB_PROFILE_SRGB,
1934                                IB_PROFILE_SRGB,
1935                                false,
1936                                width,
1937                                height,
1938                                width,
1939                                width);
1940     IMB_colormanagement_processor_apply(
1941         handle->cm_processor, float_buffer, width, height, channels, predivide);
1942     IMB_premultiply_rect_float(float_buffer, 4, width, height);
1943   }
1944   else {
1945     if (byte_buffer != NULL) {
1946       IMB_colormanagement_processor_apply_byte(
1947           handle->cm_processor, byte_buffer, width, height, channels);
1948     }
1949     if (float_buffer != NULL) {
1950       IMB_colormanagement_processor_apply(
1951           handle->cm_processor, float_buffer, width, height, channels, predivide);
1952     }
1953   }
1954 
1955   return NULL;
1956 }
1957 
processor_transform_apply_threaded(unsigned char * byte_buffer,float * float_buffer,const int width,const int height,const int channels,ColormanageProcessor * cm_processor,const bool predivide,const bool float_from_byte)1958 static void processor_transform_apply_threaded(unsigned char *byte_buffer,
1959                                                float *float_buffer,
1960                                                const int width,
1961                                                const int height,
1962                                                const int channels,
1963                                                ColormanageProcessor *cm_processor,
1964                                                const bool predivide,
1965                                                const bool float_from_byte)
1966 {
1967   ProcessorTransformInitData init_data;
1968 
1969   init_data.cm_processor = cm_processor;
1970   init_data.byte_buffer = byte_buffer;
1971   init_data.float_buffer = float_buffer;
1972   init_data.width = width;
1973   init_data.height = height;
1974   init_data.channels = channels;
1975   init_data.predivide = predivide;
1976   init_data.float_from_byte = float_from_byte;
1977 
1978   IMB_processor_apply_threaded(height,
1979                                sizeof(ProcessorTransformThread),
1980                                &init_data,
1981                                processor_transform_init_handle,
1982                                do_processor_transform_thread);
1983 }
1984 
1985 /** \} */
1986 
1987 /* -------------------------------------------------------------------- */
1988 /** \name Color Space Transformation Functions
1989  * \{ */
1990 
1991 /* Convert the whole buffer from specified by name color space to another -
1992  * internal implementation. */
colormanagement_transform_ex(unsigned char * byte_buffer,float * float_buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace,bool predivide,bool do_threaded)1993 static void colormanagement_transform_ex(unsigned char *byte_buffer,
1994                                          float *float_buffer,
1995                                          int width,
1996                                          int height,
1997                                          int channels,
1998                                          const char *from_colorspace,
1999                                          const char *to_colorspace,
2000                                          bool predivide,
2001                                          bool do_threaded)
2002 {
2003   ColormanageProcessor *cm_processor;
2004 
2005   if (from_colorspace[0] == '\0') {
2006     return;
2007   }
2008 
2009   if (STREQ(from_colorspace, to_colorspace)) {
2010     /* if source and destination color spaces are identical, skip
2011      * threading overhead and simply do nothing
2012      */
2013     return;
2014   }
2015 
2016   cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
2017 
2018   if (do_threaded) {
2019     processor_transform_apply_threaded(
2020         byte_buffer, float_buffer, width, height, channels, cm_processor, predivide, false);
2021   }
2022   else {
2023     if (byte_buffer != NULL) {
2024       IMB_colormanagement_processor_apply_byte(cm_processor, byte_buffer, width, height, channels);
2025     }
2026     if (float_buffer != NULL) {
2027       IMB_colormanagement_processor_apply(
2028           cm_processor, float_buffer, width, height, channels, predivide);
2029     }
2030   }
2031 
2032   IMB_colormanagement_processor_free(cm_processor);
2033 }
2034 
2035 /* convert the whole buffer from specified by name color space to another */
IMB_colormanagement_transform(float * buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace,bool predivide)2036 void IMB_colormanagement_transform(float *buffer,
2037                                    int width,
2038                                    int height,
2039                                    int channels,
2040                                    const char *from_colorspace,
2041                                    const char *to_colorspace,
2042                                    bool predivide)
2043 {
2044   colormanagement_transform_ex(
2045       NULL, buffer, width, height, channels, from_colorspace, to_colorspace, predivide, false);
2046 }
2047 
2048 /* convert the whole buffer from specified by name color space to another
2049  * will do threaded conversion
2050  */
IMB_colormanagement_transform_threaded(float * buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace,bool predivide)2051 void IMB_colormanagement_transform_threaded(float *buffer,
2052                                             int width,
2053                                             int height,
2054                                             int channels,
2055                                             const char *from_colorspace,
2056                                             const char *to_colorspace,
2057                                             bool predivide)
2058 {
2059   colormanagement_transform_ex(
2060       NULL, buffer, width, height, channels, from_colorspace, to_colorspace, predivide, true);
2061 }
2062 
2063 /* Similar to functions above, but operates on byte buffer. */
IMB_colormanagement_transform_byte(unsigned char * buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace)2064 void IMB_colormanagement_transform_byte(unsigned char *buffer,
2065                                         int width,
2066                                         int height,
2067                                         int channels,
2068                                         const char *from_colorspace,
2069                                         const char *to_colorspace)
2070 {
2071   colormanagement_transform_ex(
2072       buffer, NULL, width, height, channels, from_colorspace, to_colorspace, false, false);
2073 }
IMB_colormanagement_transform_byte_threaded(unsigned char * buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace)2074 void IMB_colormanagement_transform_byte_threaded(unsigned char *buffer,
2075                                                  int width,
2076                                                  int height,
2077                                                  int channels,
2078                                                  const char *from_colorspace,
2079                                                  const char *to_colorspace)
2080 {
2081   colormanagement_transform_ex(
2082       buffer, NULL, width, height, channels, from_colorspace, to_colorspace, false, true);
2083 }
2084 
2085 /* Similar to above, but gets float buffer from display one. */
IMB_colormanagement_transform_from_byte(float * float_buffer,unsigned char * byte_buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace)2086 void IMB_colormanagement_transform_from_byte(float *float_buffer,
2087                                              unsigned char *byte_buffer,
2088                                              int width,
2089                                              int height,
2090                                              int channels,
2091                                              const char *from_colorspace,
2092                                              const char *to_colorspace)
2093 {
2094   IMB_buffer_float_from_byte(float_buffer,
2095                              byte_buffer,
2096                              IB_PROFILE_SRGB,
2097                              IB_PROFILE_SRGB,
2098                              true,
2099                              width,
2100                              height,
2101                              width,
2102                              width);
2103   IMB_colormanagement_transform(
2104       float_buffer, width, height, channels, from_colorspace, to_colorspace, true);
2105 }
IMB_colormanagement_transform_from_byte_threaded(float * float_buffer,unsigned char * byte_buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace)2106 void IMB_colormanagement_transform_from_byte_threaded(float *float_buffer,
2107                                                       unsigned char *byte_buffer,
2108                                                       int width,
2109                                                       int height,
2110                                                       int channels,
2111                                                       const char *from_colorspace,
2112                                                       const char *to_colorspace)
2113 {
2114   ColormanageProcessor *cm_processor;
2115   if (from_colorspace == NULL || from_colorspace[0] == '\0') {
2116     return;
2117   }
2118   if (STREQ(from_colorspace, to_colorspace)) {
2119     /* Because this function always takes a byte buffer and returns a float buffer, it must
2120      * always do byte-to-float conversion of some kind. To avoid threading overhead
2121      * IMB_buffer_float_from_byte is used when color spaces are identical. See T51002.
2122      */
2123     IMB_buffer_float_from_byte(float_buffer,
2124                                byte_buffer,
2125                                IB_PROFILE_SRGB,
2126                                IB_PROFILE_SRGB,
2127                                false,
2128                                width,
2129                                height,
2130                                width,
2131                                width);
2132     IMB_premultiply_rect_float(float_buffer, 4, width, height);
2133     return;
2134   }
2135   cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
2136   processor_transform_apply_threaded(
2137       byte_buffer, float_buffer, width, height, channels, cm_processor, false, true);
2138   IMB_colormanagement_processor_free(cm_processor);
2139 }
2140 
IMB_colormanagement_transform_v4(float pixel[4],const char * from_colorspace,const char * to_colorspace)2141 void IMB_colormanagement_transform_v4(float pixel[4],
2142                                       const char *from_colorspace,
2143                                       const char *to_colorspace)
2144 {
2145   ColormanageProcessor *cm_processor;
2146 
2147   if (from_colorspace[0] == '\0') {
2148     return;
2149   }
2150 
2151   if (STREQ(from_colorspace, to_colorspace)) {
2152     /* if source and destination color spaces are identical, skip
2153      * threading overhead and simply do nothing
2154      */
2155     return;
2156   }
2157 
2158   cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
2159 
2160   IMB_colormanagement_processor_apply_v4(cm_processor, pixel);
2161 
2162   IMB_colormanagement_processor_free(cm_processor);
2163 }
2164 
2165 /* convert pixel from specified by descriptor color space to scene linear
2166  * used by performance-critical areas such as renderer and baker
2167  */
IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3],ColorSpace * colorspace)2168 void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], ColorSpace *colorspace)
2169 {
2170   OCIO_ConstProcessorRcPtr *processor;
2171 
2172   if (!colorspace) {
2173     /* should never happen */
2174     printf("%s: perform conversion from unknown color space\n", __func__);
2175     return;
2176   }
2177 
2178   processor = colorspace_to_scene_linear_processor(colorspace);
2179 
2180   if (processor) {
2181     OCIO_processorApplyRGB(processor, pixel);
2182   }
2183 }
2184 
2185 /* same as above, but converts colors in opposite direction */
IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3],ColorSpace * colorspace)2186 void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], ColorSpace *colorspace)
2187 {
2188   OCIO_ConstProcessorRcPtr *processor;
2189 
2190   if (!colorspace) {
2191     /* should never happen */
2192     printf("%s: perform conversion from unknown color space\n", __func__);
2193     return;
2194   }
2195 
2196   processor = colorspace_from_scene_linear_processor(colorspace);
2197 
2198   if (processor) {
2199     OCIO_processorApplyRGB(processor, pixel);
2200   }
2201 }
2202 
IMB_colormanagement_colorspace_to_scene_linear_v4(float pixel[4],bool predivide,ColorSpace * colorspace)2203 void IMB_colormanagement_colorspace_to_scene_linear_v4(float pixel[4],
2204                                                        bool predivide,
2205                                                        ColorSpace *colorspace)
2206 {
2207   OCIO_ConstProcessorRcPtr *processor;
2208 
2209   if (!colorspace) {
2210     /* should never happen */
2211     printf("%s: perform conversion from unknown color space\n", __func__);
2212     return;
2213   }
2214 
2215   processor = colorspace_to_scene_linear_processor(colorspace);
2216 
2217   if (processor) {
2218     if (predivide) {
2219       OCIO_processorApplyRGBA_predivide(processor, pixel);
2220     }
2221     else {
2222       OCIO_processorApplyRGBA(processor, pixel);
2223     }
2224   }
2225 }
2226 
IMB_colormanagement_colorspace_to_scene_linear(float * buffer,int width,int height,int channels,struct ColorSpace * colorspace,bool predivide)2227 void IMB_colormanagement_colorspace_to_scene_linear(float *buffer,
2228                                                     int width,
2229                                                     int height,
2230                                                     int channels,
2231                                                     struct ColorSpace *colorspace,
2232                                                     bool predivide)
2233 {
2234   OCIO_ConstProcessorRcPtr *processor;
2235 
2236   if (!colorspace) {
2237     /* should never happen */
2238     printf("%s: perform conversion from unknown color space\n", __func__);
2239     return;
2240   }
2241 
2242   processor = colorspace_to_scene_linear_processor(colorspace);
2243 
2244   if (processor) {
2245     OCIO_PackedImageDesc *img;
2246 
2247     img = OCIO_createOCIO_PackedImageDesc(buffer,
2248                                           width,
2249                                           height,
2250                                           channels,
2251                                           sizeof(float),
2252                                           (size_t)channels * sizeof(float),
2253                                           (size_t)channels * sizeof(float) * width);
2254 
2255     if (predivide) {
2256       OCIO_processorApply_predivide(processor, img);
2257     }
2258     else {
2259       OCIO_processorApply(processor, img);
2260     }
2261 
2262     OCIO_PackedImageDescRelease(img);
2263   }
2264 }
2265 
IMB_colormanagement_imbuf_to_byte_texture(unsigned char * out_buffer,const int offset_x,const int offset_y,const int width,const int height,const struct ImBuf * ibuf,const bool compress_as_srgb,const bool store_premultiplied)2266 void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer,
2267                                                const int offset_x,
2268                                                const int offset_y,
2269                                                const int width,
2270                                                const int height,
2271                                                const struct ImBuf *ibuf,
2272                                                const bool compress_as_srgb,
2273                                                const bool store_premultiplied)
2274 {
2275   /* Convert byte buffer for texture storage on the GPU. These have builtin
2276    * support for converting sRGB to linear, which allows us to store textures
2277    * without precision or performance loss at minimal memory usage. */
2278   BLI_assert(ibuf->rect && ibuf->rect_float == NULL);
2279 
2280   OCIO_ConstProcessorRcPtr *processor = NULL;
2281   if (compress_as_srgb && ibuf->rect_colorspace &&
2282       !IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace)) {
2283     processor = colorspace_to_scene_linear_processor(ibuf->rect_colorspace);
2284   }
2285 
2286   /* TODO(brecht): make this multi-threaded, or at least process in batches. */
2287   const unsigned char *in_buffer = (unsigned char *)ibuf->rect;
2288   const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied;
2289 
2290   for (int y = 0; y < height; y++) {
2291     const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
2292     const size_t out_offset = y * width;
2293     const unsigned char *in = in_buffer + in_offset * 4;
2294     unsigned char *out = out_buffer + out_offset * 4;
2295 
2296     if (processor) {
2297       /* Convert to scene linear, to sRGB and premultiply. */
2298       for (int x = 0; x < width; x++, in += 4, out += 4) {
2299         float pixel[4];
2300         rgba_uchar_to_float(pixel, in);
2301         OCIO_processorApplyRGB(processor, pixel);
2302         linearrgb_to_srgb_v3_v3(pixel, pixel);
2303         if (use_premultiply) {
2304           mul_v3_fl(pixel, pixel[3]);
2305         }
2306         rgba_float_to_uchar(out, pixel);
2307       }
2308     }
2309     else if (use_premultiply) {
2310       /* Premultiply only. */
2311       for (int x = 0; x < width; x++, in += 4, out += 4) {
2312         out[0] = (in[0] * in[3]) >> 8;
2313         out[1] = (in[1] * in[3]) >> 8;
2314         out[2] = (in[2] * in[3]) >> 8;
2315         out[3] = in[3];
2316       }
2317     }
2318     else {
2319       /* Copy only. */
2320       for (int x = 0; x < width; x++, in += 4, out += 4) {
2321         out[0] = in[0];
2322         out[1] = in[1];
2323         out[2] = in[2];
2324         out[3] = in[3];
2325       }
2326     }
2327   }
2328 }
2329 
IMB_colormanagement_imbuf_to_float_texture(float * out_buffer,const int offset_x,const int offset_y,const int width,const int height,const struct ImBuf * ibuf,const bool store_premultiplied)2330 void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer,
2331                                                 const int offset_x,
2332                                                 const int offset_y,
2333                                                 const int width,
2334                                                 const int height,
2335                                                 const struct ImBuf *ibuf,
2336                                                 const bool store_premultiplied)
2337 {
2338   /* Float texture are stored in scene linear color space, with premultiplied
2339    * alpha depending on the image alpha mode. */
2340   const float *in_buffer = ibuf->rect_float;
2341   const int in_channels = ibuf->channels;
2342   const bool use_unpremultiply = IMB_alpha_affects_rgb(ibuf) && !store_premultiplied;
2343 
2344   for (int y = 0; y < height; y++) {
2345     const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
2346     const size_t out_offset = y * width;
2347     const float *in = in_buffer + in_offset * in_channels;
2348     float *out = out_buffer + out_offset * 4;
2349 
2350     if (in_channels == 1) {
2351       /* Copy single channel. */
2352       for (int x = 0; x < width; x++, in += 1, out += 4) {
2353         out[0] = in[0];
2354         out[1] = in[0];
2355         out[2] = in[0];
2356         out[3] = in[0];
2357       }
2358     }
2359     else if (in_channels == 3) {
2360       /* Copy RGB. */
2361       for (int x = 0; x < width; x++, in += 3, out += 4) {
2362         out[0] = in[0];
2363         out[1] = in[1];
2364         out[2] = in[2];
2365         out[3] = 1.0f;
2366       }
2367     }
2368     else if (in_channels == 4) {
2369       /* Copy or convert RGBA. */
2370       if (use_unpremultiply) {
2371         for (int x = 0; x < width; x++, in += 4, out += 4) {
2372           premul_to_straight_v4_v4(out, in);
2373         }
2374       }
2375       else {
2376         memcpy(out, in, sizeof(float[4]) * width);
2377       }
2378     }
2379   }
2380 }
2381 
2382 /* Conversion between color picking role. Typically we would expect such a
2383  * requirements:
2384  * - It is approximately perceptually linear, so that the HSV numbers and
2385  *   the HSV cube/circle have an intuitive distribution.
2386  * - It has the same gamut as the scene linear color space.
2387  * - Color picking values 0..1 map to scene linear values in the 0..1 range,
2388  *   so that picked albedo values are energy conserving.
2389  */
IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3])2390 void IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3])
2391 {
2392   if (!global_color_picking_state.processor_to && !global_color_picking_state.failed) {
2393     /* Create processor if none exists. */
2394     BLI_mutex_lock(&processor_lock);
2395 
2396     if (!global_color_picking_state.processor_to && !global_color_picking_state.failed) {
2397       global_color_picking_state.processor_to = create_colorspace_transform_processor(
2398           global_role_scene_linear, global_role_color_picking);
2399 
2400       if (!global_color_picking_state.processor_to) {
2401         global_color_picking_state.failed = true;
2402       }
2403     }
2404 
2405     BLI_mutex_unlock(&processor_lock);
2406   }
2407 
2408   if (global_color_picking_state.processor_to) {
2409     OCIO_processorApplyRGB(global_color_picking_state.processor_to, pixel);
2410   }
2411 }
2412 
IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3])2413 void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3])
2414 {
2415   if (!global_color_picking_state.processor_from && !global_color_picking_state.failed) {
2416     /* Create processor if none exists. */
2417     BLI_mutex_lock(&processor_lock);
2418 
2419     if (!global_color_picking_state.processor_from && !global_color_picking_state.failed) {
2420       global_color_picking_state.processor_from = create_colorspace_transform_processor(
2421           global_role_color_picking, global_role_scene_linear);
2422 
2423       if (!global_color_picking_state.processor_from) {
2424         global_color_picking_state.failed = true;
2425       }
2426     }
2427 
2428     BLI_mutex_unlock(&processor_lock);
2429   }
2430 
2431   if (global_color_picking_state.processor_from) {
2432     OCIO_processorApplyRGB(global_color_picking_state.processor_from, pixel);
2433   }
2434 }
2435 
2436 /* Conversion between sRGB, for rare cases like hex color or copy/pasting
2437  * between UI theme and scene linear colors. */
IMB_colormanagement_scene_linear_to_srgb_v3(float pixel[3])2438 void IMB_colormanagement_scene_linear_to_srgb_v3(float pixel[3])
2439 {
2440   mul_m3_v3(imbuf_rgb_to_xyz, pixel);
2441   mul_m3_v3(imbuf_xyz_to_linear_srgb, pixel);
2442   linearrgb_to_srgb_v3_v3(pixel, pixel);
2443 }
2444 
IMB_colormanagement_srgb_to_scene_linear_v3(float pixel[3])2445 void IMB_colormanagement_srgb_to_scene_linear_v3(float pixel[3])
2446 {
2447   srgb_to_linearrgb_v3_v3(pixel, pixel);
2448   mul_m3_v3(imbuf_linear_srgb_to_xyz, pixel);
2449   mul_m3_v3(imbuf_xyz_to_rgb, pixel);
2450 }
2451 
2452 /* convert pixel from scene linear to display space using default view
2453  * used by performance-critical areas such as color-related widgets where we want to reduce
2454  * amount of per-widget allocations
2455  */
IMB_colormanagement_scene_linear_to_display_v3(float pixel[3],ColorManagedDisplay * display)2456 void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManagedDisplay *display)
2457 {
2458   OCIO_ConstProcessorRcPtr *processor;
2459 
2460   processor = display_from_scene_linear_processor(display);
2461 
2462   if (processor) {
2463     OCIO_processorApplyRGB(processor, pixel);
2464   }
2465 }
2466 
2467 /* same as above, but converts color in opposite direction */
IMB_colormanagement_display_to_scene_linear_v3(float pixel[3],ColorManagedDisplay * display)2468 void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], ColorManagedDisplay *display)
2469 {
2470   OCIO_ConstProcessorRcPtr *processor;
2471 
2472   processor = display_to_scene_linear_processor(display);
2473 
2474   if (processor) {
2475     OCIO_processorApplyRGB(processor, pixel);
2476   }
2477 }
2478 
IMB_colormanagement_pixel_to_display_space_v4(float result[4],const float pixel[4],const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)2479 void IMB_colormanagement_pixel_to_display_space_v4(
2480     float result[4],
2481     const float pixel[4],
2482     const ColorManagedViewSettings *view_settings,
2483     const ColorManagedDisplaySettings *display_settings)
2484 {
2485   ColormanageProcessor *cm_processor;
2486 
2487   copy_v4_v4(result, pixel);
2488 
2489   cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2490   IMB_colormanagement_processor_apply_v4(cm_processor, result);
2491   IMB_colormanagement_processor_free(cm_processor);
2492 }
2493 
IMB_colormanagement_pixel_to_display_space_v3(float result[3],const float pixel[3],const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)2494 void IMB_colormanagement_pixel_to_display_space_v3(
2495     float result[3],
2496     const float pixel[3],
2497     const ColorManagedViewSettings *view_settings,
2498     const ColorManagedDisplaySettings *display_settings)
2499 {
2500   ColormanageProcessor *cm_processor;
2501 
2502   copy_v3_v3(result, pixel);
2503 
2504   cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2505   IMB_colormanagement_processor_apply_v3(cm_processor, result);
2506   IMB_colormanagement_processor_free(cm_processor);
2507 }
2508 
colormanagement_imbuf_make_display_space(ImBuf * ibuf,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,bool make_byte)2509 static void colormanagement_imbuf_make_display_space(
2510     ImBuf *ibuf,
2511     const ColorManagedViewSettings *view_settings,
2512     const ColorManagedDisplaySettings *display_settings,
2513     bool make_byte)
2514 {
2515   if (!ibuf->rect && make_byte) {
2516     imb_addrectImBuf(ibuf);
2517   }
2518 
2519   colormanage_display_buffer_process_ex(
2520       ibuf, ibuf->rect_float, (unsigned char *)ibuf->rect, view_settings, display_settings);
2521 }
2522 
IMB_colormanagement_imbuf_make_display_space(ImBuf * ibuf,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)2523 void IMB_colormanagement_imbuf_make_display_space(
2524     ImBuf *ibuf,
2525     const ColorManagedViewSettings *view_settings,
2526     const ColorManagedDisplaySettings *display_settings)
2527 {
2528   colormanagement_imbuf_make_display_space(ibuf, view_settings, display_settings, false);
2529 }
2530 
2531 /* prepare image buffer to be saved on disk, applying color management if needed
2532  * color management would be applied if image is saving as render result and if
2533  * file format is not expecting float buffer to be in linear space (currently
2534  * JPEG2000 and TIFF are such formats -- they're storing image as float but
2535  * file itself stores applied color space).
2536  *
2537  * Both byte and float buffers would contain applied color space, and result's
2538  * float_colorspace would be set to display color space. This should be checked
2539  * in image format write callback and if float_colorspace is not NULL, no color
2540  * space transformation should be applied on this buffer.
2541  */
IMB_colormanagement_imbuf_for_write(ImBuf * ibuf,bool save_as_render,bool allocate_result,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,ImageFormatData * image_format_data)2542 ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf,
2543                                            bool save_as_render,
2544                                            bool allocate_result,
2545                                            const ColorManagedViewSettings *view_settings,
2546                                            const ColorManagedDisplaySettings *display_settings,
2547                                            ImageFormatData *image_format_data)
2548 {
2549   ImBuf *colormanaged_ibuf = ibuf;
2550   bool do_colormanagement;
2551   bool is_movie = BKE_imtype_is_movie(image_format_data->imtype);
2552   bool requires_linear_float = BKE_imtype_requires_linear_float(image_format_data->imtype);
2553   bool do_alpha_under = image_format_data->planes != R_IMF_PLANES_RGBA;
2554 
2555   if (ibuf->rect_float && ibuf->rect &&
2556       (ibuf->userflags & (IB_DISPLAY_BUFFER_INVALID | IB_RECT_INVALID)) != 0) {
2557     IMB_rect_from_float(ibuf);
2558     ibuf->userflags &= ~(IB_RECT_INVALID | IB_DISPLAY_BUFFER_INVALID);
2559   }
2560 
2561   do_colormanagement = save_as_render && (is_movie || !requires_linear_float);
2562 
2563   if (do_colormanagement || do_alpha_under) {
2564     if (allocate_result) {
2565       colormanaged_ibuf = IMB_dupImBuf(ibuf);
2566     }
2567     else {
2568       /* Render pipeline is constructing image buffer itself,
2569        * but it's re-using byte and float buffers from render result make copy of this buffers
2570        * here sine this buffers would be transformed to other color space here.
2571        */
2572 
2573       if (ibuf->rect && (ibuf->mall & IB_rect) == 0) {
2574         ibuf->rect = MEM_dupallocN(ibuf->rect);
2575         ibuf->mall |= IB_rect;
2576       }
2577 
2578       if (ibuf->rect_float && (ibuf->mall & IB_rectfloat) == 0) {
2579         ibuf->rect_float = MEM_dupallocN(ibuf->rect_float);
2580         ibuf->mall |= IB_rectfloat;
2581       }
2582     }
2583   }
2584 
2585   /* If we're saving from RGBA to RGB buffer then it's not
2586    * so much useful to just ignore alpha -- it leads to bad
2587    * artifacts especially when saving byte images.
2588    *
2589    * What we do here is we're overlaying our image on top of
2590    * background color (which is currently black).
2591    *
2592    * This is quite much the same as what Gimp does and it
2593    * seems to be what artists expects from saving.
2594    *
2595    * Do a conversion here, so image format writers could
2596    * happily assume all the alpha tricks were made already.
2597    * helps keep things locally here, not spreading it to
2598    * all possible image writers we've got.
2599    */
2600   if (do_alpha_under) {
2601     float color[3] = {0, 0, 0};
2602 
2603     if (colormanaged_ibuf->rect_float && colormanaged_ibuf->channels == 4) {
2604       IMB_alpha_under_color_float(
2605           colormanaged_ibuf->rect_float, colormanaged_ibuf->x, colormanaged_ibuf->y, color);
2606     }
2607 
2608     if (colormanaged_ibuf->rect) {
2609       IMB_alpha_under_color_byte((unsigned char *)colormanaged_ibuf->rect,
2610                                  colormanaged_ibuf->x,
2611                                  colormanaged_ibuf->y,
2612                                  color);
2613     }
2614   }
2615 
2616   if (do_colormanagement) {
2617     bool make_byte = false;
2618     const ImFileType *type;
2619 
2620     /* for proper check whether byte buffer is required by a format or not
2621      * should be pretty safe since this image buffer is supposed to be used for
2622      * saving only and ftype would be overwritten a bit later by BKE_imbuf_write
2623      */
2624     colormanaged_ibuf->ftype = BKE_image_imtype_to_ftype(image_format_data->imtype,
2625                                                          &colormanaged_ibuf->foptions);
2626 
2627     /* if file format isn't able to handle float buffer itself,
2628      * we need to allocate byte buffer and store color managed
2629      * image there
2630      */
2631     for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
2632       if (type->save && type->ftype(type, colormanaged_ibuf)) {
2633         if ((type->flag & IM_FTYPE_FLOAT) == 0) {
2634           make_byte = true;
2635         }
2636 
2637         break;
2638       }
2639     }
2640 
2641     /* perform color space conversions */
2642     colormanagement_imbuf_make_display_space(
2643         colormanaged_ibuf, view_settings, display_settings, make_byte);
2644 
2645     if (colormanaged_ibuf->rect_float) {
2646       /* float buffer isn't linear anymore,
2647        * image format write callback should check for this flag and assume
2648        * no space conversion should happen if ibuf->float_colorspace != NULL
2649        */
2650       colormanaged_ibuf->float_colorspace = display_transform_get_colorspace(view_settings,
2651                                                                              display_settings);
2652     }
2653   }
2654 
2655   if (colormanaged_ibuf != ibuf) {
2656     IMB_metadata_copy(colormanaged_ibuf, ibuf);
2657   }
2658 
2659   return colormanaged_ibuf;
2660 }
2661 
IMB_colormanagement_buffer_make_display_space(float * buffer,unsigned char * display_buffer,int width,int height,int channels,float dither,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)2662 void IMB_colormanagement_buffer_make_display_space(
2663     float *buffer,
2664     unsigned char *display_buffer,
2665     int width,
2666     int height,
2667     int channels,
2668     float dither,
2669     const ColorManagedViewSettings *view_settings,
2670     const ColorManagedDisplaySettings *display_settings)
2671 {
2672   ColormanageProcessor *cm_processor;
2673   size_t float_buffer_size = ((size_t)width) * height * channels * sizeof(float);
2674   float *display_buffer_float = MEM_mallocN(float_buffer_size, "byte_buffer_make_display_space");
2675 
2676   /* TODO(sergey): Convert float directly to byte buffer. */
2677 
2678   memcpy(display_buffer_float, buffer, float_buffer_size);
2679 
2680   cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2681 
2682   processor_transform_apply_threaded(
2683       NULL, display_buffer_float, width, height, channels, cm_processor, true, false);
2684 
2685   IMB_buffer_byte_from_float(display_buffer,
2686                              display_buffer_float,
2687                              channels,
2688                              dither,
2689                              IB_PROFILE_SRGB,
2690                              IB_PROFILE_SRGB,
2691                              true,
2692                              width,
2693                              height,
2694                              width,
2695                              width);
2696 
2697   MEM_freeN(display_buffer_float);
2698   IMB_colormanagement_processor_free(cm_processor);
2699 }
2700 
2701 /** \} */
2702 
2703 /* -------------------------------------------------------------------- */
2704 /** \name Public Display Buffers Interfaces
2705  * \{ */
2706 
2707 /* acquire display buffer for given image buffer using specified view and display settings */
IMB_display_buffer_acquire(ImBuf * ibuf,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,void ** cache_handle)2708 unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf,
2709                                           const ColorManagedViewSettings *view_settings,
2710                                           const ColorManagedDisplaySettings *display_settings,
2711                                           void **cache_handle)
2712 {
2713   unsigned char *display_buffer;
2714   size_t buffer_size;
2715   ColormanageCacheViewSettings cache_view_settings;
2716   ColormanageCacheDisplaySettings cache_display_settings;
2717   ColorManagedViewSettings default_view_settings;
2718   const ColorManagedViewSettings *applied_view_settings;
2719 
2720   *cache_handle = NULL;
2721 
2722   if (!ibuf->x || !ibuf->y) {
2723     return NULL;
2724   }
2725 
2726   if (view_settings) {
2727     applied_view_settings = view_settings;
2728   }
2729   else {
2730     /* If no view settings were specified, use default ones, which will
2731      * attempt not to do any extra color correction. */
2732     IMB_colormanagement_init_default_view_settings(&default_view_settings, display_settings);
2733     applied_view_settings = &default_view_settings;
2734   }
2735 
2736   /* early out: no float buffer and byte buffer is already in display space,
2737    * let's just use if
2738    */
2739   if (ibuf->rect_float == NULL && ibuf->rect_colorspace && ibuf->channels == 4) {
2740     if (is_ibuf_rect_in_display_space(ibuf, applied_view_settings, display_settings)) {
2741       return (unsigned char *)ibuf->rect;
2742     }
2743   }
2744 
2745   colormanage_view_settings_to_cache(ibuf, &cache_view_settings, applied_view_settings);
2746   colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
2747 
2748   if (ibuf->invalid_rect.xmin != ibuf->invalid_rect.xmax) {
2749     if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
2750       IMB_partial_display_buffer_update_threaded(ibuf,
2751                                                  ibuf->rect_float,
2752                                                  (unsigned char *)ibuf->rect,
2753                                                  ibuf->x,
2754                                                  0,
2755                                                  0,
2756                                                  applied_view_settings,
2757                                                  display_settings,
2758                                                  ibuf->invalid_rect.xmin,
2759                                                  ibuf->invalid_rect.ymin,
2760                                                  ibuf->invalid_rect.xmax,
2761                                                  ibuf->invalid_rect.ymax);
2762     }
2763 
2764     BLI_rcti_init(&ibuf->invalid_rect, 0, 0, 0, 0);
2765   }
2766 
2767   BLI_thread_lock(LOCK_COLORMANAGE);
2768 
2769   /* ensure color management bit fields exists */
2770   if (!ibuf->display_buffer_flags) {
2771     ibuf->display_buffer_flags = MEM_callocN(sizeof(unsigned int) * global_tot_display,
2772                                              "imbuf display_buffer_flags");
2773   }
2774   else if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) {
2775     /* all display buffers were marked as invalid from other areas,
2776      * now propagate this flag to internal color management routines
2777      */
2778     memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
2779 
2780     ibuf->userflags &= ~IB_DISPLAY_BUFFER_INVALID;
2781   }
2782 
2783   display_buffer = colormanage_cache_get(
2784       ibuf, &cache_view_settings, &cache_display_settings, cache_handle);
2785 
2786   if (display_buffer) {
2787     BLI_thread_unlock(LOCK_COLORMANAGE);
2788     return display_buffer;
2789   }
2790 
2791   buffer_size = DISPLAY_BUFFER_CHANNELS * ((size_t)ibuf->x) * ibuf->y * sizeof(char);
2792   display_buffer = MEM_callocN(buffer_size, "imbuf display buffer");
2793 
2794   colormanage_display_buffer_process(
2795       ibuf, display_buffer, applied_view_settings, display_settings);
2796 
2797   colormanage_cache_put(
2798       ibuf, &cache_view_settings, &cache_display_settings, display_buffer, cache_handle);
2799 
2800   BLI_thread_unlock(LOCK_COLORMANAGE);
2801 
2802   return display_buffer;
2803 }
2804 
2805 /* same as IMB_display_buffer_acquire but gets view and display settings from context */
IMB_display_buffer_acquire_ctx(const bContext * C,ImBuf * ibuf,void ** cache_handle)2806 unsigned char *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle)
2807 {
2808   ColorManagedViewSettings *view_settings;
2809   ColorManagedDisplaySettings *display_settings;
2810 
2811   IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
2812 
2813   return IMB_display_buffer_acquire(ibuf, view_settings, display_settings, cache_handle);
2814 }
2815 
IMB_display_buffer_transform_apply(unsigned char * display_buffer,float * linear_buffer,int width,int height,int channels,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,bool predivide)2816 void IMB_display_buffer_transform_apply(unsigned char *display_buffer,
2817                                         float *linear_buffer,
2818                                         int width,
2819                                         int height,
2820                                         int channels,
2821                                         const ColorManagedViewSettings *view_settings,
2822                                         const ColorManagedDisplaySettings *display_settings,
2823                                         bool predivide)
2824 {
2825   float *buffer;
2826   ColormanageProcessor *cm_processor = IMB_colormanagement_display_processor_new(view_settings,
2827                                                                                  display_settings);
2828 
2829   buffer = MEM_mallocN((size_t)channels * width * height * sizeof(float),
2830                        "display transform temp buffer");
2831   memcpy(buffer, linear_buffer, (size_t)channels * width * height * sizeof(float));
2832 
2833   IMB_colormanagement_processor_apply(cm_processor, buffer, width, height, channels, predivide);
2834 
2835   IMB_colormanagement_processor_free(cm_processor);
2836 
2837   IMB_buffer_byte_from_float(display_buffer,
2838                              buffer,
2839                              channels,
2840                              0.0f,
2841                              IB_PROFILE_SRGB,
2842                              IB_PROFILE_SRGB,
2843                              false,
2844                              width,
2845                              height,
2846                              width,
2847                              width);
2848 
2849   MEM_freeN(buffer);
2850 }
2851 
IMB_display_buffer_release(void * cache_handle)2852 void IMB_display_buffer_release(void *cache_handle)
2853 {
2854   if (cache_handle) {
2855     BLI_thread_lock(LOCK_COLORMANAGE);
2856 
2857     colormanage_cache_handle_release(cache_handle);
2858 
2859     BLI_thread_unlock(LOCK_COLORMANAGE);
2860   }
2861 }
2862 
2863 /** \} */
2864 
2865 /* -------------------------------------------------------------------- */
2866 /** \name Display Functions
2867  * \{ */
2868 
colormanage_display_get_default_name(void)2869 const char *colormanage_display_get_default_name(void)
2870 {
2871   OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
2872   const char *display_name;
2873 
2874   display_name = OCIO_configGetDefaultDisplay(config);
2875 
2876   OCIO_configRelease(config);
2877 
2878   return display_name;
2879 }
2880 
colormanage_display_get_default(void)2881 ColorManagedDisplay *colormanage_display_get_default(void)
2882 {
2883   const char *display_name = colormanage_display_get_default_name();
2884 
2885   if (display_name[0] == '\0') {
2886     return NULL;
2887   }
2888 
2889   return colormanage_display_get_named(display_name);
2890 }
2891 
colormanage_display_add(const char * name)2892 ColorManagedDisplay *colormanage_display_add(const char *name)
2893 {
2894   ColorManagedDisplay *display;
2895   int index = 0;
2896 
2897   if (global_displays.last) {
2898     ColorManagedDisplay *last_display = global_displays.last;
2899 
2900     index = last_display->index;
2901   }
2902 
2903   display = MEM_callocN(sizeof(ColorManagedDisplay), "ColorManagedDisplay");
2904 
2905   display->index = index + 1;
2906 
2907   BLI_strncpy(display->name, name, sizeof(display->name));
2908 
2909   BLI_addtail(&global_displays, display);
2910 
2911   return display;
2912 }
2913 
colormanage_display_get_named(const char * name)2914 ColorManagedDisplay *colormanage_display_get_named(const char *name)
2915 {
2916   ColorManagedDisplay *display;
2917 
2918   for (display = global_displays.first; display; display = display->next) {
2919     if (STREQ(display->name, name)) {
2920       return display;
2921     }
2922   }
2923 
2924   return NULL;
2925 }
2926 
colormanage_display_get_indexed(int index)2927 ColorManagedDisplay *colormanage_display_get_indexed(int index)
2928 {
2929   /* display indices are 1-based */
2930   return BLI_findlink(&global_displays, index - 1);
2931 }
2932 
IMB_colormanagement_display_get_named_index(const char * name)2933 int IMB_colormanagement_display_get_named_index(const char *name)
2934 {
2935   ColorManagedDisplay *display;
2936 
2937   display = colormanage_display_get_named(name);
2938 
2939   if (display) {
2940     return display->index;
2941   }
2942 
2943   return 0;
2944 }
2945 
IMB_colormanagement_display_get_indexed_name(int index)2946 const char *IMB_colormanagement_display_get_indexed_name(int index)
2947 {
2948   ColorManagedDisplay *display;
2949 
2950   display = colormanage_display_get_indexed(index);
2951 
2952   if (display) {
2953     return display->name;
2954   }
2955 
2956   return NULL;
2957 }
2958 
IMB_colormanagement_display_get_default_name(void)2959 const char *IMB_colormanagement_display_get_default_name(void)
2960 {
2961   ColorManagedDisplay *display = colormanage_display_get_default();
2962 
2963   return display->name;
2964 }
2965 
2966 /* used by performance-critical pixel processing areas, such as color widgets */
IMB_colormanagement_display_get_named(const char * name)2967 ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name)
2968 {
2969   return colormanage_display_get_named(name);
2970 }
2971 
IMB_colormanagement_display_get_none_name(void)2972 const char *IMB_colormanagement_display_get_none_name(void)
2973 {
2974   if (colormanage_display_get_named("None") != NULL) {
2975     return "None";
2976   }
2977 
2978   return colormanage_display_get_default_name();
2979 }
2980 
IMB_colormanagement_display_get_default_view_transform_name(struct ColorManagedDisplay * display)2981 const char *IMB_colormanagement_display_get_default_view_transform_name(
2982     struct ColorManagedDisplay *display)
2983 {
2984   return colormanage_view_get_default_name(display);
2985 }
2986 
2987 /** \} */
2988 
2989 /* -------------------------------------------------------------------- */
2990 /** \name View Functions
2991  * \{ */
2992 
colormanage_view_get_default_name(const ColorManagedDisplay * display)2993 const char *colormanage_view_get_default_name(const ColorManagedDisplay *display)
2994 {
2995   OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
2996   const char *name;
2997 
2998   name = OCIO_configGetDefaultView(config, display->name);
2999 
3000   OCIO_configRelease(config);
3001 
3002   return name;
3003 }
3004 
colormanage_view_get_default(const ColorManagedDisplay * display)3005 ColorManagedView *colormanage_view_get_default(const ColorManagedDisplay *display)
3006 {
3007   const char *name = colormanage_view_get_default_name(display);
3008 
3009   if (!name || name[0] == '\0') {
3010     return NULL;
3011   }
3012 
3013   return colormanage_view_get_named(name);
3014 }
3015 
colormanage_view_add(const char * name)3016 ColorManagedView *colormanage_view_add(const char *name)
3017 {
3018   ColorManagedView *view;
3019   int index = global_tot_view;
3020 
3021   view = MEM_callocN(sizeof(ColorManagedView), "ColorManagedView");
3022   view->index = index + 1;
3023   BLI_strncpy(view->name, name, sizeof(view->name));
3024 
3025   BLI_addtail(&global_views, view);
3026 
3027   global_tot_view++;
3028 
3029   return view;
3030 }
3031 
colormanage_view_get_named(const char * name)3032 ColorManagedView *colormanage_view_get_named(const char *name)
3033 {
3034   ColorManagedView *view;
3035 
3036   for (view = global_views.first; view; view = view->next) {
3037     if (STREQ(view->name, name)) {
3038       return view;
3039     }
3040   }
3041 
3042   return NULL;
3043 }
3044 
colormanage_view_get_indexed(int index)3045 ColorManagedView *colormanage_view_get_indexed(int index)
3046 {
3047   /* view transform indices are 1-based */
3048   return BLI_findlink(&global_views, index - 1);
3049 }
3050 
colormanage_view_get_named_for_display(const char * display_name,const char * name)3051 ColorManagedView *colormanage_view_get_named_for_display(const char *display_name,
3052                                                          const char *name)
3053 {
3054   ColorManagedDisplay *display = colormanage_display_get_named(display_name);
3055   if (display == NULL) {
3056     return NULL;
3057   }
3058   LISTBASE_FOREACH (LinkData *, view_link, &display->views) {
3059     ColorManagedView *view = view_link->data;
3060     if (STRCASEEQ(name, view->name)) {
3061       return view;
3062     }
3063   }
3064   return NULL;
3065 }
3066 
IMB_colormanagement_view_get_named_index(const char * name)3067 int IMB_colormanagement_view_get_named_index(const char *name)
3068 {
3069   ColorManagedView *view = colormanage_view_get_named(name);
3070 
3071   if (view) {
3072     return view->index;
3073   }
3074 
3075   return 0;
3076 }
3077 
IMB_colormanagement_view_get_indexed_name(int index)3078 const char *IMB_colormanagement_view_get_indexed_name(int index)
3079 {
3080   ColorManagedView *view = colormanage_view_get_indexed(index);
3081 
3082   if (view) {
3083     return view->name;
3084   }
3085 
3086   return NULL;
3087 }
3088 
IMB_colormanagement_view_get_default_name(const char * display_name)3089 const char *IMB_colormanagement_view_get_default_name(const char *display_name)
3090 {
3091   ColorManagedDisplay *display = colormanage_display_get_named(display_name);
3092   ColorManagedView *view = NULL;
3093 
3094   if (display) {
3095     view = colormanage_view_get_default(display);
3096   }
3097 
3098   if (view) {
3099     return view->name;
3100   }
3101 
3102   return NULL;
3103 }
3104 
3105 /** \} */
3106 
3107 /* -------------------------------------------------------------------- */
3108 /** \name Color Space Functions
3109  * \{ */
3110 
colormanage_description_strip(char * description)3111 static void colormanage_description_strip(char *description)
3112 {
3113   int i, n;
3114 
3115   for (i = (int)strlen(description) - 1; i >= 0; i--) {
3116     if (ELEM(description[i], '\r', '\n')) {
3117       description[i] = '\0';
3118     }
3119     else {
3120       break;
3121     }
3122   }
3123 
3124   for (i = 0, n = strlen(description); i < n; i++) {
3125     if (ELEM(description[i], '\r', '\n')) {
3126       description[i] = ' ';
3127     }
3128   }
3129 }
3130 
colormanage_colorspace_add(const char * name,const char * description,bool is_invertible,bool is_data)3131 ColorSpace *colormanage_colorspace_add(const char *name,
3132                                        const char *description,
3133                                        bool is_invertible,
3134                                        bool is_data)
3135 {
3136   ColorSpace *colorspace, *prev_space;
3137   int counter = 1;
3138 
3139   colorspace = MEM_callocN(sizeof(ColorSpace), "ColorSpace");
3140 
3141   BLI_strncpy(colorspace->name, name, sizeof(colorspace->name));
3142 
3143   if (description) {
3144     BLI_strncpy(colorspace->description, description, sizeof(colorspace->description));
3145 
3146     colormanage_description_strip(colorspace->description);
3147   }
3148 
3149   colorspace->is_invertible = is_invertible;
3150   colorspace->is_data = is_data;
3151 
3152   for (prev_space = global_colorspaces.first; prev_space; prev_space = prev_space->next) {
3153     if (BLI_strcasecmp(prev_space->name, colorspace->name) > 0) {
3154       break;
3155     }
3156 
3157     prev_space->index = counter++;
3158   }
3159 
3160   if (!prev_space) {
3161     BLI_addtail(&global_colorspaces, colorspace);
3162   }
3163   else {
3164     BLI_insertlinkbefore(&global_colorspaces, prev_space, colorspace);
3165   }
3166 
3167   colorspace->index = counter++;
3168   for (; prev_space; prev_space = prev_space->next) {
3169     prev_space->index = counter++;
3170   }
3171 
3172   global_tot_colorspace++;
3173 
3174   return colorspace;
3175 }
3176 
colormanage_colorspace_get_named(const char * name)3177 ColorSpace *colormanage_colorspace_get_named(const char *name)
3178 {
3179   ColorSpace *colorspace;
3180 
3181   for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) {
3182     if (STREQ(colorspace->name, name)) {
3183       return colorspace;
3184     }
3185   }
3186 
3187   return NULL;
3188 }
3189 
colormanage_colorspace_get_roled(int role)3190 ColorSpace *colormanage_colorspace_get_roled(int role)
3191 {
3192   const char *role_colorspace = IMB_colormanagement_role_colorspace_name_get(role);
3193 
3194   return colormanage_colorspace_get_named(role_colorspace);
3195 }
3196 
colormanage_colorspace_get_indexed(int index)3197 ColorSpace *colormanage_colorspace_get_indexed(int index)
3198 {
3199   /* color space indices are 1-based */
3200   return BLI_findlink(&global_colorspaces, index - 1);
3201 }
3202 
IMB_colormanagement_colorspace_get_named_index(const char * name)3203 int IMB_colormanagement_colorspace_get_named_index(const char *name)
3204 {
3205   ColorSpace *colorspace;
3206 
3207   colorspace = colormanage_colorspace_get_named(name);
3208 
3209   if (colorspace) {
3210     return colorspace->index;
3211   }
3212 
3213   return 0;
3214 }
3215 
IMB_colormanagement_colorspace_get_indexed_name(int index)3216 const char *IMB_colormanagement_colorspace_get_indexed_name(int index)
3217 {
3218   ColorSpace *colorspace;
3219 
3220   colorspace = colormanage_colorspace_get_indexed(index);
3221 
3222   if (colorspace) {
3223     return colorspace->name;
3224   }
3225 
3226   return "";
3227 }
3228 
IMB_colormanagement_colorspace_from_ibuf_ftype(ColorManagedColorspaceSettings * colorspace_settings,ImBuf * ibuf)3229 void IMB_colormanagement_colorspace_from_ibuf_ftype(
3230     ColorManagedColorspaceSettings *colorspace_settings, ImBuf *ibuf)
3231 {
3232   /* Don't modify non-color data space, it does not change with file type. */
3233   ColorSpace *colorspace = colormanage_colorspace_get_named(colorspace_settings->name);
3234 
3235   if (colorspace && colorspace->is_data) {
3236     return;
3237   }
3238 
3239   /* Get color space from file type. */
3240   const ImFileType *type;
3241 
3242   for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
3243     if (type->save && type->ftype(type, ibuf)) {
3244       const char *role_colorspace;
3245 
3246       role_colorspace = IMB_colormanagement_role_colorspace_name_get(type->default_save_role);
3247 
3248       BLI_strncpy(colorspace_settings->name, role_colorspace, sizeof(colorspace_settings->name));
3249     }
3250   }
3251 }
3252 
3253 /** \} */
3254 
3255 /* -------------------------------------------------------------------- */
3256 /** \name Looks Functions
3257  * \{ */
3258 
colormanage_look_add(const char * name,const char * process_space,bool is_noop)3259 ColorManagedLook *colormanage_look_add(const char *name, const char *process_space, bool is_noop)
3260 {
3261   ColorManagedLook *look;
3262   int index = global_tot_looks;
3263 
3264   look = MEM_callocN(sizeof(ColorManagedLook), "ColorManagedLook");
3265   look->index = index + 1;
3266   BLI_strncpy(look->name, name, sizeof(look->name));
3267   BLI_strncpy(look->ui_name, name, sizeof(look->ui_name));
3268   BLI_strncpy(look->process_space, process_space, sizeof(look->process_space));
3269   look->is_noop = is_noop;
3270 
3271   /* Detect view specific looks. */
3272   const char *separator_offset = strstr(look->name, " - ");
3273   if (separator_offset) {
3274     BLI_strncpy(look->view, look->name, separator_offset - look->name + 1);
3275     BLI_strncpy(look->ui_name, separator_offset + strlen(" - "), sizeof(look->ui_name));
3276   }
3277 
3278   BLI_addtail(&global_looks, look);
3279 
3280   global_tot_looks++;
3281 
3282   return look;
3283 }
3284 
colormanage_look_get_named(const char * name)3285 ColorManagedLook *colormanage_look_get_named(const char *name)
3286 {
3287   ColorManagedLook *look;
3288 
3289   for (look = global_looks.first; look; look = look->next) {
3290     if (STREQ(look->name, name)) {
3291       return look;
3292     }
3293   }
3294 
3295   return NULL;
3296 }
3297 
colormanage_look_get_indexed(int index)3298 ColorManagedLook *colormanage_look_get_indexed(int index)
3299 {
3300   /* look indices are 1-based */
3301   return BLI_findlink(&global_looks, index - 1);
3302 }
3303 
IMB_colormanagement_look_get_named_index(const char * name)3304 int IMB_colormanagement_look_get_named_index(const char *name)
3305 {
3306   ColorManagedLook *look;
3307 
3308   look = colormanage_look_get_named(name);
3309 
3310   if (look) {
3311     return look->index;
3312   }
3313 
3314   return 0;
3315 }
3316 
IMB_colormanagement_look_get_indexed_name(int index)3317 const char *IMB_colormanagement_look_get_indexed_name(int index)
3318 {
3319   ColorManagedLook *look;
3320 
3321   look = colormanage_look_get_indexed(index);
3322 
3323   if (look) {
3324     return look->name;
3325   }
3326 
3327   return NULL;
3328 }
3329 
3330 /** \} */
3331 
3332 /* -------------------------------------------------------------------- */
3333 /** \name RNA Helper Functions
3334  * \{ */
3335 
IMB_colormanagement_display_items_add(EnumPropertyItem ** items,int * totitem)3336 void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totitem)
3337 {
3338   ColorManagedDisplay *display;
3339 
3340   for (display = global_displays.first; display; display = display->next) {
3341     EnumPropertyItem item;
3342 
3343     item.value = display->index;
3344     item.name = display->name;
3345     item.identifier = display->name;
3346     item.icon = 0;
3347     item.description = "";
3348 
3349     RNA_enum_item_add(items, totitem, &item);
3350   }
3351 }
3352 
colormanagement_view_item_add(EnumPropertyItem ** items,int * totitem,ColorManagedView * view)3353 static void colormanagement_view_item_add(EnumPropertyItem **items,
3354                                           int *totitem,
3355                                           ColorManagedView *view)
3356 {
3357   EnumPropertyItem item;
3358 
3359   item.value = view->index;
3360   item.name = view->name;
3361   item.identifier = view->name;
3362   item.icon = 0;
3363   item.description = "";
3364 
3365   RNA_enum_item_add(items, totitem, &item);
3366 }
3367 
IMB_colormanagement_view_items_add(EnumPropertyItem ** items,int * totitem,const char * display_name)3368 void IMB_colormanagement_view_items_add(EnumPropertyItem **items,
3369                                         int *totitem,
3370                                         const char *display_name)
3371 {
3372   ColorManagedDisplay *display = colormanage_display_get_named(display_name);
3373   ColorManagedView *view;
3374 
3375   if (display) {
3376     LinkData *display_view;
3377 
3378     for (display_view = display->views.first; display_view; display_view = display_view->next) {
3379       view = display_view->data;
3380 
3381       colormanagement_view_item_add(items, totitem, view);
3382     }
3383   }
3384 }
3385 
IMB_colormanagement_look_items_add(struct EnumPropertyItem ** items,int * totitem,const char * view_name)3386 void IMB_colormanagement_look_items_add(struct EnumPropertyItem **items,
3387                                         int *totitem,
3388                                         const char *view_name)
3389 {
3390   ColorManagedLook *look;
3391 
3392   for (look = global_looks.first; look; look = look->next) {
3393     if (!colormanage_compatible_look(look, view_name)) {
3394       continue;
3395     }
3396 
3397     EnumPropertyItem item;
3398 
3399     item.value = look->index;
3400     item.name = look->ui_name;
3401     item.identifier = look->name;
3402     item.icon = 0;
3403     item.description = "";
3404 
3405     RNA_enum_item_add(items, totitem, &item);
3406   }
3407 }
3408 
IMB_colormanagement_colorspace_items_add(EnumPropertyItem ** items,int * totitem)3409 void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *totitem)
3410 {
3411   ColorSpace *colorspace;
3412 
3413   for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) {
3414     EnumPropertyItem item;
3415 
3416     if (!colorspace->is_invertible) {
3417       continue;
3418     }
3419 
3420     item.value = colorspace->index;
3421     item.name = colorspace->name;
3422     item.identifier = colorspace->name;
3423     item.icon = 0;
3424     item.description = colorspace->description;
3425 
3426     RNA_enum_item_add(items, totitem, &item);
3427   }
3428 }
3429 
3430 /** \} */
3431 
3432 /* -------------------------------------------------------------------- */
3433 /** \name Partial Display Buffer Update
3434  * \{ */
3435 
3436 /*
3437  * Partial display update is supposed to be used by such areas as
3438  * compositor and renderer, This areas are calculating tiles of the
3439  * images and because of performance reasons only this tiles should
3440  * be color managed.
3441  * This gives nice visual feedback without slowing things down.
3442  *
3443  * Updating happens for active display transformation only, all
3444  * the rest buffers would be marked as dirty
3445  */
3446 
partial_buffer_update_rect(ImBuf * ibuf,unsigned char * display_buffer,const float * linear_buffer,const unsigned char * byte_buffer,int display_stride,int linear_stride,int linear_offset_x,int linear_offset_y,ColormanageProcessor * cm_processor,const int xmin,const int ymin,const int xmax,const int ymax)3447 static void partial_buffer_update_rect(ImBuf *ibuf,
3448                                        unsigned char *display_buffer,
3449                                        const float *linear_buffer,
3450                                        const unsigned char *byte_buffer,
3451                                        int display_stride,
3452                                        int linear_stride,
3453                                        int linear_offset_x,
3454                                        int linear_offset_y,
3455                                        ColormanageProcessor *cm_processor,
3456                                        const int xmin,
3457                                        const int ymin,
3458                                        const int xmax,
3459                                        const int ymax)
3460 {
3461   int x, y;
3462   int channels = ibuf->channels;
3463   float dither = ibuf->dither;
3464   ColorSpace *rect_colorspace = ibuf->rect_colorspace;
3465   float *display_buffer_float = NULL;
3466   const int width = xmax - xmin;
3467   const int height = ymax - ymin;
3468   bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
3469 
3470   if (dither != 0.0f) {
3471     /* cm_processor is NULL in cases byte_buffer's space matches display
3472      * buffer's space
3473      * in this case we could skip extra transform and only apply dither
3474      * use 4 channels for easier byte->float->byte conversion here so
3475      * (this is only needed to apply dither, in other cases we'll convert
3476      * byte buffer to display directly)
3477      */
3478     if (!cm_processor) {
3479       channels = 4;
3480     }
3481 
3482     display_buffer_float = MEM_callocN((size_t)channels * width * height * sizeof(float),
3483                                        "display buffer for dither");
3484   }
3485 
3486   if (cm_processor) {
3487     for (y = ymin; y < ymax; y++) {
3488       for (x = xmin; x < xmax; x++) {
3489         size_t display_index = ((size_t)y * display_stride + x) * 4;
3490         size_t linear_index = ((size_t)(y - linear_offset_y) * linear_stride +
3491                                (x - linear_offset_x)) *
3492                               channels;
3493         float pixel[4];
3494 
3495         if (linear_buffer) {
3496           if (channels == 4) {
3497             copy_v4_v4(pixel, (float *)linear_buffer + linear_index);
3498           }
3499           else if (channels == 3) {
3500             copy_v3_v3(pixel, (float *)linear_buffer + linear_index);
3501             pixel[3] = 1.0f;
3502           }
3503           else if (channels == 1) {
3504             pixel[0] = linear_buffer[linear_index];
3505           }
3506           else {
3507             BLI_assert(!"Unsupported number of channels in partial buffer update");
3508           }
3509         }
3510         else if (byte_buffer) {
3511           rgba_uchar_to_float(pixel, byte_buffer + linear_index);
3512           IMB_colormanagement_colorspace_to_scene_linear_v3(pixel, rect_colorspace);
3513           straight_to_premul_v4(pixel);
3514         }
3515 
3516         if (!is_data) {
3517           IMB_colormanagement_processor_apply_pixel(cm_processor, pixel, channels);
3518         }
3519 
3520         if (display_buffer_float) {
3521           size_t index = ((size_t)(y - ymin) * width + (x - xmin)) * channels;
3522 
3523           if (channels == 4) {
3524             copy_v4_v4(display_buffer_float + index, pixel);
3525           }
3526           else if (channels == 3) {
3527             copy_v3_v3(display_buffer_float + index, pixel);
3528           }
3529           else /* if (channels == 1) */ {
3530             display_buffer_float[index] = pixel[0];
3531           }
3532         }
3533         else {
3534           if (channels == 4) {
3535             float pixel_straight[4];
3536             premul_to_straight_v4_v4(pixel_straight, pixel);
3537             rgba_float_to_uchar(display_buffer + display_index, pixel_straight);
3538           }
3539           else if (channels == 3) {
3540             rgb_float_to_uchar(display_buffer + display_index, pixel);
3541             display_buffer[display_index + 3] = 255;
3542           }
3543           else /* if (channels == 1) */ {
3544             display_buffer[display_index] = display_buffer[display_index + 1] =
3545                 display_buffer[display_index + 2] = display_buffer[display_index + 3] =
3546                     unit_float_to_uchar_clamp(pixel[0]);
3547           }
3548         }
3549       }
3550     }
3551   }
3552   else {
3553     if (display_buffer_float) {
3554       /* huh, for dither we need float buffer first, no cheaper way. currently */
3555       IMB_buffer_float_from_byte(display_buffer_float,
3556                                  byte_buffer,
3557                                  IB_PROFILE_SRGB,
3558                                  IB_PROFILE_SRGB,
3559                                  true,
3560                                  width,
3561                                  height,
3562                                  width,
3563                                  display_stride);
3564     }
3565     else {
3566       int i;
3567 
3568       for (i = ymin; i < ymax; i++) {
3569         size_t byte_offset = ((size_t)linear_stride * i + xmin) * 4;
3570         size_t display_offset = ((size_t)display_stride * i + xmin) * 4;
3571 
3572         memcpy(
3573             display_buffer + display_offset, byte_buffer + byte_offset, sizeof(char[4]) * width);
3574       }
3575     }
3576   }
3577 
3578   if (display_buffer_float) {
3579     size_t display_index = ((size_t)ymin * display_stride + xmin) * channels;
3580 
3581     IMB_buffer_byte_from_float(display_buffer + display_index,
3582                                display_buffer_float,
3583                                channels,
3584                                dither,
3585                                IB_PROFILE_SRGB,
3586                                IB_PROFILE_SRGB,
3587                                true,
3588                                width,
3589                                height,
3590                                display_stride,
3591                                width);
3592 
3593     MEM_freeN(display_buffer_float);
3594   }
3595 }
3596 
3597 typedef struct PartialThreadData {
3598   ImBuf *ibuf;
3599   unsigned char *display_buffer;
3600   const float *linear_buffer;
3601   const unsigned char *byte_buffer;
3602   int display_stride;
3603   int linear_stride;
3604   int linear_offset_x, linear_offset_y;
3605   ColormanageProcessor *cm_processor;
3606   int xmin, ymin, xmax;
3607 } PartialThreadData;
3608 
partial_buffer_update_rect_thread_do(void * data_v,int start_scanline,int num_scanlines)3609 static void partial_buffer_update_rect_thread_do(void *data_v,
3610                                                  int start_scanline,
3611                                                  int num_scanlines)
3612 {
3613   PartialThreadData *data = (PartialThreadData *)data_v;
3614   int ymin = data->ymin + start_scanline;
3615   partial_buffer_update_rect(data->ibuf,
3616                              data->display_buffer,
3617                              data->linear_buffer,
3618                              data->byte_buffer,
3619                              data->display_stride,
3620                              data->linear_stride,
3621                              data->linear_offset_x,
3622                              data->linear_offset_y,
3623                              data->cm_processor,
3624                              data->xmin,
3625                              ymin,
3626                              data->xmax,
3627                              ymin + num_scanlines);
3628 }
3629 
imb_partial_display_buffer_update_ex(ImBuf * ibuf,const float * linear_buffer,const unsigned char * byte_buffer,int stride,int offset_x,int offset_y,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,int xmin,int ymin,int xmax,int ymax,bool do_threads)3630 static void imb_partial_display_buffer_update_ex(
3631     ImBuf *ibuf,
3632     const float *linear_buffer,
3633     const unsigned char *byte_buffer,
3634     int stride,
3635     int offset_x,
3636     int offset_y,
3637     const ColorManagedViewSettings *view_settings,
3638     const ColorManagedDisplaySettings *display_settings,
3639     int xmin,
3640     int ymin,
3641     int xmax,
3642     int ymax,
3643     bool do_threads)
3644 {
3645   ColormanageCacheViewSettings cache_view_settings;
3646   ColormanageCacheDisplaySettings cache_display_settings;
3647   void *cache_handle = NULL;
3648   unsigned char *display_buffer = NULL;
3649   int buffer_width = ibuf->x;
3650 
3651   if (ibuf->display_buffer_flags) {
3652     int view_flag, display_index;
3653 
3654     colormanage_view_settings_to_cache(ibuf, &cache_view_settings, view_settings);
3655     colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
3656 
3657     view_flag = 1 << (cache_view_settings.view - 1);
3658     display_index = cache_display_settings.display - 1;
3659 
3660     BLI_thread_lock(LOCK_COLORMANAGE);
3661 
3662     if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
3663       display_buffer = colormanage_cache_get(
3664           ibuf, &cache_view_settings, &cache_display_settings, &cache_handle);
3665     }
3666 
3667     /* In some rare cases buffer's dimension could be changing directly from
3668      * different thread
3669      * this i.e. happens when image editor acquires render result
3670      */
3671     buffer_width = ibuf->x;
3672 
3673     /* Mark all other buffers as invalid. */
3674     memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
3675     ibuf->display_buffer_flags[display_index] |= view_flag;
3676 
3677     BLI_thread_unlock(LOCK_COLORMANAGE);
3678   }
3679 
3680   if (display_buffer) {
3681     ColormanageProcessor *cm_processor = NULL;
3682     bool skip_transform = false;
3683 
3684     /* Byte buffer is assumed to be in imbuf's rect space, so if byte buffer
3685      * is known we could skip display->linear->display conversion in case
3686      * display color space matches imbuf's rect space.
3687      *
3688      * But if there's a float buffer it's likely operation was performed on
3689      * it first and byte buffer is likely to be out of date here.
3690      */
3691     if (linear_buffer == NULL && byte_buffer != NULL) {
3692       skip_transform = is_ibuf_rect_in_display_space(ibuf, view_settings, display_settings);
3693     }
3694 
3695     if (!skip_transform) {
3696       cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
3697     }
3698 
3699     if (do_threads) {
3700       PartialThreadData data;
3701       data.ibuf = ibuf;
3702       data.display_buffer = display_buffer;
3703       data.linear_buffer = linear_buffer;
3704       data.byte_buffer = byte_buffer;
3705       data.display_stride = buffer_width;
3706       data.linear_stride = stride;
3707       data.linear_offset_x = offset_x;
3708       data.linear_offset_y = offset_y;
3709       data.cm_processor = cm_processor;
3710       data.xmin = xmin;
3711       data.ymin = ymin;
3712       data.xmax = xmax;
3713       IMB_processor_apply_threaded_scanlines(
3714           ymax - ymin, partial_buffer_update_rect_thread_do, &data);
3715     }
3716     else {
3717       partial_buffer_update_rect(ibuf,
3718                                  display_buffer,
3719                                  linear_buffer,
3720                                  byte_buffer,
3721                                  buffer_width,
3722                                  stride,
3723                                  offset_x,
3724                                  offset_y,
3725                                  cm_processor,
3726                                  xmin,
3727                                  ymin,
3728                                  xmax,
3729                                  ymax);
3730     }
3731 
3732     if (cm_processor) {
3733       IMB_colormanagement_processor_free(cm_processor);
3734     }
3735 
3736     IMB_display_buffer_release(cache_handle);
3737   }
3738 }
3739 
IMB_partial_display_buffer_update(ImBuf * ibuf,const float * linear_buffer,const unsigned char * byte_buffer,int stride,int offset_x,int offset_y,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,int xmin,int ymin,int xmax,int ymax)3740 void IMB_partial_display_buffer_update(ImBuf *ibuf,
3741                                        const float *linear_buffer,
3742                                        const unsigned char *byte_buffer,
3743                                        int stride,
3744                                        int offset_x,
3745                                        int offset_y,
3746                                        const ColorManagedViewSettings *view_settings,
3747                                        const ColorManagedDisplaySettings *display_settings,
3748                                        int xmin,
3749                                        int ymin,
3750                                        int xmax,
3751                                        int ymax)
3752 {
3753   imb_partial_display_buffer_update_ex(ibuf,
3754                                        linear_buffer,
3755                                        byte_buffer,
3756                                        stride,
3757                                        offset_x,
3758                                        offset_y,
3759                                        view_settings,
3760                                        display_settings,
3761                                        xmin,
3762                                        ymin,
3763                                        xmax,
3764                                        ymax,
3765                                        false);
3766 }
3767 
IMB_partial_display_buffer_update_threaded(struct ImBuf * ibuf,const float * linear_buffer,const unsigned char * byte_buffer,int stride,int offset_x,int offset_y,const struct ColorManagedViewSettings * view_settings,const struct ColorManagedDisplaySettings * display_settings,int xmin,int ymin,int xmax,int ymax)3768 void IMB_partial_display_buffer_update_threaded(
3769     struct ImBuf *ibuf,
3770     const float *linear_buffer,
3771     const unsigned char *byte_buffer,
3772     int stride,
3773     int offset_x,
3774     int offset_y,
3775     const struct ColorManagedViewSettings *view_settings,
3776     const struct ColorManagedDisplaySettings *display_settings,
3777     int xmin,
3778     int ymin,
3779     int xmax,
3780     int ymax)
3781 {
3782   int width = xmax - xmin;
3783   int height = ymax - ymin;
3784   bool do_threads = (((size_t)width) * height >= 64 * 64);
3785   imb_partial_display_buffer_update_ex(ibuf,
3786                                        linear_buffer,
3787                                        byte_buffer,
3788                                        stride,
3789                                        offset_x,
3790                                        offset_y,
3791                                        view_settings,
3792                                        display_settings,
3793                                        xmin,
3794                                        ymin,
3795                                        xmax,
3796                                        ymax,
3797                                        do_threads);
3798 }
3799 
IMB_partial_display_buffer_update_delayed(ImBuf * ibuf,int xmin,int ymin,int xmax,int ymax)3800 void IMB_partial_display_buffer_update_delayed(ImBuf *ibuf, int xmin, int ymin, int xmax, int ymax)
3801 {
3802   if (ibuf->invalid_rect.xmin == ibuf->invalid_rect.xmax) {
3803     BLI_rcti_init(&ibuf->invalid_rect, xmin, xmax, ymin, ymax);
3804   }
3805   else {
3806     rcti rect;
3807     BLI_rcti_init(&rect, xmin, xmax, ymin, ymax);
3808     BLI_rcti_union(&ibuf->invalid_rect, &rect);
3809   }
3810 }
3811 
3812 /** \} */
3813 
3814 /* -------------------------------------------------------------------- */
3815 /** \name Pixel Processor Functions
3816  * \{ */
3817 
IMB_colormanagement_display_processor_new(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)3818 ColormanageProcessor *IMB_colormanagement_display_processor_new(
3819     const ColorManagedViewSettings *view_settings,
3820     const ColorManagedDisplaySettings *display_settings)
3821 {
3822   ColormanageProcessor *cm_processor;
3823   ColorManagedViewSettings default_view_settings;
3824   const ColorManagedViewSettings *applied_view_settings;
3825   ColorSpace *display_space;
3826 
3827   cm_processor = MEM_callocN(sizeof(ColormanageProcessor), "colormanagement processor");
3828 
3829   if (view_settings) {
3830     applied_view_settings = view_settings;
3831   }
3832   else {
3833     IMB_colormanagement_init_default_view_settings(&default_view_settings, display_settings);
3834     applied_view_settings = &default_view_settings;
3835   }
3836 
3837   display_space = display_transform_get_colorspace(applied_view_settings, display_settings);
3838   if (display_space) {
3839     cm_processor->is_data_result = display_space->is_data;
3840   }
3841 
3842   cm_processor->processor = create_display_buffer_processor(applied_view_settings->look,
3843                                                             applied_view_settings->view_transform,
3844                                                             display_settings->display_device,
3845                                                             applied_view_settings->exposure,
3846                                                             applied_view_settings->gamma,
3847                                                             global_role_scene_linear,
3848                                                             false);
3849 
3850   if (applied_view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) {
3851     cm_processor->curve_mapping = BKE_curvemapping_copy(applied_view_settings->curve_mapping);
3852     BKE_curvemapping_premultiply(cm_processor->curve_mapping, false);
3853   }
3854 
3855   return cm_processor;
3856 }
3857 
IMB_colormanagement_colorspace_processor_new(const char * from_colorspace,const char * to_colorspace)3858 ColormanageProcessor *IMB_colormanagement_colorspace_processor_new(const char *from_colorspace,
3859                                                                    const char *to_colorspace)
3860 {
3861   ColormanageProcessor *cm_processor;
3862   ColorSpace *color_space;
3863 
3864   cm_processor = MEM_callocN(sizeof(ColormanageProcessor), "colormanagement processor");
3865 
3866   color_space = colormanage_colorspace_get_named(to_colorspace);
3867   cm_processor->is_data_result = color_space->is_data;
3868 
3869   cm_processor->processor = create_colorspace_transform_processor(from_colorspace, to_colorspace);
3870 
3871   return cm_processor;
3872 }
3873 
IMB_colormanagement_processor_apply_v4(ColormanageProcessor * cm_processor,float pixel[4])3874 void IMB_colormanagement_processor_apply_v4(ColormanageProcessor *cm_processor, float pixel[4])
3875 {
3876   if (cm_processor->curve_mapping) {
3877     BKE_curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
3878   }
3879 
3880   if (cm_processor->processor) {
3881     OCIO_processorApplyRGBA(cm_processor->processor, pixel);
3882   }
3883 }
3884 
IMB_colormanagement_processor_apply_v4_predivide(ColormanageProcessor * cm_processor,float pixel[4])3885 void IMB_colormanagement_processor_apply_v4_predivide(ColormanageProcessor *cm_processor,
3886                                                       float pixel[4])
3887 {
3888   if (cm_processor->curve_mapping) {
3889     BKE_curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
3890   }
3891 
3892   if (cm_processor->processor) {
3893     OCIO_processorApplyRGBA_predivide(cm_processor->processor, pixel);
3894   }
3895 }
3896 
IMB_colormanagement_processor_apply_v3(ColormanageProcessor * cm_processor,float pixel[3])3897 void IMB_colormanagement_processor_apply_v3(ColormanageProcessor *cm_processor, float pixel[3])
3898 {
3899   if (cm_processor->curve_mapping) {
3900     BKE_curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
3901   }
3902 
3903   if (cm_processor->processor) {
3904     OCIO_processorApplyRGB(cm_processor->processor, pixel);
3905   }
3906 }
3907 
IMB_colormanagement_processor_apply_pixel(struct ColormanageProcessor * cm_processor,float * pixel,int channels)3908 void IMB_colormanagement_processor_apply_pixel(struct ColormanageProcessor *cm_processor,
3909                                                float *pixel,
3910                                                int channels)
3911 {
3912   if (channels == 4) {
3913     IMB_colormanagement_processor_apply_v4_predivide(cm_processor, pixel);
3914   }
3915   else if (channels == 3) {
3916     IMB_colormanagement_processor_apply_v3(cm_processor, pixel);
3917   }
3918   else if (channels == 1) {
3919     if (cm_processor->curve_mapping) {
3920       curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, 1);
3921     }
3922   }
3923   else {
3924     BLI_assert(
3925         !"Incorrect number of channels passed to IMB_colormanagement_processor_apply_pixel");
3926   }
3927 }
3928 
IMB_colormanagement_processor_apply(ColormanageProcessor * cm_processor,float * buffer,int width,int height,int channels,bool predivide)3929 void IMB_colormanagement_processor_apply(ColormanageProcessor *cm_processor,
3930                                          float *buffer,
3931                                          int width,
3932                                          int height,
3933                                          int channels,
3934                                          bool predivide)
3935 {
3936   /* apply curve mapping */
3937   if (cm_processor->curve_mapping) {
3938     int x, y;
3939 
3940     for (y = 0; y < height; y++) {
3941       for (x = 0; x < width; x++) {
3942         float *pixel = buffer + channels * (((size_t)y) * width + x);
3943 
3944         curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, channels);
3945       }
3946     }
3947   }
3948 
3949   if (cm_processor->processor && channels >= 3) {
3950     OCIO_PackedImageDesc *img;
3951 
3952     /* apply OCIO processor */
3953     img = OCIO_createOCIO_PackedImageDesc(buffer,
3954                                           width,
3955                                           height,
3956                                           channels,
3957                                           sizeof(float),
3958                                           (size_t)channels * sizeof(float),
3959                                           (size_t)channels * sizeof(float) * width);
3960 
3961     if (predivide) {
3962       OCIO_processorApply_predivide(cm_processor->processor, img);
3963     }
3964     else {
3965       OCIO_processorApply(cm_processor->processor, img);
3966     }
3967 
3968     OCIO_PackedImageDescRelease(img);
3969   }
3970 }
3971 
IMB_colormanagement_processor_apply_byte(ColormanageProcessor * cm_processor,unsigned char * buffer,int width,int height,int channels)3972 void IMB_colormanagement_processor_apply_byte(
3973     ColormanageProcessor *cm_processor, unsigned char *buffer, int width, int height, int channels)
3974 {
3975   /* TODO(sergey): Would be nice to support arbitrary channels configurations,
3976    * but for now it's not so important.
3977    */
3978   BLI_assert(channels == 4);
3979   float pixel[4];
3980   for (int y = 0; y < height; y++) {
3981     for (int x = 0; x < width; x++) {
3982       size_t offset = channels * (((size_t)y) * width + x);
3983       rgba_uchar_to_float(pixel, buffer + offset);
3984       IMB_colormanagement_processor_apply_v4(cm_processor, pixel);
3985       rgba_float_to_uchar(buffer + offset, pixel);
3986     }
3987   }
3988 }
3989 
IMB_colormanagement_processor_free(ColormanageProcessor * cm_processor)3990 void IMB_colormanagement_processor_free(ColormanageProcessor *cm_processor)
3991 {
3992   if (cm_processor->curve_mapping) {
3993     BKE_curvemapping_free(cm_processor->curve_mapping);
3994   }
3995   if (cm_processor->processor) {
3996     OCIO_processorRelease(cm_processor->processor);
3997   }
3998 
3999   MEM_freeN(cm_processor);
4000 }
4001 
4002 /* **** OpenGL drawing routines using GLSL for color space transform ***** */
4003 
check_glsl_display_processor_changed(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,const char * from_colorspace)4004 static bool check_glsl_display_processor_changed(
4005     const ColorManagedViewSettings *view_settings,
4006     const ColorManagedDisplaySettings *display_settings,
4007     const char *from_colorspace)
4008 {
4009   return !(global_glsl_state.exposure == view_settings->exposure &&
4010            global_glsl_state.gamma == view_settings->gamma &&
4011            STREQ(global_glsl_state.look, view_settings->look) &&
4012            STREQ(global_glsl_state.view, view_settings->view_transform) &&
4013            STREQ(global_glsl_state.display, display_settings->display_device) &&
4014            STREQ(global_glsl_state.input, from_colorspace));
4015 }
4016 
curve_mapping_to_ocio_settings(CurveMapping * curve_mapping,OCIO_CurveMappingSettings * curve_mapping_settings)4017 static void curve_mapping_to_ocio_settings(CurveMapping *curve_mapping,
4018                                            OCIO_CurveMappingSettings *curve_mapping_settings)
4019 {
4020   int i;
4021 
4022   BKE_curvemapping_init(curve_mapping);
4023   BKE_curvemapping_premultiply(curve_mapping, false);
4024   BKE_curvemapping_table_RGBA(
4025       curve_mapping, &curve_mapping_settings->lut, &curve_mapping_settings->lut_size);
4026 
4027   curve_mapping_settings->use_extend_extrapolate = (curve_mapping->flag &
4028                                                     CUMA_EXTEND_EXTRAPOLATE) != 0;
4029 
4030   for (i = 0; i < 4; i++) {
4031     CurveMap *cuma = curve_mapping->cm + i;
4032     curve_mapping_settings->range[i] = cuma->range;
4033     curve_mapping_settings->mintable[i] = cuma->mintable;
4034     curve_mapping_settings->ext_in_x[i] = cuma->ext_in[0];
4035     curve_mapping_settings->ext_in_y[i] = cuma->ext_in[1];
4036     curve_mapping_settings->ext_out_x[i] = cuma->ext_out[0];
4037     curve_mapping_settings->ext_out_y[i] = cuma->ext_out[1];
4038     curve_mapping_settings->first_x[i] = cuma->table[0].x;
4039     curve_mapping_settings->first_y[i] = cuma->table[0].y;
4040     curve_mapping_settings->last_x[i] = cuma->table[CM_TABLE].x;
4041     curve_mapping_settings->last_y[i] = cuma->table[CM_TABLE].y;
4042   }
4043 
4044   copy_v3_v3(curve_mapping_settings->black, curve_mapping->black);
4045   copy_v3_v3(curve_mapping_settings->bwmul, curve_mapping->bwmul);
4046 
4047   curve_mapping_settings->cache_id = (size_t)curve_mapping + curve_mapping->changed_timestamp;
4048 }
4049 
update_glsl_display_processor(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,const char * from_colorspace)4050 static void update_glsl_display_processor(const ColorManagedViewSettings *view_settings,
4051                                           const ColorManagedDisplaySettings *display_settings,
4052                                           const char *from_colorspace)
4053 {
4054   bool use_curve_mapping = (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) != 0;
4055   bool need_update = false;
4056 
4057   need_update = global_glsl_state.processor_scene_to_ui == NULL ||
4058                 check_glsl_display_processor_changed(
4059                     view_settings, display_settings, from_colorspace) ||
4060                 use_curve_mapping != global_glsl_state.use_curve_mapping;
4061 
4062   if (use_curve_mapping && need_update == false) {
4063     need_update |= view_settings->curve_mapping->changed_timestamp !=
4064                        global_glsl_state.curve_mapping_timestamp ||
4065                    view_settings->curve_mapping != global_glsl_state.orig_curve_mapping;
4066   }
4067 
4068   /* Update state if there's no processor yet or
4069    * processor settings has been changed.
4070    */
4071   if (need_update) {
4072     OCIO_CurveMappingSettings *curve_mapping_settings = &global_glsl_state.curve_mapping_settings;
4073     CurveMapping *new_curve_mapping = NULL;
4074 
4075     /* Store settings of processor for further comparison. */
4076     BLI_strncpy(global_glsl_state.look, view_settings->look, MAX_COLORSPACE_NAME);
4077     BLI_strncpy(global_glsl_state.view, view_settings->view_transform, MAX_COLORSPACE_NAME);
4078     BLI_strncpy(global_glsl_state.display, display_settings->display_device, MAX_COLORSPACE_NAME);
4079     BLI_strncpy(global_glsl_state.input, from_colorspace, MAX_COLORSPACE_NAME);
4080     global_glsl_state.exposure = view_settings->exposure;
4081     global_glsl_state.gamma = view_settings->gamma;
4082 
4083     /* We're using curve mapping's address as a cache ID,
4084      * so we need to make sure re-allocation gives new address here.
4085      * We do this by allocating new curve mapping before freeing old one. */
4086     if (use_curve_mapping) {
4087       new_curve_mapping = BKE_curvemapping_copy(view_settings->curve_mapping);
4088     }
4089 
4090     if (global_glsl_state.curve_mapping) {
4091       BKE_curvemapping_free(global_glsl_state.curve_mapping);
4092       MEM_freeN(curve_mapping_settings->lut);
4093       global_glsl_state.curve_mapping = NULL;
4094       curve_mapping_settings->lut = NULL;
4095     }
4096 
4097     /* Fill in OCIO's curve mapping settings. */
4098     if (use_curve_mapping) {
4099       curve_mapping_to_ocio_settings(new_curve_mapping, &global_glsl_state.curve_mapping_settings);
4100 
4101       global_glsl_state.curve_mapping = new_curve_mapping;
4102       global_glsl_state.curve_mapping_timestamp = view_settings->curve_mapping->changed_timestamp;
4103       global_glsl_state.orig_curve_mapping = view_settings->curve_mapping;
4104       global_glsl_state.use_curve_mapping = true;
4105     }
4106     else {
4107       global_glsl_state.orig_curve_mapping = NULL;
4108       global_glsl_state.use_curve_mapping = false;
4109     }
4110 
4111     /* Free old processor, if any. */
4112     if (global_glsl_state.processor_scene_to_ui) {
4113       OCIO_processorRelease(global_glsl_state.processor_scene_to_ui);
4114     }
4115 
4116     if (global_glsl_state.processor_ui_to_display) {
4117       OCIO_processorRelease(global_glsl_state.processor_ui_to_display);
4118     }
4119 
4120     /* We're using display OCIO processor, no RGB curves yet. */
4121     global_glsl_state.processor_scene_to_ui = create_display_buffer_processor(
4122         global_glsl_state.look,
4123         global_glsl_state.view,
4124         global_glsl_state.display,
4125         global_glsl_state.exposure,
4126         global_glsl_state.gamma,
4127         global_glsl_state.input,
4128         true);
4129 
4130     global_glsl_state.processor_ui_to_display = create_display_encoded_buffer_processor(
4131         global_glsl_state.display);
4132   }
4133 }
4134 
IMB_colormanagement_support_glsl_draw(const ColorManagedViewSettings * UNUSED (view_settings))4135 bool IMB_colormanagement_support_glsl_draw(const ColorManagedViewSettings *UNUSED(view_settings))
4136 {
4137   return OCIO_supportGLSLDraw();
4138 }
4139 
4140 /**
4141  * Configures GLSL shader for conversion from specified to
4142  * display color space
4143  *
4144  * Will create appropriate OCIO processor and setup GLSL shader,
4145  * so further 2D texture usage will use this conversion.
4146  *
4147  * When there's no need to apply transform on 2D textures, use
4148  * IMB_colormanagement_finish_glsl_draw().
4149  *
4150  * This is low-level function, use ED_draw_imbuf_ctx if you
4151  * only need to display given image buffer
4152  */
IMB_colormanagement_setup_glsl_draw_from_space(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,struct ColorSpace * from_colorspace,float dither,bool predivide,bool do_overlay_merge)4153 bool IMB_colormanagement_setup_glsl_draw_from_space(
4154     const ColorManagedViewSettings *view_settings,
4155     const ColorManagedDisplaySettings *display_settings,
4156     struct ColorSpace *from_colorspace,
4157     float dither,
4158     bool predivide,
4159     bool do_overlay_merge)
4160 {
4161   ColorManagedViewSettings default_view_settings;
4162   const ColorManagedViewSettings *applied_view_settings;
4163 
4164   if (view_settings) {
4165     applied_view_settings = view_settings;
4166   }
4167   else {
4168     /* If no view settings were specified, use default ones, which will
4169      * attempt not to do any extra color correction. */
4170     IMB_colormanagement_init_default_view_settings(&default_view_settings, display_settings);
4171     applied_view_settings = &default_view_settings;
4172   }
4173 
4174   /* Make sure OCIO processor is up-to-date. */
4175   update_glsl_display_processor(applied_view_settings,
4176                                 display_settings,
4177                                 from_colorspace ? from_colorspace->name :
4178                                                   global_role_scene_linear);
4179 
4180   if (global_glsl_state.processor_scene_to_ui == NULL) {
4181     /* Happens when requesting non-existing color space or LUT in the
4182      * configuration file does not exist.
4183      */
4184     return false;
4185   }
4186 
4187   return OCIO_setupGLSLDraw(
4188       &global_glsl_state.ocio_glsl_state,
4189       global_glsl_state.processor_scene_to_ui,
4190       global_glsl_state.processor_ui_to_display,
4191       global_glsl_state.use_curve_mapping ? &global_glsl_state.curve_mapping_settings : NULL,
4192       dither,
4193       predivide,
4194       do_overlay_merge);
4195 }
4196 
4197 /* Configures GLSL shader for conversion from scene linear to display space */
IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,float dither,bool predivide)4198 bool IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings *view_settings,
4199                                          const ColorManagedDisplaySettings *display_settings,
4200                                          float dither,
4201                                          bool predivide)
4202 {
4203   return IMB_colormanagement_setup_glsl_draw_from_space(
4204       view_settings, display_settings, NULL, dither, predivide, false);
4205 }
4206 
4207 /**
4208  * Same as setup_glsl_draw_from_space,
4209  * but color management settings are guessing from a given context.
4210  */
IMB_colormanagement_setup_glsl_draw_from_space_ctx(const bContext * C,struct ColorSpace * from_colorspace,float dither,bool predivide)4211 bool IMB_colormanagement_setup_glsl_draw_from_space_ctx(const bContext *C,
4212                                                         struct ColorSpace *from_colorspace,
4213                                                         float dither,
4214                                                         bool predivide)
4215 {
4216   ColorManagedViewSettings *view_settings;
4217   ColorManagedDisplaySettings *display_settings;
4218 
4219   IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
4220 
4221   return IMB_colormanagement_setup_glsl_draw_from_space(
4222       view_settings, display_settings, from_colorspace, dither, predivide, false);
4223 }
4224 
4225 /* Same as setup_glsl_draw, but color management settings are guessing from a given context */
IMB_colormanagement_setup_glsl_draw_ctx(const bContext * C,float dither,bool predivide)4226 bool IMB_colormanagement_setup_glsl_draw_ctx(const bContext *C, float dither, bool predivide)
4227 {
4228   return IMB_colormanagement_setup_glsl_draw_from_space_ctx(C, NULL, dither, predivide);
4229 }
4230 
4231 /* Finish GLSL-based display space conversion */
IMB_colormanagement_finish_glsl_draw(void)4232 void IMB_colormanagement_finish_glsl_draw(void)
4233 {
4234   if (global_glsl_state.ocio_glsl_state != NULL) {
4235     OCIO_finishGLSLDraw(global_glsl_state.ocio_glsl_state);
4236   }
4237 }
4238 
4239 /** \} */
4240