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