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