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