49f25c274b886b024b8183f8fc11006d86087738
[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         display_buffer_apply_threaded(ibuf, ibuf->rect_float, display_buffer, tonecurve_func,
706                                       do_display_buffer_apply_tonemap_thread);
707 }
708
709 static void *do_display_buffer_apply_ocio_thread(void *handle_v)
710 {
711         DisplayBufferThread *handle = (DisplayBufferThread *) handle_v;
712         ConstProcessorRcPtr *processor = (ConstProcessorRcPtr *) handle->processor;
713         PackedImageDesc *img;
714         float *buffer = handle->buffer;
715         unsigned char *display_buffer = handle->display_buffer;
716         int channels = handle->channels;
717         int width = handle->width;
718         int height = handle->tot_line;
719         int dither = handle->dither;
720         int predivide = handle->predivide;
721
722         img = OCIO_createPackedImageDesc(buffer, width, height, channels, sizeof(float),
723                                          channels * sizeof(float), channels * sizeof(float) * width);
724
725         OCIO_processorApply(processor, img);
726
727         OCIO_packedImageDescRelease(img);
728
729         /* do conversion */
730         IMB_buffer_byte_from_float(display_buffer, buffer,
731                                    channels, dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
732                                    predivide, width, height, width, width);
733
734         return NULL;
735 }
736
737 static ConstProcessorRcPtr *create_display_buffer_processor(const char *view_transform, const char *display,
738                                                             float exposure, float gamma)
739 {
740         ConstConfigRcPtr *config = OCIO_getCurrentConfig();
741         DisplayTransformRcPtr *dt;
742         ExponentTransformRcPtr *et;
743         MatrixTransformRcPtr *mt;
744         ConstProcessorRcPtr *processor;
745
746         float exponent = 1.0f / MAX2(FLT_EPSILON, gamma);
747         const float exponent4f[] = {exponent, exponent, exponent, exponent};
748
749         float gain = powf(2.0f, exposure);
750         const float scale4f[] = {gain, gain, gain, gain};
751         float m44[16], offset4[4];
752
753         if (!config) {
754                 /* there's no valid OCIO configuration, can't create processor */
755
756                 return NULL;
757         }
758
759         dt = OCIO_createDisplayTransform();
760
761         /* OCIO_TODO: get rid of hardcoded input space */
762         OCIO_displayTransformSetInputColorSpaceName(dt, global_role_linear);
763
764         OCIO_displayTransformSetView(dt, view_transform);
765         OCIO_displayTransformSetDisplay(dt, display);
766
767         /* fstop exposure control */
768         OCIO_matrixTransformScale(m44, offset4, scale4f);
769         mt = OCIO_createMatrixTransform();
770         OCIO_matrixTransformSetValue(mt, m44, offset4);
771         OCIO_displayTransformSetLinearCC(dt, (ConstTransformRcPtr *) mt);
772
773         /* post-display gamma transform */
774         et = OCIO_createExponentTransform();
775         OCIO_exponentTransformSetValue(et, exponent4f);
776         OCIO_displayTransformSetDisplayCC(dt, (ConstTransformRcPtr *) et);
777
778         processor = OCIO_configGetProcessor(config, (ConstTransformRcPtr *) dt);
779
780         OCIO_exponentTransformRelease(et);
781         OCIO_displayTransformRelease(dt);
782         OCIO_configRelease(config);
783
784         return processor;
785 }
786
787 static void display_buffer_apply_ocio(ImBuf *ibuf, unsigned char *display_buffer,
788                                       const ColorManagedViewSettings *view_settings,
789                                       const ColorManagedDisplaySettings *display_settings)
790 {
791         ConstProcessorRcPtr *processor;
792         const float gamma = view_settings->gamma;
793         const float exposure = view_settings->exposure;
794         const char *view_transform = view_settings->view_transform;
795         const char *display = display_settings->display_device;
796         float *rect_float;
797
798         rect_float = MEM_dupallocN(ibuf->rect_float);
799
800         processor = create_display_buffer_processor(view_transform, display, exposure, gamma);
801
802         if (processor) {
803                 display_buffer_apply_threaded(ibuf, rect_float, display_buffer, processor,
804                                               do_display_buffer_apply_ocio_thread);
805         }
806
807         OCIO_processorRelease(processor);
808
809         MEM_freeN(rect_float);
810 }
811
812 static void colormanage_display_buffer_process(ImBuf *ibuf, unsigned char *display_buffer,
813                                                const ColorManagedViewSettings *view_settings,
814                                                const ColorManagedDisplaySettings *display_settings)
815 {
816         const char *view_transform = view_settings->view_transform;
817
818         if (!strcmp(view_transform, "ACES ODT Tonecurve")) {
819                 /* special case for Mango team, this does not actually apply
820                  * any input space -> display space conversion and just applies
821                  * a tonecurve for better linear float -> sRGB byte conversion
822                  */
823                 display_buffer_apply_tonemap(ibuf, display_buffer, IMB_ratio_preserving_odt_tonecurve);
824         }
825         else {
826                 display_buffer_apply_ocio(ibuf, display_buffer, view_settings, display_settings);
827         }
828
829 }
830
831 static void colormanage_flags_allocate(ImBuf *ibuf)
832 {
833         if (global_tot_display == 0)
834                 return;
835
836         ibuf->display_buffer_flags = MEM_callocN(sizeof(unsigned int) * global_tot_display, "imbuf display_buffer_flags");
837 }
838 #endif
839
840 void IMB_colormanage_cache_free(ImBuf *ibuf)
841 {
842         if (ibuf->display_buffer_flags) {
843                 MEM_freeN(ibuf->display_buffer_flags);
844
845                 ibuf->display_buffer_flags = NULL;
846         }
847
848         if (ibuf->colormanage_cache) {
849                 ColormnaageCacheData *cache_data = colormanage_cachedata_get(ibuf);
850                 struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
851
852                 if (cache_data) {
853                         MEM_freeN(cache_data);
854                 }
855
856                 if (moviecache) {
857                         IMB_moviecache_free(moviecache);
858                 }
859
860                 MEM_freeN(ibuf->colormanage_cache);
861
862                 ibuf->colormanage_cache = NULL;
863         }
864 }
865
866 unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
867                                           const ColorManagedDisplaySettings *display_settings, void **cache_handle)
868 {
869         const char *view_transform = view_settings->view_transform;
870
871         *cache_handle = NULL;
872
873 #ifdef WITH_OCIO
874
875         if (!ibuf->x || !ibuf->y)
876                 return NULL;
877
878         /* OCIO_TODO: support colormanaged byte buffers */
879         if (!strcmp(view_transform, "NONE") ||
880             !ibuf->rect_float ||
881             global_tot_display == 0 ||
882             global_tot_view == 0)
883         {
884                 /* currently only view-transformation is allowed, input and display
885                  * spaces are hard-coded, so if there's no view transform applying
886                  * it's safe to suppose standard byte buffer is used for display
887                  */
888
889                 if (!ibuf->rect)
890                         IMB_rect_from_float(ibuf);
891
892                 return (unsigned char *) ibuf->rect;
893         }
894         else {
895                 unsigned char *display_buffer;
896                 int buffer_size;
897                 ColormanageCacheViewSettings cache_view_settings;
898                 ColormanageCacheDisplaySettings cache_display_settings;
899
900                 colormanage_view_settings_to_cache(&cache_view_settings, view_settings);
901                 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
902
903                 /* ensure color management bit fields exists */
904                 if (!ibuf->display_buffer_flags)
905                         colormanage_flags_allocate(ibuf);
906
907                 display_buffer = colormanage_cache_get(ibuf, &cache_view_settings, &cache_display_settings, cache_handle);
908
909                 if (display_buffer) {
910                         return display_buffer;
911                 }
912
913                 /* OCIO_TODO: in case when image is being resized it is possible
914                  *            to save buffer allocation here
915                  *
916                  *            actually not because there might be other users of
917                  *            that buffer which better not to change
918                  */
919
920                 buffer_size = ibuf->channels * ibuf->x * ibuf->y * sizeof(float);
921                 display_buffer = MEM_callocN(buffer_size, "imbuf display buffer");
922
923                 colormanage_display_buffer_process(ibuf, display_buffer, view_settings, display_settings);
924
925                 colormanage_cache_put(ibuf, &cache_view_settings, &cache_display_settings, display_buffer, cache_handle);
926
927                 return display_buffer;
928         }
929 #else
930         /* no OCIO support, simply return byte buffer which was
931          * generated from float buffer (if any) using standard
932          * profiles without applying any view / display transformation */
933
934         (void) view_settings;
935         (void) view_transform;
936         (void) display_settings;
937
938         if (!ibuf->rect) {
939                 IMB_rect_from_float(ibuf);
940         }
941
942         return (unsigned char*) ibuf->rect;
943 #endif
944 }
945
946 void IMB_display_buffer_to_imbuf_rect(ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
947                                       const ColorManagedDisplaySettings *display_settings)
948 {
949 #ifdef WITH_OCIO
950         const char *view_transform = view_settings->view_transform;
951
952         if (!ibuf->rect_float)
953                 return;
954
955         if (!strcmp(view_transform, "NONE") ||
956             !ibuf->rect_float ||
957             global_tot_display == 0 ||
958             global_tot_view == 0)
959         {
960                 if (!ibuf->rect)
961                         IMB_rect_from_float(ibuf);
962         }
963         else {
964                 if (!ibuf->rect) {
965                         imb_addrectImBuf(ibuf);
966                 }
967
968                 colormanage_display_buffer_process(ibuf, (unsigned char *) ibuf->rect, view_settings, display_settings);
969         }
970 #else
971         (void) view_settings;
972         (void) display_settings;
973
974         if (!ibuf->rect)
975                 IMB_rect_from_float(ibuf);
976 #endif
977 }
978
979 void IMB_display_buffer_release(void *cache_handle)
980 {
981         if (cache_handle) {
982                 colormanage_cache_handle_release(cache_handle);
983         }
984 }
985
986 void IMB_display_buffer_invalidate(ImBuf *ibuf)
987 {
988         /* if there's no display_buffer_flags this means there's no color managed
989          * buffers created for this imbuf, no need to invalidate
990          */
991         if (ibuf->display_buffer_flags) {
992                 memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
993         }
994 }
995
996 #ifdef WITH_OCIO
997 static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings, const char *what,
998                                                const ColorManagedDisplay *default_display)
999 {
1000         if (display_settings->display_device[0] == '\0') {
1001                 BLI_strncpy(display_settings->display_device, default_display->name, sizeof(display_settings->display_device));
1002         }
1003         else {
1004                 ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device);
1005
1006                 if (!display) {
1007                         printf("Blender color management: display \"%s\" used by %s not found, setting to default (\"%s\").\n",
1008                                display_settings->display_device, what, default_display->name);
1009
1010                         BLI_strncpy(display_settings->display_device, default_display->name,
1011                                     sizeof(display_settings->display_device));
1012                 }
1013         }
1014 }
1015
1016 static void colormanage_check_view_settings(ColorManagedViewSettings *view_settings, const char *what,
1017                                             const ColorManagedView *default_view)
1018 {
1019         if (view_settings->view_transform[0] == '\0') {
1020                 BLI_strncpy(view_settings->view_transform, "NONE", sizeof(view_settings->view_transform));
1021         }
1022         else if (!strcmp(view_settings->view_transform, "NONE")) {
1023                 /* pass */
1024         }
1025         else {
1026                 ColorManagedView *view = colormanage_view_get_named(view_settings->view_transform);
1027
1028                 if (!view) {
1029                         printf("Blender color management: %s view \"%s\" not found, setting default \"%s\".\n",
1030                                what, view_settings->view_transform, default_view->name);
1031
1032                         BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1033                 }
1034         }
1035
1036         /* OCIO_TODO: move to do_versions() */
1037         if (view_settings->exposure == 0.0f && view_settings->gamma == 0.0f) {
1038                 view_settings->flag |= COLORMANAGE_VIEW_USE_GLOBAL;
1039                 view_settings->exposure = 0.0f;
1040                 view_settings->gamma = 1.0f;
1041         }
1042 }
1043 #endif
1044
1045 void IMB_colormanagement_check_file_config(Main *bmain)
1046 {
1047 #ifdef WITH_OCIO
1048         wmWindowManager *wm = bmain->wm.first;
1049         wmWindow *win;
1050         bScreen *sc;
1051         Scene *scene;
1052
1053         ColorManagedDisplay *default_display;
1054         ColorManagedView *default_view;
1055
1056         default_display = colormanage_display_get_default();
1057
1058         if (!default_display) {
1059                 /* happens when OCIO configuration is incorrect */
1060                 return;
1061         }
1062
1063         default_view = colormanage_view_get_default(default_display);
1064
1065         if (!default_view) {
1066                 /* happens when OCIO configuration is incorrect */
1067                 return;
1068         }
1069
1070         if (wm) {
1071                 for (win = wm->windows.first; win; win = win->next) {
1072                         colormanage_check_display_settings(&win->display_settings, "window", default_display);
1073
1074                         colormanage_check_view_settings(&win->view_settings, "window", default_view);
1075                 }
1076         }
1077
1078         for (sc = bmain->screen.first; sc; sc = sc->id.next) {
1079                 ScrArea *sa;
1080
1081                 for (sa = sc->areabase.first; sa; sa = sa->next) {
1082                         SpaceLink *sl;
1083                         for (sl = sa->spacedata.first; sl; sl = sl->next) {
1084
1085                                 if (sl->spacetype == SPACE_IMAGE) {
1086                                         SpaceImage *sima = (SpaceImage *) sl;
1087
1088                                         colormanage_check_view_settings(&sima->view_settings, "image editor", default_view);
1089                                 }
1090                                 else if (sl->spacetype == SPACE_NODE) {
1091                                         SpaceNode *snode = (SpaceNode *) sl;
1092
1093                                         colormanage_check_view_settings(&snode->view_settings, "node editor", default_view);
1094                                 }
1095                                 else if (sl->spacetype == SPACE_CLIP) {
1096                                         SpaceClip *sclip = (SpaceClip *) sl;
1097
1098                                         colormanage_check_view_settings(&sclip->view_settings, "clip editor", default_view);
1099                                 }
1100                         }
1101                 }
1102         }
1103
1104         for (scene = bmain->scene.first; scene; scene = scene->id.next) {
1105                 ImageFormatData *imf =  &scene->r.im_format;
1106
1107                 colormanage_check_display_settings(&imf->display_settings, "scene", default_display);
1108
1109                 colormanage_check_view_settings(&imf->view_settings, "scene", default_view);
1110         }
1111 #else
1112         (void) bmain;
1113 #endif
1114 }
1115
1116 const ColorManagedViewSettings *IMB_view_settings_get_effective(wmWindow *win,
1117                 const ColorManagedViewSettings *view_settings)
1118 {
1119         if (view_settings->flag & COLORMANAGE_VIEW_USE_GLOBAL) {
1120                 return &win->view_settings;
1121         }
1122
1123         return view_settings;
1124 }
1125
1126 /*********************** Display functions *************************/
1127
1128 #ifdef WITH_OCIO
1129 ColorManagedDisplay *colormanage_display_get_default(void)
1130 {
1131         ConstConfigRcPtr *config = OCIO_getCurrentConfig();
1132         const char *display;
1133
1134         if (!config) {
1135                 /* no valid OCIO configuration, can't get default display */
1136
1137                 return NULL;
1138         }
1139
1140         display = OCIO_configGetDefaultDisplay(config);
1141
1142         OCIO_configRelease(config);
1143
1144         if (display[0] == '\0')
1145                 return NULL;
1146
1147         return colormanage_display_get_named(display);
1148 }
1149 #endif
1150
1151 ColorManagedDisplay *colormanage_display_add(const char *name)
1152 {
1153         ColorManagedDisplay *display;
1154         int index = 0;
1155
1156         if (global_displays.last) {
1157                 ColorManagedDisplay *last_display = global_displays.last;
1158
1159                 index = last_display->index;
1160         }
1161
1162         display = MEM_callocN(sizeof(ColorManagedDisplay), "ColorManagedDisplay");
1163
1164         display->index = index + 1;
1165
1166         BLI_strncpy(display->name, name, sizeof(display->name));
1167
1168         BLI_addtail(&global_displays, display);
1169
1170         return display;
1171 }
1172
1173 ColorManagedDisplay *colormanage_display_get_named(const char *name)
1174 {
1175         ColorManagedDisplay *display;
1176
1177         for (display = global_displays.first; display; display = display->next) {
1178                 if (!strcmp(display->name, name))
1179                         return display;
1180         }
1181
1182         return NULL;
1183 }
1184
1185 ColorManagedDisplay *colormanage_display_get_indexed(int index)
1186 {
1187         /* display indices are 1-based */
1188         return BLI_findlink(&global_displays, index - 1);
1189 }
1190
1191 int IMB_colormanagement_display_get_named_index(const char *name)
1192 {
1193         ColorManagedDisplay *display;
1194
1195         display = colormanage_display_get_named(name);
1196
1197         if (display) {
1198                 return display->index;
1199         }
1200
1201         return 0;
1202 }
1203
1204 const char *IMB_colormanagement_display_get_indexed_name(int index)
1205 {
1206         ColorManagedDisplay *display;
1207
1208         display = colormanage_display_get_indexed(index);
1209
1210         if (display) {
1211                 return display->name;
1212         }
1213
1214         return NULL;
1215 }
1216
1217 const char *IMB_colormanagement_display_get_default_name(void)
1218 {
1219 #ifdef WITH_OCIO
1220         ColorManagedDisplay *display = colormanage_display_get_default();
1221
1222         return display->name;
1223 #else
1224         return NULL;
1225 #endif
1226 }
1227
1228 /*********************** View functions *************************/
1229
1230 #ifdef WITH_OCIO
1231 ColorManagedView *colormanage_view_get_default(const ColorManagedDisplay *display)
1232 {
1233         ConstConfigRcPtr *config = OCIO_getCurrentConfig();
1234         const char *name;
1235
1236         if (!config) {
1237                 /* no valid OCIO configuration, can't get default view */
1238
1239                 return NULL;
1240         }
1241
1242         name = OCIO_configGetDefaultView(config, display->name);
1243
1244         OCIO_configRelease(config);
1245
1246         if (name[0] == '\0')
1247                 return NULL;
1248
1249         return colormanage_view_get_named(name);
1250 }
1251 #endif
1252
1253 ColorManagedView *colormanage_view_add(const char *name)
1254 {
1255         ColorManagedView *view;
1256         int index = global_tot_view;
1257
1258         view = MEM_callocN(sizeof(ColorManagedView), "ColorManagedView");
1259         view->index = index + 1;
1260         BLI_strncpy(view->name, name, sizeof(view->name));
1261
1262         BLI_addtail(&global_views, view);
1263
1264         global_tot_view++;
1265
1266         return view;
1267 }
1268
1269 ColorManagedView *colormanage_view_get_named(const char *name)
1270 {
1271         ColorManagedView *view;
1272
1273         for (view = global_views.first; view; view = view->next) {
1274                 if (!strcmp(view->name, name))
1275                         return view;
1276         }
1277
1278         return NULL;
1279 }
1280
1281 ColorManagedView *colormanage_view_get_indexed(int index)
1282 {
1283         /* view transform indices are 1-based */
1284         return BLI_findlink(&global_views, index - 1);
1285 }
1286
1287 int IMB_colormanagement_view_get_named_index(const char *name)
1288 {
1289         ColorManagedView *view = colormanage_view_get_named(name);
1290
1291         if (view) {
1292                 return view->index;
1293         }
1294
1295         return 0;
1296 }
1297
1298 const char *IMB_colormanagement_view_get_indexed_name(int index)
1299 {
1300         ColorManagedView *view = colormanage_view_get_indexed(index);
1301
1302         if (view) {
1303                 return view->name;
1304         }
1305
1306         return "NONE";
1307 }
1308
1309 /*********************** RNA helper functions *************************/
1310
1311 void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totitem)
1312 {
1313         ColorManagedDisplay *display;
1314
1315         for (display = global_displays.first; display; display = display->next) {
1316                 EnumPropertyItem item;
1317
1318                 item.value = display->index;
1319                 item.name = display->name;
1320                 item.identifier = display->name;
1321                 item.icon = 0;
1322                 item.description = "";
1323
1324                 RNA_enum_item_add(items, totitem, &item);
1325         }
1326 }
1327
1328 static void colormanagement_view_item_add(EnumPropertyItem **items, int *totitem, ColorManagedView *view)
1329 {
1330         EnumPropertyItem item;
1331
1332         item.value = view->index;
1333         item.name = view->name;
1334         item.identifier = view->name;
1335         item.icon = 0;
1336         item.description = "";
1337
1338         RNA_enum_item_add(items, totitem, &item);
1339 }
1340
1341 void IMB_colormanagement_view_items_add(EnumPropertyItem **items, int *totitem, const char *display_name)
1342 {
1343         ColorManagedDisplay *display = colormanage_display_get_named(display_name);
1344         ColorManagedView *view;
1345
1346         /* OCIO_TODO: try to get rid of such a hackish stuff */
1347         view = colormanage_view_get_named("ACES ODT Tonecurve");
1348         if (view) {
1349                 colormanagement_view_item_add(items, totitem, view);
1350         }
1351
1352         if (display) {
1353                 LinkData *display_view;
1354
1355                 for (display_view = display->views.first; display_view; display_view = display_view->next) {
1356                         view = display_view->data;
1357
1358                         colormanagement_view_item_add(items, totitem, view);
1359                 }
1360         }
1361 }
1362
1363 /*********************** Partial display buffer update  *************************/
1364
1365 /*
1366  * Partial display update is supposed to be used by such areas as compositor,
1367  * which re-calculates parts of the images and requires updating only
1368  * specified areas of buffers to provide better visual feedback.
1369  *
1370  * To achieve this special context is being constructed. This context is
1371  * holding all buffers which were color managed and transformations which
1372  * need to be applied on this buffers to make them valid.
1373  *
1374  * Updating happens for all buffers from this context using given linear
1375  * float buffer and rectangle area which shall be updated.
1376  *
1377  * Updating every rectangle is thread-save operation due to buffers are
1378  * referenced by the context, so they shouldn't have been deleted
1379  * during execution.
1380  */
1381
1382 typedef struct PartialBufferUpdateItem {
1383         struct PartialBufferUpdateItem *next, *prev;
1384
1385         unsigned char *display_buffer;
1386         void *cache_handle;
1387
1388         int display, view;
1389
1390 #ifdef WITH_OCIO
1391         ConstProcessorRcPtr *processor;
1392 #endif
1393
1394         imb_tonecurveCb tonecurve_func;
1395 } PartialBufferUpdateItem;
1396
1397 typedef struct PartialBufferUpdateContext {
1398         int buffer_width;
1399         int dither, predivide;
1400
1401         ListBase items;
1402 } PartialBufferUpdateContext;
1403
1404 PartialBufferUpdateContext *IMB_partial_buffer_update_context_new(ImBuf *ibuf)
1405 {
1406         PartialBufferUpdateContext *context = NULL;
1407
1408 #ifdef WITH_OCIO
1409         int display;
1410
1411         context = MEM_callocN(sizeof(PartialBufferUpdateContext), "partial buffer update context");
1412
1413         context->buffer_width = ibuf->x;
1414
1415         context->predivide = ibuf->flags & IB_cm_predivide;
1416         context->dither = ibuf->dither;
1417
1418         if (!ibuf->display_buffer_flags) {
1419                 /* there's no cached display buffers, so no need to iterate though bit fields */
1420                 return context;
1421         }
1422
1423         for (display = 0; display < global_tot_display; display++) {
1424                 ColormanageCacheDisplaySettings display_settings = {0};
1425                 int display_index = display + 1; /* displays in configuration are 1-based */
1426                 const char *display_name = IMB_colormanagement_display_get_indexed_name(display_index);
1427                 int view_flags = ibuf->display_buffer_flags[display];
1428                 int view = 0;
1429
1430                 display_settings.display = display_index;
1431
1432                 while (view_flags != 0) {
1433                         if (view_flags % 2 == 1) {
1434                                 ColormanageCacheViewSettings view_settings = {0};
1435                                 unsigned char *display_buffer;
1436                                 void *cache_handle;
1437                                 int view_index = view + 1; /* views in configuration are 1-based */
1438
1439                                 view_settings.view = view_index;
1440
1441                                 display_buffer =
1442                                         colormanage_cache_get_validated(ibuf, &view_settings, &display_settings, &cache_handle);
1443
1444                                 if (display_buffer) {
1445                                         PartialBufferUpdateItem *item;
1446                                         const char *view_name = IMB_colormanagement_view_get_indexed_name(view_index);
1447                                         float exposure, gamma;
1448
1449                                         colormanage_cache_get_cache_data(cache_handle, &exposure, &gamma);
1450
1451                                         item = MEM_callocN(sizeof(PartialBufferUpdateItem), "partial buffer update item");
1452
1453                                         item->display_buffer = display_buffer;
1454                                         item->cache_handle = cache_handle;
1455                                         item->display = display_index;
1456                                         item->view = view_index;
1457
1458                                         if (!strcmp(view_name, "ACES ODT Tonecurve")) {
1459                                                 item->tonecurve_func = IMB_ratio_preserving_odt_tonecurve;
1460                                         }
1461                                         else {
1462                                                 ConstProcessorRcPtr *processor;
1463
1464                                                 processor = create_display_buffer_processor(view_name, display_name, exposure, gamma);
1465
1466                                                 item->processor = processor;
1467                                         }
1468
1469                                         BLI_addtail(&context->items, item);
1470                                 }
1471                         }
1472
1473                         view_flags /= 2;
1474                         view++;
1475                 }
1476         }
1477 #else
1478         (void) ibuf;
1479 #endif
1480
1481         return context;
1482 }
1483
1484 void IMB_partial_buffer_update_rect(PartialBufferUpdateContext *context, const float *linear_buffer, struct rcti *rect)
1485 {
1486 #ifdef WITH_OCIO
1487         PartialBufferUpdateItem *item;
1488
1489         for (item = context->items.first; item; item = item->next) {
1490                 if (item->processor || item->tonecurve_func) {
1491                         unsigned char *display_buffer = item->display_buffer;
1492                         int x, y;
1493
1494                         for (y = rect->ymin; y < rect->ymax; y++) {
1495                                 for (x = rect->xmin; x < rect->xmax; x++) {
1496                                         int index = (y * context->buffer_width + x) * 4;
1497                                         float pixel[4];
1498
1499                                         if (item->processor) {
1500                                                 copy_v4_v4(pixel, (float *)linear_buffer + index);
1501
1502                                                 OCIO_processorApplyRGBA(item->processor, pixel);
1503
1504                                                 rgba_float_to_uchar(display_buffer + index, pixel);
1505                                         }
1506                                         else {
1507                                                 IMB_buffer_byte_from_float_tonecurve(display_buffer + index, linear_buffer + index,
1508                                                                                      4, context->dither, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB,
1509                                                                                      context->predivide, 1, 1, 1, 1, item->tonecurve_func);
1510                                         }
1511                                 }
1512                         }
1513                 }
1514         }
1515 #else
1516         (void) context;
1517         (void) linear_buffer;
1518         (void) rect;
1519 #endif
1520 }
1521
1522 void IMB_partial_buffer_update_free(PartialBufferUpdateContext *context, ImBuf *ibuf)
1523 {
1524 #ifdef WITH_OCIO
1525         PartialBufferUpdateItem *item;
1526
1527         IMB_display_buffer_invalidate(ibuf);
1528
1529         item = context->items.first;
1530         while (item) {
1531                 PartialBufferUpdateItem *item_next = item->next;
1532
1533                 /* displays are 1-based, need to go to 0-based arrays indices */
1534                 ibuf->display_buffer_flags[item->display - 1] |= (1 << (item->view - 1));
1535
1536                 colormanage_cache_handle_release(item->cache_handle);
1537
1538                 OCIO_processorRelease(item->processor);
1539
1540                 MEM_freeN(item);
1541
1542                 item = item_next;
1543         }
1544
1545         MEM_freeN(context);
1546 #else
1547         (void) context;
1548         (void) ibuf;
1549 #endif
1550 }