1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2012 by Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup imbuf
22 */
23
24 #include "IMB_colormanagement.h"
25 #include "IMB_colormanagement_intern.h"
26
27 #include <math.h>
28 #include <string.h>
29
30 #include "DNA_color_types.h"
31 #include "DNA_image_types.h"
32 #include "DNA_movieclip_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_space_types.h"
35
36 #include "IMB_filetype.h"
37 #include "IMB_filter.h"
38 #include "IMB_imbuf.h"
39 #include "IMB_imbuf_types.h"
40 #include "IMB_metadata.h"
41 #include "IMB_moviecache.h"
42
43 #include "MEM_guardedalloc.h"
44
45 #include "BLI_blenlib.h"
46 #include "BLI_math.h"
47 #include "BLI_math_color.h"
48 #include "BLI_rect.h"
49 #include "BLI_string.h"
50 #include "BLI_threads.h"
51
52 #include "BKE_appdir.h"
53 #include "BKE_colortools.h"
54 #include "BKE_context.h"
55 #include "BKE_image.h"
56 #include "BKE_main.h"
57 #include "BKE_sequencer.h"
58
59 #include "RNA_define.h"
60
61 #include <ocio_capi.h>
62
63 /* -------------------------------------------------------------------- */
64 /** \name Global declarations
65 * \{ */
66
67 #define DISPLAY_BUFFER_CHANNELS 4
68
69 /* ** list of all supported color spaces, displays and views */
70 static char global_role_data[MAX_COLORSPACE_NAME];
71 static char global_role_scene_linear[MAX_COLORSPACE_NAME];
72 static char global_role_color_picking[MAX_COLORSPACE_NAME];
73 static char global_role_texture_painting[MAX_COLORSPACE_NAME];
74 static char global_role_default_byte[MAX_COLORSPACE_NAME];
75 static char global_role_default_float[MAX_COLORSPACE_NAME];
76 static char global_role_default_sequencer[MAX_COLORSPACE_NAME];
77
78 static ListBase global_colorspaces = {NULL, NULL};
79 static ListBase global_displays = {NULL, NULL};
80 static ListBase global_views = {NULL, NULL};
81 static ListBase global_looks = {NULL, NULL};
82
83 static int global_tot_colorspace = 0;
84 static int global_tot_display = 0;
85 static int global_tot_view = 0;
86 static int global_tot_looks = 0;
87
88 /* Luma coefficients and XYZ to RGB to be initialized by OCIO. */
89 float imbuf_luma_coefficients[3] = {0.0f};
90 float imbuf_xyz_to_rgb[3][3] = {{0.0f}};
91 float imbuf_rgb_to_xyz[3][3] = {{0.0f}};
92 static float imbuf_xyz_to_linear_srgb[3][3] = {{0.0f}};
93 static float imbuf_linear_srgb_to_xyz[3][3] = {{0.0f}};
94
95 /* lock used by pre-cached processors getters, so processor wouldn't
96 * be created several times
97 * LOCK_COLORMANAGE can not be used since this mutex could be needed to
98 * be locked before pre-cached processor are creating
99 */
100 static pthread_mutex_t processor_lock = BLI_MUTEX_INITIALIZER;
101
102 typedef struct ColormanageProcessor {
103 OCIO_ConstProcessorRcPtr *processor;
104 CurveMapping *curve_mapping;
105 bool is_data_result;
106 } ColormanageProcessor;
107
108 static struct global_glsl_state {
109 /* Actual processor used for GLSL baked LUTs. */
110 /* UI colorspace here refers to the display linear color space,
111 * i.e: The linear color space w.r.t. display chromaticity and radiometry.
112 * We separate the colormanagement process into two steps to be able to
113 * merge UI using alpha blending in the correct color space. */
114 OCIO_ConstProcessorRcPtr *processor_scene_to_ui;
115 OCIO_ConstProcessorRcPtr *processor_ui_to_display;
116
117 /* Settings of processor for comparison. */
118 char look[MAX_COLORSPACE_NAME];
119 char view[MAX_COLORSPACE_NAME];
120 char display[MAX_COLORSPACE_NAME];
121 char input[MAX_COLORSPACE_NAME];
122 float exposure, gamma;
123
124 CurveMapping *curve_mapping, *orig_curve_mapping;
125 bool use_curve_mapping;
126 int curve_mapping_timestamp;
127 OCIO_CurveMappingSettings curve_mapping_settings;
128
129 /* Container for GLSL state needed for OCIO module. */
130 struct OCIO_GLSLDrawState *ocio_glsl_state;
131 } global_glsl_state = {NULL};
132
133 static struct global_color_picking_state {
134 /* Cached processor for color picking conversion. */
135 OCIO_ConstProcessorRcPtr *processor_to;
136 OCIO_ConstProcessorRcPtr *processor_from;
137 bool failed;
138 } global_color_picking_state = {NULL};
139
140 /** \} */
141
142 /* -------------------------------------------------------------------- */
143 /** \name Color Managed Cache
144 * \{ */
145
146 /**
147 * Cache Implementation Notes
148 * ==========================
149 *
150 * All color management cache stuff is stored in two properties of
151 * image buffers:
152 *
153 * 1. display_buffer_flags
154 *
155 * This is a bit field which used to mark calculated transformations
156 * for particular image buffer. Index inside of this array means index
157 * of a color managed display. Element with given index matches view
158 * transformations applied for a given display. So if bit B of array
159 * element B is set to 1, this means display buffer with display index
160 * of A and view transform of B was ever calculated for this imbuf.
161 *
162 * In contrast with indices in global lists of displays and views this
163 * indices are 0-based, not 1-based. This is needed to save some bytes
164 * of memory.
165 *
166 * 2. colormanage_cache
167 *
168 * This is a pointer to a structure which holds all data which is
169 * needed for color management cache to work.
170 *
171 * It contains two parts:
172 * - data
173 * - moviecache
174 *
175 * Data field is used to store additional information about cached
176 * buffers which affects on whether cached buffer could be used.
177 * This data can't go to cache key because changes in this data
178 * shouldn't lead extra buffers adding to cache, it shall
179 * invalidate cached images.
180 *
181 * Currently such a data contains only exposure and gamma, but
182 * would likely extended further.
183 *
184 * data field is not null only for elements of cache, not used for
185 * original image buffers.
186 *
187 * Color management cache is using generic MovieCache implementation
188 * to make it easier to deal with memory limitation.
189 *
190 * Currently color management is using the same memory limitation
191 * pool as sequencer and clip editor are using which means color
192 * managed buffers would be removed from the cache as soon as new
193 * frames are loading for the movie clip and there's no space in
194 * cache.
195 *
196 * Every image buffer has got own movie cache instance, which
197 * means keys for color managed buffers could be really simple
198 * and look up in this cache would be fast and independent from
199 * overall amount of color managed images.
200 */
201
202 /* NOTE: ColormanageCacheViewSettings and ColormanageCacheDisplaySettings are
203 * quite the same as ColorManagedViewSettings and ColorManageDisplaySettings
204 * but they holds indexes of all transformations and color spaces, not
205 * their names.
206 *
207 * This helps avoid extra colorspace / display / view lookup without
208 * requiring to pass all variables which affects on display buffer
209 * to color management cache system and keeps calls small and nice.
210 */
211 typedef struct ColormanageCacheViewSettings {
212 int flag;
213 int look;
214 int view;
215 float exposure;
216 float gamma;
217 float dither;
218 CurveMapping *curve_mapping;
219 } ColormanageCacheViewSettings;
220
221 typedef struct ColormanageCacheDisplaySettings {
222 int display;
223 } ColormanageCacheDisplaySettings;
224
225 typedef struct ColormanageCacheKey {
226 int view; /* view transformation used for display buffer */
227 int display; /* display device name */
228 } ColormanageCacheKey;
229
230 typedef struct ColormanageCacheData {
231 int flag; /* view flags of cached buffer */
232 int look; /* Additional artistics transform */
233 float exposure; /* exposure value cached buffer is calculated with */
234 float gamma; /* gamma value cached buffer is calculated with */
235 float dither; /* dither value cached buffer is calculated with */
236 CurveMapping *curve_mapping; /* curve mapping used for cached buffer */
237 int curve_mapping_timestamp; /* time stamp of curve mapping used for cached buffer */
238 } ColormanageCacheData;
239
240 typedef struct ColormanageCache {
241 struct MovieCache *moviecache;
242
243 ColormanageCacheData *data;
244 } ColormanageCache;
245
colormanage_moviecache_get(const ImBuf * ibuf)246 static struct MovieCache *colormanage_moviecache_get(const ImBuf *ibuf)
247 {
248 if (!ibuf->colormanage_cache) {
249 return NULL;
250 }
251
252 return ibuf->colormanage_cache->moviecache;
253 }
254
colormanage_cachedata_get(const ImBuf * ibuf)255 static ColormanageCacheData *colormanage_cachedata_get(const ImBuf *ibuf)
256 {
257 if (!ibuf->colormanage_cache) {
258 return NULL;
259 }
260
261 return ibuf->colormanage_cache->data;
262 }
263
colormanage_hashhash(const void * key_v)264 static unsigned int colormanage_hashhash(const void *key_v)
265 {
266 const ColormanageCacheKey *key = key_v;
267
268 unsigned int rval = (key->display << 16) | (key->view % 0xffff);
269
270 return rval;
271 }
272
colormanage_hashcmp(const void * av,const void * bv)273 static bool colormanage_hashcmp(const void *av, const void *bv)
274 {
275 const ColormanageCacheKey *a = av;
276 const ColormanageCacheKey *b = bv;
277
278 return ((a->view != b->view) || (a->display != b->display));
279 }
280
colormanage_moviecache_ensure(ImBuf * ibuf)281 static struct MovieCache *colormanage_moviecache_ensure(ImBuf *ibuf)
282 {
283 if (!ibuf->colormanage_cache) {
284 ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage cache");
285 }
286
287 if (!ibuf->colormanage_cache->moviecache) {
288 struct MovieCache *moviecache;
289
290 moviecache = IMB_moviecache_create("colormanage cache",
291 sizeof(ColormanageCacheKey),
292 colormanage_hashhash,
293 colormanage_hashcmp);
294
295 ibuf->colormanage_cache->moviecache = moviecache;
296 }
297
298 return ibuf->colormanage_cache->moviecache;
299 }
300
colormanage_cachedata_set(ImBuf * ibuf,ColormanageCacheData * data)301 static void colormanage_cachedata_set(ImBuf *ibuf, ColormanageCacheData *data)
302 {
303 if (!ibuf->colormanage_cache) {
304 ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage cache");
305 }
306
307 ibuf->colormanage_cache->data = data;
308 }
309
colormanage_view_settings_to_cache(ImBuf * ibuf,ColormanageCacheViewSettings * cache_view_settings,const ColorManagedViewSettings * view_settings)310 static void colormanage_view_settings_to_cache(ImBuf *ibuf,
311 ColormanageCacheViewSettings *cache_view_settings,
312 const ColorManagedViewSettings *view_settings)
313 {
314 int look = IMB_colormanagement_look_get_named_index(view_settings->look);
315 int view = IMB_colormanagement_view_get_named_index(view_settings->view_transform);
316
317 cache_view_settings->look = look;
318 cache_view_settings->view = view;
319 cache_view_settings->exposure = view_settings->exposure;
320 cache_view_settings->gamma = view_settings->gamma;
321 cache_view_settings->dither = ibuf->dither;
322 cache_view_settings->flag = view_settings->flag;
323 cache_view_settings->curve_mapping = view_settings->curve_mapping;
324 }
325
colormanage_display_settings_to_cache(ColormanageCacheDisplaySettings * cache_display_settings,const ColorManagedDisplaySettings * display_settings)326 static void colormanage_display_settings_to_cache(
327 ColormanageCacheDisplaySettings *cache_display_settings,
328 const ColorManagedDisplaySettings *display_settings)
329 {
330 int display = IMB_colormanagement_display_get_named_index(display_settings->display_device);
331
332 cache_display_settings->display = display;
333 }
334
colormanage_settings_to_key(ColormanageCacheKey * key,const ColormanageCacheViewSettings * view_settings,const ColormanageCacheDisplaySettings * display_settings)335 static void colormanage_settings_to_key(ColormanageCacheKey *key,
336 const ColormanageCacheViewSettings *view_settings,
337 const ColormanageCacheDisplaySettings *display_settings)
338 {
339 key->view = view_settings->view;
340 key->display = display_settings->display;
341 }
342
colormanage_cache_get_ibuf(ImBuf * ibuf,ColormanageCacheKey * key,void ** cache_handle)343 static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf,
344 ColormanageCacheKey *key,
345 void **cache_handle)
346 {
347 ImBuf *cache_ibuf;
348 struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
349
350 if (!moviecache) {
351 /* If there's no moviecache it means no color management was applied
352 * on given image buffer before. */
353 return NULL;
354 }
355
356 *cache_handle = NULL;
357
358 cache_ibuf = IMB_moviecache_get(moviecache, key);
359
360 *cache_handle = cache_ibuf;
361
362 return cache_ibuf;
363 }
364
colormanage_cache_get(ImBuf * ibuf,const ColormanageCacheViewSettings * view_settings,const ColormanageCacheDisplaySettings * display_settings,void ** cache_handle)365 static unsigned char *colormanage_cache_get(
366 ImBuf *ibuf,
367 const ColormanageCacheViewSettings *view_settings,
368 const ColormanageCacheDisplaySettings *display_settings,
369 void **cache_handle)
370 {
371 ColormanageCacheKey key;
372 ImBuf *cache_ibuf;
373 int view_flag = 1 << (view_settings->view - 1);
374 CurveMapping *curve_mapping = view_settings->curve_mapping;
375 int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
376
377 colormanage_settings_to_key(&key, view_settings, display_settings);
378
379 /* check whether image was marked as dirty for requested transform */
380 if ((ibuf->display_buffer_flags[display_settings->display - 1] & view_flag) == 0) {
381 return NULL;
382 }
383
384 cache_ibuf = colormanage_cache_get_ibuf(ibuf, &key, cache_handle);
385
386 if (cache_ibuf) {
387 ColormanageCacheData *cache_data;
388
389 BLI_assert(cache_ibuf->x == ibuf->x && cache_ibuf->y == ibuf->y);
390
391 /* only buffers with different color space conversions are being stored
392 * in cache separately. buffer which were used only different exposure/gamma
393 * are re-suing the same cached buffer
394 *
395 * check here which exposure/gamma/curve was used for cached buffer and if they're
396 * different from requested buffer should be re-generated
397 */
398 cache_data = colormanage_cachedata_get(cache_ibuf);
399
400 if (cache_data->look != view_settings->look ||
401 cache_data->exposure != view_settings->exposure ||
402 cache_data->gamma != view_settings->gamma || cache_data->dither != view_settings->dither ||
403 cache_data->flag != view_settings->flag || cache_data->curve_mapping != curve_mapping ||
404 cache_data->curve_mapping_timestamp != curve_mapping_timestamp) {
405 *cache_handle = NULL;
406
407 IMB_freeImBuf(cache_ibuf);
408
409 return NULL;
410 }
411
412 return (unsigned char *)cache_ibuf->rect;
413 }
414
415 return NULL;
416 }
417
colormanage_cache_put(ImBuf * ibuf,const ColormanageCacheViewSettings * view_settings,const ColormanageCacheDisplaySettings * display_settings,unsigned char * display_buffer,void ** cache_handle)418 static void colormanage_cache_put(ImBuf *ibuf,
419 const ColormanageCacheViewSettings *view_settings,
420 const ColormanageCacheDisplaySettings *display_settings,
421 unsigned char *display_buffer,
422 void **cache_handle)
423 {
424 ColormanageCacheKey key;
425 ImBuf *cache_ibuf;
426 ColormanageCacheData *cache_data;
427 int view_flag = 1 << (view_settings->view - 1);
428 struct MovieCache *moviecache = colormanage_moviecache_ensure(ibuf);
429 CurveMapping *curve_mapping = view_settings->curve_mapping;
430 int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0;
431
432 colormanage_settings_to_key(&key, view_settings, display_settings);
433
434 /* mark display buffer as valid */
435 ibuf->display_buffer_flags[display_settings->display - 1] |= view_flag;
436
437 /* buffer itself */
438 cache_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0);
439 cache_ibuf->rect = (unsigned int *)display_buffer;
440
441 cache_ibuf->mall |= IB_rect;
442 cache_ibuf->flags |= IB_rect;
443
444 /* Store data which is needed to check whether cached buffer
445 * could be used for color managed display settings. */
446 cache_data = MEM_callocN(sizeof(ColormanageCacheData), "color manage cache imbuf data");
447 cache_data->look = view_settings->look;
448 cache_data->exposure = view_settings->exposure;
449 cache_data->gamma = view_settings->gamma;
450 cache_data->dither = view_settings->dither;
451 cache_data->flag = view_settings->flag;
452 cache_data->curve_mapping = curve_mapping;
453 cache_data->curve_mapping_timestamp = curve_mapping_timestamp;
454
455 colormanage_cachedata_set(cache_ibuf, cache_data);
456
457 *cache_handle = cache_ibuf;
458
459 IMB_moviecache_put(moviecache, &key, cache_ibuf);
460 }
461
colormanage_cache_handle_release(void * cache_handle)462 static void colormanage_cache_handle_release(void *cache_handle)
463 {
464 ImBuf *cache_ibuf = cache_handle;
465
466 IMB_freeImBuf(cache_ibuf);
467 }
468
469 /** \} */
470
471 /* -------------------------------------------------------------------- */
472 /** \name Initialization / De-initialization
473 * \{ */
474
colormanage_role_color_space_name_get(OCIO_ConstConfigRcPtr * config,char * colorspace_name,const char * role,const char * backup_role)475 static void colormanage_role_color_space_name_get(OCIO_ConstConfigRcPtr *config,
476 char *colorspace_name,
477 const char *role,
478 const char *backup_role)
479 {
480 OCIO_ConstColorSpaceRcPtr *ociocs;
481
482 ociocs = OCIO_configGetColorSpace(config, role);
483
484 if (!ociocs && backup_role) {
485 ociocs = OCIO_configGetColorSpace(config, backup_role);
486 }
487
488 if (ociocs) {
489 const char *name = OCIO_colorSpaceGetName(ociocs);
490
491 /* assume function was called with buffer properly allocated to MAX_COLORSPACE_NAME chars */
492 BLI_strncpy(colorspace_name, name, MAX_COLORSPACE_NAME);
493 OCIO_colorSpaceRelease(ociocs);
494 }
495 else {
496 printf("Color management: Error could not find role %s role.\n", role);
497 }
498 }
499
colormanage_load_config(OCIO_ConstConfigRcPtr * config)500 static void colormanage_load_config(OCIO_ConstConfigRcPtr *config)
501 {
502 int tot_colorspace, tot_display, tot_display_view, tot_looks;
503 int index, viewindex, viewindex2;
504 const char *name;
505
506 /* get roles */
507 colormanage_role_color_space_name_get(config, global_role_data, OCIO_ROLE_DATA, NULL);
508 colormanage_role_color_space_name_get(
509 config, global_role_scene_linear, OCIO_ROLE_SCENE_LINEAR, NULL);
510 colormanage_role_color_space_name_get(
511 config, global_role_color_picking, OCIO_ROLE_COLOR_PICKING, NULL);
512 colormanage_role_color_space_name_get(
513 config, global_role_texture_painting, OCIO_ROLE_TEXTURE_PAINT, NULL);
514 colormanage_role_color_space_name_get(
515 config, global_role_default_sequencer, OCIO_ROLE_DEFAULT_SEQUENCER, OCIO_ROLE_SCENE_LINEAR);
516 colormanage_role_color_space_name_get(
517 config, global_role_default_byte, OCIO_ROLE_DEFAULT_BYTE, OCIO_ROLE_TEXTURE_PAINT);
518 colormanage_role_color_space_name_get(
519 config, global_role_default_float, OCIO_ROLE_DEFAULT_FLOAT, OCIO_ROLE_SCENE_LINEAR);
520
521 /* load colorspaces */
522 tot_colorspace = OCIO_configGetNumColorSpaces(config);
523 for (index = 0; index < tot_colorspace; index++) {
524 OCIO_ConstColorSpaceRcPtr *ocio_colorspace;
525 const char *description;
526 bool is_invertible, is_data;
527
528 name = OCIO_configGetColorSpaceNameByIndex(config, index);
529
530 ocio_colorspace = OCIO_configGetColorSpace(config, name);
531 description = OCIO_colorSpaceGetDescription(ocio_colorspace);
532 is_invertible = OCIO_colorSpaceIsInvertible(ocio_colorspace);
533 is_data = OCIO_colorSpaceIsData(ocio_colorspace);
534
535 colormanage_colorspace_add(name, description, is_invertible, is_data);
536
537 OCIO_colorSpaceRelease(ocio_colorspace);
538 }
539
540 /* load displays */
541 viewindex2 = 0;
542 tot_display = OCIO_configGetNumDisplays(config);
543
544 for (index = 0; index < tot_display; index++) {
545 const char *displayname;
546 ColorManagedDisplay *display;
547
548 displayname = OCIO_configGetDisplay(config, index);
549
550 display = colormanage_display_add(displayname);
551
552 /* load views */
553 tot_display_view = OCIO_configGetNumViews(config, displayname);
554 for (viewindex = 0; viewindex < tot_display_view; viewindex++, viewindex2++) {
555 const char *viewname;
556 ColorManagedView *view;
557 LinkData *display_view;
558
559 viewname = OCIO_configGetView(config, displayname, viewindex);
560
561 /* first check if view transform with given name was already loaded */
562 view = colormanage_view_get_named(viewname);
563
564 if (!view) {
565 view = colormanage_view_add(viewname);
566 }
567
568 display_view = BLI_genericNodeN(view);
569
570 BLI_addtail(&display->views, display_view);
571 }
572 }
573
574 global_tot_display = tot_display;
575
576 /* load looks */
577 tot_looks = OCIO_configGetNumLooks(config);
578 colormanage_look_add("None", "", true);
579 for (index = 0; index < tot_looks; index++) {
580 OCIO_ConstLookRcPtr *ocio_look;
581 const char *process_space;
582
583 name = OCIO_configGetLookNameByIndex(config, index);
584 ocio_look = OCIO_configGetLook(config, name);
585 process_space = OCIO_lookGetProcessSpace(ocio_look);
586 OCIO_lookRelease(ocio_look);
587
588 colormanage_look_add(name, process_space, false);
589 }
590
591 /* Load luminance coefficients. */
592 OCIO_configGetDefaultLumaCoefs(config, imbuf_luma_coefficients);
593 OCIO_configGetXYZtoRGB(config, imbuf_xyz_to_rgb);
594 invert_m3_m3(imbuf_rgb_to_xyz, imbuf_xyz_to_rgb);
595 copy_m3_m3(imbuf_xyz_to_linear_srgb, OCIO_XYZ_TO_LINEAR_SRGB);
596 invert_m3_m3(imbuf_linear_srgb_to_xyz, imbuf_xyz_to_linear_srgb);
597 }
598
colormanage_free_config(void)599 static void colormanage_free_config(void)
600 {
601 ColorSpace *colorspace;
602 ColorManagedDisplay *display;
603
604 /* free color spaces */
605 colorspace = global_colorspaces.first;
606 while (colorspace) {
607 ColorSpace *colorspace_next = colorspace->next;
608
609 /* free precomputer processors */
610 if (colorspace->to_scene_linear) {
611 OCIO_processorRelease((OCIO_ConstProcessorRcPtr *)colorspace->to_scene_linear);
612 }
613
614 if (colorspace->from_scene_linear) {
615 OCIO_processorRelease((OCIO_ConstProcessorRcPtr *)colorspace->from_scene_linear);
616 }
617
618 /* free color space itself */
619 MEM_freeN(colorspace);
620
621 colorspace = colorspace_next;
622 }
623 BLI_listbase_clear(&global_colorspaces);
624 global_tot_colorspace = 0;
625
626 /* free displays */
627 display = global_displays.first;
628 while (display) {
629 ColorManagedDisplay *display_next = display->next;
630
631 /* free precomputer processors */
632 if (display->to_scene_linear) {
633 OCIO_processorRelease((OCIO_ConstProcessorRcPtr *)display->to_scene_linear);
634 }
635
636 if (display->from_scene_linear) {
637 OCIO_processorRelease((OCIO_ConstProcessorRcPtr *)display->from_scene_linear);
638 }
639
640 /* free list of views */
641 BLI_freelistN(&display->views);
642
643 MEM_freeN(display);
644 display = display_next;
645 }
646 BLI_listbase_clear(&global_displays);
647 global_tot_display = 0;
648
649 /* free views */
650 BLI_freelistN(&global_views);
651 global_tot_view = 0;
652
653 /* free looks */
654 BLI_freelistN(&global_looks);
655 global_tot_looks = 0;
656
657 OCIO_exit();
658 }
659
colormanagement_init(void)660 void colormanagement_init(void)
661 {
662 const char *ocio_env;
663 const char *configdir;
664 char configfile[FILE_MAX];
665 OCIO_ConstConfigRcPtr *config = NULL;
666
667 OCIO_init();
668
669 ocio_env = BLI_getenv("OCIO");
670
671 if (ocio_env && ocio_env[0] != '\0') {
672 config = OCIO_configCreateFromEnv();
673 if (config != NULL) {
674 printf("Color management: Using %s as a configuration file\n", ocio_env);
675 }
676 }
677
678 if (config == NULL) {
679 configdir = BKE_appdir_folder_id(BLENDER_DATAFILES, "colormanagement");
680
681 if (configdir) {
682 BLI_join_dirfile(configfile, sizeof(configfile), configdir, BCM_CONFIG_FILE);
683
684 #ifdef WIN32
685 {
686 /* quite a hack to support loading configuration from path with non-acii symbols */
687
688 char short_name[256];
689 BLI_get_short_name(short_name, configfile);
690 config = OCIO_configCreateFromFile(short_name);
691 }
692 #else
693 config = OCIO_configCreateFromFile(configfile);
694 #endif
695 }
696 }
697
698 if (config == NULL) {
699 printf("Color management: using fallback mode for management\n");
700
701 config = OCIO_configCreateFallback();
702 }
703
704 if (config) {
705 OCIO_setCurrentConfig(config);
706
707 colormanage_load_config(config);
708
709 OCIO_configRelease(config);
710 }
711
712 /* If there are no valid display/views, use fallback mode. */
713 if (global_tot_display == 0 || global_tot_view == 0) {
714 printf("Color management: no displays/views in the config, using fallback mode instead\n");
715
716 /* Free old config. */
717 colormanage_free_config();
718
719 /* Initialize fallback config. */
720 config = OCIO_configCreateFallback();
721 colormanage_load_config(config);
722 }
723
724 BLI_init_srgb_conversion();
725 }
726
colormanagement_exit(void)727 void colormanagement_exit(void)
728 {
729 if (global_glsl_state.processor_scene_to_ui) {
730 OCIO_processorRelease(global_glsl_state.processor_scene_to_ui);
731 }
732
733 if (global_glsl_state.processor_ui_to_display) {
734 OCIO_processorRelease(global_glsl_state.processor_ui_to_display);
735 }
736
737 if (global_glsl_state.curve_mapping) {
738 BKE_curvemapping_free(global_glsl_state.curve_mapping);
739 }
740
741 if (global_glsl_state.curve_mapping_settings.lut) {
742 MEM_freeN(global_glsl_state.curve_mapping_settings.lut);
743 }
744
745 if (global_glsl_state.ocio_glsl_state) {
746 OCIO_freeOGLState(global_glsl_state.ocio_glsl_state);
747 }
748
749 if (global_color_picking_state.processor_to) {
750 OCIO_processorRelease(global_color_picking_state.processor_to);
751 }
752
753 if (global_color_picking_state.processor_from) {
754 OCIO_processorRelease(global_color_picking_state.processor_from);
755 }
756
757 memset(&global_glsl_state, 0, sizeof(global_glsl_state));
758 memset(&global_color_picking_state, 0, sizeof(global_color_picking_state));
759
760 colormanage_free_config();
761 }
762
763 /** \} */
764
765 /* -------------------------------------------------------------------- */
766 /** \name Internal functions
767 * \{ */
768
colormanage_compatible_look(ColorManagedLook * look,const char * view_name)769 static bool colormanage_compatible_look(ColorManagedLook *look, const char *view_name)
770 {
771 if (look->is_noop) {
772 return true;
773 }
774
775 /* Skip looks only relevant to specific view transforms. */
776 return (look->view[0] == 0 || (view_name && STREQ(look->view, view_name)));
777 }
778
colormanage_cache_free(ImBuf * ibuf)779 void colormanage_cache_free(ImBuf *ibuf)
780 {
781 if (ibuf->display_buffer_flags) {
782 MEM_freeN(ibuf->display_buffer_flags);
783
784 ibuf->display_buffer_flags = NULL;
785 }
786
787 if (ibuf->colormanage_cache) {
788 ColormanageCacheData *cache_data = colormanage_cachedata_get(ibuf);
789 struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
790
791 if (cache_data) {
792 MEM_freeN(cache_data);
793 }
794
795 if (moviecache) {
796 IMB_moviecache_free(moviecache);
797 }
798
799 MEM_freeN(ibuf->colormanage_cache);
800
801 ibuf->colormanage_cache = NULL;
802 }
803 }
804
IMB_colormanagement_display_settings_from_ctx(const bContext * C,ColorManagedViewSettings ** r_view_settings,ColorManagedDisplaySettings ** r_display_settings)805 void IMB_colormanagement_display_settings_from_ctx(
806 const bContext *C,
807 ColorManagedViewSettings **r_view_settings,
808 ColorManagedDisplaySettings **r_display_settings)
809 {
810 Scene *scene = CTX_data_scene(C);
811 SpaceImage *sima = CTX_wm_space_image(C);
812
813 *r_view_settings = &scene->view_settings;
814 *r_display_settings = &scene->display_settings;
815
816 if (sima && sima->image) {
817 if ((sima->image->flag & IMA_VIEW_AS_RENDER) == 0) {
818 *r_view_settings = NULL;
819 }
820 }
821 }
822
IMB_colormanagement_get_display_colorspace_name(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)823 const char *IMB_colormanagement_get_display_colorspace_name(
824 const ColorManagedViewSettings *view_settings,
825 const ColorManagedDisplaySettings *display_settings)
826 {
827 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
828
829 const char *display = display_settings->display_device;
830 const char *view = view_settings->view_transform;
831 const char *colorspace_name;
832
833 colorspace_name = OCIO_configGetDisplayColorSpaceName(config, display, view);
834
835 OCIO_configRelease(config);
836
837 return colorspace_name;
838 }
839
display_transform_get_colorspace(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)840 static ColorSpace *display_transform_get_colorspace(
841 const ColorManagedViewSettings *view_settings,
842 const ColorManagedDisplaySettings *display_settings)
843 {
844 const char *colorspace_name = IMB_colormanagement_get_display_colorspace_name(view_settings,
845 display_settings);
846
847 if (colorspace_name) {
848 return colormanage_colorspace_get_named(colorspace_name);
849 }
850
851 return NULL;
852 }
853
create_display_buffer_processor(const char * look,const char * view_transform,const char * display,float exposure,float gamma,const char * from_colorspace,const bool linear_output)854 static OCIO_ConstProcessorRcPtr *create_display_buffer_processor(const char *look,
855 const char *view_transform,
856 const char *display,
857 float exposure,
858 float gamma,
859 const char *from_colorspace,
860 const bool linear_output)
861 {
862 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
863 OCIO_DisplayTransformRcPtr *dt;
864 OCIO_ConstProcessorRcPtr *processor;
865 ColorManagedLook *look_descr = colormanage_look_get_named(look);
866
867 dt = OCIO_createDisplayTransform();
868
869 OCIO_displayTransformSetInputColorSpaceName(dt, from_colorspace);
870 OCIO_displayTransformSetView(dt, view_transform);
871 OCIO_displayTransformSetDisplay(dt, display);
872
873 if (look_descr->is_noop == false && colormanage_compatible_look(look_descr, view_transform)) {
874 OCIO_displayTransformSetLooksOverrideEnabled(dt, true);
875 OCIO_displayTransformSetLooksOverride(dt, look);
876 }
877
878 /* fstop exposure control */
879 if (exposure != 0.0f) {
880 OCIO_MatrixTransformRcPtr *mt;
881 float gain = powf(2.0f, exposure);
882 const float scale4f[] = {gain, gain, gain, 1.0f};
883 float m44[16], offset4[4];
884
885 OCIO_matrixTransformScale(m44, offset4, scale4f);
886 mt = OCIO_createMatrixTransform();
887 OCIO_matrixTransformSetValue(mt, m44, offset4);
888 OCIO_displayTransformSetLinearCC(dt, (OCIO_ConstTransformRcPtr *)mt);
889
890 OCIO_matrixTransformRelease(mt);
891 }
892
893 /* post-display gamma transform */
894 if (gamma != 1.0f) {
895 OCIO_ExponentTransformRcPtr *et;
896 float exponent = 1.0f / MAX2(FLT_EPSILON, gamma);
897 const float exponent4f[] = {exponent, exponent, exponent, exponent};
898
899 et = OCIO_createExponentTransform();
900 OCIO_exponentTransformSetValue(et, exponent4f);
901 OCIO_displayTransformSetDisplayCC(dt, (OCIO_ConstTransformRcPtr *)et);
902
903 OCIO_exponentTransformRelease(et);
904 }
905
906 OCIO_GroupTransformRcPtr *gt = OCIO_createGroupTransform();
907 OCIO_groupTransformSetDirection(gt, true);
908 OCIO_groupTransformPushBack(gt, (OCIO_ConstTransformRcPtr *)dt);
909
910 if (linear_output) {
911 /* TODO use correct function display. */
912 OCIO_ExponentTransformRcPtr *et = OCIO_createExponentTransform();
913 OCIO_exponentTransformSetValue(et, (float[4]){2.2f, 2.2f, 2.2f, 1.0f});
914 OCIO_groupTransformPushBack(gt, (OCIO_ConstTransformRcPtr *)et);
915 OCIO_exponentTransformRelease(et);
916 }
917
918 processor = OCIO_configGetProcessor(config, (OCIO_ConstTransformRcPtr *)gt);
919
920 OCIO_groupTransformRelease(gt);
921 OCIO_displayTransformRelease(dt);
922 OCIO_configRelease(config);
923
924 return processor;
925 }
926
create_display_encoded_buffer_processor(const char * UNUSED (display))927 static OCIO_ConstProcessorRcPtr *create_display_encoded_buffer_processor(
928 const char *UNUSED(display))
929 {
930 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
931 OCIO_ConstProcessorRcPtr *processor;
932
933 /* TODO use correct function display. */
934 OCIO_ExponentTransformRcPtr *et = OCIO_createExponentTransform();
935 OCIO_exponentTransformSetValue(et, (float[4]){1.0f / 2.2f, 1.0f / 2.2f, 1.0f / 2.2f, 1.0f});
936
937 processor = OCIO_configGetProcessor(config, (OCIO_ConstTransformRcPtr *)et);
938
939 OCIO_exponentTransformRelease(et);
940 OCIO_configRelease(config);
941
942 return processor;
943 }
944
create_colorspace_transform_processor(const char * from_colorspace,const char * to_colorspace)945 static OCIO_ConstProcessorRcPtr *create_colorspace_transform_processor(const char *from_colorspace,
946 const char *to_colorspace)
947 {
948 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
949 OCIO_ConstProcessorRcPtr *processor;
950
951 processor = OCIO_configGetProcessorWithNames(config, from_colorspace, to_colorspace);
952
953 OCIO_configRelease(config);
954
955 return processor;
956 }
957
colorspace_to_scene_linear_processor(ColorSpace * colorspace)958 static OCIO_ConstProcessorRcPtr *colorspace_to_scene_linear_processor(ColorSpace *colorspace)
959 {
960 if (colorspace->to_scene_linear == NULL) {
961 BLI_mutex_lock(&processor_lock);
962
963 if (colorspace->to_scene_linear == NULL) {
964 OCIO_ConstProcessorRcPtr *to_scene_linear;
965 to_scene_linear = create_colorspace_transform_processor(colorspace->name,
966 global_role_scene_linear);
967 colorspace->to_scene_linear = (struct OCIO_ConstProcessorRcPtr *)to_scene_linear;
968 }
969
970 BLI_mutex_unlock(&processor_lock);
971 }
972
973 return (OCIO_ConstProcessorRcPtr *)colorspace->to_scene_linear;
974 }
975
colorspace_from_scene_linear_processor(ColorSpace * colorspace)976 static OCIO_ConstProcessorRcPtr *colorspace_from_scene_linear_processor(ColorSpace *colorspace)
977 {
978 if (colorspace->from_scene_linear == NULL) {
979 BLI_mutex_lock(&processor_lock);
980
981 if (colorspace->from_scene_linear == NULL) {
982 OCIO_ConstProcessorRcPtr *from_scene_linear;
983 from_scene_linear = create_colorspace_transform_processor(global_role_scene_linear,
984 colorspace->name);
985 colorspace->from_scene_linear = (struct OCIO_ConstProcessorRcPtr *)from_scene_linear;
986 }
987
988 BLI_mutex_unlock(&processor_lock);
989 }
990
991 return (OCIO_ConstProcessorRcPtr *)colorspace->from_scene_linear;
992 }
993
display_from_scene_linear_processor(ColorManagedDisplay * display)994 static OCIO_ConstProcessorRcPtr *display_from_scene_linear_processor(ColorManagedDisplay *display)
995 {
996 if (display->from_scene_linear == NULL) {
997 BLI_mutex_lock(&processor_lock);
998
999 if (display->from_scene_linear == NULL) {
1000 const char *view_name = colormanage_view_get_default_name(display);
1001 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
1002 OCIO_ConstProcessorRcPtr *processor = NULL;
1003
1004 if (view_name && config) {
1005 const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(
1006 config, display->name, view_name);
1007 processor = OCIO_configGetProcessorWithNames(
1008 config, global_role_scene_linear, view_colorspace);
1009
1010 OCIO_configRelease(config);
1011 }
1012
1013 display->from_scene_linear = (struct OCIO_ConstProcessorRcPtr *)processor;
1014 }
1015
1016 BLI_mutex_unlock(&processor_lock);
1017 }
1018
1019 return (OCIO_ConstProcessorRcPtr *)display->from_scene_linear;
1020 }
1021
display_to_scene_linear_processor(ColorManagedDisplay * display)1022 static OCIO_ConstProcessorRcPtr *display_to_scene_linear_processor(ColorManagedDisplay *display)
1023 {
1024 if (display->to_scene_linear == NULL) {
1025 BLI_mutex_lock(&processor_lock);
1026
1027 if (display->to_scene_linear == NULL) {
1028 const char *view_name = colormanage_view_get_default_name(display);
1029 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
1030 OCIO_ConstProcessorRcPtr *processor = NULL;
1031
1032 if (view_name && config) {
1033 const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(
1034 config, display->name, view_name);
1035 processor = OCIO_configGetProcessorWithNames(
1036 config, view_colorspace, global_role_scene_linear);
1037
1038 OCIO_configRelease(config);
1039 }
1040
1041 display->to_scene_linear = (struct OCIO_ConstProcessorRcPtr *)processor;
1042 }
1043
1044 BLI_mutex_unlock(&processor_lock);
1045 }
1046
1047 return (OCIO_ConstProcessorRcPtr *)display->to_scene_linear;
1048 }
1049
IMB_colormanagement_init_default_view_settings(ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)1050 void IMB_colormanagement_init_default_view_settings(
1051 ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
1052 {
1053 /* First, try use "Standard" view transform of the requested device. */
1054 ColorManagedView *default_view = colormanage_view_get_named_for_display(
1055 display_settings->display_device, "Standard");
1056 /* If that fails, we fall back to the default view transform of the display
1057 * as per OCIO configuration. */
1058 if (default_view == NULL) {
1059 ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device);
1060 if (display != NULL) {
1061 default_view = colormanage_view_get_default(display);
1062 }
1063 }
1064 if (default_view != NULL) {
1065 BLI_strncpy(
1066 view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1067 }
1068 else {
1069 view_settings->view_transform[0] = '\0';
1070 }
1071 /* TODO(sergey): Find a way to safely/reliable un-hardcode this. */
1072 BLI_strncpy(view_settings->look, "None", sizeof(view_settings->look));
1073 /* Initialize rest of the settings. */
1074 view_settings->flag = 0;
1075 view_settings->gamma = 1.0f;
1076 view_settings->exposure = 0.0f;
1077 view_settings->curve_mapping = NULL;
1078 }
1079
curve_mapping_apply_pixel(CurveMapping * curve_mapping,float * pixel,int channels)1080 static void curve_mapping_apply_pixel(CurveMapping *curve_mapping, float *pixel, int channels)
1081 {
1082 if (channels == 1) {
1083 pixel[0] = BKE_curvemap_evaluateF(curve_mapping, curve_mapping->cm, pixel[0]);
1084 }
1085 else if (channels == 2) {
1086 pixel[0] = BKE_curvemap_evaluateF(curve_mapping, curve_mapping->cm, pixel[0]);
1087 pixel[1] = BKE_curvemap_evaluateF(curve_mapping, curve_mapping->cm, pixel[1]);
1088 }
1089 else {
1090 BKE_curvemapping_evaluate_premulRGBF(curve_mapping, pixel, pixel);
1091 }
1092 }
1093
colorspace_set_default_role(char * colorspace,int size,int role)1094 void colorspace_set_default_role(char *colorspace, int size, int role)
1095 {
1096 if (colorspace && colorspace[0] == '\0') {
1097 const char *role_colorspace;
1098
1099 role_colorspace = IMB_colormanagement_role_colorspace_name_get(role);
1100
1101 BLI_strncpy(colorspace, role_colorspace, size);
1102 }
1103 }
1104
colormanage_imbuf_set_default_spaces(ImBuf * ibuf)1105 void colormanage_imbuf_set_default_spaces(ImBuf *ibuf)
1106 {
1107 ibuf->rect_colorspace = colormanage_colorspace_get_named(global_role_default_byte);
1108 }
1109
colormanage_imbuf_make_linear(ImBuf * ibuf,const char * from_colorspace)1110 void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace)
1111 {
1112 ColorSpace *colorspace = colormanage_colorspace_get_named(from_colorspace);
1113
1114 if (colorspace && colorspace->is_data) {
1115 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1116 return;
1117 }
1118
1119 if (ibuf->rect_float) {
1120 const char *to_colorspace = global_role_scene_linear;
1121 const bool predivide = IMB_alpha_affects_rgb(ibuf);
1122
1123 if (ibuf->rect) {
1124 imb_freerectImBuf(ibuf);
1125 }
1126
1127 IMB_colormanagement_transform(ibuf->rect_float,
1128 ibuf->x,
1129 ibuf->y,
1130 ibuf->channels,
1131 from_colorspace,
1132 to_colorspace,
1133 predivide);
1134 }
1135 }
1136
1137 /** \} */
1138
1139 /* -------------------------------------------------------------------- */
1140 /** \name Generic Functions
1141 * \{ */
1142
colormanage_check_display_settings(ColorManagedDisplaySettings * display_settings,const char * what,const ColorManagedDisplay * default_display)1143 static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings,
1144 const char *what,
1145 const ColorManagedDisplay *default_display)
1146 {
1147 if (display_settings->display_device[0] == '\0') {
1148 BLI_strncpy(display_settings->display_device,
1149 default_display->name,
1150 sizeof(display_settings->display_device));
1151 }
1152 else {
1153 ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device);
1154
1155 if (!display) {
1156 printf(
1157 "Color management: display \"%s\" used by %s not found, setting to default (\"%s\").\n",
1158 display_settings->display_device,
1159 what,
1160 default_display->name);
1161
1162 BLI_strncpy(display_settings->display_device,
1163 default_display->name,
1164 sizeof(display_settings->display_device));
1165 }
1166 }
1167 }
1168
colormanage_check_view_settings(ColorManagedDisplaySettings * display_settings,ColorManagedViewSettings * view_settings,const char * what)1169 static void colormanage_check_view_settings(ColorManagedDisplaySettings *display_settings,
1170 ColorManagedViewSettings *view_settings,
1171 const char *what)
1172 {
1173 ColorManagedDisplay *display;
1174 ColorManagedView *default_view = NULL;
1175 ColorManagedLook *default_look = (ColorManagedLook *)global_looks.first;
1176
1177 if (view_settings->view_transform[0] == '\0') {
1178 display = colormanage_display_get_named(display_settings->display_device);
1179
1180 if (display) {
1181 default_view = colormanage_view_get_default(display);
1182 }
1183
1184 if (default_view) {
1185 BLI_strncpy(view_settings->view_transform,
1186 default_view->name,
1187 sizeof(view_settings->view_transform));
1188 }
1189 }
1190 else {
1191 ColorManagedView *view = colormanage_view_get_named(view_settings->view_transform);
1192
1193 if (!view) {
1194 display = colormanage_display_get_named(display_settings->display_device);
1195
1196 if (display) {
1197 default_view = colormanage_view_get_default(display);
1198 }
1199
1200 if (default_view) {
1201 printf("Color management: %s view \"%s\" not found, setting default \"%s\".\n",
1202 what,
1203 view_settings->view_transform,
1204 default_view->name);
1205
1206 BLI_strncpy(view_settings->view_transform,
1207 default_view->name,
1208 sizeof(view_settings->view_transform));
1209 }
1210 }
1211 }
1212
1213 if (view_settings->look[0] == '\0') {
1214 BLI_strncpy(view_settings->look, default_look->name, sizeof(view_settings->look));
1215 }
1216 else {
1217 ColorManagedLook *look = colormanage_look_get_named(view_settings->look);
1218 if (look == NULL) {
1219 printf("Color management: %s look \"%s\" not found, setting default \"%s\".\n",
1220 what,
1221 view_settings->look,
1222 default_look->name);
1223
1224 BLI_strncpy(view_settings->look, default_look->name, sizeof(view_settings->look));
1225 }
1226 }
1227
1228 /* OCIO_TODO: move to do_versions() */
1229 if (view_settings->exposure == 0.0f && view_settings->gamma == 0.0f) {
1230 view_settings->exposure = 0.0f;
1231 view_settings->gamma = 1.0f;
1232 }
1233 }
1234
colormanage_check_colorspace_settings(ColorManagedColorspaceSettings * colorspace_settings,const char * what)1235 static void colormanage_check_colorspace_settings(
1236 ColorManagedColorspaceSettings *colorspace_settings, const char *what)
1237 {
1238 if (colorspace_settings->name[0] == '\0') {
1239 /* pass */
1240 }
1241 else {
1242 ColorSpace *colorspace = colormanage_colorspace_get_named(colorspace_settings->name);
1243
1244 if (!colorspace) {
1245 printf("Color management: %s colorspace \"%s\" not found, will use default instead.\n",
1246 what,
1247 colorspace_settings->name);
1248
1249 BLI_strncpy(colorspace_settings->name, "", sizeof(colorspace_settings->name));
1250 }
1251 }
1252
1253 (void)what;
1254 }
1255
IMB_colormanagement_check_file_config(Main * bmain)1256 void IMB_colormanagement_check_file_config(Main *bmain)
1257 {
1258 Scene *scene;
1259 Image *image;
1260 MovieClip *clip;
1261
1262 ColorManagedDisplay *default_display;
1263
1264 default_display = colormanage_display_get_default();
1265
1266 if (!default_display) {
1267 /* happens when OCIO configuration is incorrect */
1268 return;
1269 }
1270
1271 for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
1272 ColorManagedColorspaceSettings *sequencer_colorspace_settings;
1273
1274 /* check scene color management settings */
1275 colormanage_check_display_settings(&scene->display_settings, "scene", default_display);
1276 colormanage_check_view_settings(&scene->display_settings, &scene->view_settings, "scene");
1277
1278 sequencer_colorspace_settings = &scene->sequencer_colorspace_settings;
1279
1280 colormanage_check_colorspace_settings(sequencer_colorspace_settings, "sequencer");
1281
1282 if (sequencer_colorspace_settings->name[0] == '\0') {
1283 BLI_strncpy(
1284 sequencer_colorspace_settings->name, global_role_default_sequencer, MAX_COLORSPACE_NAME);
1285 }
1286
1287 /* check sequencer strip input color space settings */
1288 Sequence *seq;
1289 SEQ_ALL_BEGIN (scene->ed, seq) {
1290 if (seq->strip) {
1291 colormanage_check_colorspace_settings(&seq->strip->colorspace_settings, "sequencer strip");
1292 }
1293 }
1294 SEQ_ALL_END;
1295 }
1296
1297 /* ** check input color space settings ** */
1298
1299 for (image = bmain->images.first; image; image = image->id.next) {
1300 colormanage_check_colorspace_settings(&image->colorspace_settings, "image");
1301 }
1302
1303 for (clip = bmain->movieclips.first; clip; clip = clip->id.next) {
1304 colormanage_check_colorspace_settings(&clip->colorspace_settings, "clip");
1305 }
1306 }
1307
IMB_colormanagement_validate_settings(const ColorManagedDisplaySettings * display_settings,ColorManagedViewSettings * view_settings)1308 void IMB_colormanagement_validate_settings(const ColorManagedDisplaySettings *display_settings,
1309 ColorManagedViewSettings *view_settings)
1310 {
1311 ColorManagedDisplay *display;
1312 ColorManagedView *default_view = NULL;
1313 LinkData *view_link;
1314
1315 display = colormanage_display_get_named(display_settings->display_device);
1316
1317 default_view = colormanage_view_get_default(display);
1318
1319 for (view_link = display->views.first; view_link; view_link = view_link->next) {
1320 ColorManagedView *view = view_link->data;
1321
1322 if (STREQ(view->name, view_settings->view_transform)) {
1323 break;
1324 }
1325 }
1326
1327 if (view_link == NULL && default_view) {
1328 BLI_strncpy(
1329 view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform));
1330 }
1331 }
1332
IMB_colormanagement_role_colorspace_name_get(int role)1333 const char *IMB_colormanagement_role_colorspace_name_get(int role)
1334 {
1335 switch (role) {
1336 case COLOR_ROLE_DATA:
1337 return global_role_data;
1338 case COLOR_ROLE_SCENE_LINEAR:
1339 return global_role_scene_linear;
1340 case COLOR_ROLE_COLOR_PICKING:
1341 return global_role_color_picking;
1342 case COLOR_ROLE_TEXTURE_PAINTING:
1343 return global_role_texture_painting;
1344 case COLOR_ROLE_DEFAULT_SEQUENCER:
1345 return global_role_default_sequencer;
1346 case COLOR_ROLE_DEFAULT_FLOAT:
1347 return global_role_default_float;
1348 case COLOR_ROLE_DEFAULT_BYTE:
1349 return global_role_default_byte;
1350 default:
1351 printf("Unknown role was passed to %s\n", __func__);
1352 BLI_assert(0);
1353 break;
1354 }
1355
1356 return NULL;
1357 }
1358
IMB_colormanagement_check_is_data(ImBuf * ibuf,const char * name)1359 void IMB_colormanagement_check_is_data(ImBuf *ibuf, const char *name)
1360 {
1361 ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1362
1363 if (colorspace && colorspace->is_data) {
1364 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1365 }
1366 else {
1367 ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1368 }
1369 }
1370
IMB_colormanagegent_copy_settings(ImBuf * ibuf_src,ImBuf * ibuf_dst)1371 void IMB_colormanagegent_copy_settings(ImBuf *ibuf_src, ImBuf *ibuf_dst)
1372 {
1373 IMB_colormanagement_assign_rect_colorspace(ibuf_dst,
1374 IMB_colormanagement_get_rect_colorspace(ibuf_src));
1375 IMB_colormanagement_assign_float_colorspace(ibuf_dst,
1376 IMB_colormanagement_get_float_colorspace(ibuf_src));
1377 if (ibuf_src->flags & IB_alphamode_premul) {
1378 ibuf_dst->flags |= IB_alphamode_premul;
1379 }
1380 else if (ibuf_src->flags & IB_alphamode_channel_packed) {
1381 ibuf_dst->flags |= IB_alphamode_channel_packed;
1382 }
1383 else if (ibuf_src->flags & IB_alphamode_ignore) {
1384 ibuf_dst->flags |= IB_alphamode_ignore;
1385 }
1386 }
1387
IMB_colormanagement_assign_float_colorspace(ImBuf * ibuf,const char * name)1388 void IMB_colormanagement_assign_float_colorspace(ImBuf *ibuf, const char *name)
1389 {
1390 ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1391
1392 ibuf->float_colorspace = colorspace;
1393
1394 if (colorspace && colorspace->is_data) {
1395 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1396 }
1397 else {
1398 ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1399 }
1400 }
1401
IMB_colormanagement_assign_rect_colorspace(ImBuf * ibuf,const char * name)1402 void IMB_colormanagement_assign_rect_colorspace(ImBuf *ibuf, const char *name)
1403 {
1404 ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1405
1406 ibuf->rect_colorspace = colorspace;
1407
1408 if (colorspace && colorspace->is_data) {
1409 ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA;
1410 }
1411 else {
1412 ibuf->colormanage_flag &= ~IMB_COLORMANAGE_IS_DATA;
1413 }
1414 }
1415
IMB_colormanagement_get_float_colorspace(ImBuf * ibuf)1416 const char *IMB_colormanagement_get_float_colorspace(ImBuf *ibuf)
1417 {
1418 if (ibuf->float_colorspace) {
1419 return ibuf->float_colorspace->name;
1420 }
1421
1422 return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR);
1423 }
1424
IMB_colormanagement_get_rect_colorspace(ImBuf * ibuf)1425 const char *IMB_colormanagement_get_rect_colorspace(ImBuf *ibuf)
1426 {
1427 if (ibuf->rect_colorspace) {
1428 return ibuf->rect_colorspace->name;
1429 }
1430
1431 return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE);
1432 }
1433
IMB_colormanagement_space_is_data(ColorSpace * colorspace)1434 bool IMB_colormanagement_space_is_data(ColorSpace *colorspace)
1435 {
1436 return (colorspace && colorspace->is_data);
1437 }
1438
colormanage_ensure_srgb_scene_linear_info(ColorSpace * colorspace)1439 static void colormanage_ensure_srgb_scene_linear_info(ColorSpace *colorspace)
1440 {
1441 if (!colorspace->info.cached) {
1442 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
1443 OCIO_ConstColorSpaceRcPtr *ocio_colorspace = OCIO_configGetColorSpace(config,
1444 colorspace->name);
1445
1446 bool is_scene_linear, is_srgb;
1447 OCIO_colorSpaceIsBuiltin(config, ocio_colorspace, &is_scene_linear, &is_srgb);
1448
1449 OCIO_colorSpaceRelease(ocio_colorspace);
1450 OCIO_configRelease(config);
1451
1452 colorspace->info.is_scene_linear = is_scene_linear;
1453 colorspace->info.is_srgb = is_srgb;
1454 colorspace->info.cached = true;
1455 }
1456 }
1457
IMB_colormanagement_space_is_scene_linear(ColorSpace * colorspace)1458 bool IMB_colormanagement_space_is_scene_linear(ColorSpace *colorspace)
1459 {
1460 colormanage_ensure_srgb_scene_linear_info(colorspace);
1461 return (colorspace && colorspace->info.is_scene_linear);
1462 }
1463
IMB_colormanagement_space_is_srgb(ColorSpace * colorspace)1464 bool IMB_colormanagement_space_is_srgb(ColorSpace *colorspace)
1465 {
1466 colormanage_ensure_srgb_scene_linear_info(colorspace);
1467 return (colorspace && colorspace->info.is_srgb);
1468 }
1469
IMB_colormanagement_space_name_is_data(const char * name)1470 bool IMB_colormanagement_space_name_is_data(const char *name)
1471 {
1472 ColorSpace *colorspace = colormanage_colorspace_get_named(name);
1473 return (colorspace && colorspace->is_data);
1474 }
1475
IMB_colormangement_get_xyz_to_rgb()1476 const float *IMB_colormangement_get_xyz_to_rgb()
1477 {
1478 return &imbuf_xyz_to_rgb[0][0];
1479 }
1480
1481 /** \} */
1482
1483 /* -------------------------------------------------------------------- */
1484 /** \name Threaded Display Buffer Transform Routines
1485 * \{ */
1486
1487 typedef struct DisplayBufferThread {
1488 ColormanageProcessor *cm_processor;
1489
1490 const float *buffer;
1491 unsigned char *byte_buffer;
1492
1493 float *display_buffer;
1494 unsigned char *display_buffer_byte;
1495
1496 int width;
1497 int start_line;
1498 int tot_line;
1499
1500 int channels;
1501 float dither;
1502 bool is_data;
1503 bool predivide;
1504
1505 const char *byte_colorspace;
1506 const char *float_colorspace;
1507 } DisplayBufferThread;
1508
1509 typedef struct DisplayBufferInitData {
1510 ImBuf *ibuf;
1511 ColormanageProcessor *cm_processor;
1512 const float *buffer;
1513 unsigned char *byte_buffer;
1514
1515 float *display_buffer;
1516 unsigned char *display_buffer_byte;
1517
1518 int width;
1519
1520 const char *byte_colorspace;
1521 const char *float_colorspace;
1522 } DisplayBufferInitData;
1523
display_buffer_init_handle(void * handle_v,int start_line,int tot_line,void * init_data_v)1524 static void display_buffer_init_handle(void *handle_v,
1525 int start_line,
1526 int tot_line,
1527 void *init_data_v)
1528 {
1529 DisplayBufferThread *handle = (DisplayBufferThread *)handle_v;
1530 DisplayBufferInitData *init_data = (DisplayBufferInitData *)init_data_v;
1531 ImBuf *ibuf = init_data->ibuf;
1532
1533 int channels = ibuf->channels;
1534 float dither = ibuf->dither;
1535 bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
1536
1537 size_t offset = ((size_t)channels) * start_line * ibuf->x;
1538 size_t display_buffer_byte_offset = ((size_t)DISPLAY_BUFFER_CHANNELS) * start_line * ibuf->x;
1539
1540 memset(handle, 0, sizeof(DisplayBufferThread));
1541
1542 handle->cm_processor = init_data->cm_processor;
1543
1544 if (init_data->buffer) {
1545 handle->buffer = init_data->buffer + offset;
1546 }
1547
1548 if (init_data->byte_buffer) {
1549 handle->byte_buffer = init_data->byte_buffer + offset;
1550 }
1551
1552 if (init_data->display_buffer) {
1553 handle->display_buffer = init_data->display_buffer + offset;
1554 }
1555
1556 if (init_data->display_buffer_byte) {
1557 handle->display_buffer_byte = init_data->display_buffer_byte + display_buffer_byte_offset;
1558 }
1559
1560 handle->width = ibuf->x;
1561
1562 handle->start_line = start_line;
1563 handle->tot_line = tot_line;
1564
1565 handle->channels = channels;
1566 handle->dither = dither;
1567 handle->is_data = is_data;
1568 handle->predivide = IMB_alpha_affects_rgb(ibuf);
1569
1570 handle->byte_colorspace = init_data->byte_colorspace;
1571 handle->float_colorspace = init_data->float_colorspace;
1572 }
1573
display_buffer_apply_get_linear_buffer(DisplayBufferThread * handle,int height,float * linear_buffer,bool * is_straight_alpha)1574 static void display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle,
1575 int height,
1576 float *linear_buffer,
1577 bool *is_straight_alpha)
1578 {
1579 int channels = handle->channels;
1580 int width = handle->width;
1581
1582 size_t buffer_size = ((size_t)channels) * width * height;
1583
1584 bool is_data = handle->is_data;
1585 bool is_data_display = handle->cm_processor->is_data_result;
1586 bool predivide = handle->predivide;
1587
1588 if (!handle->buffer) {
1589 unsigned char *byte_buffer = handle->byte_buffer;
1590
1591 const char *from_colorspace = handle->byte_colorspace;
1592 const char *to_colorspace = global_role_scene_linear;
1593
1594 float *fp;
1595 unsigned char *cp;
1596 const size_t i_last = ((size_t)width) * height;
1597 size_t i;
1598
1599 /* first convert byte buffer to float, keep in image space */
1600 for (i = 0, fp = linear_buffer, cp = byte_buffer; i != i_last;
1601 i++, fp += channels, cp += channels) {
1602 if (channels == 3) {
1603 rgb_uchar_to_float(fp, cp);
1604 }
1605 else if (channels == 4) {
1606 rgba_uchar_to_float(fp, cp);
1607 }
1608 else {
1609 BLI_assert(!"Buffers of 3 or 4 channels are only supported here");
1610 }
1611 }
1612
1613 if (!is_data && !is_data_display) {
1614 /* convert float buffer to scene linear space */
1615 IMB_colormanagement_transform(
1616 linear_buffer, width, height, channels, from_colorspace, to_colorspace, false);
1617 }
1618
1619 *is_straight_alpha = true;
1620 }
1621 else if (handle->float_colorspace) {
1622 /* currently float is non-linear only in sequencer, which is working
1623 * in its own color space even to handle float buffers.
1624 * This color space is the same for byte and float images.
1625 * Need to convert float buffer to linear space before applying display transform
1626 */
1627
1628 const char *from_colorspace = handle->float_colorspace;
1629 const char *to_colorspace = global_role_scene_linear;
1630
1631 memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1632
1633 if (!is_data && !is_data_display) {
1634 IMB_colormanagement_transform(
1635 linear_buffer, width, height, channels, from_colorspace, to_colorspace, predivide);
1636 }
1637
1638 *is_straight_alpha = false;
1639 }
1640 else {
1641 /* some processors would want to modify float original buffer
1642 * before converting it into display byte buffer, so we need to
1643 * make sure original's ImBuf buffers wouldn't be modified by
1644 * using duplicated buffer here
1645 */
1646
1647 memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float));
1648
1649 *is_straight_alpha = false;
1650 }
1651 }
1652
do_display_buffer_apply_thread(void * handle_v)1653 static void *do_display_buffer_apply_thread(void *handle_v)
1654 {
1655 DisplayBufferThread *handle = (DisplayBufferThread *)handle_v;
1656 ColormanageProcessor *cm_processor = handle->cm_processor;
1657 float *display_buffer = handle->display_buffer;
1658 unsigned char *display_buffer_byte = handle->display_buffer_byte;
1659 int channels = handle->channels;
1660 int width = handle->width;
1661 int height = handle->tot_line;
1662 float dither = handle->dither;
1663 bool is_data = handle->is_data;
1664
1665 if (cm_processor == NULL) {
1666 if (display_buffer_byte && display_buffer_byte != handle->byte_buffer) {
1667 IMB_buffer_byte_from_byte(display_buffer_byte,
1668 handle->byte_buffer,
1669 IB_PROFILE_SRGB,
1670 IB_PROFILE_SRGB,
1671 false,
1672 width,
1673 height,
1674 width,
1675 width);
1676 }
1677
1678 if (display_buffer) {
1679 IMB_buffer_float_from_byte(display_buffer,
1680 handle->byte_buffer,
1681 IB_PROFILE_SRGB,
1682 IB_PROFILE_SRGB,
1683 false,
1684 width,
1685 height,
1686 width,
1687 width);
1688 }
1689 }
1690 else {
1691 bool is_straight_alpha;
1692 float *linear_buffer = MEM_mallocN(((size_t)channels) * width * height * sizeof(float),
1693 "color conversion linear buffer");
1694
1695 display_buffer_apply_get_linear_buffer(handle, height, linear_buffer, &is_straight_alpha);
1696
1697 bool predivide = handle->predivide && (is_straight_alpha == false);
1698
1699 if (is_data) {
1700 /* special case for data buffers - no color space conversions,
1701 * only generate byte buffers
1702 */
1703 }
1704 else {
1705 /* apply processor */
1706 IMB_colormanagement_processor_apply(
1707 cm_processor, linear_buffer, width, height, channels, predivide);
1708 }
1709
1710 /* copy result to output buffers */
1711 if (display_buffer_byte) {
1712 /* do conversion */
1713 IMB_buffer_byte_from_float(display_buffer_byte,
1714 linear_buffer,
1715 channels,
1716 dither,
1717 IB_PROFILE_SRGB,
1718 IB_PROFILE_SRGB,
1719 predivide,
1720 width,
1721 height,
1722 width,
1723 width);
1724 }
1725
1726 if (display_buffer) {
1727 memcpy(display_buffer, linear_buffer, ((size_t)width) * height * channels * sizeof(float));
1728
1729 if (is_straight_alpha && channels == 4) {
1730 const size_t i_last = ((size_t)width) * height;
1731 size_t i;
1732 float *fp;
1733
1734 for (i = 0, fp = display_buffer; i != i_last; i++, fp += channels) {
1735 straight_to_premul_v4(fp);
1736 }
1737 }
1738 }
1739
1740 MEM_freeN(linear_buffer);
1741 }
1742
1743 return NULL;
1744 }
1745
display_buffer_apply_threaded(ImBuf * ibuf,const float * buffer,unsigned char * byte_buffer,float * display_buffer,unsigned char * display_buffer_byte,ColormanageProcessor * cm_processor)1746 static void display_buffer_apply_threaded(ImBuf *ibuf,
1747 const float *buffer,
1748 unsigned char *byte_buffer,
1749 float *display_buffer,
1750 unsigned char *display_buffer_byte,
1751 ColormanageProcessor *cm_processor)
1752 {
1753 DisplayBufferInitData init_data;
1754
1755 init_data.ibuf = ibuf;
1756 init_data.cm_processor = cm_processor;
1757 init_data.buffer = buffer;
1758 init_data.byte_buffer = byte_buffer;
1759 init_data.display_buffer = display_buffer;
1760 init_data.display_buffer_byte = display_buffer_byte;
1761
1762 if (ibuf->rect_colorspace != NULL) {
1763 init_data.byte_colorspace = ibuf->rect_colorspace->name;
1764 }
1765 else {
1766 /* happens for viewer images, which are not so simple to determine where to
1767 * set image buffer's color spaces
1768 */
1769 init_data.byte_colorspace = global_role_default_byte;
1770 }
1771
1772 if (ibuf->float_colorspace != NULL) {
1773 /* sequencer stores float buffers in non-linear space */
1774 init_data.float_colorspace = ibuf->float_colorspace->name;
1775 }
1776 else {
1777 init_data.float_colorspace = NULL;
1778 }
1779
1780 IMB_processor_apply_threaded(ibuf->y,
1781 sizeof(DisplayBufferThread),
1782 &init_data,
1783 display_buffer_init_handle,
1784 do_display_buffer_apply_thread);
1785 }
1786
is_ibuf_rect_in_display_space(ImBuf * ibuf,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)1787 static bool is_ibuf_rect_in_display_space(ImBuf *ibuf,
1788 const ColorManagedViewSettings *view_settings,
1789 const ColorManagedDisplaySettings *display_settings)
1790 {
1791 if ((view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) == 0 &&
1792 view_settings->exposure == 0.0f && view_settings->gamma == 1.0f) {
1793 const char *from_colorspace = ibuf->rect_colorspace->name;
1794 const char *to_colorspace = IMB_colormanagement_get_display_colorspace_name(view_settings,
1795 display_settings);
1796 ColorManagedLook *look_descr = colormanage_look_get_named(view_settings->look);
1797 if (look_descr != NULL && !STREQ(look_descr->process_space, "")) {
1798 return false;
1799 }
1800
1801 if (to_colorspace && STREQ(from_colorspace, to_colorspace)) {
1802 return true;
1803 }
1804 }
1805
1806 return false;
1807 }
1808
colormanage_display_buffer_process_ex(ImBuf * ibuf,float * display_buffer,unsigned char * display_buffer_byte,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)1809 static void colormanage_display_buffer_process_ex(
1810 ImBuf *ibuf,
1811 float *display_buffer,
1812 unsigned char *display_buffer_byte,
1813 const ColorManagedViewSettings *view_settings,
1814 const ColorManagedDisplaySettings *display_settings)
1815 {
1816 ColormanageProcessor *cm_processor = NULL;
1817 bool skip_transform = false;
1818
1819 /* if we're going to transform byte buffer, check whether transformation would
1820 * happen to the same color space as byte buffer itself is
1821 * this would save byte -> float -> byte conversions making display buffer
1822 * computation noticeable faster
1823 */
1824 if (ibuf->rect_float == NULL && ibuf->rect_colorspace) {
1825 skip_transform = is_ibuf_rect_in_display_space(ibuf, view_settings, display_settings);
1826 }
1827
1828 if (skip_transform == false) {
1829 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1830 }
1831
1832 display_buffer_apply_threaded(ibuf,
1833 ibuf->rect_float,
1834 (unsigned char *)ibuf->rect,
1835 display_buffer,
1836 display_buffer_byte,
1837 cm_processor);
1838
1839 if (cm_processor) {
1840 IMB_colormanagement_processor_free(cm_processor);
1841 }
1842 }
1843
colormanage_display_buffer_process(ImBuf * ibuf,unsigned char * display_buffer,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)1844 static void colormanage_display_buffer_process(ImBuf *ibuf,
1845 unsigned char *display_buffer,
1846 const ColorManagedViewSettings *view_settings,
1847 const ColorManagedDisplaySettings *display_settings)
1848 {
1849 colormanage_display_buffer_process_ex(
1850 ibuf, NULL, display_buffer, view_settings, display_settings);
1851 }
1852
1853 /** \} */
1854
1855 /* -------------------------------------------------------------------- */
1856 /** \name Threaded Processor Transform Routines
1857 * \{ */
1858
1859 typedef struct ProcessorTransformThread {
1860 ColormanageProcessor *cm_processor;
1861 unsigned char *byte_buffer;
1862 float *float_buffer;
1863 int width;
1864 int start_line;
1865 int tot_line;
1866 int channels;
1867 bool predivide;
1868 bool float_from_byte;
1869 } ProcessorTransformThread;
1870
1871 typedef struct ProcessorTransformInit {
1872 ColormanageProcessor *cm_processor;
1873 unsigned char *byte_buffer;
1874 float *float_buffer;
1875 int width;
1876 int height;
1877 int channels;
1878 bool predivide;
1879 bool float_from_byte;
1880 } ProcessorTransformInitData;
1881
processor_transform_init_handle(void * handle_v,int start_line,int tot_line,void * init_data_v)1882 static void processor_transform_init_handle(void *handle_v,
1883 int start_line,
1884 int tot_line,
1885 void *init_data_v)
1886 {
1887 ProcessorTransformThread *handle = (ProcessorTransformThread *)handle_v;
1888 ProcessorTransformInitData *init_data = (ProcessorTransformInitData *)init_data_v;
1889
1890 const int channels = init_data->channels;
1891 const int width = init_data->width;
1892 const bool predivide = init_data->predivide;
1893 const bool float_from_byte = init_data->float_from_byte;
1894
1895 const size_t offset = ((size_t)channels) * start_line * width;
1896
1897 memset(handle, 0, sizeof(ProcessorTransformThread));
1898
1899 handle->cm_processor = init_data->cm_processor;
1900
1901 if (init_data->byte_buffer != NULL) {
1902 /* TODO(serge): Offset might be different for byte and float buffers. */
1903 handle->byte_buffer = init_data->byte_buffer + offset;
1904 }
1905 if (init_data->float_buffer != NULL) {
1906 handle->float_buffer = init_data->float_buffer + offset;
1907 }
1908
1909 handle->width = width;
1910
1911 handle->start_line = start_line;
1912 handle->tot_line = tot_line;
1913
1914 handle->channels = channels;
1915 handle->predivide = predivide;
1916 handle->float_from_byte = float_from_byte;
1917 }
1918
do_processor_transform_thread(void * handle_v)1919 static void *do_processor_transform_thread(void *handle_v)
1920 {
1921 ProcessorTransformThread *handle = (ProcessorTransformThread *)handle_v;
1922 unsigned char *byte_buffer = handle->byte_buffer;
1923 float *float_buffer = handle->float_buffer;
1924 const int channels = handle->channels;
1925 const int width = handle->width;
1926 const int height = handle->tot_line;
1927 const bool predivide = handle->predivide;
1928 const bool float_from_byte = handle->float_from_byte;
1929
1930 if (float_from_byte) {
1931 IMB_buffer_float_from_byte(float_buffer,
1932 byte_buffer,
1933 IB_PROFILE_SRGB,
1934 IB_PROFILE_SRGB,
1935 false,
1936 width,
1937 height,
1938 width,
1939 width);
1940 IMB_colormanagement_processor_apply(
1941 handle->cm_processor, float_buffer, width, height, channels, predivide);
1942 IMB_premultiply_rect_float(float_buffer, 4, width, height);
1943 }
1944 else {
1945 if (byte_buffer != NULL) {
1946 IMB_colormanagement_processor_apply_byte(
1947 handle->cm_processor, byte_buffer, width, height, channels);
1948 }
1949 if (float_buffer != NULL) {
1950 IMB_colormanagement_processor_apply(
1951 handle->cm_processor, float_buffer, width, height, channels, predivide);
1952 }
1953 }
1954
1955 return NULL;
1956 }
1957
processor_transform_apply_threaded(unsigned char * byte_buffer,float * float_buffer,const int width,const int height,const int channels,ColormanageProcessor * cm_processor,const bool predivide,const bool float_from_byte)1958 static void processor_transform_apply_threaded(unsigned char *byte_buffer,
1959 float *float_buffer,
1960 const int width,
1961 const int height,
1962 const int channels,
1963 ColormanageProcessor *cm_processor,
1964 const bool predivide,
1965 const bool float_from_byte)
1966 {
1967 ProcessorTransformInitData init_data;
1968
1969 init_data.cm_processor = cm_processor;
1970 init_data.byte_buffer = byte_buffer;
1971 init_data.float_buffer = float_buffer;
1972 init_data.width = width;
1973 init_data.height = height;
1974 init_data.channels = channels;
1975 init_data.predivide = predivide;
1976 init_data.float_from_byte = float_from_byte;
1977
1978 IMB_processor_apply_threaded(height,
1979 sizeof(ProcessorTransformThread),
1980 &init_data,
1981 processor_transform_init_handle,
1982 do_processor_transform_thread);
1983 }
1984
1985 /** \} */
1986
1987 /* -------------------------------------------------------------------- */
1988 /** \name Color Space Transformation Functions
1989 * \{ */
1990
1991 /* Convert the whole buffer from specified by name color space to another -
1992 * internal implementation. */
colormanagement_transform_ex(unsigned char * byte_buffer,float * float_buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace,bool predivide,bool do_threaded)1993 static void colormanagement_transform_ex(unsigned char *byte_buffer,
1994 float *float_buffer,
1995 int width,
1996 int height,
1997 int channels,
1998 const char *from_colorspace,
1999 const char *to_colorspace,
2000 bool predivide,
2001 bool do_threaded)
2002 {
2003 ColormanageProcessor *cm_processor;
2004
2005 if (from_colorspace[0] == '\0') {
2006 return;
2007 }
2008
2009 if (STREQ(from_colorspace, to_colorspace)) {
2010 /* if source and destination color spaces are identical, skip
2011 * threading overhead and simply do nothing
2012 */
2013 return;
2014 }
2015
2016 cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
2017
2018 if (do_threaded) {
2019 processor_transform_apply_threaded(
2020 byte_buffer, float_buffer, width, height, channels, cm_processor, predivide, false);
2021 }
2022 else {
2023 if (byte_buffer != NULL) {
2024 IMB_colormanagement_processor_apply_byte(cm_processor, byte_buffer, width, height, channels);
2025 }
2026 if (float_buffer != NULL) {
2027 IMB_colormanagement_processor_apply(
2028 cm_processor, float_buffer, width, height, channels, predivide);
2029 }
2030 }
2031
2032 IMB_colormanagement_processor_free(cm_processor);
2033 }
2034
2035 /* convert the whole buffer from specified by name color space to another */
IMB_colormanagement_transform(float * buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace,bool predivide)2036 void IMB_colormanagement_transform(float *buffer,
2037 int width,
2038 int height,
2039 int channels,
2040 const char *from_colorspace,
2041 const char *to_colorspace,
2042 bool predivide)
2043 {
2044 colormanagement_transform_ex(
2045 NULL, buffer, width, height, channels, from_colorspace, to_colorspace, predivide, false);
2046 }
2047
2048 /* convert the whole buffer from specified by name color space to another
2049 * will do threaded conversion
2050 */
IMB_colormanagement_transform_threaded(float * buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace,bool predivide)2051 void IMB_colormanagement_transform_threaded(float *buffer,
2052 int width,
2053 int height,
2054 int channels,
2055 const char *from_colorspace,
2056 const char *to_colorspace,
2057 bool predivide)
2058 {
2059 colormanagement_transform_ex(
2060 NULL, buffer, width, height, channels, from_colorspace, to_colorspace, predivide, true);
2061 }
2062
2063 /* Similar to functions above, but operates on byte buffer. */
IMB_colormanagement_transform_byte(unsigned char * buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace)2064 void IMB_colormanagement_transform_byte(unsigned char *buffer,
2065 int width,
2066 int height,
2067 int channels,
2068 const char *from_colorspace,
2069 const char *to_colorspace)
2070 {
2071 colormanagement_transform_ex(
2072 buffer, NULL, width, height, channels, from_colorspace, to_colorspace, false, false);
2073 }
IMB_colormanagement_transform_byte_threaded(unsigned char * buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace)2074 void IMB_colormanagement_transform_byte_threaded(unsigned char *buffer,
2075 int width,
2076 int height,
2077 int channels,
2078 const char *from_colorspace,
2079 const char *to_colorspace)
2080 {
2081 colormanagement_transform_ex(
2082 buffer, NULL, width, height, channels, from_colorspace, to_colorspace, false, true);
2083 }
2084
2085 /* Similar to above, but gets float buffer from display one. */
IMB_colormanagement_transform_from_byte(float * float_buffer,unsigned char * byte_buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace)2086 void IMB_colormanagement_transform_from_byte(float *float_buffer,
2087 unsigned char *byte_buffer,
2088 int width,
2089 int height,
2090 int channels,
2091 const char *from_colorspace,
2092 const char *to_colorspace)
2093 {
2094 IMB_buffer_float_from_byte(float_buffer,
2095 byte_buffer,
2096 IB_PROFILE_SRGB,
2097 IB_PROFILE_SRGB,
2098 true,
2099 width,
2100 height,
2101 width,
2102 width);
2103 IMB_colormanagement_transform(
2104 float_buffer, width, height, channels, from_colorspace, to_colorspace, true);
2105 }
IMB_colormanagement_transform_from_byte_threaded(float * float_buffer,unsigned char * byte_buffer,int width,int height,int channels,const char * from_colorspace,const char * to_colorspace)2106 void IMB_colormanagement_transform_from_byte_threaded(float *float_buffer,
2107 unsigned char *byte_buffer,
2108 int width,
2109 int height,
2110 int channels,
2111 const char *from_colorspace,
2112 const char *to_colorspace)
2113 {
2114 ColormanageProcessor *cm_processor;
2115 if (from_colorspace == NULL || from_colorspace[0] == '\0') {
2116 return;
2117 }
2118 if (STREQ(from_colorspace, to_colorspace)) {
2119 /* Because this function always takes a byte buffer and returns a float buffer, it must
2120 * always do byte-to-float conversion of some kind. To avoid threading overhead
2121 * IMB_buffer_float_from_byte is used when color spaces are identical. See T51002.
2122 */
2123 IMB_buffer_float_from_byte(float_buffer,
2124 byte_buffer,
2125 IB_PROFILE_SRGB,
2126 IB_PROFILE_SRGB,
2127 false,
2128 width,
2129 height,
2130 width,
2131 width);
2132 IMB_premultiply_rect_float(float_buffer, 4, width, height);
2133 return;
2134 }
2135 cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
2136 processor_transform_apply_threaded(
2137 byte_buffer, float_buffer, width, height, channels, cm_processor, false, true);
2138 IMB_colormanagement_processor_free(cm_processor);
2139 }
2140
IMB_colormanagement_transform_v4(float pixel[4],const char * from_colorspace,const char * to_colorspace)2141 void IMB_colormanagement_transform_v4(float pixel[4],
2142 const char *from_colorspace,
2143 const char *to_colorspace)
2144 {
2145 ColormanageProcessor *cm_processor;
2146
2147 if (from_colorspace[0] == '\0') {
2148 return;
2149 }
2150
2151 if (STREQ(from_colorspace, to_colorspace)) {
2152 /* if source and destination color spaces are identical, skip
2153 * threading overhead and simply do nothing
2154 */
2155 return;
2156 }
2157
2158 cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace);
2159
2160 IMB_colormanagement_processor_apply_v4(cm_processor, pixel);
2161
2162 IMB_colormanagement_processor_free(cm_processor);
2163 }
2164
2165 /* convert pixel from specified by descriptor color space to scene linear
2166 * used by performance-critical areas such as renderer and baker
2167 */
IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3],ColorSpace * colorspace)2168 void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], ColorSpace *colorspace)
2169 {
2170 OCIO_ConstProcessorRcPtr *processor;
2171
2172 if (!colorspace) {
2173 /* should never happen */
2174 printf("%s: perform conversion from unknown color space\n", __func__);
2175 return;
2176 }
2177
2178 processor = colorspace_to_scene_linear_processor(colorspace);
2179
2180 if (processor) {
2181 OCIO_processorApplyRGB(processor, pixel);
2182 }
2183 }
2184
2185 /* same as above, but converts colors in opposite direction */
IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3],ColorSpace * colorspace)2186 void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], ColorSpace *colorspace)
2187 {
2188 OCIO_ConstProcessorRcPtr *processor;
2189
2190 if (!colorspace) {
2191 /* should never happen */
2192 printf("%s: perform conversion from unknown color space\n", __func__);
2193 return;
2194 }
2195
2196 processor = colorspace_from_scene_linear_processor(colorspace);
2197
2198 if (processor) {
2199 OCIO_processorApplyRGB(processor, pixel);
2200 }
2201 }
2202
IMB_colormanagement_colorspace_to_scene_linear_v4(float pixel[4],bool predivide,ColorSpace * colorspace)2203 void IMB_colormanagement_colorspace_to_scene_linear_v4(float pixel[4],
2204 bool predivide,
2205 ColorSpace *colorspace)
2206 {
2207 OCIO_ConstProcessorRcPtr *processor;
2208
2209 if (!colorspace) {
2210 /* should never happen */
2211 printf("%s: perform conversion from unknown color space\n", __func__);
2212 return;
2213 }
2214
2215 processor = colorspace_to_scene_linear_processor(colorspace);
2216
2217 if (processor) {
2218 if (predivide) {
2219 OCIO_processorApplyRGBA_predivide(processor, pixel);
2220 }
2221 else {
2222 OCIO_processorApplyRGBA(processor, pixel);
2223 }
2224 }
2225 }
2226
IMB_colormanagement_colorspace_to_scene_linear(float * buffer,int width,int height,int channels,struct ColorSpace * colorspace,bool predivide)2227 void IMB_colormanagement_colorspace_to_scene_linear(float *buffer,
2228 int width,
2229 int height,
2230 int channels,
2231 struct ColorSpace *colorspace,
2232 bool predivide)
2233 {
2234 OCIO_ConstProcessorRcPtr *processor;
2235
2236 if (!colorspace) {
2237 /* should never happen */
2238 printf("%s: perform conversion from unknown color space\n", __func__);
2239 return;
2240 }
2241
2242 processor = colorspace_to_scene_linear_processor(colorspace);
2243
2244 if (processor) {
2245 OCIO_PackedImageDesc *img;
2246
2247 img = OCIO_createOCIO_PackedImageDesc(buffer,
2248 width,
2249 height,
2250 channels,
2251 sizeof(float),
2252 (size_t)channels * sizeof(float),
2253 (size_t)channels * sizeof(float) * width);
2254
2255 if (predivide) {
2256 OCIO_processorApply_predivide(processor, img);
2257 }
2258 else {
2259 OCIO_processorApply(processor, img);
2260 }
2261
2262 OCIO_PackedImageDescRelease(img);
2263 }
2264 }
2265
IMB_colormanagement_imbuf_to_byte_texture(unsigned char * out_buffer,const int offset_x,const int offset_y,const int width,const int height,const struct ImBuf * ibuf,const bool compress_as_srgb,const bool store_premultiplied)2266 void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer,
2267 const int offset_x,
2268 const int offset_y,
2269 const int width,
2270 const int height,
2271 const struct ImBuf *ibuf,
2272 const bool compress_as_srgb,
2273 const bool store_premultiplied)
2274 {
2275 /* Convert byte buffer for texture storage on the GPU. These have builtin
2276 * support for converting sRGB to linear, which allows us to store textures
2277 * without precision or performance loss at minimal memory usage. */
2278 BLI_assert(ibuf->rect && ibuf->rect_float == NULL);
2279
2280 OCIO_ConstProcessorRcPtr *processor = NULL;
2281 if (compress_as_srgb && ibuf->rect_colorspace &&
2282 !IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace)) {
2283 processor = colorspace_to_scene_linear_processor(ibuf->rect_colorspace);
2284 }
2285
2286 /* TODO(brecht): make this multi-threaded, or at least process in batches. */
2287 const unsigned char *in_buffer = (unsigned char *)ibuf->rect;
2288 const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied;
2289
2290 for (int y = 0; y < height; y++) {
2291 const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
2292 const size_t out_offset = y * width;
2293 const unsigned char *in = in_buffer + in_offset * 4;
2294 unsigned char *out = out_buffer + out_offset * 4;
2295
2296 if (processor) {
2297 /* Convert to scene linear, to sRGB and premultiply. */
2298 for (int x = 0; x < width; x++, in += 4, out += 4) {
2299 float pixel[4];
2300 rgba_uchar_to_float(pixel, in);
2301 OCIO_processorApplyRGB(processor, pixel);
2302 linearrgb_to_srgb_v3_v3(pixel, pixel);
2303 if (use_premultiply) {
2304 mul_v3_fl(pixel, pixel[3]);
2305 }
2306 rgba_float_to_uchar(out, pixel);
2307 }
2308 }
2309 else if (use_premultiply) {
2310 /* Premultiply only. */
2311 for (int x = 0; x < width; x++, in += 4, out += 4) {
2312 out[0] = (in[0] * in[3]) >> 8;
2313 out[1] = (in[1] * in[3]) >> 8;
2314 out[2] = (in[2] * in[3]) >> 8;
2315 out[3] = in[3];
2316 }
2317 }
2318 else {
2319 /* Copy only. */
2320 for (int x = 0; x < width; x++, in += 4, out += 4) {
2321 out[0] = in[0];
2322 out[1] = in[1];
2323 out[2] = in[2];
2324 out[3] = in[3];
2325 }
2326 }
2327 }
2328 }
2329
IMB_colormanagement_imbuf_to_float_texture(float * out_buffer,const int offset_x,const int offset_y,const int width,const int height,const struct ImBuf * ibuf,const bool store_premultiplied)2330 void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer,
2331 const int offset_x,
2332 const int offset_y,
2333 const int width,
2334 const int height,
2335 const struct ImBuf *ibuf,
2336 const bool store_premultiplied)
2337 {
2338 /* Float texture are stored in scene linear color space, with premultiplied
2339 * alpha depending on the image alpha mode. */
2340 const float *in_buffer = ibuf->rect_float;
2341 const int in_channels = ibuf->channels;
2342 const bool use_unpremultiply = IMB_alpha_affects_rgb(ibuf) && !store_premultiplied;
2343
2344 for (int y = 0; y < height; y++) {
2345 const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
2346 const size_t out_offset = y * width;
2347 const float *in = in_buffer + in_offset * in_channels;
2348 float *out = out_buffer + out_offset * 4;
2349
2350 if (in_channels == 1) {
2351 /* Copy single channel. */
2352 for (int x = 0; x < width; x++, in += 1, out += 4) {
2353 out[0] = in[0];
2354 out[1] = in[0];
2355 out[2] = in[0];
2356 out[3] = in[0];
2357 }
2358 }
2359 else if (in_channels == 3) {
2360 /* Copy RGB. */
2361 for (int x = 0; x < width; x++, in += 3, out += 4) {
2362 out[0] = in[0];
2363 out[1] = in[1];
2364 out[2] = in[2];
2365 out[3] = 1.0f;
2366 }
2367 }
2368 else if (in_channels == 4) {
2369 /* Copy or convert RGBA. */
2370 if (use_unpremultiply) {
2371 for (int x = 0; x < width; x++, in += 4, out += 4) {
2372 premul_to_straight_v4_v4(out, in);
2373 }
2374 }
2375 else {
2376 memcpy(out, in, sizeof(float[4]) * width);
2377 }
2378 }
2379 }
2380 }
2381
2382 /* Conversion between color picking role. Typically we would expect such a
2383 * requirements:
2384 * - It is approximately perceptually linear, so that the HSV numbers and
2385 * the HSV cube/circle have an intuitive distribution.
2386 * - It has the same gamut as the scene linear color space.
2387 * - Color picking values 0..1 map to scene linear values in the 0..1 range,
2388 * so that picked albedo values are energy conserving.
2389 */
IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3])2390 void IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3])
2391 {
2392 if (!global_color_picking_state.processor_to && !global_color_picking_state.failed) {
2393 /* Create processor if none exists. */
2394 BLI_mutex_lock(&processor_lock);
2395
2396 if (!global_color_picking_state.processor_to && !global_color_picking_state.failed) {
2397 global_color_picking_state.processor_to = create_colorspace_transform_processor(
2398 global_role_scene_linear, global_role_color_picking);
2399
2400 if (!global_color_picking_state.processor_to) {
2401 global_color_picking_state.failed = true;
2402 }
2403 }
2404
2405 BLI_mutex_unlock(&processor_lock);
2406 }
2407
2408 if (global_color_picking_state.processor_to) {
2409 OCIO_processorApplyRGB(global_color_picking_state.processor_to, pixel);
2410 }
2411 }
2412
IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3])2413 void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3])
2414 {
2415 if (!global_color_picking_state.processor_from && !global_color_picking_state.failed) {
2416 /* Create processor if none exists. */
2417 BLI_mutex_lock(&processor_lock);
2418
2419 if (!global_color_picking_state.processor_from && !global_color_picking_state.failed) {
2420 global_color_picking_state.processor_from = create_colorspace_transform_processor(
2421 global_role_color_picking, global_role_scene_linear);
2422
2423 if (!global_color_picking_state.processor_from) {
2424 global_color_picking_state.failed = true;
2425 }
2426 }
2427
2428 BLI_mutex_unlock(&processor_lock);
2429 }
2430
2431 if (global_color_picking_state.processor_from) {
2432 OCIO_processorApplyRGB(global_color_picking_state.processor_from, pixel);
2433 }
2434 }
2435
2436 /* Conversion between sRGB, for rare cases like hex color or copy/pasting
2437 * between UI theme and scene linear colors. */
IMB_colormanagement_scene_linear_to_srgb_v3(float pixel[3])2438 void IMB_colormanagement_scene_linear_to_srgb_v3(float pixel[3])
2439 {
2440 mul_m3_v3(imbuf_rgb_to_xyz, pixel);
2441 mul_m3_v3(imbuf_xyz_to_linear_srgb, pixel);
2442 linearrgb_to_srgb_v3_v3(pixel, pixel);
2443 }
2444
IMB_colormanagement_srgb_to_scene_linear_v3(float pixel[3])2445 void IMB_colormanagement_srgb_to_scene_linear_v3(float pixel[3])
2446 {
2447 srgb_to_linearrgb_v3_v3(pixel, pixel);
2448 mul_m3_v3(imbuf_linear_srgb_to_xyz, pixel);
2449 mul_m3_v3(imbuf_xyz_to_rgb, pixel);
2450 }
2451
2452 /* convert pixel from scene linear to display space using default view
2453 * used by performance-critical areas such as color-related widgets where we want to reduce
2454 * amount of per-widget allocations
2455 */
IMB_colormanagement_scene_linear_to_display_v3(float pixel[3],ColorManagedDisplay * display)2456 void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManagedDisplay *display)
2457 {
2458 OCIO_ConstProcessorRcPtr *processor;
2459
2460 processor = display_from_scene_linear_processor(display);
2461
2462 if (processor) {
2463 OCIO_processorApplyRGB(processor, pixel);
2464 }
2465 }
2466
2467 /* same as above, but converts color in opposite direction */
IMB_colormanagement_display_to_scene_linear_v3(float pixel[3],ColorManagedDisplay * display)2468 void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], ColorManagedDisplay *display)
2469 {
2470 OCIO_ConstProcessorRcPtr *processor;
2471
2472 processor = display_to_scene_linear_processor(display);
2473
2474 if (processor) {
2475 OCIO_processorApplyRGB(processor, pixel);
2476 }
2477 }
2478
IMB_colormanagement_pixel_to_display_space_v4(float result[4],const float pixel[4],const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)2479 void IMB_colormanagement_pixel_to_display_space_v4(
2480 float result[4],
2481 const float pixel[4],
2482 const ColorManagedViewSettings *view_settings,
2483 const ColorManagedDisplaySettings *display_settings)
2484 {
2485 ColormanageProcessor *cm_processor;
2486
2487 copy_v4_v4(result, pixel);
2488
2489 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2490 IMB_colormanagement_processor_apply_v4(cm_processor, result);
2491 IMB_colormanagement_processor_free(cm_processor);
2492 }
2493
IMB_colormanagement_pixel_to_display_space_v3(float result[3],const float pixel[3],const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)2494 void IMB_colormanagement_pixel_to_display_space_v3(
2495 float result[3],
2496 const float pixel[3],
2497 const ColorManagedViewSettings *view_settings,
2498 const ColorManagedDisplaySettings *display_settings)
2499 {
2500 ColormanageProcessor *cm_processor;
2501
2502 copy_v3_v3(result, pixel);
2503
2504 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2505 IMB_colormanagement_processor_apply_v3(cm_processor, result);
2506 IMB_colormanagement_processor_free(cm_processor);
2507 }
2508
colormanagement_imbuf_make_display_space(ImBuf * ibuf,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,bool make_byte)2509 static void colormanagement_imbuf_make_display_space(
2510 ImBuf *ibuf,
2511 const ColorManagedViewSettings *view_settings,
2512 const ColorManagedDisplaySettings *display_settings,
2513 bool make_byte)
2514 {
2515 if (!ibuf->rect && make_byte) {
2516 imb_addrectImBuf(ibuf);
2517 }
2518
2519 colormanage_display_buffer_process_ex(
2520 ibuf, ibuf->rect_float, (unsigned char *)ibuf->rect, view_settings, display_settings);
2521 }
2522
IMB_colormanagement_imbuf_make_display_space(ImBuf * ibuf,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)2523 void IMB_colormanagement_imbuf_make_display_space(
2524 ImBuf *ibuf,
2525 const ColorManagedViewSettings *view_settings,
2526 const ColorManagedDisplaySettings *display_settings)
2527 {
2528 colormanagement_imbuf_make_display_space(ibuf, view_settings, display_settings, false);
2529 }
2530
2531 /* prepare image buffer to be saved on disk, applying color management if needed
2532 * color management would be applied if image is saving as render result and if
2533 * file format is not expecting float buffer to be in linear space (currently
2534 * JPEG2000 and TIFF are such formats -- they're storing image as float but
2535 * file itself stores applied color space).
2536 *
2537 * Both byte and float buffers would contain applied color space, and result's
2538 * float_colorspace would be set to display color space. This should be checked
2539 * in image format write callback and if float_colorspace is not NULL, no color
2540 * space transformation should be applied on this buffer.
2541 */
IMB_colormanagement_imbuf_for_write(ImBuf * ibuf,bool save_as_render,bool allocate_result,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,ImageFormatData * image_format_data)2542 ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf,
2543 bool save_as_render,
2544 bool allocate_result,
2545 const ColorManagedViewSettings *view_settings,
2546 const ColorManagedDisplaySettings *display_settings,
2547 ImageFormatData *image_format_data)
2548 {
2549 ImBuf *colormanaged_ibuf = ibuf;
2550 bool do_colormanagement;
2551 bool is_movie = BKE_imtype_is_movie(image_format_data->imtype);
2552 bool requires_linear_float = BKE_imtype_requires_linear_float(image_format_data->imtype);
2553 bool do_alpha_under = image_format_data->planes != R_IMF_PLANES_RGBA;
2554
2555 if (ibuf->rect_float && ibuf->rect &&
2556 (ibuf->userflags & (IB_DISPLAY_BUFFER_INVALID | IB_RECT_INVALID)) != 0) {
2557 IMB_rect_from_float(ibuf);
2558 ibuf->userflags &= ~(IB_RECT_INVALID | IB_DISPLAY_BUFFER_INVALID);
2559 }
2560
2561 do_colormanagement = save_as_render && (is_movie || !requires_linear_float);
2562
2563 if (do_colormanagement || do_alpha_under) {
2564 if (allocate_result) {
2565 colormanaged_ibuf = IMB_dupImBuf(ibuf);
2566 }
2567 else {
2568 /* Render pipeline is constructing image buffer itself,
2569 * but it's re-using byte and float buffers from render result make copy of this buffers
2570 * here sine this buffers would be transformed to other color space here.
2571 */
2572
2573 if (ibuf->rect && (ibuf->mall & IB_rect) == 0) {
2574 ibuf->rect = MEM_dupallocN(ibuf->rect);
2575 ibuf->mall |= IB_rect;
2576 }
2577
2578 if (ibuf->rect_float && (ibuf->mall & IB_rectfloat) == 0) {
2579 ibuf->rect_float = MEM_dupallocN(ibuf->rect_float);
2580 ibuf->mall |= IB_rectfloat;
2581 }
2582 }
2583 }
2584
2585 /* If we're saving from RGBA to RGB buffer then it's not
2586 * so much useful to just ignore alpha -- it leads to bad
2587 * artifacts especially when saving byte images.
2588 *
2589 * What we do here is we're overlaying our image on top of
2590 * background color (which is currently black).
2591 *
2592 * This is quite much the same as what Gimp does and it
2593 * seems to be what artists expects from saving.
2594 *
2595 * Do a conversion here, so image format writers could
2596 * happily assume all the alpha tricks were made already.
2597 * helps keep things locally here, not spreading it to
2598 * all possible image writers we've got.
2599 */
2600 if (do_alpha_under) {
2601 float color[3] = {0, 0, 0};
2602
2603 if (colormanaged_ibuf->rect_float && colormanaged_ibuf->channels == 4) {
2604 IMB_alpha_under_color_float(
2605 colormanaged_ibuf->rect_float, colormanaged_ibuf->x, colormanaged_ibuf->y, color);
2606 }
2607
2608 if (colormanaged_ibuf->rect) {
2609 IMB_alpha_under_color_byte((unsigned char *)colormanaged_ibuf->rect,
2610 colormanaged_ibuf->x,
2611 colormanaged_ibuf->y,
2612 color);
2613 }
2614 }
2615
2616 if (do_colormanagement) {
2617 bool make_byte = false;
2618 const ImFileType *type;
2619
2620 /* for proper check whether byte buffer is required by a format or not
2621 * should be pretty safe since this image buffer is supposed to be used for
2622 * saving only and ftype would be overwritten a bit later by BKE_imbuf_write
2623 */
2624 colormanaged_ibuf->ftype = BKE_image_imtype_to_ftype(image_format_data->imtype,
2625 &colormanaged_ibuf->foptions);
2626
2627 /* if file format isn't able to handle float buffer itself,
2628 * we need to allocate byte buffer and store color managed
2629 * image there
2630 */
2631 for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
2632 if (type->save && type->ftype(type, colormanaged_ibuf)) {
2633 if ((type->flag & IM_FTYPE_FLOAT) == 0) {
2634 make_byte = true;
2635 }
2636
2637 break;
2638 }
2639 }
2640
2641 /* perform color space conversions */
2642 colormanagement_imbuf_make_display_space(
2643 colormanaged_ibuf, view_settings, display_settings, make_byte);
2644
2645 if (colormanaged_ibuf->rect_float) {
2646 /* float buffer isn't linear anymore,
2647 * image format write callback should check for this flag and assume
2648 * no space conversion should happen if ibuf->float_colorspace != NULL
2649 */
2650 colormanaged_ibuf->float_colorspace = display_transform_get_colorspace(view_settings,
2651 display_settings);
2652 }
2653 }
2654
2655 if (colormanaged_ibuf != ibuf) {
2656 IMB_metadata_copy(colormanaged_ibuf, ibuf);
2657 }
2658
2659 return colormanaged_ibuf;
2660 }
2661
IMB_colormanagement_buffer_make_display_space(float * buffer,unsigned char * display_buffer,int width,int height,int channels,float dither,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)2662 void IMB_colormanagement_buffer_make_display_space(
2663 float *buffer,
2664 unsigned char *display_buffer,
2665 int width,
2666 int height,
2667 int channels,
2668 float dither,
2669 const ColorManagedViewSettings *view_settings,
2670 const ColorManagedDisplaySettings *display_settings)
2671 {
2672 ColormanageProcessor *cm_processor;
2673 size_t float_buffer_size = ((size_t)width) * height * channels * sizeof(float);
2674 float *display_buffer_float = MEM_mallocN(float_buffer_size, "byte_buffer_make_display_space");
2675
2676 /* TODO(sergey): Convert float directly to byte buffer. */
2677
2678 memcpy(display_buffer_float, buffer, float_buffer_size);
2679
2680 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
2681
2682 processor_transform_apply_threaded(
2683 NULL, display_buffer_float, width, height, channels, cm_processor, true, false);
2684
2685 IMB_buffer_byte_from_float(display_buffer,
2686 display_buffer_float,
2687 channels,
2688 dither,
2689 IB_PROFILE_SRGB,
2690 IB_PROFILE_SRGB,
2691 true,
2692 width,
2693 height,
2694 width,
2695 width);
2696
2697 MEM_freeN(display_buffer_float);
2698 IMB_colormanagement_processor_free(cm_processor);
2699 }
2700
2701 /** \} */
2702
2703 /* -------------------------------------------------------------------- */
2704 /** \name Public Display Buffers Interfaces
2705 * \{ */
2706
2707 /* acquire display buffer for given image buffer using specified view and display settings */
IMB_display_buffer_acquire(ImBuf * ibuf,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,void ** cache_handle)2708 unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf,
2709 const ColorManagedViewSettings *view_settings,
2710 const ColorManagedDisplaySettings *display_settings,
2711 void **cache_handle)
2712 {
2713 unsigned char *display_buffer;
2714 size_t buffer_size;
2715 ColormanageCacheViewSettings cache_view_settings;
2716 ColormanageCacheDisplaySettings cache_display_settings;
2717 ColorManagedViewSettings default_view_settings;
2718 const ColorManagedViewSettings *applied_view_settings;
2719
2720 *cache_handle = NULL;
2721
2722 if (!ibuf->x || !ibuf->y) {
2723 return NULL;
2724 }
2725
2726 if (view_settings) {
2727 applied_view_settings = view_settings;
2728 }
2729 else {
2730 /* If no view settings were specified, use default ones, which will
2731 * attempt not to do any extra color correction. */
2732 IMB_colormanagement_init_default_view_settings(&default_view_settings, display_settings);
2733 applied_view_settings = &default_view_settings;
2734 }
2735
2736 /* early out: no float buffer and byte buffer is already in display space,
2737 * let's just use if
2738 */
2739 if (ibuf->rect_float == NULL && ibuf->rect_colorspace && ibuf->channels == 4) {
2740 if (is_ibuf_rect_in_display_space(ibuf, applied_view_settings, display_settings)) {
2741 return (unsigned char *)ibuf->rect;
2742 }
2743 }
2744
2745 colormanage_view_settings_to_cache(ibuf, &cache_view_settings, applied_view_settings);
2746 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
2747
2748 if (ibuf->invalid_rect.xmin != ibuf->invalid_rect.xmax) {
2749 if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
2750 IMB_partial_display_buffer_update_threaded(ibuf,
2751 ibuf->rect_float,
2752 (unsigned char *)ibuf->rect,
2753 ibuf->x,
2754 0,
2755 0,
2756 applied_view_settings,
2757 display_settings,
2758 ibuf->invalid_rect.xmin,
2759 ibuf->invalid_rect.ymin,
2760 ibuf->invalid_rect.xmax,
2761 ibuf->invalid_rect.ymax);
2762 }
2763
2764 BLI_rcti_init(&ibuf->invalid_rect, 0, 0, 0, 0);
2765 }
2766
2767 BLI_thread_lock(LOCK_COLORMANAGE);
2768
2769 /* ensure color management bit fields exists */
2770 if (!ibuf->display_buffer_flags) {
2771 ibuf->display_buffer_flags = MEM_callocN(sizeof(unsigned int) * global_tot_display,
2772 "imbuf display_buffer_flags");
2773 }
2774 else if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) {
2775 /* all display buffers were marked as invalid from other areas,
2776 * now propagate this flag to internal color management routines
2777 */
2778 memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
2779
2780 ibuf->userflags &= ~IB_DISPLAY_BUFFER_INVALID;
2781 }
2782
2783 display_buffer = colormanage_cache_get(
2784 ibuf, &cache_view_settings, &cache_display_settings, cache_handle);
2785
2786 if (display_buffer) {
2787 BLI_thread_unlock(LOCK_COLORMANAGE);
2788 return display_buffer;
2789 }
2790
2791 buffer_size = DISPLAY_BUFFER_CHANNELS * ((size_t)ibuf->x) * ibuf->y * sizeof(char);
2792 display_buffer = MEM_callocN(buffer_size, "imbuf display buffer");
2793
2794 colormanage_display_buffer_process(
2795 ibuf, display_buffer, applied_view_settings, display_settings);
2796
2797 colormanage_cache_put(
2798 ibuf, &cache_view_settings, &cache_display_settings, display_buffer, cache_handle);
2799
2800 BLI_thread_unlock(LOCK_COLORMANAGE);
2801
2802 return display_buffer;
2803 }
2804
2805 /* same as IMB_display_buffer_acquire but gets view and display settings from context */
IMB_display_buffer_acquire_ctx(const bContext * C,ImBuf * ibuf,void ** cache_handle)2806 unsigned char *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle)
2807 {
2808 ColorManagedViewSettings *view_settings;
2809 ColorManagedDisplaySettings *display_settings;
2810
2811 IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
2812
2813 return IMB_display_buffer_acquire(ibuf, view_settings, display_settings, cache_handle);
2814 }
2815
IMB_display_buffer_transform_apply(unsigned char * display_buffer,float * linear_buffer,int width,int height,int channels,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,bool predivide)2816 void IMB_display_buffer_transform_apply(unsigned char *display_buffer,
2817 float *linear_buffer,
2818 int width,
2819 int height,
2820 int channels,
2821 const ColorManagedViewSettings *view_settings,
2822 const ColorManagedDisplaySettings *display_settings,
2823 bool predivide)
2824 {
2825 float *buffer;
2826 ColormanageProcessor *cm_processor = IMB_colormanagement_display_processor_new(view_settings,
2827 display_settings);
2828
2829 buffer = MEM_mallocN((size_t)channels * width * height * sizeof(float),
2830 "display transform temp buffer");
2831 memcpy(buffer, linear_buffer, (size_t)channels * width * height * sizeof(float));
2832
2833 IMB_colormanagement_processor_apply(cm_processor, buffer, width, height, channels, predivide);
2834
2835 IMB_colormanagement_processor_free(cm_processor);
2836
2837 IMB_buffer_byte_from_float(display_buffer,
2838 buffer,
2839 channels,
2840 0.0f,
2841 IB_PROFILE_SRGB,
2842 IB_PROFILE_SRGB,
2843 false,
2844 width,
2845 height,
2846 width,
2847 width);
2848
2849 MEM_freeN(buffer);
2850 }
2851
IMB_display_buffer_release(void * cache_handle)2852 void IMB_display_buffer_release(void *cache_handle)
2853 {
2854 if (cache_handle) {
2855 BLI_thread_lock(LOCK_COLORMANAGE);
2856
2857 colormanage_cache_handle_release(cache_handle);
2858
2859 BLI_thread_unlock(LOCK_COLORMANAGE);
2860 }
2861 }
2862
2863 /** \} */
2864
2865 /* -------------------------------------------------------------------- */
2866 /** \name Display Functions
2867 * \{ */
2868
colormanage_display_get_default_name(void)2869 const char *colormanage_display_get_default_name(void)
2870 {
2871 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
2872 const char *display_name;
2873
2874 display_name = OCIO_configGetDefaultDisplay(config);
2875
2876 OCIO_configRelease(config);
2877
2878 return display_name;
2879 }
2880
colormanage_display_get_default(void)2881 ColorManagedDisplay *colormanage_display_get_default(void)
2882 {
2883 const char *display_name = colormanage_display_get_default_name();
2884
2885 if (display_name[0] == '\0') {
2886 return NULL;
2887 }
2888
2889 return colormanage_display_get_named(display_name);
2890 }
2891
colormanage_display_add(const char * name)2892 ColorManagedDisplay *colormanage_display_add(const char *name)
2893 {
2894 ColorManagedDisplay *display;
2895 int index = 0;
2896
2897 if (global_displays.last) {
2898 ColorManagedDisplay *last_display = global_displays.last;
2899
2900 index = last_display->index;
2901 }
2902
2903 display = MEM_callocN(sizeof(ColorManagedDisplay), "ColorManagedDisplay");
2904
2905 display->index = index + 1;
2906
2907 BLI_strncpy(display->name, name, sizeof(display->name));
2908
2909 BLI_addtail(&global_displays, display);
2910
2911 return display;
2912 }
2913
colormanage_display_get_named(const char * name)2914 ColorManagedDisplay *colormanage_display_get_named(const char *name)
2915 {
2916 ColorManagedDisplay *display;
2917
2918 for (display = global_displays.first; display; display = display->next) {
2919 if (STREQ(display->name, name)) {
2920 return display;
2921 }
2922 }
2923
2924 return NULL;
2925 }
2926
colormanage_display_get_indexed(int index)2927 ColorManagedDisplay *colormanage_display_get_indexed(int index)
2928 {
2929 /* display indices are 1-based */
2930 return BLI_findlink(&global_displays, index - 1);
2931 }
2932
IMB_colormanagement_display_get_named_index(const char * name)2933 int IMB_colormanagement_display_get_named_index(const char *name)
2934 {
2935 ColorManagedDisplay *display;
2936
2937 display = colormanage_display_get_named(name);
2938
2939 if (display) {
2940 return display->index;
2941 }
2942
2943 return 0;
2944 }
2945
IMB_colormanagement_display_get_indexed_name(int index)2946 const char *IMB_colormanagement_display_get_indexed_name(int index)
2947 {
2948 ColorManagedDisplay *display;
2949
2950 display = colormanage_display_get_indexed(index);
2951
2952 if (display) {
2953 return display->name;
2954 }
2955
2956 return NULL;
2957 }
2958
IMB_colormanagement_display_get_default_name(void)2959 const char *IMB_colormanagement_display_get_default_name(void)
2960 {
2961 ColorManagedDisplay *display = colormanage_display_get_default();
2962
2963 return display->name;
2964 }
2965
2966 /* used by performance-critical pixel processing areas, such as color widgets */
IMB_colormanagement_display_get_named(const char * name)2967 ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name)
2968 {
2969 return colormanage_display_get_named(name);
2970 }
2971
IMB_colormanagement_display_get_none_name(void)2972 const char *IMB_colormanagement_display_get_none_name(void)
2973 {
2974 if (colormanage_display_get_named("None") != NULL) {
2975 return "None";
2976 }
2977
2978 return colormanage_display_get_default_name();
2979 }
2980
IMB_colormanagement_display_get_default_view_transform_name(struct ColorManagedDisplay * display)2981 const char *IMB_colormanagement_display_get_default_view_transform_name(
2982 struct ColorManagedDisplay *display)
2983 {
2984 return colormanage_view_get_default_name(display);
2985 }
2986
2987 /** \} */
2988
2989 /* -------------------------------------------------------------------- */
2990 /** \name View Functions
2991 * \{ */
2992
colormanage_view_get_default_name(const ColorManagedDisplay * display)2993 const char *colormanage_view_get_default_name(const ColorManagedDisplay *display)
2994 {
2995 OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
2996 const char *name;
2997
2998 name = OCIO_configGetDefaultView(config, display->name);
2999
3000 OCIO_configRelease(config);
3001
3002 return name;
3003 }
3004
colormanage_view_get_default(const ColorManagedDisplay * display)3005 ColorManagedView *colormanage_view_get_default(const ColorManagedDisplay *display)
3006 {
3007 const char *name = colormanage_view_get_default_name(display);
3008
3009 if (!name || name[0] == '\0') {
3010 return NULL;
3011 }
3012
3013 return colormanage_view_get_named(name);
3014 }
3015
colormanage_view_add(const char * name)3016 ColorManagedView *colormanage_view_add(const char *name)
3017 {
3018 ColorManagedView *view;
3019 int index = global_tot_view;
3020
3021 view = MEM_callocN(sizeof(ColorManagedView), "ColorManagedView");
3022 view->index = index + 1;
3023 BLI_strncpy(view->name, name, sizeof(view->name));
3024
3025 BLI_addtail(&global_views, view);
3026
3027 global_tot_view++;
3028
3029 return view;
3030 }
3031
colormanage_view_get_named(const char * name)3032 ColorManagedView *colormanage_view_get_named(const char *name)
3033 {
3034 ColorManagedView *view;
3035
3036 for (view = global_views.first; view; view = view->next) {
3037 if (STREQ(view->name, name)) {
3038 return view;
3039 }
3040 }
3041
3042 return NULL;
3043 }
3044
colormanage_view_get_indexed(int index)3045 ColorManagedView *colormanage_view_get_indexed(int index)
3046 {
3047 /* view transform indices are 1-based */
3048 return BLI_findlink(&global_views, index - 1);
3049 }
3050
colormanage_view_get_named_for_display(const char * display_name,const char * name)3051 ColorManagedView *colormanage_view_get_named_for_display(const char *display_name,
3052 const char *name)
3053 {
3054 ColorManagedDisplay *display = colormanage_display_get_named(display_name);
3055 if (display == NULL) {
3056 return NULL;
3057 }
3058 LISTBASE_FOREACH (LinkData *, view_link, &display->views) {
3059 ColorManagedView *view = view_link->data;
3060 if (STRCASEEQ(name, view->name)) {
3061 return view;
3062 }
3063 }
3064 return NULL;
3065 }
3066
IMB_colormanagement_view_get_named_index(const char * name)3067 int IMB_colormanagement_view_get_named_index(const char *name)
3068 {
3069 ColorManagedView *view = colormanage_view_get_named(name);
3070
3071 if (view) {
3072 return view->index;
3073 }
3074
3075 return 0;
3076 }
3077
IMB_colormanagement_view_get_indexed_name(int index)3078 const char *IMB_colormanagement_view_get_indexed_name(int index)
3079 {
3080 ColorManagedView *view = colormanage_view_get_indexed(index);
3081
3082 if (view) {
3083 return view->name;
3084 }
3085
3086 return NULL;
3087 }
3088
IMB_colormanagement_view_get_default_name(const char * display_name)3089 const char *IMB_colormanagement_view_get_default_name(const char *display_name)
3090 {
3091 ColorManagedDisplay *display = colormanage_display_get_named(display_name);
3092 ColorManagedView *view = NULL;
3093
3094 if (display) {
3095 view = colormanage_view_get_default(display);
3096 }
3097
3098 if (view) {
3099 return view->name;
3100 }
3101
3102 return NULL;
3103 }
3104
3105 /** \} */
3106
3107 /* -------------------------------------------------------------------- */
3108 /** \name Color Space Functions
3109 * \{ */
3110
colormanage_description_strip(char * description)3111 static void colormanage_description_strip(char *description)
3112 {
3113 int i, n;
3114
3115 for (i = (int)strlen(description) - 1; i >= 0; i--) {
3116 if (ELEM(description[i], '\r', '\n')) {
3117 description[i] = '\0';
3118 }
3119 else {
3120 break;
3121 }
3122 }
3123
3124 for (i = 0, n = strlen(description); i < n; i++) {
3125 if (ELEM(description[i], '\r', '\n')) {
3126 description[i] = ' ';
3127 }
3128 }
3129 }
3130
colormanage_colorspace_add(const char * name,const char * description,bool is_invertible,bool is_data)3131 ColorSpace *colormanage_colorspace_add(const char *name,
3132 const char *description,
3133 bool is_invertible,
3134 bool is_data)
3135 {
3136 ColorSpace *colorspace, *prev_space;
3137 int counter = 1;
3138
3139 colorspace = MEM_callocN(sizeof(ColorSpace), "ColorSpace");
3140
3141 BLI_strncpy(colorspace->name, name, sizeof(colorspace->name));
3142
3143 if (description) {
3144 BLI_strncpy(colorspace->description, description, sizeof(colorspace->description));
3145
3146 colormanage_description_strip(colorspace->description);
3147 }
3148
3149 colorspace->is_invertible = is_invertible;
3150 colorspace->is_data = is_data;
3151
3152 for (prev_space = global_colorspaces.first; prev_space; prev_space = prev_space->next) {
3153 if (BLI_strcasecmp(prev_space->name, colorspace->name) > 0) {
3154 break;
3155 }
3156
3157 prev_space->index = counter++;
3158 }
3159
3160 if (!prev_space) {
3161 BLI_addtail(&global_colorspaces, colorspace);
3162 }
3163 else {
3164 BLI_insertlinkbefore(&global_colorspaces, prev_space, colorspace);
3165 }
3166
3167 colorspace->index = counter++;
3168 for (; prev_space; prev_space = prev_space->next) {
3169 prev_space->index = counter++;
3170 }
3171
3172 global_tot_colorspace++;
3173
3174 return colorspace;
3175 }
3176
colormanage_colorspace_get_named(const char * name)3177 ColorSpace *colormanage_colorspace_get_named(const char *name)
3178 {
3179 ColorSpace *colorspace;
3180
3181 for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) {
3182 if (STREQ(colorspace->name, name)) {
3183 return colorspace;
3184 }
3185 }
3186
3187 return NULL;
3188 }
3189
colormanage_colorspace_get_roled(int role)3190 ColorSpace *colormanage_colorspace_get_roled(int role)
3191 {
3192 const char *role_colorspace = IMB_colormanagement_role_colorspace_name_get(role);
3193
3194 return colormanage_colorspace_get_named(role_colorspace);
3195 }
3196
colormanage_colorspace_get_indexed(int index)3197 ColorSpace *colormanage_colorspace_get_indexed(int index)
3198 {
3199 /* color space indices are 1-based */
3200 return BLI_findlink(&global_colorspaces, index - 1);
3201 }
3202
IMB_colormanagement_colorspace_get_named_index(const char * name)3203 int IMB_colormanagement_colorspace_get_named_index(const char *name)
3204 {
3205 ColorSpace *colorspace;
3206
3207 colorspace = colormanage_colorspace_get_named(name);
3208
3209 if (colorspace) {
3210 return colorspace->index;
3211 }
3212
3213 return 0;
3214 }
3215
IMB_colormanagement_colorspace_get_indexed_name(int index)3216 const char *IMB_colormanagement_colorspace_get_indexed_name(int index)
3217 {
3218 ColorSpace *colorspace;
3219
3220 colorspace = colormanage_colorspace_get_indexed(index);
3221
3222 if (colorspace) {
3223 return colorspace->name;
3224 }
3225
3226 return "";
3227 }
3228
IMB_colormanagement_colorspace_from_ibuf_ftype(ColorManagedColorspaceSettings * colorspace_settings,ImBuf * ibuf)3229 void IMB_colormanagement_colorspace_from_ibuf_ftype(
3230 ColorManagedColorspaceSettings *colorspace_settings, ImBuf *ibuf)
3231 {
3232 /* Don't modify non-color data space, it does not change with file type. */
3233 ColorSpace *colorspace = colormanage_colorspace_get_named(colorspace_settings->name);
3234
3235 if (colorspace && colorspace->is_data) {
3236 return;
3237 }
3238
3239 /* Get color space from file type. */
3240 const ImFileType *type;
3241
3242 for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
3243 if (type->save && type->ftype(type, ibuf)) {
3244 const char *role_colorspace;
3245
3246 role_colorspace = IMB_colormanagement_role_colorspace_name_get(type->default_save_role);
3247
3248 BLI_strncpy(colorspace_settings->name, role_colorspace, sizeof(colorspace_settings->name));
3249 }
3250 }
3251 }
3252
3253 /** \} */
3254
3255 /* -------------------------------------------------------------------- */
3256 /** \name Looks Functions
3257 * \{ */
3258
colormanage_look_add(const char * name,const char * process_space,bool is_noop)3259 ColorManagedLook *colormanage_look_add(const char *name, const char *process_space, bool is_noop)
3260 {
3261 ColorManagedLook *look;
3262 int index = global_tot_looks;
3263
3264 look = MEM_callocN(sizeof(ColorManagedLook), "ColorManagedLook");
3265 look->index = index + 1;
3266 BLI_strncpy(look->name, name, sizeof(look->name));
3267 BLI_strncpy(look->ui_name, name, sizeof(look->ui_name));
3268 BLI_strncpy(look->process_space, process_space, sizeof(look->process_space));
3269 look->is_noop = is_noop;
3270
3271 /* Detect view specific looks. */
3272 const char *separator_offset = strstr(look->name, " - ");
3273 if (separator_offset) {
3274 BLI_strncpy(look->view, look->name, separator_offset - look->name + 1);
3275 BLI_strncpy(look->ui_name, separator_offset + strlen(" - "), sizeof(look->ui_name));
3276 }
3277
3278 BLI_addtail(&global_looks, look);
3279
3280 global_tot_looks++;
3281
3282 return look;
3283 }
3284
colormanage_look_get_named(const char * name)3285 ColorManagedLook *colormanage_look_get_named(const char *name)
3286 {
3287 ColorManagedLook *look;
3288
3289 for (look = global_looks.first; look; look = look->next) {
3290 if (STREQ(look->name, name)) {
3291 return look;
3292 }
3293 }
3294
3295 return NULL;
3296 }
3297
colormanage_look_get_indexed(int index)3298 ColorManagedLook *colormanage_look_get_indexed(int index)
3299 {
3300 /* look indices are 1-based */
3301 return BLI_findlink(&global_looks, index - 1);
3302 }
3303
IMB_colormanagement_look_get_named_index(const char * name)3304 int IMB_colormanagement_look_get_named_index(const char *name)
3305 {
3306 ColorManagedLook *look;
3307
3308 look = colormanage_look_get_named(name);
3309
3310 if (look) {
3311 return look->index;
3312 }
3313
3314 return 0;
3315 }
3316
IMB_colormanagement_look_get_indexed_name(int index)3317 const char *IMB_colormanagement_look_get_indexed_name(int index)
3318 {
3319 ColorManagedLook *look;
3320
3321 look = colormanage_look_get_indexed(index);
3322
3323 if (look) {
3324 return look->name;
3325 }
3326
3327 return NULL;
3328 }
3329
3330 /** \} */
3331
3332 /* -------------------------------------------------------------------- */
3333 /** \name RNA Helper Functions
3334 * \{ */
3335
IMB_colormanagement_display_items_add(EnumPropertyItem ** items,int * totitem)3336 void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totitem)
3337 {
3338 ColorManagedDisplay *display;
3339
3340 for (display = global_displays.first; display; display = display->next) {
3341 EnumPropertyItem item;
3342
3343 item.value = display->index;
3344 item.name = display->name;
3345 item.identifier = display->name;
3346 item.icon = 0;
3347 item.description = "";
3348
3349 RNA_enum_item_add(items, totitem, &item);
3350 }
3351 }
3352
colormanagement_view_item_add(EnumPropertyItem ** items,int * totitem,ColorManagedView * view)3353 static void colormanagement_view_item_add(EnumPropertyItem **items,
3354 int *totitem,
3355 ColorManagedView *view)
3356 {
3357 EnumPropertyItem item;
3358
3359 item.value = view->index;
3360 item.name = view->name;
3361 item.identifier = view->name;
3362 item.icon = 0;
3363 item.description = "";
3364
3365 RNA_enum_item_add(items, totitem, &item);
3366 }
3367
IMB_colormanagement_view_items_add(EnumPropertyItem ** items,int * totitem,const char * display_name)3368 void IMB_colormanagement_view_items_add(EnumPropertyItem **items,
3369 int *totitem,
3370 const char *display_name)
3371 {
3372 ColorManagedDisplay *display = colormanage_display_get_named(display_name);
3373 ColorManagedView *view;
3374
3375 if (display) {
3376 LinkData *display_view;
3377
3378 for (display_view = display->views.first; display_view; display_view = display_view->next) {
3379 view = display_view->data;
3380
3381 colormanagement_view_item_add(items, totitem, view);
3382 }
3383 }
3384 }
3385
IMB_colormanagement_look_items_add(struct EnumPropertyItem ** items,int * totitem,const char * view_name)3386 void IMB_colormanagement_look_items_add(struct EnumPropertyItem **items,
3387 int *totitem,
3388 const char *view_name)
3389 {
3390 ColorManagedLook *look;
3391
3392 for (look = global_looks.first; look; look = look->next) {
3393 if (!colormanage_compatible_look(look, view_name)) {
3394 continue;
3395 }
3396
3397 EnumPropertyItem item;
3398
3399 item.value = look->index;
3400 item.name = look->ui_name;
3401 item.identifier = look->name;
3402 item.icon = 0;
3403 item.description = "";
3404
3405 RNA_enum_item_add(items, totitem, &item);
3406 }
3407 }
3408
IMB_colormanagement_colorspace_items_add(EnumPropertyItem ** items,int * totitem)3409 void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *totitem)
3410 {
3411 ColorSpace *colorspace;
3412
3413 for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) {
3414 EnumPropertyItem item;
3415
3416 if (!colorspace->is_invertible) {
3417 continue;
3418 }
3419
3420 item.value = colorspace->index;
3421 item.name = colorspace->name;
3422 item.identifier = colorspace->name;
3423 item.icon = 0;
3424 item.description = colorspace->description;
3425
3426 RNA_enum_item_add(items, totitem, &item);
3427 }
3428 }
3429
3430 /** \} */
3431
3432 /* -------------------------------------------------------------------- */
3433 /** \name Partial Display Buffer Update
3434 * \{ */
3435
3436 /*
3437 * Partial display update is supposed to be used by such areas as
3438 * compositor and renderer, This areas are calculating tiles of the
3439 * images and because of performance reasons only this tiles should
3440 * be color managed.
3441 * This gives nice visual feedback without slowing things down.
3442 *
3443 * Updating happens for active display transformation only, all
3444 * the rest buffers would be marked as dirty
3445 */
3446
partial_buffer_update_rect(ImBuf * ibuf,unsigned char * display_buffer,const float * linear_buffer,const unsigned char * byte_buffer,int display_stride,int linear_stride,int linear_offset_x,int linear_offset_y,ColormanageProcessor * cm_processor,const int xmin,const int ymin,const int xmax,const int ymax)3447 static void partial_buffer_update_rect(ImBuf *ibuf,
3448 unsigned char *display_buffer,
3449 const float *linear_buffer,
3450 const unsigned char *byte_buffer,
3451 int display_stride,
3452 int linear_stride,
3453 int linear_offset_x,
3454 int linear_offset_y,
3455 ColormanageProcessor *cm_processor,
3456 const int xmin,
3457 const int ymin,
3458 const int xmax,
3459 const int ymax)
3460 {
3461 int x, y;
3462 int channels = ibuf->channels;
3463 float dither = ibuf->dither;
3464 ColorSpace *rect_colorspace = ibuf->rect_colorspace;
3465 float *display_buffer_float = NULL;
3466 const int width = xmax - xmin;
3467 const int height = ymax - ymin;
3468 bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
3469
3470 if (dither != 0.0f) {
3471 /* cm_processor is NULL in cases byte_buffer's space matches display
3472 * buffer's space
3473 * in this case we could skip extra transform and only apply dither
3474 * use 4 channels for easier byte->float->byte conversion here so
3475 * (this is only needed to apply dither, in other cases we'll convert
3476 * byte buffer to display directly)
3477 */
3478 if (!cm_processor) {
3479 channels = 4;
3480 }
3481
3482 display_buffer_float = MEM_callocN((size_t)channels * width * height * sizeof(float),
3483 "display buffer for dither");
3484 }
3485
3486 if (cm_processor) {
3487 for (y = ymin; y < ymax; y++) {
3488 for (x = xmin; x < xmax; x++) {
3489 size_t display_index = ((size_t)y * display_stride + x) * 4;
3490 size_t linear_index = ((size_t)(y - linear_offset_y) * linear_stride +
3491 (x - linear_offset_x)) *
3492 channels;
3493 float pixel[4];
3494
3495 if (linear_buffer) {
3496 if (channels == 4) {
3497 copy_v4_v4(pixel, (float *)linear_buffer + linear_index);
3498 }
3499 else if (channels == 3) {
3500 copy_v3_v3(pixel, (float *)linear_buffer + linear_index);
3501 pixel[3] = 1.0f;
3502 }
3503 else if (channels == 1) {
3504 pixel[0] = linear_buffer[linear_index];
3505 }
3506 else {
3507 BLI_assert(!"Unsupported number of channels in partial buffer update");
3508 }
3509 }
3510 else if (byte_buffer) {
3511 rgba_uchar_to_float(pixel, byte_buffer + linear_index);
3512 IMB_colormanagement_colorspace_to_scene_linear_v3(pixel, rect_colorspace);
3513 straight_to_premul_v4(pixel);
3514 }
3515
3516 if (!is_data) {
3517 IMB_colormanagement_processor_apply_pixel(cm_processor, pixel, channels);
3518 }
3519
3520 if (display_buffer_float) {
3521 size_t index = ((size_t)(y - ymin) * width + (x - xmin)) * channels;
3522
3523 if (channels == 4) {
3524 copy_v4_v4(display_buffer_float + index, pixel);
3525 }
3526 else if (channels == 3) {
3527 copy_v3_v3(display_buffer_float + index, pixel);
3528 }
3529 else /* if (channels == 1) */ {
3530 display_buffer_float[index] = pixel[0];
3531 }
3532 }
3533 else {
3534 if (channels == 4) {
3535 float pixel_straight[4];
3536 premul_to_straight_v4_v4(pixel_straight, pixel);
3537 rgba_float_to_uchar(display_buffer + display_index, pixel_straight);
3538 }
3539 else if (channels == 3) {
3540 rgb_float_to_uchar(display_buffer + display_index, pixel);
3541 display_buffer[display_index + 3] = 255;
3542 }
3543 else /* if (channels == 1) */ {
3544 display_buffer[display_index] = display_buffer[display_index + 1] =
3545 display_buffer[display_index + 2] = display_buffer[display_index + 3] =
3546 unit_float_to_uchar_clamp(pixel[0]);
3547 }
3548 }
3549 }
3550 }
3551 }
3552 else {
3553 if (display_buffer_float) {
3554 /* huh, for dither we need float buffer first, no cheaper way. currently */
3555 IMB_buffer_float_from_byte(display_buffer_float,
3556 byte_buffer,
3557 IB_PROFILE_SRGB,
3558 IB_PROFILE_SRGB,
3559 true,
3560 width,
3561 height,
3562 width,
3563 display_stride);
3564 }
3565 else {
3566 int i;
3567
3568 for (i = ymin; i < ymax; i++) {
3569 size_t byte_offset = ((size_t)linear_stride * i + xmin) * 4;
3570 size_t display_offset = ((size_t)display_stride * i + xmin) * 4;
3571
3572 memcpy(
3573 display_buffer + display_offset, byte_buffer + byte_offset, sizeof(char[4]) * width);
3574 }
3575 }
3576 }
3577
3578 if (display_buffer_float) {
3579 size_t display_index = ((size_t)ymin * display_stride + xmin) * channels;
3580
3581 IMB_buffer_byte_from_float(display_buffer + display_index,
3582 display_buffer_float,
3583 channels,
3584 dither,
3585 IB_PROFILE_SRGB,
3586 IB_PROFILE_SRGB,
3587 true,
3588 width,
3589 height,
3590 display_stride,
3591 width);
3592
3593 MEM_freeN(display_buffer_float);
3594 }
3595 }
3596
3597 typedef struct PartialThreadData {
3598 ImBuf *ibuf;
3599 unsigned char *display_buffer;
3600 const float *linear_buffer;
3601 const unsigned char *byte_buffer;
3602 int display_stride;
3603 int linear_stride;
3604 int linear_offset_x, linear_offset_y;
3605 ColormanageProcessor *cm_processor;
3606 int xmin, ymin, xmax;
3607 } PartialThreadData;
3608
partial_buffer_update_rect_thread_do(void * data_v,int start_scanline,int num_scanlines)3609 static void partial_buffer_update_rect_thread_do(void *data_v,
3610 int start_scanline,
3611 int num_scanlines)
3612 {
3613 PartialThreadData *data = (PartialThreadData *)data_v;
3614 int ymin = data->ymin + start_scanline;
3615 partial_buffer_update_rect(data->ibuf,
3616 data->display_buffer,
3617 data->linear_buffer,
3618 data->byte_buffer,
3619 data->display_stride,
3620 data->linear_stride,
3621 data->linear_offset_x,
3622 data->linear_offset_y,
3623 data->cm_processor,
3624 data->xmin,
3625 ymin,
3626 data->xmax,
3627 ymin + num_scanlines);
3628 }
3629
imb_partial_display_buffer_update_ex(ImBuf * ibuf,const float * linear_buffer,const unsigned char * byte_buffer,int stride,int offset_x,int offset_y,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,int xmin,int ymin,int xmax,int ymax,bool do_threads)3630 static void imb_partial_display_buffer_update_ex(
3631 ImBuf *ibuf,
3632 const float *linear_buffer,
3633 const unsigned char *byte_buffer,
3634 int stride,
3635 int offset_x,
3636 int offset_y,
3637 const ColorManagedViewSettings *view_settings,
3638 const ColorManagedDisplaySettings *display_settings,
3639 int xmin,
3640 int ymin,
3641 int xmax,
3642 int ymax,
3643 bool do_threads)
3644 {
3645 ColormanageCacheViewSettings cache_view_settings;
3646 ColormanageCacheDisplaySettings cache_display_settings;
3647 void *cache_handle = NULL;
3648 unsigned char *display_buffer = NULL;
3649 int buffer_width = ibuf->x;
3650
3651 if (ibuf->display_buffer_flags) {
3652 int view_flag, display_index;
3653
3654 colormanage_view_settings_to_cache(ibuf, &cache_view_settings, view_settings);
3655 colormanage_display_settings_to_cache(&cache_display_settings, display_settings);
3656
3657 view_flag = 1 << (cache_view_settings.view - 1);
3658 display_index = cache_display_settings.display - 1;
3659
3660 BLI_thread_lock(LOCK_COLORMANAGE);
3661
3662 if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
3663 display_buffer = colormanage_cache_get(
3664 ibuf, &cache_view_settings, &cache_display_settings, &cache_handle);
3665 }
3666
3667 /* In some rare cases buffer's dimension could be changing directly from
3668 * different thread
3669 * this i.e. happens when image editor acquires render result
3670 */
3671 buffer_width = ibuf->x;
3672
3673 /* Mark all other buffers as invalid. */
3674 memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
3675 ibuf->display_buffer_flags[display_index] |= view_flag;
3676
3677 BLI_thread_unlock(LOCK_COLORMANAGE);
3678 }
3679
3680 if (display_buffer) {
3681 ColormanageProcessor *cm_processor = NULL;
3682 bool skip_transform = false;
3683
3684 /* Byte buffer is assumed to be in imbuf's rect space, so if byte buffer
3685 * is known we could skip display->linear->display conversion in case
3686 * display color space matches imbuf's rect space.
3687 *
3688 * But if there's a float buffer it's likely operation was performed on
3689 * it first and byte buffer is likely to be out of date here.
3690 */
3691 if (linear_buffer == NULL && byte_buffer != NULL) {
3692 skip_transform = is_ibuf_rect_in_display_space(ibuf, view_settings, display_settings);
3693 }
3694
3695 if (!skip_transform) {
3696 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
3697 }
3698
3699 if (do_threads) {
3700 PartialThreadData data;
3701 data.ibuf = ibuf;
3702 data.display_buffer = display_buffer;
3703 data.linear_buffer = linear_buffer;
3704 data.byte_buffer = byte_buffer;
3705 data.display_stride = buffer_width;
3706 data.linear_stride = stride;
3707 data.linear_offset_x = offset_x;
3708 data.linear_offset_y = offset_y;
3709 data.cm_processor = cm_processor;
3710 data.xmin = xmin;
3711 data.ymin = ymin;
3712 data.xmax = xmax;
3713 IMB_processor_apply_threaded_scanlines(
3714 ymax - ymin, partial_buffer_update_rect_thread_do, &data);
3715 }
3716 else {
3717 partial_buffer_update_rect(ibuf,
3718 display_buffer,
3719 linear_buffer,
3720 byte_buffer,
3721 buffer_width,
3722 stride,
3723 offset_x,
3724 offset_y,
3725 cm_processor,
3726 xmin,
3727 ymin,
3728 xmax,
3729 ymax);
3730 }
3731
3732 if (cm_processor) {
3733 IMB_colormanagement_processor_free(cm_processor);
3734 }
3735
3736 IMB_display_buffer_release(cache_handle);
3737 }
3738 }
3739
IMB_partial_display_buffer_update(ImBuf * ibuf,const float * linear_buffer,const unsigned char * byte_buffer,int stride,int offset_x,int offset_y,const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,int xmin,int ymin,int xmax,int ymax)3740 void IMB_partial_display_buffer_update(ImBuf *ibuf,
3741 const float *linear_buffer,
3742 const unsigned char *byte_buffer,
3743 int stride,
3744 int offset_x,
3745 int offset_y,
3746 const ColorManagedViewSettings *view_settings,
3747 const ColorManagedDisplaySettings *display_settings,
3748 int xmin,
3749 int ymin,
3750 int xmax,
3751 int ymax)
3752 {
3753 imb_partial_display_buffer_update_ex(ibuf,
3754 linear_buffer,
3755 byte_buffer,
3756 stride,
3757 offset_x,
3758 offset_y,
3759 view_settings,
3760 display_settings,
3761 xmin,
3762 ymin,
3763 xmax,
3764 ymax,
3765 false);
3766 }
3767
IMB_partial_display_buffer_update_threaded(struct ImBuf * ibuf,const float * linear_buffer,const unsigned char * byte_buffer,int stride,int offset_x,int offset_y,const struct ColorManagedViewSettings * view_settings,const struct ColorManagedDisplaySettings * display_settings,int xmin,int ymin,int xmax,int ymax)3768 void IMB_partial_display_buffer_update_threaded(
3769 struct ImBuf *ibuf,
3770 const float *linear_buffer,
3771 const unsigned char *byte_buffer,
3772 int stride,
3773 int offset_x,
3774 int offset_y,
3775 const struct ColorManagedViewSettings *view_settings,
3776 const struct ColorManagedDisplaySettings *display_settings,
3777 int xmin,
3778 int ymin,
3779 int xmax,
3780 int ymax)
3781 {
3782 int width = xmax - xmin;
3783 int height = ymax - ymin;
3784 bool do_threads = (((size_t)width) * height >= 64 * 64);
3785 imb_partial_display_buffer_update_ex(ibuf,
3786 linear_buffer,
3787 byte_buffer,
3788 stride,
3789 offset_x,
3790 offset_y,
3791 view_settings,
3792 display_settings,
3793 xmin,
3794 ymin,
3795 xmax,
3796 ymax,
3797 do_threads);
3798 }
3799
IMB_partial_display_buffer_update_delayed(ImBuf * ibuf,int xmin,int ymin,int xmax,int ymax)3800 void IMB_partial_display_buffer_update_delayed(ImBuf *ibuf, int xmin, int ymin, int xmax, int ymax)
3801 {
3802 if (ibuf->invalid_rect.xmin == ibuf->invalid_rect.xmax) {
3803 BLI_rcti_init(&ibuf->invalid_rect, xmin, xmax, ymin, ymax);
3804 }
3805 else {
3806 rcti rect;
3807 BLI_rcti_init(&rect, xmin, xmax, ymin, ymax);
3808 BLI_rcti_union(&ibuf->invalid_rect, &rect);
3809 }
3810 }
3811
3812 /** \} */
3813
3814 /* -------------------------------------------------------------------- */
3815 /** \name Pixel Processor Functions
3816 * \{ */
3817
IMB_colormanagement_display_processor_new(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings)3818 ColormanageProcessor *IMB_colormanagement_display_processor_new(
3819 const ColorManagedViewSettings *view_settings,
3820 const ColorManagedDisplaySettings *display_settings)
3821 {
3822 ColormanageProcessor *cm_processor;
3823 ColorManagedViewSettings default_view_settings;
3824 const ColorManagedViewSettings *applied_view_settings;
3825 ColorSpace *display_space;
3826
3827 cm_processor = MEM_callocN(sizeof(ColormanageProcessor), "colormanagement processor");
3828
3829 if (view_settings) {
3830 applied_view_settings = view_settings;
3831 }
3832 else {
3833 IMB_colormanagement_init_default_view_settings(&default_view_settings, display_settings);
3834 applied_view_settings = &default_view_settings;
3835 }
3836
3837 display_space = display_transform_get_colorspace(applied_view_settings, display_settings);
3838 if (display_space) {
3839 cm_processor->is_data_result = display_space->is_data;
3840 }
3841
3842 cm_processor->processor = create_display_buffer_processor(applied_view_settings->look,
3843 applied_view_settings->view_transform,
3844 display_settings->display_device,
3845 applied_view_settings->exposure,
3846 applied_view_settings->gamma,
3847 global_role_scene_linear,
3848 false);
3849
3850 if (applied_view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) {
3851 cm_processor->curve_mapping = BKE_curvemapping_copy(applied_view_settings->curve_mapping);
3852 BKE_curvemapping_premultiply(cm_processor->curve_mapping, false);
3853 }
3854
3855 return cm_processor;
3856 }
3857
IMB_colormanagement_colorspace_processor_new(const char * from_colorspace,const char * to_colorspace)3858 ColormanageProcessor *IMB_colormanagement_colorspace_processor_new(const char *from_colorspace,
3859 const char *to_colorspace)
3860 {
3861 ColormanageProcessor *cm_processor;
3862 ColorSpace *color_space;
3863
3864 cm_processor = MEM_callocN(sizeof(ColormanageProcessor), "colormanagement processor");
3865
3866 color_space = colormanage_colorspace_get_named(to_colorspace);
3867 cm_processor->is_data_result = color_space->is_data;
3868
3869 cm_processor->processor = create_colorspace_transform_processor(from_colorspace, to_colorspace);
3870
3871 return cm_processor;
3872 }
3873
IMB_colormanagement_processor_apply_v4(ColormanageProcessor * cm_processor,float pixel[4])3874 void IMB_colormanagement_processor_apply_v4(ColormanageProcessor *cm_processor, float pixel[4])
3875 {
3876 if (cm_processor->curve_mapping) {
3877 BKE_curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
3878 }
3879
3880 if (cm_processor->processor) {
3881 OCIO_processorApplyRGBA(cm_processor->processor, pixel);
3882 }
3883 }
3884
IMB_colormanagement_processor_apply_v4_predivide(ColormanageProcessor * cm_processor,float pixel[4])3885 void IMB_colormanagement_processor_apply_v4_predivide(ColormanageProcessor *cm_processor,
3886 float pixel[4])
3887 {
3888 if (cm_processor->curve_mapping) {
3889 BKE_curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
3890 }
3891
3892 if (cm_processor->processor) {
3893 OCIO_processorApplyRGBA_predivide(cm_processor->processor, pixel);
3894 }
3895 }
3896
IMB_colormanagement_processor_apply_v3(ColormanageProcessor * cm_processor,float pixel[3])3897 void IMB_colormanagement_processor_apply_v3(ColormanageProcessor *cm_processor, float pixel[3])
3898 {
3899 if (cm_processor->curve_mapping) {
3900 BKE_curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel);
3901 }
3902
3903 if (cm_processor->processor) {
3904 OCIO_processorApplyRGB(cm_processor->processor, pixel);
3905 }
3906 }
3907
IMB_colormanagement_processor_apply_pixel(struct ColormanageProcessor * cm_processor,float * pixel,int channels)3908 void IMB_colormanagement_processor_apply_pixel(struct ColormanageProcessor *cm_processor,
3909 float *pixel,
3910 int channels)
3911 {
3912 if (channels == 4) {
3913 IMB_colormanagement_processor_apply_v4_predivide(cm_processor, pixel);
3914 }
3915 else if (channels == 3) {
3916 IMB_colormanagement_processor_apply_v3(cm_processor, pixel);
3917 }
3918 else if (channels == 1) {
3919 if (cm_processor->curve_mapping) {
3920 curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, 1);
3921 }
3922 }
3923 else {
3924 BLI_assert(
3925 !"Incorrect number of channels passed to IMB_colormanagement_processor_apply_pixel");
3926 }
3927 }
3928
IMB_colormanagement_processor_apply(ColormanageProcessor * cm_processor,float * buffer,int width,int height,int channels,bool predivide)3929 void IMB_colormanagement_processor_apply(ColormanageProcessor *cm_processor,
3930 float *buffer,
3931 int width,
3932 int height,
3933 int channels,
3934 bool predivide)
3935 {
3936 /* apply curve mapping */
3937 if (cm_processor->curve_mapping) {
3938 int x, y;
3939
3940 for (y = 0; y < height; y++) {
3941 for (x = 0; x < width; x++) {
3942 float *pixel = buffer + channels * (((size_t)y) * width + x);
3943
3944 curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, channels);
3945 }
3946 }
3947 }
3948
3949 if (cm_processor->processor && channels >= 3) {
3950 OCIO_PackedImageDesc *img;
3951
3952 /* apply OCIO processor */
3953 img = OCIO_createOCIO_PackedImageDesc(buffer,
3954 width,
3955 height,
3956 channels,
3957 sizeof(float),
3958 (size_t)channels * sizeof(float),
3959 (size_t)channels * sizeof(float) * width);
3960
3961 if (predivide) {
3962 OCIO_processorApply_predivide(cm_processor->processor, img);
3963 }
3964 else {
3965 OCIO_processorApply(cm_processor->processor, img);
3966 }
3967
3968 OCIO_PackedImageDescRelease(img);
3969 }
3970 }
3971
IMB_colormanagement_processor_apply_byte(ColormanageProcessor * cm_processor,unsigned char * buffer,int width,int height,int channels)3972 void IMB_colormanagement_processor_apply_byte(
3973 ColormanageProcessor *cm_processor, unsigned char *buffer, int width, int height, int channels)
3974 {
3975 /* TODO(sergey): Would be nice to support arbitrary channels configurations,
3976 * but for now it's not so important.
3977 */
3978 BLI_assert(channels == 4);
3979 float pixel[4];
3980 for (int y = 0; y < height; y++) {
3981 for (int x = 0; x < width; x++) {
3982 size_t offset = channels * (((size_t)y) * width + x);
3983 rgba_uchar_to_float(pixel, buffer + offset);
3984 IMB_colormanagement_processor_apply_v4(cm_processor, pixel);
3985 rgba_float_to_uchar(buffer + offset, pixel);
3986 }
3987 }
3988 }
3989
IMB_colormanagement_processor_free(ColormanageProcessor * cm_processor)3990 void IMB_colormanagement_processor_free(ColormanageProcessor *cm_processor)
3991 {
3992 if (cm_processor->curve_mapping) {
3993 BKE_curvemapping_free(cm_processor->curve_mapping);
3994 }
3995 if (cm_processor->processor) {
3996 OCIO_processorRelease(cm_processor->processor);
3997 }
3998
3999 MEM_freeN(cm_processor);
4000 }
4001
4002 /* **** OpenGL drawing routines using GLSL for color space transform ***** */
4003
check_glsl_display_processor_changed(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,const char * from_colorspace)4004 static bool check_glsl_display_processor_changed(
4005 const ColorManagedViewSettings *view_settings,
4006 const ColorManagedDisplaySettings *display_settings,
4007 const char *from_colorspace)
4008 {
4009 return !(global_glsl_state.exposure == view_settings->exposure &&
4010 global_glsl_state.gamma == view_settings->gamma &&
4011 STREQ(global_glsl_state.look, view_settings->look) &&
4012 STREQ(global_glsl_state.view, view_settings->view_transform) &&
4013 STREQ(global_glsl_state.display, display_settings->display_device) &&
4014 STREQ(global_glsl_state.input, from_colorspace));
4015 }
4016
curve_mapping_to_ocio_settings(CurveMapping * curve_mapping,OCIO_CurveMappingSettings * curve_mapping_settings)4017 static void curve_mapping_to_ocio_settings(CurveMapping *curve_mapping,
4018 OCIO_CurveMappingSettings *curve_mapping_settings)
4019 {
4020 int i;
4021
4022 BKE_curvemapping_init(curve_mapping);
4023 BKE_curvemapping_premultiply(curve_mapping, false);
4024 BKE_curvemapping_table_RGBA(
4025 curve_mapping, &curve_mapping_settings->lut, &curve_mapping_settings->lut_size);
4026
4027 curve_mapping_settings->use_extend_extrapolate = (curve_mapping->flag &
4028 CUMA_EXTEND_EXTRAPOLATE) != 0;
4029
4030 for (i = 0; i < 4; i++) {
4031 CurveMap *cuma = curve_mapping->cm + i;
4032 curve_mapping_settings->range[i] = cuma->range;
4033 curve_mapping_settings->mintable[i] = cuma->mintable;
4034 curve_mapping_settings->ext_in_x[i] = cuma->ext_in[0];
4035 curve_mapping_settings->ext_in_y[i] = cuma->ext_in[1];
4036 curve_mapping_settings->ext_out_x[i] = cuma->ext_out[0];
4037 curve_mapping_settings->ext_out_y[i] = cuma->ext_out[1];
4038 curve_mapping_settings->first_x[i] = cuma->table[0].x;
4039 curve_mapping_settings->first_y[i] = cuma->table[0].y;
4040 curve_mapping_settings->last_x[i] = cuma->table[CM_TABLE].x;
4041 curve_mapping_settings->last_y[i] = cuma->table[CM_TABLE].y;
4042 }
4043
4044 copy_v3_v3(curve_mapping_settings->black, curve_mapping->black);
4045 copy_v3_v3(curve_mapping_settings->bwmul, curve_mapping->bwmul);
4046
4047 curve_mapping_settings->cache_id = (size_t)curve_mapping + curve_mapping->changed_timestamp;
4048 }
4049
update_glsl_display_processor(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,const char * from_colorspace)4050 static void update_glsl_display_processor(const ColorManagedViewSettings *view_settings,
4051 const ColorManagedDisplaySettings *display_settings,
4052 const char *from_colorspace)
4053 {
4054 bool use_curve_mapping = (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) != 0;
4055 bool need_update = false;
4056
4057 need_update = global_glsl_state.processor_scene_to_ui == NULL ||
4058 check_glsl_display_processor_changed(
4059 view_settings, display_settings, from_colorspace) ||
4060 use_curve_mapping != global_glsl_state.use_curve_mapping;
4061
4062 if (use_curve_mapping && need_update == false) {
4063 need_update |= view_settings->curve_mapping->changed_timestamp !=
4064 global_glsl_state.curve_mapping_timestamp ||
4065 view_settings->curve_mapping != global_glsl_state.orig_curve_mapping;
4066 }
4067
4068 /* Update state if there's no processor yet or
4069 * processor settings has been changed.
4070 */
4071 if (need_update) {
4072 OCIO_CurveMappingSettings *curve_mapping_settings = &global_glsl_state.curve_mapping_settings;
4073 CurveMapping *new_curve_mapping = NULL;
4074
4075 /* Store settings of processor for further comparison. */
4076 BLI_strncpy(global_glsl_state.look, view_settings->look, MAX_COLORSPACE_NAME);
4077 BLI_strncpy(global_glsl_state.view, view_settings->view_transform, MAX_COLORSPACE_NAME);
4078 BLI_strncpy(global_glsl_state.display, display_settings->display_device, MAX_COLORSPACE_NAME);
4079 BLI_strncpy(global_glsl_state.input, from_colorspace, MAX_COLORSPACE_NAME);
4080 global_glsl_state.exposure = view_settings->exposure;
4081 global_glsl_state.gamma = view_settings->gamma;
4082
4083 /* We're using curve mapping's address as a cache ID,
4084 * so we need to make sure re-allocation gives new address here.
4085 * We do this by allocating new curve mapping before freeing old one. */
4086 if (use_curve_mapping) {
4087 new_curve_mapping = BKE_curvemapping_copy(view_settings->curve_mapping);
4088 }
4089
4090 if (global_glsl_state.curve_mapping) {
4091 BKE_curvemapping_free(global_glsl_state.curve_mapping);
4092 MEM_freeN(curve_mapping_settings->lut);
4093 global_glsl_state.curve_mapping = NULL;
4094 curve_mapping_settings->lut = NULL;
4095 }
4096
4097 /* Fill in OCIO's curve mapping settings. */
4098 if (use_curve_mapping) {
4099 curve_mapping_to_ocio_settings(new_curve_mapping, &global_glsl_state.curve_mapping_settings);
4100
4101 global_glsl_state.curve_mapping = new_curve_mapping;
4102 global_glsl_state.curve_mapping_timestamp = view_settings->curve_mapping->changed_timestamp;
4103 global_glsl_state.orig_curve_mapping = view_settings->curve_mapping;
4104 global_glsl_state.use_curve_mapping = true;
4105 }
4106 else {
4107 global_glsl_state.orig_curve_mapping = NULL;
4108 global_glsl_state.use_curve_mapping = false;
4109 }
4110
4111 /* Free old processor, if any. */
4112 if (global_glsl_state.processor_scene_to_ui) {
4113 OCIO_processorRelease(global_glsl_state.processor_scene_to_ui);
4114 }
4115
4116 if (global_glsl_state.processor_ui_to_display) {
4117 OCIO_processorRelease(global_glsl_state.processor_ui_to_display);
4118 }
4119
4120 /* We're using display OCIO processor, no RGB curves yet. */
4121 global_glsl_state.processor_scene_to_ui = create_display_buffer_processor(
4122 global_glsl_state.look,
4123 global_glsl_state.view,
4124 global_glsl_state.display,
4125 global_glsl_state.exposure,
4126 global_glsl_state.gamma,
4127 global_glsl_state.input,
4128 true);
4129
4130 global_glsl_state.processor_ui_to_display = create_display_encoded_buffer_processor(
4131 global_glsl_state.display);
4132 }
4133 }
4134
IMB_colormanagement_support_glsl_draw(const ColorManagedViewSettings * UNUSED (view_settings))4135 bool IMB_colormanagement_support_glsl_draw(const ColorManagedViewSettings *UNUSED(view_settings))
4136 {
4137 return OCIO_supportGLSLDraw();
4138 }
4139
4140 /**
4141 * Configures GLSL shader for conversion from specified to
4142 * display color space
4143 *
4144 * Will create appropriate OCIO processor and setup GLSL shader,
4145 * so further 2D texture usage will use this conversion.
4146 *
4147 * When there's no need to apply transform on 2D textures, use
4148 * IMB_colormanagement_finish_glsl_draw().
4149 *
4150 * This is low-level function, use ED_draw_imbuf_ctx if you
4151 * only need to display given image buffer
4152 */
IMB_colormanagement_setup_glsl_draw_from_space(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,struct ColorSpace * from_colorspace,float dither,bool predivide,bool do_overlay_merge)4153 bool IMB_colormanagement_setup_glsl_draw_from_space(
4154 const ColorManagedViewSettings *view_settings,
4155 const ColorManagedDisplaySettings *display_settings,
4156 struct ColorSpace *from_colorspace,
4157 float dither,
4158 bool predivide,
4159 bool do_overlay_merge)
4160 {
4161 ColorManagedViewSettings default_view_settings;
4162 const ColorManagedViewSettings *applied_view_settings;
4163
4164 if (view_settings) {
4165 applied_view_settings = view_settings;
4166 }
4167 else {
4168 /* If no view settings were specified, use default ones, which will
4169 * attempt not to do any extra color correction. */
4170 IMB_colormanagement_init_default_view_settings(&default_view_settings, display_settings);
4171 applied_view_settings = &default_view_settings;
4172 }
4173
4174 /* Make sure OCIO processor is up-to-date. */
4175 update_glsl_display_processor(applied_view_settings,
4176 display_settings,
4177 from_colorspace ? from_colorspace->name :
4178 global_role_scene_linear);
4179
4180 if (global_glsl_state.processor_scene_to_ui == NULL) {
4181 /* Happens when requesting non-existing color space or LUT in the
4182 * configuration file does not exist.
4183 */
4184 return false;
4185 }
4186
4187 return OCIO_setupGLSLDraw(
4188 &global_glsl_state.ocio_glsl_state,
4189 global_glsl_state.processor_scene_to_ui,
4190 global_glsl_state.processor_ui_to_display,
4191 global_glsl_state.use_curve_mapping ? &global_glsl_state.curve_mapping_settings : NULL,
4192 dither,
4193 predivide,
4194 do_overlay_merge);
4195 }
4196
4197 /* Configures GLSL shader for conversion from scene linear to display space */
IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings * view_settings,const ColorManagedDisplaySettings * display_settings,float dither,bool predivide)4198 bool IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings *view_settings,
4199 const ColorManagedDisplaySettings *display_settings,
4200 float dither,
4201 bool predivide)
4202 {
4203 return IMB_colormanagement_setup_glsl_draw_from_space(
4204 view_settings, display_settings, NULL, dither, predivide, false);
4205 }
4206
4207 /**
4208 * Same as setup_glsl_draw_from_space,
4209 * but color management settings are guessing from a given context.
4210 */
IMB_colormanagement_setup_glsl_draw_from_space_ctx(const bContext * C,struct ColorSpace * from_colorspace,float dither,bool predivide)4211 bool IMB_colormanagement_setup_glsl_draw_from_space_ctx(const bContext *C,
4212 struct ColorSpace *from_colorspace,
4213 float dither,
4214 bool predivide)
4215 {
4216 ColorManagedViewSettings *view_settings;
4217 ColorManagedDisplaySettings *display_settings;
4218
4219 IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
4220
4221 return IMB_colormanagement_setup_glsl_draw_from_space(
4222 view_settings, display_settings, from_colorspace, dither, predivide, false);
4223 }
4224
4225 /* Same as setup_glsl_draw, but color management settings are guessing from a given context */
IMB_colormanagement_setup_glsl_draw_ctx(const bContext * C,float dither,bool predivide)4226 bool IMB_colormanagement_setup_glsl_draw_ctx(const bContext *C, float dither, bool predivide)
4227 {
4228 return IMB_colormanagement_setup_glsl_draw_from_space_ctx(C, NULL, dither, predivide);
4229 }
4230
4231 /* Finish GLSL-based display space conversion */
IMB_colormanagement_finish_glsl_draw(void)4232 void IMB_colormanagement_finish_glsl_draw(void)
4233 {
4234 if (global_glsl_state.ocio_glsl_state != NULL) {
4235 OCIO_finishGLSLDraw(global_glsl_state.ocio_glsl_state);
4236 }
4237 }
4238
4239 /** \} */
4240