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