Fixed issue with missed cache update when disabling movie clip channels
[blender.git] / source / blender / imbuf / intern / colormanagement.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2012 by Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Xavier Thomas,
24  *                 Lukas Toenne,
25  *                 Sergey Sharybin
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  *
29  */
30
31 #include "IMB_colormanagement.h"
32 #include "IMB_colormanagement_intern.h"
33
34 #include <string.h>
35 #include <math.h>
36
37 #include "DNA_color_types.h"
38 #include "DNA_scene_types.h"
39 #include "DNA_screen_types.h"
40 #include "DNA_space_types.h"
41 #include "DNA_windowmanager_types.h"
42
43 #include "IMB_filter.h"
44 #include "IMB_imbuf.h"
45 #include "IMB_imbuf_types.h"
46 #include "IMB_moviecache.h"
47
48 #include "MEM_guardedalloc.h"
49
50 #include "BLI_blenlib.h"
51 #include "BLI_math.h"
52 #include "BLI_math_color.h"
53 #include "BLI_path_util.h"
54 #include "BLI_string.h"
55 #include "BLI_threads.h"
56
57 #include "BKE_utildefines.h"
58 #include "BKE_main.h"
59
60 #include "RNA_define.h"
61
62 #ifdef WITH_OCIO
63 #  include <ocio_capi.h>
64 #endif
65
66 /*********************** Global declarations *************************/
67
68 /* ** list of all supported color spaces, displays and views */
69 #ifdef WITH_OCIO
70 static ListBase global_colorspaces = {NULL};
71
72 static char global_role_linear[64];
73 static char global_role_color_picking[64];
74 static char global_role_texture_painting[64];
75
76 #endif
77
78 static ListBase global_displays = {NULL};
79 static ListBase global_views = {NULL};
80
81 static int global_tot_display = 0;
82 static int global_tot_view = 0;
83
84 /*********************** Color managed cache *************************/
85
86 /* Cache Implementation Notes
87  * ==========================
88  *
89  * All color management cache stuff is stored in two properties of
90  * image buffers:
91  *
92  *   1. display_buffer_flags
93  *
94  *      This is a bit field which used to mark calculated transformations
95  *      for particular image buffer. Index inside of this array means index
96  *      of a color managed display. Element with given index matches view
97  *      transformations applied for a given display. So if bit B of array
98  *      element B is set to 1, this means display buffer with display index
99  *      of A and view transform of B was ever calculated for this imbuf.
100  *
101  *      In contrast with indices in global lists of displays and views this
102  *      indices are 0-based, not 1-based. This is needed to save some bytes
103  *      of memory.
104  *
105  *   2. colormanage_cache
106  *
107  *      This is a pointer to a structure which holds all data which is
108  *      needed for color management cache to work.
109  *
110  *      It contains two parts:
111  *        - data
112  *        - moviecache
113  *
114  *      Data field is used to store additional information about cached
115  *      buffers which affects on whether cached buffer could be used.
116  *      This data can't go to cache key because changes in this data
117  *      shouldn't lead extra buffers adding to cache, it shall
118  *      invalidate cached images.
119  *
120  *      Currently such a data contains only exposure and gamma, but
121  *      would likely extended further.
122  *
123  *      data field is not null only for elements of cache, not used for
124  *      original image buffers.
125  *
126  *      Color management cache is using generic MovieCache implementation
127  *      to make it easier to deal with memory limitation.
128  *
129  *      Currently color management is using the same memory limitation
130  *      pool as sequencer and clip editor are using which means color
131  *      managed buffers would be removed from the cache as soon as new
132  *      frames are loading for the movie clip and there's no space in
133  *      cache.
134  *
135  *      Every image buffer has got own movie cache instance, which
136  *      means keys for color managed buffers could be really simple
137  *      and look up in this cache would be fast and independent from
138  *      overall amount of color managed images.
139  */
140
141 /* NOTE: ColormanageCacheViewSettings and ColormanageCacheDisplaySettings are
142  *       quite the same as ColorManagedViewSettings and ColorManageDisplaySettings
143  *       but they holds indexes of all transformations and color spaces, not
144  *       their names.
145  *
146  *       This helps avoid extra colorsmace / display / view lookup without
147  *       requiring to pass all variables which affects on display buffer
148  *       to color management cache system and keeps calls small and nice.
149  */
150 typedef struct ColormanageCacheViewSettings {
151         int view;
152         float exposure;
153         float gamma;
154 } ColormanageCacheViewSettings;
155
156 typedef struct ColormanageCacheDisplaySettings {
157         int display;
158 } ColormanageCacheDisplaySettings;
159
160 typedef struct ColormanageCacheKey {
161         int view;            /* view transformation used for display buffer */
162         int display;         /* display device name */
163 } ColormanageCacheKey;
164
165 typedef struct ColormnaageCacheData {
166         float exposure;  /* exposure value cached buffer is calculated with */
167         float gamma;     /* gamma value cached buffer is calculated with */
168 } ColormnaageCacheData;
169
170 typedef struct ColormanageCache {
171         struct MovieCache *moviecache;
172
173         ColormnaageCacheData *data;
174 } ColormanageCache;
175
176 static struct MovieCache *colormanage_moviecache_get(const ImBuf *ibuf)
177 {
178         if (!ibuf->colormanage_cache)
179                 return NULL;
180
181         return ibuf->colormanage_cache->moviecache;
182 }
183
184 static ColormnaageCacheData *colormanage_cachedata_get(const ImBuf *ibuf)
185 {
186         if (!ibuf->colormanage_cache)
187                 return NULL;
188
189         return ibuf->colormanage_cache->data;
190 }
191
192 #ifdef WITH_OCIO
193 static unsigned int colormanage_hashhash(const void *key_v)
194 {
195         ColormanageCacheKey *key = (ColormanageCacheKey *)key_v;
196
197         unsigned int rval = (key->display << 16) | (key->view % 0xffff);
198
199         return rval;
200 }
201
202 static int colormanage_hashcmp(const void *av, const void *bv)
203 {
204         const ColormanageCacheKey *a = (ColormanageCacheKey *) av;
205         const ColormanageCacheKey *b = (ColormanageCacheKey *) bv;
206
207         if (a->view < b->view)
208                 return -1;
209         else if (a->view > b->view)
210                 return 1;
211
212         if (a->display < b->display)
213                 return -1;
214         else if (a->display > b->display)
215                 return 1;
216
217         return 0;
218 }
219
220 static struct MovieCache *colormanage_moviecache_ensure(ImBuf *ibuf)
221 {
222         if (!ibuf->colormanage_cache) {
223                 ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage ca cache");
224         }
225
226         if (!ibuf->colormanage_cache->moviecache) {
227                 struct MovieCache *moviecache;
228
229                 moviecache = IMB_moviecache_create("colormanage cache", sizeof(ColormanageCacheKey), colormanage_hashhash, colormanage_hashcmp);
230
231                 ibuf->colormanage_cache->moviecache = moviecache;
232         }
233
234         return ibuf->colormanage_cache->moviecache;
235 }
236
237 static void colormanage_cachedata_set(ImBuf *ibuf, ColormnaageCacheData *data)
238 {
239         if (!ibuf->colormanage_cache) {
240                 ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage ca cache");
241         }
242
243         ibuf->colormanage_cache->data = data;
244 }
245
246 static void colormanage_view_settings_to_cache(ColormanageCacheViewSettings *cache_view_settings,
247                                                const ColorManagedViewSettings *view_settings)
248 {
249         int view = IMB_colormanagement_view_get_named_index(view_settings->view_transform);
250
251         cache_view_settings->view = view;
252         cache_view_settings->exposure = view_settings->exposure;
253         cache_view_settings->gamma = view_settings->gamma;
254 }
255
256 static void colormanage_display_settings_to_cache(ColormanageCacheDisplaySettings *cache_display_settings,
257                                                   const ColorManagedDisplaySettings *display_settings)
258 {
259         int display = IMB_colormanagement_display_get_named_index(display_settings->display_device);
260
261         cache_display_settings->display = display;
262 }
263
264 static void colormanage_settings_to_key(ColormanageCacheKey *key,
265                                         const ColormanageCacheViewSettings *view_settings,
266                                         const ColormanageCacheDisplaySettings *display_settings)
267 {
268         key->view = view_settings->view;
269         key->display = display_settings->display;
270 }
271
272 static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf, ColormanageCacheKey *key, void **cache_handle)
273 {
274         ImBuf *cache_ibuf;
275         struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
276
277         if (!moviecache) {
278                 /* if there's no moviecache it means no color management was applied before */
279
280                 return NULL;
281         }
282
283         *cache_handle = NULL;
284
285         cache_ibuf = IMB_moviecache_get(moviecache, key);
286
287         *cache_handle = cache_ibuf;
288
289         return cache_ibuf;
290 }
291
292 static unsigned char *colormanage_cache_get(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings,
293                                             const ColormanageCacheDisplaySettings *display_settings,
294                                             void **cache_handle)
295 {
296         ColormanageCacheKey key;
297         ImBuf *cache_ibuf;
298         int view_flag = 1 << (view_settings->view - 1);
299
300         colormanage_settings_to_key(&key, view_settings, display_settings);
301
302         /* check whether image was marked as dirty for requested transform */
303         if ((ibuf->display_buffer_flags[display_settings->display - 1] & view_flag) == 0) {
304                 return NULL;
305         }
306
307         cache_ibuf = colormanage_cache_get_ibuf(ibuf, &key, cache_handle);
308
309         if (cache_ibuf) {
310                 ColormnaageCacheData *cache_data;
311
312                 /* only buffers with different color space conversions are being stored
313                  * in cache separately. buffer which were used only different exposure/gamma
314                  * are re-suing the same cached buffer
315                  *
316                  * check here which exposure/gamma was used for cached buffer and if they're
317                  * different from requested buffer should be re-generated
318                  */
319                 cache_data = colormanage_cachedata_get(cache_ibuf);
320
321                 if (cache_data->exposure != view_settings->exposure ||
322                         cache_data->gamma != view_settings->gamma)
323                 {
324                         IMB_freeImBuf(cache_ibuf);
325
326                         return NULL;
327                 }
328
329                 return (unsigned char *) cache_ibuf->rect;
330         }
331
332         return NULL;
333 }
334
335 static void colormanage_cache_put(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings,
336                                   const ColormanageCacheDisplaySettings *display_settings,
337                                   unsigned char *display_buffer, void **cache_handle)
338 {
339         ColormanageCacheKey key;
340         ImBuf *cache_ibuf;
341         ColormnaageCacheData *cache_data;
342         int view_flag = 1 << (view_settings->view - 1);
343         struct MovieCache *moviecache = colormanage_moviecache_ensure(ibuf);
344
345         colormanage_settings_to_key(&key, view_settings, display_settings);
346
347         /* mark display buffer as valid */
348         ibuf->display_buffer_flags[display_settings->display - 1] |= view_flag;
349
350         /* buffer itself */
351         cache_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0);
352         cache_ibuf->rect = (unsigned int *) display_buffer;
353
354         cache_ibuf->mall |= IB_rect;
355         cache_ibuf->flags |= IB_rect;
356
357         /* store data which is needed to check whether cached buffer could be used for color managed display settings */
358         cache_data = MEM_callocN(sizeof(ColormnaageCacheData), "color manage cache imbuf data");
359         cache_data->exposure = view_settings->exposure;
360         cache_data->gamma = view_settings->gamma;
361
362         colormanage_cachedata_set(cache_ibuf, cache_data);
363
364         *cache_handle = cache_ibuf;
365
366         IMB_moviecache_put(moviecache, &key, cache_ibuf);
367 }
368
369 /* validation function checks whether there's buffer with given display transform
370  * in the cache and if so, check whether it matches resolution of source buffer.
371  * if resolution is different new buffer would be put into the cache and it'll
372  * be returned as a result
373  *
374  * this function does not check exposure / gamma because currently it's only
375  * used by partial buffer update functions which uses the same exposure / gamma
376  * settings as cached buffer had
377  */
378 static unsigned char *colormanage_cache_get_validated(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings,
379                                                       const ColormanageCacheDisplaySettings *display_settings,
380                                                       void **cache_handle)
381 {
382         ColormanageCacheKey key;
383         ImBuf *cache_ibuf;
384
385         colormanage_settings_to_key(&key, view_settings, display_settings);
386
387         cache_ibuf = colormanage_cache_get_ibuf(ibuf, &key, cache_handle);
388
389         if (cache_ibuf) {
390                 if (cache_ibuf->x != ibuf->x || cache_ibuf->y != ibuf->y) {
391                         ColormanageCacheViewSettings new_view_settings = *view_settings;
392                         ColormnaageCacheData *cache_data;
393                         unsigned char *display_buffer;
394                         int buffer_size;
395
396                         /* use the same settings as original cached buffer  */
397                         cache_data = colormanage_cachedata_get(cache_ibuf);
398                         new_view_settings.exposure = cache_data->exposure;
399                         new_view_settings.gamma = cache_data->gamma;
400
401                         buffer_size = ibuf->channels * ibuf->x * ibuf->y * sizeof(float);
402                         display_buffer = MEM_callocN(buffer_size, "imbuf validated display buffer");
403
404                         colormanage_cache_put(ibuf, &new_view_settings, display_settings, display_buffer, cache_handle);
405
406                         IMB_freeImBuf(cache_ibuf);
407
408                         return display_buffer;
409                 }
410
411                 return (unsigned char *) cache_ibuf->rect;
412         }
413
414         return NULL;
415 }
416
417 /* return view settings which are stored in cached buffer, not in key itself */
418 static void colormanage_cache_get_cache_data(void *cache_handle, float *exposure, float *gamma)
419 {
420         ImBuf *cache_ibuf = (ImBuf *) cache_handle;
421         ColormnaageCacheData *cache_data;
422
423         cache_data = colormanage_cachedata_get(cache_ibuf);
424
425         *exposure = cache_data->exposure;
426         *gamma = cache_data->gamma;
427 }
428 #endif
429
430 static void colormanage_cache_handle_release(void *cache_handle)
431 {
432         ImBuf *cache_ibuf = cache_handle;
433
434         IMB_freeImBuf(cache_ibuf);
435 }
436
437 /*********************** Initialization / De-initialization *************************/
438
439 #ifdef WITH_OCIO
440 static void colormanage_role_color_space_name_get(ConstConfigRcPtr *config, char *colorspace_name,
441                                                   int max_colorspace_name, const char *role, const char *role_name)
442 {
443         ConstColorSpaceRcPtr *ociocs;
444
445         ociocs = OCIO_configGetColorSpace(config, role);
446
447         if (ociocs) {
448                 const char *name = OCIO_colorSpaceGetName(ociocs);
449
450                 BLI_strncpy(colorspace_name, name, max_colorspace_name);
451                 OCIO_colorSpaceRelease(ociocs);
452         }
453         else {
454                 printf("Blender color management: Error could not find %s role.\n", role_name);
455         }
456 }
457
458 static void colormanage_load_config(ConstConfigRcPtr *config)
459 {
460         ConstColorSpaceRcPtr *ociocs;
461         int tot_colorspace, tot_display, tot_display_view, index, viewindex, viewindex2;
462         const char *name;
463
464         /* get roles */
465         colormanage_role_color_space_name_get(config, global_role_linear, sizeof(global_role_linear),
466                                               OCIO_ROLE_SCENE_LINEAR, "scene linear");
467
468         colormanage_role_color_space_name_get(config, global_role_color_picking, sizeof(global_role_color_picking),
469                                               OCIO_ROLE_COLOR_PICKING, "color picking");
470
471         colormanage_role_color_space_name_get(config, global_role_texture_painting, sizeof(global_role_texture_painting),
472                                               OCIO_ROLE_TEXTURE_PAINT, "texture_painting");
473
474         /* load colorspaces */
475         tot_colorspace = OCIO_configGetNumColorSpaces(config);
476         for (index = 0 ; index < tot_colorspace; index++) {
477                 ColorSpace *colorspace;
478
479                 name = OCIO_configGetColorSpaceNameByIndex(config, index);
480                 ociocs = OCIO_configGetColorSpace(config, name);
481
482                 colorspace = MEM_callocN(sizeof(ColorSpace), "ColorSpace");
483                 colorspace->index = index + 1;
484
485                 BLI_strncpy(colorspace->name, name, sizeof(colorspace->name));
486
487                 BLI_addtail(&global_colorspaces, colorspace);
488
489                 OCIO_colorSpaceRelease(ociocs);
490         }
491
492         /* load displays */
493         viewindex2 = 0;
494         tot_display = OCIO_configGetNumDisplays(config);
495
496         for (index = 0 ; index < tot_display; index++) {
497                 const char *displayname;
498                 ColorManagedDisplay *display;
499
500                 displayname = OCIO_configGetDisplay(config, index);
501
502                 display = colormanage_display_add(displayname);
503
504                 /* load views */
505                 tot_display_view = OCIO_configGetNumViews(config, displayname);
506                 for (viewindex = 0 ; viewindex < tot_display_view; viewindex++, viewindex2++) {
507                         const char *viewname;
508                         ColorManagedView *view;
509                         LinkData *display_view;
510
511                         viewname = OCIO_configGetView(config, displayname, viewindex);
512
513                         /* first check if view transform with given name was already loaded */
514                         view = colormanage_view_get_named(viewname);
515
516                         if (!view) {
517                                 view = colormanage_view_add(viewname);
518                         }
519
520                         display_view = BLI_genericNodeN(view);
521
522                         BLI_addtail(&display->views, display_view);
523                 }
524         }
525
526         global_tot_display = tot_display;
527 }
528
529 void colormanage_free_config(void)
530 {
531         ColorSpace *colorspace;
532         ColorManagedDisplay *display;
533         ColorManagedView *view;
534
535         colorspace = global_colorspaces.first;
536         while (colorspace) {
537                 ColorSpace *colorspace_next = colorspace->next;
538
539                 MEM_freeN(colorspace);
540                 colorspace = colorspace_next;
541         }
542
543         display = global_displays.first;
544         while (display) {
545                 ColorManagedDisplay *display_next = display->next;
546                 LinkData *display_view = display->views.first;
547
548                 while (display_view) {
549                         LinkData *display_view_next = display_view->next;
550
551                         MEM_freeN(display_view);
552                         display_view = display_view_next;
553                 }
554
555                 MEM_freeN(display);
556                 display = display_next;
557         }
558
559         view = global_views.first;
560         while (view) {
561                 ColorManagedView *view_next = view->next;
562
563                 MEM_freeN(view);
564                 view = view_next;
565         }
566 }
567 #endif
568
569 void IMB_colormanagement_init(void)
570 {
571 #ifdef WITH_OCIO
572         const char *ocio_env;
573         const char *configdir;
574         char configfile[FILE_MAX];
575         ConstConfigRcPtr *config = NULL;
576
577         ocio_env = getenv("OCIO");
578
579         if (ocio_env) {
580                 config = OCIO_configCreateFromEnv();
581         }
582         else {
583                 configdir = BLI_get_folder(BLENDER_DATAFILES, "colormanagement");
584
585                 if (configdir)  {
586                         BLI_join_dirfile(configfile, sizeof(configfile), configdir, BCM_CONFIG_FILE);
587
588                         config = OCIO_configCreateFromFile(configfile);
589                 }
590         }
591
592         if (config) {
593                 OCIO_setCurrentConfig(config);
594
595                 colormanage_load_config(config);
596         }
597
598         OCIO_configRelease(config);
599
600         /* special views, which does not depend on OCIO  */
601         colormanage_view_add("ACES ODT Tonecurve");
602 #endif
603 }
604
605 void IMB_colormanagement_exit(void)
606 {
607 #ifdef WITH_OCIO
608         colormanage_free_config();
609 #endif
610 }
611
612 /*********************** Public display buffers interfaces *************************/
613
614 #ifdef WITH_OCIO
615 typedef struct DisplayBufferThread {
616         void *processor;
617
618         float *buffer;
619         unsigned char *display_buffer;
620
621         int width;
622         int start_line;
623         int tot_line;
624
625         int channels;
626         int dither;
627         int predivide;
628 } DisplayBufferThread;
629
630 static void display_buffer_apply_threaded(ImBuf *ibuf, float *buffer, unsigned char *display_buffer,
631                                           void *processor, void *(do_thread) (void *))
632 {
633         DisplayBufferThread handles[BLENDER_MAX_THREADS];
634         ListBase threads;
635
636         int predivide = ibuf->flags & IB_cm_predivide;
637         int i, tot_thread = BLI_system_thread_count();
638         int start_line, tot_line;
639
640         if (tot_thread > 1)
641                 BLI_init_threads(&threads, do_thread, tot_thread);
642
643         start_line = 0;
644         tot_line = ((float)(ibuf->y / tot_thread)) + 0.5f;
645
646         for (i = 0; i < tot_thread; i++) {
647                 int offset = ibuf->channels * start_line * ibuf->x;
648
649                 handles[i].processor = processor;
650
651                 handles[i].buffer = buffer + offset;
652                 handles[i].display_buffer = display_buffer + offset;
653                 handles[i].width = ibuf->x;
654
655                 handles[i].start_line = start_line;
656
657                 if (i < tot_thread - 1) {
658                         handles[i].tot_line = tot_line;
659                 }
660                 else {
661                         handles[i].tot_line = ibuf->y - start_line;
662                 }
663
664                 handles[i].channels = ibuf->channels;
665                 handles[i].dither = ibuf->dither;
666                 handles[i].predivide = predivide;
667
668                 if (tot_thread > 1)
669                         BLI_insert_thread(&threads, &handles[i]);
670
671                 start_line += tot_line;
672         }
673
674         if (tot_thread > 1)
675                 BLI_end_threads(&threads);
676         else
677                 do_thread(&handles[0]);
678 }
679
680 static void *do_display_buffer_apply_tonemap_thread(void *handle_v)
681 {
682         DisplayBufferThread *handle = (DisplayBufferThread *) handle_v;
683         imb_tonecurveCb tonecurve_func = (imb_tonecurveCb) handle->processor;
684
685         float *buffer = handle->buffer;
686         unsigned char *display_buffer = handle->display_buffer;
687
688         int channels = handle->channels;
689         int width = handle->width;
690         int height = handle->tot_line;
691         int dither = handle->dither;
692         int predivide = handle->predivide;
693
694         IMB_buffer_byte_from_float_tonecurve(display_buffer, buffer, channels, dither,
695                                              IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB,
696                                              predivide, width, height, width, width,
697                                              tonecurve_func);
698
699         return NULL;
700 }
701
702 static void display_buffer_apply_tonemap(ImBuf *ibuf, unsigned char *display_buffer,
703                                          imb_tonecurveCb tonecurve_func)
704 {
705         /* XXX: IMB_buffer_byte_from_float_tonecurve isn't thread-safe because of
706          *      possible non-initialized sRGB conversion stuff. Make sure it's properly
707          *      initialized before starting threads, but likely this stuff should be
708          *      initialized somewhere before to avoid possible issues in other issues.
709          */
710         BLI_init_srgb_conversion();
711
712         display_buffer_apply_threaded(ibuf, ibuf->rect_float, display_buffer, tonecurve_func,
713                                       do_display_buffer_apply_tonemap_thread);
714 }
715
716 static void *do_display_buffer_apply_ocio_thread(void *handle_v)
717 {
718         DisplayBufferThread *handle = (DisplayBufferThread *) handle_v;
719         ConstProcessorRcPtr *processor = (ConstProcessorRcPtr *) handle->processor;
720         PackedImageDesc *img;
721         float *buffer = handle->buffer;
722         unsigned char *display_buffer = handle->display_buffer;
723         int channels = handle->channels;
724         int width = handle->width;
725         int height = handle->tot_line;
726         int dither = handle->dither;
727         int predivide = handle->predivide;
728
729         img = OCIO_createPackedImageDesc(buffer, width, height, channels, sizeof(float),
730                                          channels * sizeof(float), channels * sizeof(float) * width);
731
732         OCIO_processorApply(processor, img);
733
734         OCIO_packedImageDescRelease(img);
735
736         /* do conversion */
737         IMB_buffer_byte_from_float(display_buffer, buffer,
738                                    channels, dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
739                                    predivide, width, height, width, width);
740
741         return NULL;
742 }
743
744 static ConstProcessorRcPtr *create_display_buffer_processor(const char *view_transform, const char *display,
745                                                             float exposure, float gamma)
746 {
747         ConstConfigRcPtr *config = OCIO_getCurrentConfig();
748         DisplayTransformRcPtr *dt;
749         ExponentTransformRcPtr *et;
750         MatrixTransformRcPtr *mt;
751         ConstProcessorRcPtr *processor;
752
753         float exponent = 1.0f / MAX2(FLT_EPSILON, gamma);
754         const float exponent4f[] = {exponent, exponent, exponent, exponent};
755
756         float gain = powf(2.0f, exposure);
757         const float scale4f[] = {gain, gain, gain, gain};
758         float m44[16], offset4[4];
759
760         if (!config) {
761                 /* there's no valid OCIO configuration, can't create processor */
762
763                 return NULL;
764         }
765
766         dt = OCIO_createDisplayTransform();
767
768         /* OCIO_TODO: get rid of hardcoded input space */
769         OCIO_displayTransformSetInputColorSpaceName(dt, global_role_linear);
770
771         OCIO_displayTransformSetView(dt, view_transform);
772         OCIO_displayTransformSetDisplay(dt, display);
773
774         /* fstop exposure control */
775         OCIO_matrixTransformScale(m44, offset4, scale4f);
776         mt = OCIO_createMatrixTransform();
777         OCIO_matrixTransformSetValue(mt, m44, offset4);
778         OCIO_displayTransformSetLinearCC(dt, (ConstTransformRcPtr *) mt);
779
780         /* post-display gamma transform */
781         et = OCIO_createExponentTransform();
782         OCIO_exponentTransformSetValue(et, exponent4f);
783         OCIO_displayTransformSetDisplayCC(dt, (ConstTransformRcPtr *) et);
784
785         processor = OCIO_configGetProcessor(config, (ConstTransformRcPtr *) dt);
786
787         OCIO_exponentTransformRelease(et);
788         OCIO_displayTransformRelease(dt);
789         OCIO_configRelease(config);
790
791         return processor;
792 }
793
794 static void display_buffer_apply_ocio(ImBuf *ibuf, unsigned char *display_buffer,
795                                       const ColorManagedViewSettings *view_settings,
796                                       const ColorManagedDisplaySettings *display_settings)
797 {
798         ConstProcessorRcPtr *processor;
799         const float gamma = view_settings->gamma;
800         const float exposure = view_settings->exposure;
801         const char *view_transform = view_settings->view_transform;
802         const char *display = display_settings->display_device;
803         float *rect_float;
804
805         rect_float = MEM_dupallocN(ibuf->rect_float);
806
807         processor = create_display_buffer_processor(view_transform, display, exposure, gamma);
808
809         if (processor) {
810                 display_buffer_apply_threaded(ibuf, rect_float, display_buffer, processor,
811                                               do_display_buffer_apply_ocio_thread);
812         }
813
814         OCIO_processorRelease(processor);
815
816         MEM_freeN(rect_float);
817 }
818
819 static void colormanage_display_buffer_process(ImBuf *ibuf, unsigned char *display_buffer,
820                                                const ColorManagedViewSettings *view_settings,
821                                                const ColorManagedDisplaySettings *display_settings)
822 {
823         const char *view_transform = view_settings->view_transform;
824
825         if (!strcmp(view_transform, "ACES ODT Tonecurve")) {
826                 /* special case for Mango team, this does not actually apply
827                  * any input space -> display space conversion and just applies
828                  * a tonecurve for better linear float -> sRGB byte conversion
829                  */
830                 display_buffer_apply_tonemap(ibuf, display_buffer, IMB_ratio_preserving_odt_tonecurve);
831         }
832         else {
833                 display_buffer_apply_ocio(ibuf, display_buffer, view_settings, display_settings);
834         }
835
836 }
837
838 static void colormanage_flags_allocate(ImBuf *ibuf)
839 {
840         if (global_tot_display == 0)
841                 return;
842
843         ibuf->display_buffer_flags = MEM_callocN(sizeof(unsigned int) * global_tot_display, "imbuf display_buffer_flags");
844 }
845 #endif
846
847 void IMB_colormanage_cache_free(ImBuf *ibuf)
848 {
849         if (ibuf->display_buffer_flags) {
850                 MEM_freeN(ibuf->display_buffer_flags);
851
852                 ibuf->display_buffer_flags = NULL;
853         }
854
855         if (ibuf->colormanage_cache) {
856                 ColormnaageCacheData *cache_data = colormanage_cachedata_get(ibuf);
857                 struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
858
859                 if (cache_data) {
860                         MEM_freeN(cache_data);
861                 }
862
863                 if (moviecache) {
864                         IMB_moviecache_free(moviecache);
865                 }
866
867                 MEM_freeN(ibuf->colormanage_cache);
868
869                 ibuf->colormanage_cache = NULL;
870         }
871 }
872
873 unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
874                                           const ColorManagedDisplaySettings *display_settings, void **cache_handle)
875 {
876         const char *view_transform = view_settings->view_transform;
877
878         *cache_handle = NULL;
879
880 #ifdef WITH_OCIO
881
882         if (!ibuf->x || !ibuf->y)
883                 return NULL;
884
885         /* OCIO_TODO: support colormanaged byte buffers */
886         if (!strcmp(view_transform, "NONE") ||
887             !ibuf->rect_float ||
888             global_tot_display == 0 ||
889             global_tot_view == 0)
890         {
891                 /* currently only view-transformation is allowed, input and display
892                  * spaces are hard-coded, so if there's no view transform applying
893                  * it's safe to suppose standard byte buffer is used for display
894                  */
895
896                 if (ibuf->rect_float && (ibuf->rect == NULL || (ibuf->userflags & IB_RECT_INVALID)))
897                         IMB_rect_from_float(ibuf);
898
899                 return (unsigned char *) ibuf->rect;
900         }
901         else {
902                 unsigned char *display_buffer;
903                 int buffer_size;
904                 ColormanageCacheViewSettings cache_view_settings;
905                 ColormanageCacheDisplaySettings cache_display_settings;
906
907                 colormanage_view_settings_to_cache(&cache_view_settings, view_settings);
908                 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
909
910                 /* ensure color management bit fields exists */
911                 if (!ibuf->display_buffer_flags)
912                         colormanage_flags_allocate(ibuf);
913
914                 display_buffer = colormanage_cache_get(ibuf, &cache_view_settings, &cache_display_settings, cache_handle);
915
916                 if (display_buffer) {
917                         return display_buffer;
918                 }
919
920                 /* OCIO_TODO: in case when image is being resized it is possible
921                  *            to save buffer allocation here
922                  *
923                  *            actually not because there might be other users of
924                  *            that buffer which better not to change
925                  */
926
927                 buffer_size = ibuf->channels * ibuf->x * ibuf->y * sizeof(float);
928                 display_buffer = MEM_callocN(buffer_size, "imbuf display buffer");
929
930                 colormanage_display_buffer_process(ibuf, display_buffer, view_settings, display_settings);
931
932                 colormanage_cache_put(ibuf, &cache_view_settings, &cache_display_settings, display_buffer, cache_handle);
933
934                 return display_buffer;
935         }
936 #else
937         /* no OCIO support, simply return byte buffer which was
938          * generated from float buffer (if any) using standard
939          * profiles without applying any view / display transformation */
940
941         (void) view_settings;
942         (void) view_transform;
943         (void) display_settings;
944
945         if (ibuf->rect_float && (ibuf->rect == NULL || (ibuf->userflags & IB_RECT_INVALID)))
946                 IMB_rect_from_float(ibuf);
947
948         return (unsigned char*) ibuf->rect;
949 #endif
950 }
951
952 void IMB_display_buffer_to_imbuf_rect(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
953                                       const ColorManagedDisplaySettings *display_settings)
954 {
955 #ifdef WITH_OCIO
956         const char *view_transform = view_settings->view_transform;
957
958         if (!ibuf->rect_float)
959                 return;
960
961         if (!strcmp(view_transform, "NONE") ||
962             !ibuf->rect_float ||
963             global_tot_display == 0 ||
964             global_tot_view == 0)
965         {
966                 if (!ibuf->rect)
967                         IMB_rect_from_float(ibuf);
968         }
969         else {
970                 if (!ibuf->rect) {
971                         imb_addrectImBuf(ibuf);
972                 }
973
974                 colormanage_display_buffer_process(ibuf, (unsigned char *) ibuf->rect, view_settings, display_settings);
975         }
976 #else
977         (void) view_settings;
978         (void) display_settings;
979
980         if (!ibuf->rect)
981                 IMB_rect_from_float(ibuf);
982 #endif
983 }
984
985 void IMB_display_buffer_release(void *cache_handle)
986 {
987         if (cache_handle) {
988                 colormanage_cache_handle_release(cache_handle);
989         }
990 }
991
992 void IMB_display_buffer_invalidate(ImBuf *ibuf)
993 {
994         /* if there's no display_buffer_flags this means there's no color managed
995          * buffers created for this imbuf, no need to invalidate
996          */
997         if (ibuf->display_buffer_flags) {
998                 memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
999         }
1000 }
1001
1002 #ifdef WITH_OCIO
1003 static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings, const char *what,
1004                                                const ColorManagedDisplay *default_display)
1005 {
1006         if (display_settings->display_device[0] == '\0') {
1007                 BLI_strncpy(display_settings->display_device, default_display->name, sizeof(display_settings->display_device));
1008         }
1009         else {
1010                 ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device);
1011
1012                 if (!display) {
1013                         printf("Blender color management: display \"%s\" used by %s not found, setting to default (\"%s\").\n",
1014                                display_settings->display_device, what, default_display->name);
1015
1016                         BLI_strncpy(display_settings->display_device, default_display->name,
1017                                     sizeof(display_settings->display_device));
1018                 }
1019         }
1020 }
1021
1022 static void colormanage_check_view_settings(ColorManagedViewSettings *view_settings, const char *what,
1023                                             const ColorManagedView *default_view)
1024 {
1025         if (view_settings->view_transform[0] == '\0') {
1026                 BLI_strncpy(view_settings->view_transform, "NONE", sizeof(view_settings->view_transform));
1027         }
1028         else if (!strcmp(view_settings->view_transform, "NONE")) {
1029                 /* pass */
1030         }
1031         else {
1032                 ColorManagedView *view = colormanage_view_get_named(view_settings->view_transform);
1033
1034                 if (!view) {
1035                         printf("Blender color management: %s view \"%s\" not found, setting default \"%s\".\n",
1036                                what, view_settings->view_transform, default_view->name);
1037
1038                         BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1039                 }
1040         }
1041
1042         /* OCIO_TODO: move to do_versions() */
1043         if (view_settings->exposure == 0.0f && view_settings->gamma == 0.0f) {
1044                 view_settings->flag |= COLORMANAGE_VIEW_USE_GLOBAL;
1045                 view_settings->exposure = 0.0f;
1046                 view_settings->gamma = 1.0f;
1047         }
1048 }
1049 #endif
1050
1051 void IMB_colormanagement_check_file_config(Main *bmain)
1052 {
1053 #ifdef WITH_OCIO
1054         wmWindowManager *wm = bmain->wm.first;
1055         wmWindow *win;
1056         bScreen *sc;
1057         Scene *scene;
1058
1059         ColorManagedDisplay *default_display;
1060         ColorManagedView *default_view;
1061
1062         default_display = colormanage_display_get_default();
1063
1064         if (!default_display) {
1065                 /* happens when OCIO configuration is incorrect */
1066                 return;
1067         }
1068
1069         default_view = colormanage_view_get_default(default_display);
1070
1071         if (!default_view) {
1072                 /* happens when OCIO configuration is incorrect */
1073                 return;
1074         }
1075
1076         if (wm) {
1077                 for (win = wm->windows.first; win; win = win->next) {
1078                         colormanage_check_display_settings(&win->display_settings, "window", default_display);
1079
1080                         colormanage_check_view_settings(&win->view_settings, "window", default_view);
1081                 }
1082         }
1083
1084         for (sc = bmain->screen.first; sc; sc = sc->id.next) {
1085                 ScrArea *sa;
1086
1087                 for (sa = sc->areabase.first; sa; sa = sa->next) {
1088                         SpaceLink *sl;
1089                         for (sl = sa->spacedata.first; sl; sl = sl->next) {
1090
1091                                 if (sl->spacetype == SPACE_IMAGE) {
1092                                         SpaceImage *sima = (SpaceImage *) sl;
1093
1094                                         colormanage_check_view_settings(&sima->view_settings, "image editor", default_view);
1095                                 }
1096                                 else if (sl->spacetype == SPACE_NODE) {
1097                                         SpaceNode *snode = (SpaceNode *) sl;
1098
1099                                         colormanage_check_view_settings(&snode->view_settings, "node editor", default_view);
1100                                 }
1101                                 else if (sl->spacetype == SPACE_CLIP) {
1102                                         SpaceClip *sclip = (SpaceClip *) sl;
1103
1104                                         colormanage_check_view_settings(&sclip->view_settings, "clip editor", default_view);
1105                                 }
1106                         }
1107                 }
1108         }
1109
1110         for (scene = bmain->scene.first; scene; scene = scene->id.next) {
1111                 ImageFormatData *imf =  &scene->r.im_format;
1112
1113                 colormanage_check_display_settings(&imf->display_settings, "scene", default_display);
1114
1115                 colormanage_check_view_settings(&imf->view_settings, "scene", default_view);
1116         }
1117 #else
1118         (void) bmain;
1119 #endif
1120 }
1121
1122 const ColorManagedViewSettings *IMB_view_settings_get_effective(wmWindow *win,
1123                 const ColorManagedViewSettings *view_settings)
1124 {
1125         if (view_settings->flag & COLORMANAGE_VIEW_USE_GLOBAL) {
1126                 return &win->view_settings;
1127         }
1128
1129         return view_settings;
1130 }
1131
1132 /*********************** Display functions *************************/
1133
1134 #ifdef WITH_OCIO
1135 ColorManagedDisplay *colormanage_display_get_default(void)
1136 {
1137         ConstConfigRcPtr *config = OCIO_getCurrentConfig();
1138         const char *display;
1139
1140         if (!config) {
1141                 /* no valid OCIO configuration, can't get default display */
1142
1143                 return NULL;
1144         }
1145
1146         display = OCIO_configGetDefaultDisplay(config);
1147
1148         OCIO_configRelease(config);
1149
1150         if (display[0] == '\0')
1151                 return NULL;
1152
1153         return colormanage_display_get_named(display);
1154 }
1155 #endif
1156
1157 ColorManagedDisplay *colormanage_display_add(const char *name)
1158 {
1159         ColorManagedDisplay *display;
1160         int index = 0;
1161
1162         if (global_displays.last) {
1163                 ColorManagedDisplay *last_display = global_displays.last;
1164
1165                 index = last_display->index;
1166         }
1167
1168         display = MEM_callocN(sizeof(ColorManagedDisplay), "ColorManagedDisplay");
1169
1170         display->index = index + 1;
1171
1172         BLI_strncpy(display->name, name, sizeof(display->name));
1173
1174         BLI_addtail(&global_displays, display);
1175
1176         return display;
1177 }
1178
1179 ColorManagedDisplay *colormanage_display_get_named(const char *name)
1180 {
1181         ColorManagedDisplay *display;
1182
1183         for (display = global_displays.first; display; display = display->next) {
1184                 if (!strcmp(display->name, name))
1185                         return display;
1186         }
1187
1188         return NULL;
1189 }
1190
1191 ColorManagedDisplay *colormanage_display_get_indexed(int index)
1192 {
1193         /* display indices are 1-based */
1194         return BLI_findlink(&global_displays, index - 1);
1195 }
1196
1197 int IMB_colormanagement_display_get_named_index(const char *name)
1198 {
1199         ColorManagedDisplay *display;
1200
1201         display = colormanage_display_get_named(name);
1202
1203         if (display) {
1204                 return display->index;
1205         }
1206
1207         return 0;
1208 }
1209
1210 const char *IMB_colormanagement_display_get_indexed_name(int index)
1211 {
1212         ColorManagedDisplay *display;
1213
1214         display = colormanage_display_get_indexed(index);
1215
1216         if (display) {
1217                 return display->name;
1218         }
1219
1220         return NULL;
1221 }
1222
1223 const char *IMB_colormanagement_display_get_default_name(void)
1224 {
1225 #ifdef WITH_OCIO
1226         ColorManagedDisplay *display = colormanage_display_get_default();
1227
1228         return display->name;
1229 #else
1230         return NULL;
1231 #endif
1232 }
1233
1234 /*********************** View functions *************************/
1235
1236 #ifdef WITH_OCIO
1237 ColorManagedView *colormanage_view_get_default(const ColorManagedDisplay *display)
1238 {
1239         ConstConfigRcPtr *config = OCIO_getCurrentConfig();
1240         const char *name;
1241
1242         if (!config) {
1243                 /* no valid OCIO configuration, can't get default view */
1244
1245                 return NULL;
1246         }
1247
1248         name = OCIO_configGetDefaultView(config, display->name);
1249
1250         OCIO_configRelease(config);
1251
1252         if (name[0] == '\0')
1253                 return NULL;
1254
1255         return colormanage_view_get_named(name);
1256 }
1257 #endif
1258
1259 ColorManagedView *colormanage_view_add(const char *name)
1260 {
1261         ColorManagedView *view;
1262         int index = global_tot_view;
1263
1264         view = MEM_callocN(sizeof(ColorManagedView), "ColorManagedView");
1265         view->index = index + 1;
1266         BLI_strncpy(view->name, name, sizeof(view->name));
1267
1268         BLI_addtail(&global_views, view);
1269
1270         global_tot_view++;
1271
1272         return view;
1273 }
1274
1275 ColorManagedView *colormanage_view_get_named(const char *name)
1276 {
1277         ColorManagedView *view;
1278
1279         for (view = global_views.first; view; view = view->next) {
1280                 if (!strcmp(view->name, name))
1281                         return view;
1282         }
1283
1284         return NULL;
1285 }
1286
1287 ColorManagedView *colormanage_view_get_indexed(int index)
1288 {
1289         /* view transform indices are 1-based */
1290         return BLI_findlink(&global_views, index - 1);
1291 }
1292
1293 int IMB_colormanagement_view_get_named_index(const char *name)
1294 {
1295         ColorManagedView *view = colormanage_view_get_named(name);
1296
1297         if (view) {
1298                 return view->index;
1299         }
1300
1301         return 0;
1302 }
1303
1304 const char *IMB_colormanagement_view_get_indexed_name(int index)
1305 {
1306         ColorManagedView *view = colormanage_view_get_indexed(index);
1307
1308         if (view) {
1309                 return view->name;
1310         }
1311
1312         return "NONE";
1313 }
1314
1315 /*********************** RNA helper functions *************************/
1316
1317 void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totitem)
1318 {
1319         ColorManagedDisplay *display;
1320
1321         for (display = global_displays.first; display; display = display->next) {
1322                 EnumPropertyItem item;
1323
1324                 item.value = display->index;
1325                 item.name = display->name;
1326                 item.identifier = display->name;
1327                 item.icon = 0;
1328                 item.description = "";
1329
1330                 RNA_enum_item_add(items, totitem, &item);
1331         }
1332 }
1333
1334 static void colormanagement_view_item_add(EnumPropertyItem **items, int *totitem, ColorManagedView *view)
1335 {
1336         EnumPropertyItem item;
1337
1338         item.value = view->index;
1339         item.name = view->name;
1340         item.identifier = view->name;
1341         item.icon = 0;
1342         item.description = "";
1343
1344         RNA_enum_item_add(items, totitem, &item);
1345 }
1346
1347 void IMB_colormanagement_view_items_add(EnumPropertyItem **items, int *totitem, const char *display_name)
1348 {
1349         ColorManagedDisplay *display = colormanage_display_get_named(display_name);
1350         ColorManagedView *view;
1351
1352         /* OCIO_TODO: try to get rid of such a hackish stuff */
1353         view = colormanage_view_get_named("ACES ODT Tonecurve");
1354         if (view) {
1355                 colormanagement_view_item_add(items, totitem, view);
1356         }
1357
1358         if (display) {
1359                 LinkData *display_view;
1360
1361                 for (display_view = display->views.first; display_view; display_view = display_view->next) {
1362                         view = display_view->data;
1363
1364                         colormanagement_view_item_add(items, totitem, view);
1365                 }
1366         }
1367 }
1368
1369 /*********************** Partial display buffer update  *************************/
1370
1371 /*
1372  * Partial display update is supposed to be used by such areas as compositor,
1373  * which re-calculates parts of the images and requires updating only
1374  * specified areas of buffers to provide better visual feedback.
1375  *
1376  * To achieve this special context is being constructed. This context is
1377  * holding all buffers which were color managed and transformations which
1378  * need to be applied on this buffers to make them valid.
1379  *
1380  * Updating happens for all buffers from this context using given linear
1381  * float buffer and rectangle area which shall be updated.
1382  *
1383  * Updating every rectangle is thread-save operation due to buffers are
1384  * referenced by the context, so they shouldn't have been deleted
1385  * during execution.
1386  */
1387
1388 typedef struct PartialBufferUpdateItem {
1389         struct PartialBufferUpdateItem *next, *prev;
1390
1391         unsigned char *display_buffer;
1392         void *cache_handle;
1393
1394         int display, view;
1395
1396 #ifdef WITH_OCIO
1397         ConstProcessorRcPtr *processor;
1398 #endif
1399
1400         imb_tonecurveCb tonecurve_func;
1401 } PartialBufferUpdateItem;
1402
1403 typedef struct PartialBufferUpdateContext {
1404         int buffer_width;
1405         int dither, predivide;
1406
1407         ListBase items;
1408 } PartialBufferUpdateContext;
1409
1410 PartialBufferUpdateContext *IMB_partial_buffer_update_context_new(ImBuf *ibuf)
1411 {
1412         PartialBufferUpdateContext *context = NULL;
1413
1414 #ifdef WITH_OCIO
1415         int display;
1416
1417         context = MEM_callocN(sizeof(PartialBufferUpdateContext), "partial buffer update context");
1418
1419         context->buffer_width = ibuf->x;
1420
1421         context->predivide = ibuf->flags & IB_cm_predivide;
1422         context->dither = ibuf->dither;
1423
1424         if (!ibuf->display_buffer_flags) {
1425                 /* there's no cached display buffers, so no need to iterate though bit fields */
1426                 return context;
1427         }
1428
1429         for (display = 0; display < global_tot_display; display++) {
1430                 ColormanageCacheDisplaySettings display_settings = {0};
1431                 int display_index = display + 1; /* displays in configuration are 1-based */
1432                 const char *display_name = IMB_colormanagement_display_get_indexed_name(display_index);
1433                 int view_flags = ibuf->display_buffer_flags[display];
1434                 int view = 0;
1435
1436                 display_settings.display = display_index;
1437
1438                 while (view_flags != 0) {
1439                         if (view_flags % 2 == 1) {
1440                                 ColormanageCacheViewSettings view_settings = {0};
1441                                 unsigned char *display_buffer;
1442                                 void *cache_handle;
1443                                 int view_index = view + 1; /* views in configuration are 1-based */
1444
1445                                 view_settings.view = view_index;
1446
1447                                 display_buffer =
1448                                         colormanage_cache_get_validated(ibuf, &view_settings, &display_settings, &cache_handle);
1449
1450                                 if (display_buffer) {
1451                                         PartialBufferUpdateItem *item;
1452                                         const char *view_name = IMB_colormanagement_view_get_indexed_name(view_index);
1453                                         float exposure, gamma;
1454
1455                                         colormanage_cache_get_cache_data(cache_handle, &exposure, &gamma);
1456
1457                                         item = MEM_callocN(sizeof(PartialBufferUpdateItem), "partial buffer update item");
1458
1459                                         item->display_buffer = display_buffer;
1460                                         item->cache_handle = cache_handle;
1461                                         item->display = display_index;
1462                                         item->view = view_index;
1463
1464                                         if (!strcmp(view_name, "ACES ODT Tonecurve")) {
1465                                                 item->tonecurve_func = IMB_ratio_preserving_odt_tonecurve;
1466                                         }
1467                                         else {
1468                                                 ConstProcessorRcPtr *processor;
1469
1470                                                 processor = create_display_buffer_processor(view_name, display_name, exposure, gamma);
1471
1472                                                 item->processor = processor;
1473                                         }
1474
1475                                         BLI_addtail(&context->items, item);
1476                                 }
1477                         }
1478
1479                         view_flags /= 2;
1480                         view++;
1481                 }
1482         }
1483 #else
1484         (void) ibuf;
1485 #endif
1486
1487         return context;
1488 }
1489
1490 void IMB_partial_buffer_update_rect(PartialBufferUpdateContext *context, const float *linear_buffer, struct rcti *rect)
1491 {
1492 #ifdef WITH_OCIO
1493         PartialBufferUpdateItem *item;
1494
1495         for (item = context->items.first; item; item = item->next) {
1496                 if (item->processor || item->tonecurve_func) {
1497                         unsigned char *display_buffer = item->display_buffer;
1498                         int x, y;
1499
1500                         for (y = rect->ymin; y < rect->ymax; y++) {
1501                                 for (x = rect->xmin; x < rect->xmax; x++) {
1502                                         int index = (y * context->buffer_width + x) * 4;
1503                                         float pixel[4];
1504
1505                                         if (item->processor) {
1506                                                 copy_v4_v4(pixel, (float *)linear_buffer + index);
1507
1508                                                 OCIO_processorApplyRGBA(item->processor, pixel);
1509
1510                                                 rgba_float_to_uchar(display_buffer + index, pixel);
1511                                         }
1512                                         else {
1513                                                 IMB_buffer_byte_from_float_tonecurve(display_buffer + index, linear_buffer + index,
1514                                                                                      4, context->dither, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB,
1515                                                                                      context->predivide, 1, 1, 1, 1, item->tonecurve_func);
1516                                         }
1517                                 }
1518                         }
1519                 }
1520         }
1521 #else
1522         (void) context;
1523         (void) linear_buffer;
1524         (void) rect;
1525 #endif
1526 }
1527
1528 void IMB_partial_buffer_update_free(PartialBufferUpdateContext *context, ImBuf *ibuf)
1529 {
1530 #ifdef WITH_OCIO
1531         PartialBufferUpdateItem *item;
1532
1533         IMB_display_buffer_invalidate(ibuf);
1534
1535         item = context->items.first;
1536         while (item) {
1537                 PartialBufferUpdateItem *item_next = item->next;
1538
1539                 /* displays are 1-based, need to go to 0-based arrays indices */
1540                 ibuf->display_buffer_flags[item->display - 1] |= (1 << (item->view - 1));
1541
1542                 colormanage_cache_handle_release(item->cache_handle);
1543
1544                 OCIO_processorRelease(item->processor);
1545
1546                 MEM_freeN(item);
1547
1548                 item = item_next;
1549         }
1550
1551         MEM_freeN(context);
1552 #else
1553         (void) context;
1554         (void) ibuf;
1555 #endif
1556 }