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