diff options
178 files changed, 5024 insertions, 1186 deletions
diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 74bb720b618..b777fcddcf2 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -316,10 +316,7 @@ class RENDER_PT_shading(RenderButtonsPanel, Panel): col = split.column() col.prop(rd, "use_raytrace", text="Ray Tracing") - col.prop(rd, "use_color_management") - sub = col.row() - sub.active = rd.use_color_management == True - sub.prop(rd, "use_color_unpremultiply") + col.prop(rd, "use_color_unpremultiply") col.prop(rd, "alpha_mode", text="Alpha") @@ -467,7 +464,7 @@ class RENDER_PT_output(RenderButtonsPanel, Panel): flow.prop(rd, "use_placeholder") flow.prop(rd, "use_file_extension") - layout.template_image_settings(image_settings) + layout.template_image_settings(image_settings, color_management=False) if file_format == 'QUICKTIME_CARBON': layout.operator("scene.render_data_set_quicktime_codec") diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py index 8449d26f6e6..2ccdb9cee26 100644 --- a/release/scripts/startup/bl_ui/properties_scene.py +++ b/release/scripts/startup/bl_ui/properties_scene.py @@ -231,6 +231,32 @@ class SCENE_PT_simplify(SceneButtonsPanel, Panel): col.prop(rd, "simplify_ao_sss", text="AO and SSS") +class SCENE_PT_color_management(Panel): + bl_label = "Color Management" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "scene" + + def draw(self, context): + layout = self.layout + + scene = context.scene + + col = layout.column() + col.label(text="Display:") + col.prop(scene.display_settings, "display_device") + + col = layout.column() + col.separator() + col.label(text="Render:") + col.template_colormanaged_view_settings(scene, "view_settings") + + col = layout.column() + col.separator() + col.label(text="Sequencer:") + col.prop(scene.sequencer_colorspace_settings, "name") + + class SCENE_PT_custom_props(SceneButtonsPanel, PropertyPanel, Panel): COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} _context_path = "scene" diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index dd13b8a9b51..39c892930f4 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -884,6 +884,8 @@ class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel): strip = act_strip(context) sequencer = context.scene.sequence_editor + layout.prop(strip, "use_linear_modifiers") + layout.operator_menu_enum("sequencer.strip_modifier_add", "type") for mod in strip.modifiers: diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 0732e02b5fc..ce10951d6ff 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -33,6 +33,7 @@ #define __BLF_API_H__ struct rctf; +struct ColorManagedDisplay; int BLF_init(int points, int dpi); void BLF_exit(void); @@ -151,7 +152,7 @@ void BLF_shadow_offset(int fontid, int x, int y); * * BLF_buffer(NULL, NULL, 0, 0, 0, FALSE); */ -void BLF_buffer(int fontid, float *fbuf, unsigned char *cbuf, int w, int h, int nch, int do_color_management); +void BLF_buffer(int fontid, float *fbuf, unsigned char *cbuf, int w, int h, int nch, struct ColorManagedDisplay *display); /* Set the color to be used for text. */ void BLF_buffer_col(int fontid, float r, float g, float b, float a); diff --git a/source/blender/blenfont/CMakeLists.txt b/source/blender/blenfont/CMakeLists.txt index ebf9ff4db39..da60710a137 100644 --- a/source/blender/blenfont/CMakeLists.txt +++ b/source/blender/blenfont/CMakeLists.txt @@ -27,6 +27,7 @@ set(INC ../blenlib ../editors/include ../makesdna + ../imbuf ../../../intern/guardedalloc ) diff --git a/source/blender/blenfont/SConscript b/source/blender/blenfont/SConscript index c46960d2311..d3c7b1c2fcc 100644 --- a/source/blender/blenfont/SConscript +++ b/source/blender/blenfont/SConscript @@ -4,7 +4,7 @@ Import ('env') sources = env.Glob('intern/*.c') -incs = '. intern #/intern/guardedalloc ../blenkernel ../blenlib ../makesdna ../editors/include' +incs = '. intern #/intern/guardedalloc ../blenkernel ../blenlib ../makesdna ../imbuf ../editors/include' incs += ' #/extern/glew/include' incs += ' ' + env['BF_FREETYPE_INC'] incs += ' ' + env['BF_GETTEXT_INC'] diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index d4739b37f93..92fcb576e7d 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -50,7 +50,6 @@ #include "blf_internal_types.h" #include "blf_internal.h" - /* Max number of font in memory. * Take care that now every font have a glyph cache per size/dpi, * so we don't need load the same font with different size, just @@ -746,7 +745,7 @@ void BLF_shadow_offset(int fontid, int x, int y) } } -void BLF_buffer(int fontid, float *fbuf, unsigned char *cbuf, int w, int h, int nch, int do_color_management) +void BLF_buffer(int fontid, float *fbuf, unsigned char *cbuf, int w, int h, int nch, struct ColorManagedDisplay *display) { FontBLF *font = BLF_get(fontid); @@ -756,7 +755,7 @@ void BLF_buffer(int fontid, float *fbuf, unsigned char *cbuf, int w, int h, int font->buf_info.w = w; font->buf_info.h = h; font->buf_info.ch = nch; - font->buf_info.do_color_management = do_color_management; + font->buf_info.display = display; } } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index cb05c89921e..02b66993c0a 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -53,6 +53,8 @@ #include "BIF_gl.h" #include "BLF_api.h" +#include "IMB_colormanagement.h" + #include "blf_internal_types.h" #include "blf_internal.h" @@ -245,11 +247,12 @@ void blf_font_buffer(FontBLF *font, const char *str) blf_font_ensure_ascii_table(font); /* another buffer specific call for color conversion */ - if (buf_info->do_color_management) { - srgb_to_linearrgb_v4(b_col_float, buf_info->col); + if (buf_info->display) { + copy_v4_v4(b_col_float, buf_info->col); + IMB_colormanagement_display_to_scene_linear_v3(b_col_float, buf_info->display); } else { - copy_v4_v4(b_col_float, buf_info->col); + srgb_to_linearrgb_v4(b_col_float, buf_info->col); } while (str[i]) { diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 4c617da7583..1acc3dad4cf 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -145,8 +145,8 @@ typedef struct FontBufInfoBLF { /* number of channels. */ int ch; - /* is the float buffer linear */ - int do_color_management; + /* display device used for color management */ + struct ColorManagedDisplay *display; /* and the color, the alphas is get from the glyph! * color is srgb space */ diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index 1e67027ae8c..fed82d7e93d 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -42,7 +42,7 @@ extern "C" { * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ #define BLENDER_VERSION 263 -#define BLENDER_SUBVERSION 18 +#define BLENDER_SUBVERSION 19 /* 262 was the last editmesh release but its has compatibility code for bmesh data, * so set the minversion to 2.61 */ diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h index 9b7fc1fddad..cd67059ea8c 100644 --- a/source/blender/blenkernel/BKE_colortools.h +++ b/source/blender/blenkernel/BKE_colortools.h @@ -31,6 +31,9 @@ * \ingroup bke */ +struct ColorManagedColorspaceSettings; +struct ColorManagedDisplaySettings; +struct ColorManagedViewSettings; struct CurveMapping; struct CurveMap; struct CurveMapPoint; @@ -89,9 +92,25 @@ void curvemapping_do_ibuf(struct CurveMapping *cumap, struct ImBu void curvemapping_premultiply(struct CurveMapping *cumap, int restore); -void BKE_histogram_update_sample_line(struct Histogram *hist, struct ImBuf *ibuf, const short use_color_management); -void scopes_update(struct Scopes *scopes, struct ImBuf *ibuf, int use_color_management); +void BKE_histogram_update_sample_line(struct Histogram *hist, struct ImBuf *ibuf, + const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings); +void scopes_update(struct Scopes *scopes, struct ImBuf *ibuf, const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings); void scopes_free(struct Scopes *scopes); void scopes_new(struct Scopes *scopes); +void BKE_color_managed_display_settings_init(struct ColorManagedDisplaySettings *settings); +void BKE_color_managed_display_settings_copy(struct ColorManagedDisplaySettings *new_settings, + const struct ColorManagedDisplaySettings *settings); + +void BKE_color_managed_view_settings_init(struct ColorManagedViewSettings *settings); +void BKE_color_managed_view_settings_copy(struct ColorManagedViewSettings *new_settings, + const struct ColorManagedViewSettings *settings); +void BKE_color_managed_view_settings_free(struct ColorManagedViewSettings *settings); + +void BKE_color_managed_colorspace_settings_init(struct ColorManagedColorspaceSettings *colorspace_settings); +void BKE_color_managed_colorspace_settings_copy(struct ColorManagedColorspaceSettings *colorspace_settings, + const struct ColorManagedColorspaceSettings *settings); + #endif diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 67461281674..ac324b2af00 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -46,6 +46,8 @@ struct Object; struct ImageFormatData; struct Main; +#define IMA_MAX_SPACE 64 + /* call from library */ void BKE_image_free(struct Image *me); @@ -64,6 +66,7 @@ int BKE_imtype_is_movie(const char imtype); int BKE_imtype_supports_zbuf(const char imtype); int BKE_imtype_supports_compress(const char imtype); int BKE_imtype_supports_quality(const char imtype); +int BKE_imtype_supports_float(const char imtype); char BKE_imtype_valid_channels(const char imtype); char BKE_imtype_valid_depths(const char imtype); @@ -72,7 +75,7 @@ char BKE_imtype_from_arg(const char *arg); void BKE_imformat_defaults(struct ImageFormatData *im_format); void BKE_imbuf_to_image_format(struct ImageFormatData *im_format, const struct ImBuf *imbuf); -struct anim *openanim(const char *name, int flags, int streamindex); +struct anim *openanim(const char *name, int flags, int streamindex, char colorspace[IMA_MAX_SPACE]); void BKE_image_de_interlace(struct Image *ima, int odd); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 438d5110666..6ad2ad924e2 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -64,6 +64,8 @@ struct Tex; struct SpaceNode; struct ARegion; struct Object; +struct ColorManagedViewSettings; +struct ColorManagedDisplaySettings; /* ************** NODE TYPE DEFINITIONS ***** */ @@ -725,7 +727,8 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria struct CompBuf; struct bNodeTreeExec *ntreeCompositBeginExecTree(struct bNodeTree *ntree, int use_tree_data); void ntreeCompositEndExecTree(struct bNodeTreeExec *exec, int use_tree_data); -void ntreeCompositExecTree(struct bNodeTree *ntree, struct RenderData *rd, int rendering, int do_previews); +void ntreeCompositExecTree(struct bNodeTree *ntree, struct RenderData *rd, int rendering, int do_previews, + const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings); void ntreeCompositTagRender(struct Scene *sce); int ntreeCompositTagAnimated(struct bNodeTree *ntree); void ntreeCompositTagGenerators(struct bNodeTree *ntree); diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index bce4243190a..023b7e85c40 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -108,6 +108,8 @@ float get_render_aosss_error(struct RenderData *r, float error); int BKE_scene_use_new_shading_nodes(struct Scene *scene); +void BKE_scene_disable_color_management(struct Scene *scene); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 51a64c97ca6..0c571f62f0e 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -177,6 +177,16 @@ struct ImBuf *BKE_sequencer_give_ibuf_seqbase(SeqRenderData context, float cfra, void BKE_sequencer_give_ibuf_prefetch_request(SeqRenderData context, float cfra, int chan_shown); /* ********************************************************************** + * sequencer.c + * + * sequencer color space functions + * ********************************************************************** */ + +void BKE_sequencer_imbuf_to_sequencer_space(struct Scene *scene, struct ImBuf *ibuf, int make_float); +void BKE_sequencer_imbuf_from_sequencer_space(struct Scene *scene, struct ImBuf *ibuf); +void BKE_sequencer_pixel_from_sequencer_space_v4(struct Scene *scene, float pixel[4]); + +/* ********************************************************************** * sequencer scene functions * ********************************************************************** */ struct Editing *BKE_sequencer_editing_get(struct Scene *scene, int alloc); @@ -350,7 +360,7 @@ struct Sequence *BKE_sequencer_add_sound_strip(struct bContext *C, ListBase *seq struct Sequence *BKE_sequencer_add_movie_strip(struct bContext *C, ListBase *seqbasep, struct SeqLoadInfo *seq_load); /* view3d draw callback, run when not in background view */ -typedef struct ImBuf *(*SequencerDrawView)(struct Scene *, struct Object *, int, int, unsigned int, int, int, char[256]); +typedef struct ImBuf *(*SequencerDrawView)(struct Scene *, struct Object *, int, int, unsigned int, int, int, int, char[256]); extern SequencerDrawView sequencer_view3d_cb; /* copy/paste */ diff --git a/source/blender/blenkernel/SConscript b/source/blender/blenkernel/SConscript index 68ca3170bd8..d1a35b122e8 100644 --- a/source/blender/blenkernel/SConscript +++ b/source/blender/blenkernel/SConscript @@ -3,6 +3,10 @@ Import ('env') import os sources = env.Glob('intern/*.c') +sources.remove('intern' + os.sep + 'mask_rasterize.c') +sources.remove('intern' + os.sep + 'mask.c') + +sources_mask = env.Glob('intern/mask*.c') incs = '. #/intern/guardedalloc #/intern/memutil' incs += ' ../blenlib ../blenfont ../makesdna ../windowmanager' @@ -118,7 +122,10 @@ if env['WITH_BF_INTERNATIONAL']: if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'): incs += ' ' + env['BF_PTHREADS_INC'] + if env['OURPLATFORM'] in ('win32-vc', 'win64-vc'): env.BlenderLib ( libname = 'bf_blenkernel', sources = sources, includes = Split(incs), defines = defs, libtype=['core','player'], priority = [166,25]) #, cc_compileflags = env['CCFLAGS'].append('/WX') ) else: env.BlenderLib ( libname = 'bf_blenkernel', sources = sources, includes = Split(incs), defines = defs, libtype=['core','player', 'player2'], priority = [166,25,0] ) + +env.BlenderLib ( libname = 'bf_blenkernel_mask', sources = sources_mask, includes = Split(incs), defines = defs, libtype=['core','player', 'player2'], priority = [200,25,0] ) diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index 45e2167af2f..99b788e80ce 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -89,6 +89,8 @@ #include "WM_api.h" // XXXXX BAD, very BAD dependency (bad level call) - remove asap, elubie +#include "IMB_colormanagement.h" + #ifdef WITH_PYTHON # include "BPY_extern.h" #endif @@ -320,7 +322,11 @@ static void setup_app_data(bContext *C, BlendFileData *bfd, const char *filepath /* baseflags, groups, make depsgraph, etc */ BKE_scene_set_background(G.main, CTX_data_scene(C)); - + + if (mode != 'u') { + IMB_colormanagement_check_file_config(G.main); + } + MEM_freeN(bfd); (void)curscene; /* quiet warning */ diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index d65c81b4f9b..19c8c2cd632 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -49,6 +49,7 @@ #include "BKE_fcurve.h" +#include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -174,6 +175,7 @@ void curvemapping_set_black_white(CurveMapping *cumap, const float black[3], con } curvemapping_set_black_white_ex(cumap->black, cumap->white, cumap->bwmul); + cumap->changed_timestamp++; } /* ***************** operations on single curve ************* */ @@ -996,7 +998,8 @@ static void save_sample_line(Scopes *scopes, const int idx, const float fx, cons } } -void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const short use_color_management) +void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings) { int i, x, y; float *fp; @@ -1008,6 +1011,8 @@ void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const short int y1 = 0.5f + hist->co[0][1] * ibuf->y; int y2 = 0.5f + hist->co[1][1] * ibuf->y; + struct ColormanageProcessor *cm_processor = NULL; + hist->channels = 3; hist->x_resolution = 256; hist->xmax = 1.0f; @@ -1015,6 +1020,9 @@ void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const short if (ibuf->rect == NULL && ibuf->rect_float == NULL) return; + if (ibuf->rect_float) + cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); + /* persistent draw */ hist->flag |= HISTO_FLAG_SAMPLELINE; /* keep drawing the flag after */ @@ -1029,10 +1037,8 @@ void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const short if (ibuf->rect_float) { fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); - if (use_color_management) - linearrgb_to_srgb_v3_v3(rgb, fp); - else - copy_v3_v3(rgb, fp); + copy_v3_v3(rgb, fp); + IMB_colormanagement_processor_apply_v3(cm_processor, rgb); hist->data_luma[i] = rgb_to_luma(rgb); hist->data_r[i] = rgb[0]; @@ -1050,9 +1056,13 @@ void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const short } } } + + if (cm_processor) + IMB_colormanagement_processor_free(cm_processor); } -void scopes_update(Scopes *scopes, ImBuf *ibuf, int use_color_management) +void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings) { int x, y, c; unsigned int n, nl; @@ -1065,6 +1075,8 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, int use_color_management) int ycc_mode = -1; const short is_float = (ibuf->rect_float != NULL); + struct ColormanageProcessor *cm_processor = NULL; + if (ibuf->rect == NULL && ibuf->rect_float == NULL) return; if (scopes->ok == 1) return; @@ -1134,6 +1146,9 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, int use_color_management) else rc = (unsigned char *)ibuf->rect; + if (ibuf->rect_float) + cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); + for (y = 0; y < ibuf->y; y++) { if (savedlines < scopes->sample_lines && y >= ((savedlines) * ibuf->y) / (scopes->sample_lines + 1)) { saveline = 1; @@ -1144,11 +1159,8 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, int use_color_management) for (x = 0; x < ibuf->x; x++) { if (is_float) { - if (use_color_management) - linearrgb_to_srgb_v3_v3(rgba, rf); - else - copy_v3_v3(rgba, rf); - rgba[3] = rf[3]; + copy_v4_v4(rgba, rf); + IMB_colormanagement_processor_apply_v4(cm_processor, rgba); } else { for (c = 0; c < 4; c++) @@ -1219,6 +1231,9 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, int use_color_management) MEM_freeN(bin_b); MEM_freeN(bin_a); + if (cm_processor) + IMB_colormanagement_processor_free(cm_processor); + scopes->ok = 1; } @@ -1257,3 +1272,58 @@ void scopes_new(Scopes *scopes) scopes->waveform_3 = NULL; scopes->vecscope = NULL; } + +void BKE_color_managed_display_settings_init(ColorManagedDisplaySettings *settings) +{ + const char *display_name = IMB_colormanagement_display_get_default_name(); + + BLI_strncpy(settings->display_device, display_name, sizeof(settings->display_device)); +} + +void BKE_color_managed_display_settings_copy(ColorManagedDisplaySettings *new_settings, + const ColorManagedDisplaySettings *settings) +{ + BLI_strncpy(new_settings->display_device, settings->display_device, sizeof(new_settings->display_device)); +} + +void BKE_color_managed_view_settings_init(ColorManagedViewSettings *settings) +{ + /* OCIO_TODO: use default view transform here when OCIO is completely integrated + * and proper versioning stuff is added. + * for now use NONE to be compatible with all current files + */ + BLI_strncpy(settings->view_transform, "Default", sizeof(settings->view_transform)); + + settings->gamma = 1.0f; + settings->exposure = 0.0f; +} + +void BKE_color_managed_view_settings_copy(ColorManagedViewSettings *new_settings, + const ColorManagedViewSettings *settings) +{ + BLI_strncpy(new_settings->view_transform, settings->view_transform, sizeof(new_settings->view_transform)); + + new_settings->flag = settings->flag; + new_settings->exposure = settings->exposure; + new_settings->gamma = settings->gamma; + + if (settings->curve_mapping) + new_settings->curve_mapping = curvemapping_copy(settings->curve_mapping); +} + +void BKE_color_managed_view_settings_free(ColorManagedViewSettings *settings) +{ + if (settings->curve_mapping) + curvemapping_free(settings->curve_mapping); +} + +void BKE_color_managed_colorspace_settings_init(ColorManagedColorspaceSettings *colorspace_settings) +{ + BLI_strncpy(colorspace_settings->name, "", sizeof(colorspace_settings->name)); +} + +void BKE_color_managed_colorspace_settings_copy(ColorManagedColorspaceSettings *colorspace_settings, + const ColorManagedColorspaceSettings *settings) +{ + BLI_strncpy(colorspace_settings->name, settings->name, sizeof(colorspace_settings->name)); +} diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 3f756e74b26..d514a0a7dd0 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -47,6 +47,7 @@ #include "MEM_guardedalloc.h" +#include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" @@ -70,6 +71,7 @@ #include "BLI_bpath.h" #include "BKE_bmfont.h" +#include "BKE_colortools.h" #include "BKE_global.h" #include "BKE_icons.h" #include "BKE_image.h" @@ -243,6 +245,11 @@ static Image *image_alloc(const char *name, short source, short type) ima->source = source; ima->type = type; + + if (source == IMA_SRC_VIEWER) + ima->flag |= IMA_VIEW_AS_RENDER; + + BKE_color_managed_colorspace_settings_init(&ima->colorspace_settings); } return ima; } @@ -325,6 +332,8 @@ Image *BKE_image_copy(Image *ima) nima->aspx = ima->aspx; nima->aspy = ima->aspy; + BKE_color_managed_colorspace_settings_copy(&nima->colorspace_settings, &ima->colorspace_settings); + return nima; } @@ -603,12 +612,10 @@ static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char if (floatbuf) { ibuf = IMB_allocImBuf(width, height, depth, IB_rectfloat); rect_float = ibuf->rect_float; - ibuf->profile = IB_PROFILE_LINEAR_RGB; } else { ibuf = IMB_allocImBuf(width, height, depth, IB_rect); rect = (unsigned char *)ibuf->rect; - ibuf->profile = IB_PROFILE_SRGB; } BLI_strncpy(ibuf->name, name, sizeof(ibuf->name)); @@ -1009,6 +1016,19 @@ int BKE_imtype_supports_quality(const char imtype) return 0; } +int BKE_imtype_supports_float(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_CINEON: + case R_IMF_IMTYPE_DPX: + case R_IMF_IMTYPE_RADHDR: + case R_IMF_IMTYPE_OPENEXR: + case R_IMF_IMTYPE_MULTILAYER: + return TRUE; + } + return 0; +} + char BKE_imtype_valid_channels(const char imtype) { char chan_flag = IMA_CHAN_FLAG_RGB; /* assume all support rgb */ @@ -1477,10 +1497,11 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec int x, y, y_ofs; float h_fixed; const int mono = blf_mono_font_render; // XXX + struct ColorManagedDisplay *display; + const char *display_device; /* this could be an argument if we want to operate on non linear float imbuf's * for now though this is only used for renders which use scene settings */ - const int do_color_management = (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) != 0; #define BUFF_MARGIN_X 2 #define BUFF_MARGIN_Y 1 @@ -1488,6 +1509,9 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec if (!rect && !rectf) return; + display_device = scene->display_settings.display_device; + display = IMB_colormanagement_display_get_named(display_device); + stampdata(scene, camera, &stamp_data, 1); /* TODO, do_versions */ @@ -1497,7 +1521,7 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec /* set before return */ BLF_size(mono, scene->r.stamp_font_id, 72); - BLF_buffer(mono, rectf, rect, width, height, channels, do_color_management); + BLF_buffer(mono, rectf, rect, width, height, channels, display); BLF_buffer_col(mono, scene->r.fg_stamp[0], scene->r.fg_stamp[1], scene->r.fg_stamp[2], 1.0); pad = BLF_width_max(mono); @@ -1514,7 +1538,7 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec y -= h; /* also a little of space to the background. */ - buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, do_color_management, + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, x - BUFF_MARGIN_X, y - BUFF_MARGIN_Y, w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); /* and draw the text. */ @@ -1531,7 +1555,7 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec y -= h; /* and space for background. */ - buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, do_color_management, + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, 0, y - BUFF_MARGIN_Y, w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); BLF_position(mono, x, y + y_ofs, 0.0); @@ -1547,7 +1571,7 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec y -= h; /* and space for background. */ - buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, do_color_management, + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, 0, y - BUFF_MARGIN_Y, w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); BLF_position(mono, x, y + y_ofs, 0.0); @@ -1563,7 +1587,7 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec y -= h; /* and space for background. */ - buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, do_color_management, + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, 0, y - BUFF_MARGIN_Y, w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); BLF_position(mono, x, y + y_ofs, 0.0); @@ -1578,7 +1602,7 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec BLF_width_and_height(mono, stamp_data.marker, &w, &h); h = h_fixed; /* extra space for background. */ - buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, do_color_management, + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, x - BUFF_MARGIN_X, y - BUFF_MARGIN_Y, w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); /* and pad the text. */ @@ -1594,7 +1618,7 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec BLF_width_and_height(mono, stamp_data.time, &w, &h); h = h_fixed; /* extra space for background */ - buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, do_color_management, + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, x - BUFF_MARGIN_X, y, x + w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); /* and pad the text. */ @@ -1609,7 +1633,7 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec BLF_width_and_height(mono, stamp_data.frame, &w, &h); h = h_fixed; /* extra space for background. */ - buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, do_color_management, + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, x - BUFF_MARGIN_X, y - BUFF_MARGIN_Y, x + w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); /* and pad the text. */ @@ -1624,7 +1648,7 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec BLF_width_and_height(mono, stamp_data.camera, &w, &h); h = h_fixed; /* extra space for background. */ - buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, do_color_management, + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, x - BUFF_MARGIN_X, y - BUFF_MARGIN_Y, x + w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); BLF_position(mono, x, y + y_ofs, 0.0); BLF_draw_buffer(mono, stamp_data.camera); @@ -1637,7 +1661,7 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec BLF_width_and_height(mono, stamp_data.cameralens, &w, &h); h = h_fixed; /* extra space for background. */ - buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, do_color_management, + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, x - BUFF_MARGIN_X, y - BUFF_MARGIN_Y, x + w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); BLF_position(mono, x, y + y_ofs, 0.0); BLF_draw_buffer(mono, stamp_data.cameralens); @@ -1650,7 +1674,7 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec x = width - w - 2; /* extra space for background. */ - buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, do_color_management, + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, x - BUFF_MARGIN_X, y - BUFF_MARGIN_Y, x + w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); /* and pad the text. */ @@ -1666,7 +1690,7 @@ void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rec y = height - h; /* extra space for background. */ - buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, do_color_management, + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, x - BUFF_MARGIN_X, y - BUFF_MARGIN_Y, x + w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); BLF_position(mono, x, y + y_ofs, 0.0); @@ -1878,12 +1902,12 @@ void BKE_makepicstring(char *string, const char *base, const char *relbase, int } /* used by sequencer too */ -struct anim *openanim(const char *name, int flags, int streamindex) +struct anim *openanim(const char *name, int flags, int streamindex, char colorspace[IMA_MAX_SPACE]) { struct anim *anim; struct ImBuf *ibuf; - anim = IMB_open_anim(name, flags, streamindex); + anim = IMB_open_anim(name, flags, streamindex, colorspace); if (anim == NULL) return NULL; ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); @@ -2226,7 +2250,7 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) flag |= IB_premul; /* read ibuf */ - ibuf = IMB_loadiffname(name, flag); + ibuf = IMB_loadiffname(name, flag, ima->colorspace_settings.name); #if 0 if (ibuf) { @@ -2304,7 +2328,6 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int f ibuf->flags |= IB_rectfloat; ibuf->mall = IB_rectfloat; ibuf->channels = rpass->channels; - ibuf->profile = IB_PROFILE_LINEAR_RGB; image_initialize_after_load(ima, ibuf); image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : 0, frame); @@ -2334,7 +2357,7 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) BKE_image_user_file_path(iuser, ima, str); /* FIXME: make several stream accessible in image editor, too*/ - ima->anim = openanim(str, IB_rect, 0); + ima->anim = openanim(str, IB_rect, 0, ima->colorspace_settings.name); /* let's initialize this user */ if (ima->anim && iuser && iuser->frames == 0) @@ -2385,8 +2408,8 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) flag = IB_rect | IB_multilayer; if (ima->flag & IMA_DO_PREMUL) flag |= IB_premul; - ibuf = IMB_ibImageFromMemory((unsigned char *)ima->packedfile->data, - ima->packedfile->size, flag, "<packed data>"); + ibuf = IMB_ibImageFromMemory((unsigned char *)ima->packedfile->data, ima->packedfile->size, flag, + ima->colorspace_settings.name, "<packed data>"); } else { flag = IB_rect | IB_multilayer | IB_metadata; @@ -2398,7 +2421,7 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) BKE_image_user_file_path(iuser, ima, str); /* read ibuf */ - ibuf = IMB_loadiffname(str, flag); + ibuf = IMB_loadiffname(str, flag, ima->colorspace_settings.name); } if (ibuf) { @@ -2455,7 +2478,6 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser) ibuf->rect_float = rpass->rect; ibuf->flags |= IB_rectfloat; ibuf->channels = rpass->channels; - ibuf->profile = IB_PROFILE_LINEAR_RGB; image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : IMA_NO_INDEX, 0); } @@ -2561,6 +2583,12 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); } + /* invalidate color managed buffers if render result changed */ + BLI_lock_thread(LOCK_COLORMANAGE); + if (ibuf->x != rres.rectx || ibuf->y != rres.recty || ibuf->rect_float != rectf) { + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + } + ibuf->x = rres.rectx; ibuf->y = rres.recty; @@ -2592,8 +2620,8 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ ibuf->flags &= ~IB_zbuffloat; } - /* since its possible to access the buffer from the image directly, set the profile [#25073] */ - ibuf->profile = (iuser->scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) ? IB_PROFILE_LINEAR_RGB : IB_PROFILE_NONE; + BLI_unlock_thread(LOCK_COLORMANAGE); + ibuf->dither = dither; if (iuser->scene->r.color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE) { diff --git a/source/blender/blenkernel/intern/image_gen.c b/source/blender/blenkernel/intern/image_gen.c index 4d7013b9f73..37572eebed6 100644 --- a/source/blender/blenkernel/intern/image_gen.c +++ b/source/blender/blenkernel/intern/image_gen.c @@ -289,7 +289,11 @@ static void checker_board_text(unsigned char *rect, float *rect_float, int width BLF_size(mono, 54, 72); /* hard coded size! */ - BLF_buffer(mono, rect_float, rect, width, height, 4, TRUE); + /* OCIO_TODO: using NULL as display will assume using sRGB display + * this is correct since currently generated images are assumed to be in sRGB space, + * but this would probably needed to be fixed in some way + */ + BLF_buffer(mono, rect_float, rect, width, height, 4, NULL); for (y = 0; y < height; y += step) { text[1] = '1'; diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 1a6a9503887..97d4c150b84 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -66,6 +66,7 @@ #include "BKE_animsys.h" #include "BKE_constraint.h" +#include "BKE_colortools.h" #include "BKE_library.h" #include "BKE_global.h" #include "BKE_main.h" @@ -73,6 +74,7 @@ #include "BKE_image.h" /* openanim */ #include "BKE_tracking.h" +#include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" #include "IMB_moviecache.h" @@ -197,19 +199,25 @@ static ImBuf *movieclip_load_sequence_file(MovieClip *clip, MovieClipUser *user, struct ImBuf *ibuf; char name[FILE_MAX]; int loadflag, use_proxy = FALSE; + char *colorspace; use_proxy = (flag & MCLIP_USE_PROXY) && user->render_size != MCLIP_PROXY_RENDER_SIZE_FULL; if (use_proxy) { int undistort = user->render_flag & MCLIP_PROXY_RENDER_UNDISTORT; get_proxy_fname(clip, user->render_size, undistort, framenr, name); + + /* proxies were built using default color space settings */ + colorspace = NULL; } - else + else { get_sequence_fname(clip, framenr, name); + colorspace = clip->colorspace_settings.name; + } loadflag = IB_rect | IB_multilayer; /* read ibuf */ - ibuf = IMB_loadiffname(name, loadflag); + ibuf = IMB_loadiffname(name, loadflag, colorspace); return ibuf; } @@ -223,7 +231,7 @@ static void movieclip_open_anim_file(MovieClip *clip) BLI_path_abs(str, ID_BLEND_PATH(G.main, &clip->id)); /* FIXME: make several stream accessible in image editor, too */ - clip->anim = openanim(str, IB_rect, 0); + clip->anim = openanim(str, IB_rect, 0, clip->colorspace_settings.name); if (clip->anim) { if (clip->flag & MCLIP_USE_PROXY_CUSTOM_DIR) { @@ -478,6 +486,7 @@ static MovieClip *movieclip_alloc(const char *name) clip->aspx = clip->aspy = 1.0f; BKE_tracking_settings_init(&clip->tracking); + BKE_color_managed_colorspace_settings_init(&clip->colorspace_settings); clip->proxy.build_size_flag = IMB_PROXY_25; clip->proxy.build_tc_flag = IMB_TC_RECORD_RUN | diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index b862a824d50..4f3921936e8 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -1117,19 +1117,20 @@ void BKE_simulate_ocean_cache(struct OceanCache *och, int frame) /* if image is already loaded in mem, return */ if (och->ibufs_disp[f] != NULL) return; + /* use default color spaces since we know for sure cache files were saved with default settings too */ cache_filename(string, och->bakepath, och->relbase, frame, CACHE_TYPE_DISPLACE); - och->ibufs_disp[f] = IMB_loadiffname(string, 0); + och->ibufs_disp[f] = IMB_loadiffname(string, 0, NULL); //if (och->ibufs_disp[f] == NULL) printf("error loading %s\n", string); //else printf("loaded cache %s\n", string); cache_filename(string, och->bakepath, och->relbase, frame, CACHE_TYPE_FOAM); - och->ibufs_foam[f] = IMB_loadiffname(string, 0); + och->ibufs_foam[f] = IMB_loadiffname(string, 0, NULL); //if (och->ibufs_foam[f] == NULL) printf("error loading %s\n", string); //else printf("loaded cache %s\n", string); cache_filename(string, och->bakepath, och->relbase, frame, CACHE_TYPE_NORMAL); - och->ibufs_norm[f] = IMB_loadiffname(string, 0); + och->ibufs_norm[f] = IMB_loadiffname(string, 0, NULL); //if (och->ibufs_norm[f] == NULL) printf("error loading %s\n", string); //else printf("loaded cache %s\n", string); } @@ -1172,8 +1173,6 @@ void BKE_bake_ocean(struct Ocean *o, struct OceanCache *och, void (*update_cb)(v ibuf_disp = IMB_allocImBuf(res_x, res_y, 32, IB_rectfloat); ibuf_normal = IMB_allocImBuf(res_x, res_y, 32, IB_rectfloat); - ibuf_disp->profile = ibuf_foam->profile = ibuf_normal->profile = IB_PROFILE_LINEAR_RGB; - BKE_simulate_ocean(o, och->time[i], och->wave_scale, och->chop_amount); /* add new foam */ diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 145bd6a88e5..2dec72560a2 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -57,6 +57,7 @@ #include "BKE_anim.h" #include "BKE_animsys.h" +#include "BKE_colortools.h" #include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_group.h" @@ -76,6 +77,8 @@ #include "RE_engine.h" +#include "IMB_colormanagement.h" + //XXX #include "BIF_previewrender.h" //XXX #include "BIF_editseq.h" @@ -132,6 +135,8 @@ Scene *BKE_scene_copy(Scene *sce, int type) MEM_freeN(scen->toolsettings); } else { + ImageFormatData *im_format, *im_formatn; + scen = BKE_libblock_copy(&sce->id); BLI_duplicatelist(&(scen->base), &(sce->base)); @@ -167,6 +172,13 @@ Scene *BKE_scene_copy(Scene *sce, int type) obase = obase->next; base = base->next; } + + /* copy color management settings */ + im_format = &sce->r.im_format; + im_formatn = &scen->r.im_format; + + BKE_color_managed_display_settings_copy(&scen->display_settings, &sce->display_settings); + BKE_color_managed_view_settings_copy(&scen->view_settings, &sce->view_settings); } /* tool settings */ @@ -332,6 +344,8 @@ void BKE_scene_free(Scene *sce) MEM_freeN(sce->fps_info); sound_destroy_scene(sce); + + BKE_color_managed_view_settings_free(&sce->view_settings); } Scene *BKE_scene_add(const char *name) @@ -372,7 +386,14 @@ Scene *BKE_scene_add(const char *name) sce->r.frs_sec_base = 1; sce->r.edgeint = 10; sce->r.ocres = 128; + + /* OCIO_TODO: for forwards compatibiliy only, so if no tonecurve are used, + * images would look in the same way as in current blender + * + * perhaps at some point should be completely deprecated? + */ sce->r.color_mgt_flag |= R_COLOR_MANAGEMENT; + sce->r.gauss = 1.0; /* deprecated but keep for upwards compat */ @@ -546,6 +567,9 @@ Scene *BKE_scene_add(const char *name) sound_create_scene(sce); + BKE_color_managed_display_settings_init(&sce->display_settings); + BKE_color_managed_view_settings_init(&sce->view_settings); + return sce; } @@ -1240,3 +1264,19 @@ void BKE_scene_base_flag_from_objects(struct Scene *scene) base = base->next; } } + +void BKE_scene_disable_color_management(Scene *scene) +{ + ColorManagedDisplaySettings *display_settings = &scene->display_settings; + ColorManagedViewSettings *view_settings = &scene->view_settings; + const char *view; + + /* NOTE: None display with Default view should always exist in OCIO configuration, otherwise it wouldn't work as expected */ + BLI_strncpy(display_settings->display_device, "None", sizeof(display_settings->display_device)); + + view = IMB_colormanagement_view_get_default_name(display_settings->display_device); + + if (view) { + BLI_strncpy(view_settings->view_transform, view, sizeof(view_settings->view_transform)); + } +} diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c index 4dbe0b6290c..eaf3ec384c8 100644 --- a/source/blender/blenkernel/intern/seqeffects.c +++ b/source/blender/blenkernel/intern/seqeffects.c @@ -52,6 +52,7 @@ #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" +#include "IMB_colormanagement.h" #include "RNA_access.h" @@ -119,13 +120,13 @@ static ImBuf *prepare_effect_imbufs(SeqRenderData context, ImBuf *ibuf1, ImBuf * } if (ibuf1 && !ibuf1->rect_float && out->rect_float) { - IMB_float_from_rect_simple(ibuf1); + BKE_sequencer_imbuf_to_sequencer_space(context.scene, ibuf1, TRUE); } if (ibuf2 && !ibuf2->rect_float && out->rect_float) { - IMB_float_from_rect_simple(ibuf2); + BKE_sequencer_imbuf_to_sequencer_space(context.scene, ibuf2, TRUE); } if (ibuf3 && !ibuf3->rect_float && out->rect_float) { - IMB_float_from_rect_simple(ibuf3); + BKE_sequencer_imbuf_to_sequencer_space(context.scene, ibuf3, TRUE); } if (ibuf1 && !ibuf1->rect && !out->rect_float) { @@ -137,7 +138,7 @@ static ImBuf *prepare_effect_imbufs(SeqRenderData context, ImBuf *ibuf1, ImBuf * if (ibuf3 && !ibuf3->rect && !out->rect_float) { IMB_rect_from_float(ibuf3); } - + return out; } diff --git a/source/blender/blenkernel/intern/seqmodifier.c b/source/blender/blenkernel/intern/seqmodifier.c index 65fcd0d85d6..b0dcad64722 100644 --- a/source/blender/blenkernel/intern/seqmodifier.c +++ b/source/blender/blenkernel/intern/seqmodifier.c @@ -581,6 +581,11 @@ ImBuf *BKE_sequence_modifier_apply_stack(SeqRenderData context, Sequence *seq, I SequenceModifierData *smd; ImBuf *processed_ibuf = ibuf; + if (seq->modifiers.first && (seq->flag & SEQ_USE_LINEAR_MODIFIERS)) { + processed_ibuf = IMB_dupImBuf(ibuf); + BKE_sequencer_imbuf_from_sequencer_space(context.scene, processed_ibuf); + } + for (smd = seq->modifiers.first; smd; smd = smd->next) { SequenceModifierTypeInfo *smti = BKE_sequence_modifier_type_info_get(smd->type); @@ -605,6 +610,10 @@ ImBuf *BKE_sequence_modifier_apply_stack(SeqRenderData context, Sequence *seq, I } } + if (seq->modifiers.first && (seq->flag & SEQ_USE_LINEAR_MODIFIERS)) { + BKE_sequencer_imbuf_to_sequencer_space(context.scene, processed_ibuf, FALSE); + } + return processed_ibuf; } diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 2596bf57f05..8d7ca94ed48 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -71,6 +71,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_colormanagement.h" #include "BKE_context.h" #include "BKE_sound.h" @@ -308,6 +309,81 @@ void BKE_sequencer_editing_free(Scene *scene) scene->ed = NULL; } +/*********************** Sequencer color space functions *************************/ + +static void sequencer_imbuf_assign_spaces(Scene *scene, ImBuf *ibuf) +{ + IMB_colormanagement_imbuf_assign_float_space(ibuf, &scene->sequencer_colorspace_settings); +} + +void BKE_sequencer_imbuf_to_sequencer_space(Scene *scene, ImBuf *ibuf, int make_float) +{ + const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); + const char *to_colorspace = scene->sequencer_colorspace_settings.name; + int predivide = ibuf->flags & IB_cm_predivide; + + if (!ibuf->rect_float) { + if (make_float && ibuf->rect) { + /* when converting byte buffer to float in sequencer we need to make float + * buffer be in sequencer's working space, which is currently only doable + * from linear space. + * + */ + + /* + * OCIO_TODO: would be nice to support direct single transform from byte to sequencer's + */ + + IMB_float_from_rect(ibuf); + } + else { + /* if there's only byte buffer in image it's already in compositor's working space, + * nothing to do here + */ + + return; + } + } + + if (from_colorspace && from_colorspace[0] != '\0') { + if (ibuf->rect) + imb_freerectImBuf(ibuf); + + IMB_colormanagement_transform_threaded(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels, + from_colorspace, to_colorspace, predivide); + } +} + +void BKE_sequencer_imbuf_from_sequencer_space(Scene *scene, ImBuf *ibuf) +{ + const char *from_colorspace = scene->sequencer_colorspace_settings.name; + const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); + + if (!ibuf->rect_float) + return; + + if (to_colorspace && to_colorspace[0] != '\0') { + int predivide = ibuf->flags & IB_cm_predivide; + + IMB_colormanagement_transform_threaded(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels, + from_colorspace, to_colorspace, predivide); + } +} + +void BKE_sequencer_pixel_from_sequencer_space_v4(struct Scene *scene, float pixel[4]) +{ + const char *from_colorspace = scene->sequencer_colorspace_settings.name; + const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); + + if (to_colorspace && to_colorspace[0] != '\0') { + IMB_colormanagement_transform_v4(pixel, from_colorspace, to_colorspace); + } + else { + /* if no color management enables fallback to legacy conversion */ + srgb_to_linearrgb_v4(pixel, pixel); + } +} + /*********************** sequencer pipeline functions *************************/ SeqRenderData BKE_sequencer_new_render_data(Main *bmain, Scene *scene, int rectx, int recty, int preview_render_size) @@ -597,7 +673,9 @@ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, int lock_range) BLI_path_abs(str, G.main->name); if (seq->anim) IMB_free_anim(seq->anim); - seq->anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), seq->streamindex); + + /* OCIO_TODO: support configurable input space for strips */ + seq->anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), seq->streamindex, NULL); if (!seq->anim) { return; @@ -1094,7 +1172,8 @@ static void seq_open_anim_file(Sequence *seq) seq->strip->dir, seq->strip->stripdata->name); BLI_path_abs(name, G.main->name); - seq->anim = openanim(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), seq->streamindex); + /* OCIO_TODO: support configurable input space for strips */ + seq->anim = openanim(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), seq->streamindex, NULL); if (seq->anim == NULL) { return; @@ -1200,8 +1279,9 @@ static ImBuf *seq_proxy_fetch(SeqRenderData context, Sequence *seq, int cfra) if (seq_proxy_get_fname(seq, cfra, render_size, name) == 0) { return NULL; } - - seq->strip->proxy->anim = openanim(name, IB_rect, 0); + + /* proxies are generated in default color space */ + seq->strip->proxy->anim = openanim(name, IB_rect, 0, NULL); } if (seq->strip->proxy->anim == NULL) { return NULL; @@ -1219,7 +1299,13 @@ static ImBuf *seq_proxy_fetch(SeqRenderData context, Sequence *seq, int cfra) } if (BLI_exists(name)) { - return IMB_loadiffname(name, IB_rect); + /* OCIO_TODO: support configurable spaces for strips */ + ImBuf *ibuf = IMB_loadiffname(name, IB_rect, NULL); + + if (ibuf) + sequencer_imbuf_assign_spaces(context.scene, ibuf); + + return ibuf; } else { return NULL; @@ -1257,7 +1343,8 @@ static void seq_proxy_build_frame(SeqRenderData context, Sequence *seq, int cfra ibuf->planes = 24; BLI_make_existing_file(name); - + + /* OCIO_TODO: support per-strip color space settings */ ok = IMB_saveiff(ibuf, name, IB_rect | IB_zbuf | IB_zbuffloat); if (ok == 0) { perror(name); @@ -1706,7 +1793,7 @@ int BKE_sequencer_input_have_to_preprocess(SeqRenderData UNUSED(context), Sequen { float mul; - if (seq->flag & (SEQ_FILTERY | SEQ_USE_CROP | SEQ_USE_TRANSFORM | SEQ_FLIPX | SEQ_FLIPY | SEQ_MAKE_PREMUL)) { + if (seq->flag & (SEQ_FILTERY | SEQ_USE_CROP | SEQ_USE_TRANSFORM | SEQ_FLIPX | SEQ_FLIPY | SEQ_MAKE_PREMUL | SEQ_MAKE_FLOAT)) { return TRUE; } @@ -1826,8 +1913,9 @@ static ImBuf *input_preprocess(SeqRenderData context, Sequence *seq, float cfra, } if (seq->flag & SEQ_MAKE_FLOAT) { - if (!ibuf->rect_float) - IMB_float_from_rect_simple(ibuf); + if (!ibuf->rect_float) { + BKE_sequencer_imbuf_to_sequencer_space(context.scene, ibuf, TRUE); + } if (ibuf->rect) { imb_freerectImBuf(ibuf); @@ -1893,6 +1981,8 @@ static void copy_to_ibuf_still(SeqRenderData context, Sequence *seq, float nr, I * changing the cached image... */ ibuf = IMB_dupImBuf(ibuf); + sequencer_imbuf_assign_spaces(context.scene, ibuf); + if (nr == 0) { BKE_sequencer_cache_put(context, seq, seq->start, SEQ_STRIPELEM_IBUF_STARTSTILL, ibuf); } @@ -2307,7 +2397,7 @@ static ImBuf *seq_render_scene_strip(SeqRenderData context, Sequence *seq, float /* opengl offscreen render */ BKE_scene_update_for_newframe(context.bmain, scene, scene->lay); ibuf = sequencer_view3d_cb(scene, camera, context.rectx, context.recty, - IB_rect, context.scene->r.seq_prev_type, TRUE, err_out); + IB_rect, context.scene->r.seq_prev_type, TRUE, FALSE, err_out); if (ibuf == NULL) { fprintf(stderr, "seq_render_scene_strip failed to get opengl buffer: %s\n", err_out); } @@ -2338,11 +2428,7 @@ static ImBuf *seq_render_scene_strip(SeqRenderData context, Sequence *seq, float } /* float buffers in the sequencer are not linear */ - if (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) - ibuf->profile = IB_PROFILE_LINEAR_RGB; - else - ibuf->profile = IB_PROFILE_NONE; - IMB_convert_profile(ibuf, IB_PROFILE_SRGB); + BKE_sequencer_imbuf_to_sequencer_space(context.scene, ibuf, FALSE); } else if (rres.rect32) { ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect); @@ -2442,14 +2528,14 @@ static ImBuf *do_render_strip_uncached(SeqRenderData context, Sequence *seq, flo BLI_path_abs(name, G.main->name); } - if (s_elem && (ibuf = IMB_loadiffname(name, IB_rect))) { + /* OCIO_TODO: support configurable space for image strips */ + if (s_elem && (ibuf = IMB_loadiffname(name, IB_rect, NULL))) { /* we don't need both (speed reasons)! */ if (ibuf->rect_float && ibuf->rect) imb_freerectImBuf(ibuf); /* all sequencer color is done in SRGB space, linear gives odd crossfades */ - if (ibuf->profile == IB_PROFILE_LINEAR_RGB) - IMB_convert_profile(ibuf, IB_PROFILE_NONE); + BKE_sequencer_imbuf_to_sequencer_space(context.scene, ibuf, FALSE); copy_to_ibuf_still(context, seq, nr, ibuf); @@ -2497,6 +2583,7 @@ static ImBuf *do_render_strip_uncached(SeqRenderData context, Sequence *seq, flo case SEQ_TYPE_MOVIECLIP: { ibuf = seq_render_movieclip_strip(context, seq, nr); + sequencer_imbuf_assign_spaces(context.scene, ibuf); if (ibuf && use_preprocess) { ImBuf *i = IMB_dupImBuf(ibuf); @@ -2520,6 +2607,9 @@ static ImBuf *do_render_strip_uncached(SeqRenderData context, Sequence *seq, flo } } + if (ibuf) + sequencer_imbuf_assign_spaces(context.scene, ibuf); + return ibuf; } @@ -2561,8 +2651,10 @@ static ImBuf *seq_render_strip(SeqRenderData context, Sequence *seq, float cfra) } } - if (ibuf == NULL) + if (ibuf == NULL) { ibuf = IMB_allocImBuf(context.rectx, context.recty, 32, IB_rect); + sequencer_imbuf_assign_spaces(context.scene, ibuf); + } if (ibuf->x != context.rectx || ibuf->y != context.recty) use_preprocess = TRUE; @@ -2641,12 +2733,12 @@ static ImBuf *seq_render_strip_stack(SeqRenderData context, ListBase *seqbasep, if (count == 1) { out = seq_render_strip(context, seq_arr[0], cfra); + BKE_sequencer_cache_put(context, seq_arr[0], cfra, SEQ_STRIPELEM_IBUF_COMP, out); return out; } - for (i = count - 1; i >= 0; i--) { int early_out; Sequence *seq = seq_arr[i]; @@ -3946,7 +4038,8 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad BLI_strncpy(path, seq_load->path, sizeof(path)); BLI_path_abs(path, G.main->name); - an = openanim(path, IB_rect, 0); + /* OCIO_TODO: support configurable input space for strips */ + an = openanim(path, IB_rect, 0, NULL); if (an == NULL) return NULL; diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index ff48f5db9aa..97ebc3a90ba 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -1627,7 +1627,6 @@ ImBuf *BKE_tracking_sample_pattern(int frame_width, int frame_height, ImBuf *sea float *mask = NULL; pattern_ibuf = IMB_allocImBuf(num_samples_x, num_samples_y, 32, IB_rectfloat); - pattern_ibuf->profile = IB_PROFILE_LINEAR_RGB; if (!search_ibuf->rect_float) { IMB_float_from_rect(search_ibuf); @@ -1722,7 +1721,6 @@ ImBuf *BKE_tracking_get_search_imbuf(ImBuf *ibuf, MovieTrackingTrack *track, Mov h = (marker->search_max[1] - marker->search_min[1]) * ibuf->y; searchibuf = IMB_allocImBuf(w, h, 32, ibuf->rect_float ? IB_rectfloat : IB_rect); - searchibuf->profile = ibuf->profile; IMB_rectcpy(searchibuf, ibuf, 0, 0, x, y, w, h); @@ -3317,7 +3315,6 @@ static ImBuf *stabilization_allocate_ibuf(ImBuf *cacheibuf, ImBuf *srcibuf, int } else { cacheibuf = IMB_allocImBuf(srcibuf->x, srcibuf->y, srcibuf->planes, flags); - cacheibuf->profile = srcibuf->profile; } return cacheibuf; diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index 902373bcd6b..9cd801f819d 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -77,6 +77,7 @@ int BLI_system_thread_count(void); /* gets the number of threads the system #define LOCK_OPENGL 5 #define LOCK_NODES 6 #define LOCK_MOVIECLIP 7 +#define LOCK_COLORMANAGE 8 void BLI_lock_thread(int type); void BLI_unlock_thread(int type); diff --git a/source/blender/blenlib/intern/threads.c b/source/blender/blenlib/intern/threads.c index 9994f89acd5..66527b9b92a 100644 --- a/source/blender/blenlib/intern/threads.c +++ b/source/blender/blenlib/intern/threads.c @@ -113,6 +113,7 @@ static pthread_mutex_t _rcache_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t _opengl_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t _nodes_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t _movieclip_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t _colormanage_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_t mainid; static int thread_levels = 0; /* threads can be invoked inside threads */ @@ -351,6 +352,8 @@ void BLI_lock_thread(int type) pthread_mutex_lock(&_nodes_lock); else if (type == LOCK_MOVIECLIP) pthread_mutex_lock(&_movieclip_lock); + else if (type == LOCK_COLORMANAGE) + pthread_mutex_lock(&_colormanage_lock); } void BLI_unlock_thread(int type) @@ -371,6 +374,8 @@ void BLI_unlock_thread(int type) pthread_mutex_unlock(&_nodes_lock); else if (type == LOCK_MOVIECLIP) pthread_mutex_unlock(&_movieclip_lock); + else if (type == LOCK_COLORMANAGE) + pthread_mutex_unlock(&_colormanage_lock); } /* Mutex Locks */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index a7f8026ad5d..c6483442b00 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4900,6 +4900,14 @@ static void direct_link_sequence_modifiers(FileData *fd, ListBase *lb) } } +static void direct_link_view_settings(FileData *fd, ColorManagedViewSettings *view_settings) +{ + view_settings->curve_mapping = newdataadr(fd, view_settings->curve_mapping); + + if (view_settings->curve_mapping) + direct_link_curvemapping(fd, view_settings->curve_mapping); +} + static void direct_link_scene(FileData *fd, Scene *sce) { Editing *ed; @@ -5077,6 +5085,8 @@ static void direct_link_scene(FileData *fd, Scene *sce) sce->nodetree = newdataadr(fd, sce->nodetree); if (sce->nodetree) direct_link_nodetree(fd, sce->nodetree); + + direct_link_view_settings(fd, &sce->view_settings); } /* ************ READ WM ***************** */ @@ -5290,6 +5300,13 @@ static void lib_link_screen(FileData *fd, Main *main) * so fingers crossed this works fine! */ sseq->gpd = newlibadr_us(fd, sc->id.lib, sseq->gpd); + + sseq->scopes.reference_ibuf = NULL; + sseq->scopes.zebra_ibuf = NULL; + sseq->scopes.waveform_ibuf = NULL; + sseq->scopes.sep_waveform_ibuf = NULL; + sseq->scopes.vector_ibuf = NULL; + sseq->scopes.histogram_ibuf = NULL; } else if (sl->spacetype == SPACE_NLA) { SpaceNla *snla= (SpaceNla *)sl; @@ -7902,6 +7919,47 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } } + /* color management pipeline changes compatibility code */ + if (main->versionfile < 263 || (main->versionfile == 263 && main->subversionfile < 19)) { + Scene *scene; + Image *ima; + int colormanagement_disabled = FALSE; + + /* make scenes which are not using color management have got None as display device, + * so they wouldn't perform linear-to-sRGB conversion on display + */ + for (scene = main->scene.first; scene; scene = scene->id.next) { + if ((scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) == 0) { + ColorManagedDisplaySettings *display_settings = &scene->display_settings; + + if (display_settings->display_device[0] == 0) { + BKE_scene_disable_color_management(scene); + + } + + colormanagement_disabled = TRUE; + } + } + + for (ima = main->image.first; ima; ima = ima->id.next) { + if (ima->source == IMA_SRC_VIEWER) { + ima->flag |= IMA_VIEW_AS_RENDER; + } + else if (colormanagement_disabled) { + /* if colormanagement not used, set image's color space to raw, so no sRGB->linear conversion + * would happen on display and render + * there's no clear way to check whether color management is enabled or not in render engine + * so set all images to raw if there's at least one scene with color management disabled + * this would still behave incorrect in cases when color management was used for only some + * of scenes, but such a setup is crazy anyway and think it's fair enough to break compatibility + * in that cases + */ + + BLI_strncpy(ima->colorspace_settings.name, "Raw", sizeof(ima->colorspace_settings.name)); + } + } + } + /* WATCH IT!!!: pointers from libdata have not been converted yet here! */ /* WATCH IT 2!: Userdef struct init has to be in editors/interface/resources.c! */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 109a844352f..b8d63b3c5d5 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2119,6 +2119,13 @@ static void write_sequence_modifiers(WriteData *wd, ListBase *modbase) } } +static void write_view_settings(WriteData *wd, ColorManagedViewSettings *view_settings) +{ + if (view_settings->curve_mapping) { + write_curvemapping(wd, view_settings->curve_mapping); + } +} + static void write_scenes(WriteData *wd, ListBase *scebase) { Scene *sce; @@ -2261,7 +2268,9 @@ static void write_scenes(WriteData *wd, ListBase *scebase) writestruct(wd, DATA, "bNodeTree", 1, sce->nodetree); write_nodetree(wd, sce->nodetree); } - + + write_view_settings(wd, &sce->view_settings); + sce= sce->id.next; } /* flush helps the compression for undo-save */ diff --git a/source/blender/collada/ImageExporter.cpp b/source/blender/collada/ImageExporter.cpp index a15dadda8cf..f1f1efdbd33 100644 --- a/source/blender/collada/ImageExporter.cpp +++ b/source/blender/collada/ImageExporter.cpp @@ -103,7 +103,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies) // So we have to export it. The export will keep the image state intact, // so the exported file will not be associated with the image. - if (BKE_imbuf_write_as(imbuf, export_path, &imageFormat, true) == 0) { + if (BKE_imbuf_write_as(imbuf, export_path, &imageFormat, image->colorspace_settings.name, true) == 0) { fprintf(stderr, "Collada export: Cannot export image to:\n%s\n", export_path); return; } diff --git a/source/blender/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h index f35bf71392c..a64583b68ff 100644 --- a/source/blender/compositor/COM_compositor.h +++ b/source/blender/compositor/COM_compositor.h @@ -24,6 +24,7 @@ extern "C" { #endif +#include "DNA_color_types.h" #include "DNA_node_types.h" /** @@ -299,8 +300,20 @@ extern "C" { * * - output nodes can have different priorities in the WorkScheduler. * This is implemented in the COM_execute function. + * + * @param viewSettings + * reference to view settings used for color management + * + * @param displaySettings + * reference to display settings used for color management + * + * OCIO_TODO: this options only used in rare cases, namely in output file node, + * so probably this settings could be passed in a nicer way. + * should be checked further, probably it'll be also needed for preview + * generation in display space */ -void COM_execute(RenderData *rd, bNodeTree *editingtree, int rendering); +void COM_execute(RenderData *rd, bNodeTree *editingtree, int rendering, + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings); /** * @brief Deinitialize the compositor caches and allocated memory. diff --git a/source/blender/compositor/intern/COM_CompositorContext.cpp b/source/blender/compositor/intern/COM_CompositorContext.cpp index fbdb4cd6b28..e1cc25d028a 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.cpp +++ b/source/blender/compositor/intern/COM_CompositorContext.cpp @@ -31,6 +31,8 @@ CompositorContext::CompositorContext() this->m_hasActiveOpenCLDevices = false; this->m_activegNode = NULL; this->m_fastCalculation = false; + this->m_viewSettings = NULL; + this->m_displaySettings = NULL; } const int CompositorContext::getFramenumber() const @@ -42,13 +44,3 @@ const int CompositorContext::getFramenumber() const return -1; /* this should never happen */ } } - -const int CompositorContext::isColorManaged() const -{ - if (this->m_rd) { - return this->m_rd->color_mgt_flag & R_COLOR_MANAGEMENT; - } - else { - return 0; /* this should never happen */ - } -} diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index 2f6abf39985..2f5e8c0648d 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -27,6 +27,7 @@ #include "BKE_text.h" #include <string> #include "DNA_node_types.h" +#include "DNA_color_types.h" #include "BLI_rect.h" #include "DNA_scene_types.h" #include "COM_defines.h" @@ -79,6 +80,9 @@ private: */ bool m_fastCalculation; + /* @brief color management settings */ + const ColorManagedViewSettings *m_viewSettings; + const ColorManagedDisplaySettings *m_displaySettings; public: /** * @brief constructor initializes the context with default values. @@ -126,6 +130,26 @@ public: const RenderData *getRenderData() const { return this->m_rd; } /** + * @brief set view settings of color color management + */ + void setViewSettings(const ColorManagedViewSettings *viewSettings) { this->m_viewSettings = viewSettings; } + + /** + * @brief get view settings of color color management + */ + const ColorManagedViewSettings *getViewSettings() const { return this->m_viewSettings; } + + /** + * @brief set display settings of color color management + */ + void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings) { this->m_displaySettings = displaySettings; } + + /** + * @brief get display settings of color color management + */ + const ColorManagedDisplaySettings *getDisplaySettings() const { return this->m_displaySettings; } + + /** * @brief set the quality */ void setQuality(CompositorQuality quality) { this->m_quality = quality; } @@ -152,8 +176,6 @@ public: int getChunksize() { return this->getbNodeTree()->chunksize; } - const int isColorManaged() const; - void setFastCalculation(bool fastCalculation) {this->m_fastCalculation = fastCalculation;} bool isFastCalculation() {return this->m_fastCalculation;} }; diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cpp b/source/blender/compositor/intern/COM_ExecutionSystem.cpp index 801505e9d39..a13717c9d86 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cpp +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cpp @@ -44,7 +44,8 @@ #include "MEM_guardedalloc.h" #endif -ExecutionSystem::ExecutionSystem(RenderData *rd, bNodeTree *editingtree, bool rendering, bool fastcalculation) +ExecutionSystem::ExecutionSystem(RenderData *rd, bNodeTree *editingtree, bool rendering, bool fastcalculation, + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings) { this->m_context.setbNodeTree(editingtree); this->m_context.setFastCalculation(fastcalculation); @@ -69,6 +70,9 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, bNodeTree *editingtree, bool re ExecutionSystemHelper::addbNodeTree(*this, 0, editingtree, NULL); this->m_context.setRenderData(rd); + this->m_context.setViewSettings(viewSettings); + this->m_context.setDisplaySettings(displaySettings); + this->convertToOperations(); this->groupOperations(); /* group operations in ExecutionGroups */ unsigned int index; diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index 39e7bc80279..56a60bf7a03 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -25,6 +25,7 @@ class ExecutionGroup; #ifndef _COM_ExecutionSystem_h #define _COM_ExecutionSystem_h +#include "DNA_color_types.h" #include "DNA_node_types.h" #include <vector> #include "COM_Node.h" @@ -156,7 +157,8 @@ public: * @param editingtree [bNodeTree *] * @param rendering [true false] */ - ExecutionSystem(RenderData *rd, bNodeTree *editingtree, bool rendering, bool fastcalculation); + ExecutionSystem(RenderData *rd, bNodeTree *editingtree, bool rendering, bool fastcalculation, + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings); /** * Destructor diff --git a/source/blender/compositor/intern/COM_Node.cpp b/source/blender/compositor/intern/COM_Node.cpp index 50393d14f35..5922b0e6b08 100644 --- a/source/blender/compositor/intern/COM_Node.cpp +++ b/source/blender/compositor/intern/COM_Node.cpp @@ -75,12 +75,12 @@ void Node::addSetValueOperation(ExecutionSystem *graph, InputSocket *inputsocket graph->addOperation(operation); } -void Node::addPreviewOperation(ExecutionSystem *system, OutputSocket *outputSocket) +void Node::addPreviewOperation(ExecutionSystem *system, CompositorContext *context, OutputSocket *outputSocket) { if (this->isInActiveGroup()) { if (!(this->getbNode()->flag & NODE_HIDDEN)) { // do not calculate previews of hidden nodes. if (this->getbNode()->flag & NODE_PREVIEW) { - PreviewOperation *operation = new PreviewOperation(); + PreviewOperation *operation = new PreviewOperation(context->getViewSettings(), context->getDisplaySettings()); system->addOperation(operation); operation->setbNode(this->getbNode()); operation->setbNodeTree(system->getContext().getbNodeTree()); @@ -90,11 +90,11 @@ void Node::addPreviewOperation(ExecutionSystem *system, OutputSocket *outputSock } } -void Node::addPreviewOperation(ExecutionSystem *system, InputSocket *inputSocket) +void Node::addPreviewOperation(ExecutionSystem *system, CompositorContext *context, InputSocket *inputSocket) { if (inputSocket->isConnected() && this->isInActiveGroup()) { OutputSocket *outputsocket = inputSocket->getConnection()->getFromSocket(); - this->addPreviewOperation(system, outputsocket); + this->addPreviewOperation(system, context, outputsocket); } } diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h index 7ce40e3cb34..bfccd069ad1 100644 --- a/source/blender/compositor/intern/COM_Node.h +++ b/source/blender/compositor/intern/COM_Node.h @@ -135,8 +135,8 @@ public: */ OutputSocket *findOutputSocketBybNodeSocket(bNodeSocket *socket); protected: - void addPreviewOperation(ExecutionSystem *system, InputSocket *inputSocket); - void addPreviewOperation(ExecutionSystem *system, OutputSocket *outputSocket); + void addPreviewOperation(ExecutionSystem *system, CompositorContext *context, InputSocket *inputSocket); + void addPreviewOperation(ExecutionSystem *system, CompositorContext *context, OutputSocket *outputSocket); bNodeSocket *getEditorInputSocket(int editorNodeInputSocketIndex); bNodeSocket *getEditorOutputSocket(int editorNodeOutputSocketIndex); diff --git a/source/blender/compositor/intern/COM_compositor.cpp b/source/blender/compositor/intern/COM_compositor.cpp index cb9166c929d..5cfcd83f8f1 100644 --- a/source/blender/compositor/intern/COM_compositor.cpp +++ b/source/blender/compositor/intern/COM_compositor.cpp @@ -42,7 +42,9 @@ void intern_freeCompositorCaches() deintializeDistortionCache(); } -void COM_execute(RenderData *rd, bNodeTree *editingtree, int rendering) +void COM_execute(RenderData *rd, bNodeTree *editingtree, int rendering, + const ColorManagedViewSettings *viewSettings, + const ColorManagedDisplaySettings *displaySettings) { /* initialize mutex, TODO this mutex init is actually not thread safe and * should be done somewhere as part of blender startup, all the other @@ -71,7 +73,7 @@ void COM_execute(RenderData *rd, bNodeTree *editingtree, int rendering) bool twopass = (editingtree->flag & NTREE_TWO_PASS) > 0 && !rendering; /* initialize execution system */ if (twopass) { - ExecutionSystem *system = new ExecutionSystem(rd, editingtree, rendering, twopass); + ExecutionSystem *system = new ExecutionSystem(rd, editingtree, rendering, twopass, viewSettings, displaySettings); system->execute(); delete system; @@ -84,7 +86,7 @@ void COM_execute(RenderData *rd, bNodeTree *editingtree, int rendering) } - ExecutionSystem *system = new ExecutionSystem(rd, editingtree, rendering, false); + ExecutionSystem *system = new ExecutionSystem(rd, editingtree, rendering, false, viewSettings, displaySettings); system->execute(); delete system; diff --git a/source/blender/compositor/nodes/COM_BlurNode.cpp b/source/blender/compositor/nodes/COM_BlurNode.cpp index e39ef2b3f23..4f120ea5a6e 100644 --- a/source/blender/compositor/nodes/COM_BlurNode.cpp +++ b/source/blender/compositor/nodes/COM_BlurNode.cpp @@ -57,7 +57,7 @@ void BlurNode::convertToOperations(ExecutionSystem *graph, CompositorContext *co this->getInputSocket(1)->relinkConnections(operationfgb->getInputSocket(1), 1, graph); this->getOutputSocket(0)->relinkConnections(operationfgb->getOutputSocket(0)); graph->addOperation(operationfgb); - addPreviewOperation(graph, operationfgb->getOutputSocket()); + addPreviewOperation(graph, context, operationfgb->getOutputSocket()); } else if (editorNode->custom1 & CMP_NODEFLAG_BLUR_VARIABLE_SIZE) { MathAddOperation *clamp = new MathAddOperation(); @@ -97,7 +97,7 @@ void BlurNode::convertToOperations(ExecutionSystem *graph, CompositorContext *co addLink(graph, operationy->getOutputSocket(), operation->getInputSocket(1)); graph->addOperation(operation); this->getOutputSocket(0)->relinkConnections(operation->getOutputSocket()); - addPreviewOperation(graph, operation->getOutputSocket()); + addPreviewOperation(graph, context, operation->getOutputSocket()); } else if (!data->bokeh) { GaussianXBlurOperation *operationx = new GaussianXBlurOperation(); @@ -115,7 +115,7 @@ void BlurNode::convertToOperations(ExecutionSystem *graph, CompositorContext *co graph->addOperation(operationy); addLink(graph, operationx->getOutputSocket(), operationy->getInputSocket(0)); addLink(graph, operationx->getInputSocket(1)->getConnection()->getFromSocket(), operationy->getInputSocket(1)); - addPreviewOperation(graph, operationy->getOutputSocket()); + addPreviewOperation(graph, context, operationy->getOutputSocket()); if (!connectedSizeSocket) { operationx->setSize(size); @@ -131,7 +131,7 @@ void BlurNode::convertToOperations(ExecutionSystem *graph, CompositorContext *co operation->setQuality(quality); graph->addOperation(operation); this->getOutputSocket(0)->relinkConnections(operation->getOutputSocket()); - addPreviewOperation(graph, operation->getOutputSocket()); + addPreviewOperation(graph, context, operation->getOutputSocket()); if (!connectedSizeSocket) { operation->setSize(size); diff --git a/source/blender/compositor/nodes/COM_BokehImageNode.cpp b/source/blender/compositor/nodes/COM_BokehImageNode.cpp index f6abbbb9a9a..a89ed9e0c64 100644 --- a/source/blender/compositor/nodes/COM_BokehImageNode.cpp +++ b/source/blender/compositor/nodes/COM_BokehImageNode.cpp @@ -35,5 +35,5 @@ void BokehImageNode::convertToOperations(ExecutionSystem *graph, CompositorConte this->getOutputSocket(0)->relinkConnections(operation->getOutputSocket(0)); graph->addOperation(operation); operation->setData((NodeBokehImage *)this->getbNode()->storage); - addPreviewOperation(graph, operation->getOutputSocket(0)); + addPreviewOperation(graph, context, operation->getOutputSocket(0)); } diff --git a/source/blender/compositor/nodes/COM_ChannelMatteNode.cpp b/source/blender/compositor/nodes/COM_ChannelMatteNode.cpp index eab921863e9..c1511ee611b 100644 --- a/source/blender/compositor/nodes/COM_ChannelMatteNode.cpp +++ b/source/blender/compositor/nodes/COM_ChannelMatteNode.cpp @@ -84,7 +84,7 @@ void ChannelMatteNode::convertToOperations(ExecutionSystem *graph, CompositorCon graph->addOperation(operationAlpha); addLink(graph, operation->getOutputSocket(), operationAlpha->getInputSocket(1)); - addPreviewOperation(graph, operationAlpha->getOutputSocket()); + addPreviewOperation(graph, context, operationAlpha->getOutputSocket()); if (outputSocketImage->isConnected()) { outputSocketImage->relinkConnections(operationAlpha->getOutputSocket()); diff --git a/source/blender/compositor/nodes/COM_ChromaMatteNode.cpp b/source/blender/compositor/nodes/COM_ChromaMatteNode.cpp index 0fb09157351..4c4b77ba6cc 100644 --- a/source/blender/compositor/nodes/COM_ChromaMatteNode.cpp +++ b/source/blender/compositor/nodes/COM_ChromaMatteNode.cpp @@ -65,7 +65,7 @@ void ChromaMatteNode::convertToOperations(ExecutionSystem *graph, CompositorCont addLink(graph, operation->getOutputSocket(), operationAlpha->getInputSocket(1)); graph->addOperation(operationAlpha); - addPreviewOperation(graph, operationAlpha->getOutputSocket()); + addPreviewOperation(graph, context, operationAlpha->getOutputSocket()); if (outputSocketImage->isConnected()) { outputSocketImage->relinkConnections(operationAlpha->getOutputSocket()); diff --git a/source/blender/compositor/nodes/COM_ColorMatteNode.cpp b/source/blender/compositor/nodes/COM_ColorMatteNode.cpp index 38ab6ba8da2..54e6762961b 100644 --- a/source/blender/compositor/nodes/COM_ColorMatteNode.cpp +++ b/source/blender/compositor/nodes/COM_ColorMatteNode.cpp @@ -62,7 +62,7 @@ void ColorMatteNode::convertToOperations(ExecutionSystem *graph, CompositorConte addLink(graph, operationRGBToHSV_Image->getInputSocket(0)->getConnection()->getFromSocket(), operationAlpha->getInputSocket(0)); addLink(graph, operation->getOutputSocket(), operationAlpha->getInputSocket(1)); graph->addOperation(operationAlpha); - addPreviewOperation(graph, operationAlpha->getOutputSocket()); + addPreviewOperation(graph, context, operationAlpha->getOutputSocket()); if (outputSocketImage->isConnected()) { outputSocketImage->relinkConnections(operationAlpha->getOutputSocket()); diff --git a/source/blender/compositor/nodes/COM_CompositorNode.cpp b/source/blender/compositor/nodes/COM_CompositorNode.cpp index d0d66a81c77..e3313750e66 100644 --- a/source/blender/compositor/nodes/COM_CompositorNode.cpp +++ b/source/blender/compositor/nodes/COM_CompositorNode.cpp @@ -45,5 +45,5 @@ void CompositorNode::convertToOperations(ExecutionSystem *graph, CompositorConte alphaSocket->relinkConnections(compositorOperation->getInputSocket(1)); depthSocket->relinkConnections(compositorOperation->getInputSocket(2)); graph->addOperation(compositorOperation); - addPreviewOperation(graph, compositorOperation->getInputSocket(0)); + addPreviewOperation(graph, context, compositorOperation->getInputSocket(0)); } diff --git a/source/blender/compositor/nodes/COM_DespeckleNode.cpp b/source/blender/compositor/nodes/COM_DespeckleNode.cpp index 603ddcd1389..a97714c870e 100644 --- a/source/blender/compositor/nodes/COM_DespeckleNode.cpp +++ b/source/blender/compositor/nodes/COM_DespeckleNode.cpp @@ -44,7 +44,7 @@ void DespeckleNode::convertToOperations(ExecutionSystem *graph, CompositorContex inputImageSocket->relinkConnections(operation->getInputSocket(0), 1, graph); inputSocket->relinkConnections(operation->getInputSocket(1), 0, graph); outputSocket->relinkConnections(operation->getOutputSocket()); - addPreviewOperation(graph, operation->getOutputSocket(0)); + addPreviewOperation(graph, context, operation->getOutputSocket(0)); graph->addOperation(operation); } diff --git a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cpp b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cpp index 6dca049a6ae..b5ad07be319 100644 --- a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cpp +++ b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cpp @@ -51,5 +51,5 @@ void DifferenceMatteNode::convertToOperations(ExecutionSystem *graph, Compositor addLink(graph, operationSet->getOutputSocket(), operation->getInputSocket(1)); outputSocketImage->relinkConnections(operation->getOutputSocket()); graph->addOperation(operation); - addPreviewOperation(graph, operation->getOutputSocket()); + addPreviewOperation(graph, context, operation->getOutputSocket()); } diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.cpp b/source/blender/compositor/nodes/COM_DilateErodeNode.cpp index cecc3bf6e86..5bd2f78d8a6 100644 --- a/source/blender/compositor/nodes/COM_DilateErodeNode.cpp +++ b/source/blender/compositor/nodes/COM_DilateErodeNode.cpp @@ -106,7 +106,7 @@ void DilateErodeNode::convertToOperations(ExecutionSystem *graph, CompositorCont graph->addOperation(operationy); addLink(graph, operationx->getOutputSocket(), operationy->getInputSocket(0)); // addLink(graph, operationx->getInputSocket(1)->getConnection()->getFromSocket(), operationy->getInputSocket(1)); // no size input yet - addPreviewOperation(graph, operationy->getOutputSocket()); + addPreviewOperation(graph, context, operationy->getOutputSocket()); /* TODO? */ /* see gaussian blue node for original usage */ diff --git a/source/blender/compositor/nodes/COM_DistanceMatteNode.cpp b/source/blender/compositor/nodes/COM_DistanceMatteNode.cpp index 87e7b9d0788..782c897f6fb 100644 --- a/source/blender/compositor/nodes/COM_DistanceMatteNode.cpp +++ b/source/blender/compositor/nodes/COM_DistanceMatteNode.cpp @@ -77,7 +77,7 @@ void DistanceMatteNode::convertToOperations(ExecutionSystem *graph, CompositorCo addLink(graph, operation->getOutputSocket(), operationAlpha->getInputSocket(1)); graph->addOperation(operationAlpha); - addPreviewOperation(graph, operationAlpha->getOutputSocket()); + addPreviewOperation(graph, context, operationAlpha->getOutputSocket()); if (outputSocketImage->isConnected()) { outputSocketImage->relinkConnections(operationAlpha->getOutputSocket()); diff --git a/source/blender/compositor/nodes/COM_FilterNode.cpp b/source/blender/compositor/nodes/COM_FilterNode.cpp index 021ec6c2508..990cdb480ca 100644 --- a/source/blender/compositor/nodes/COM_FilterNode.cpp +++ b/source/blender/compositor/nodes/COM_FilterNode.cpp @@ -77,7 +77,7 @@ void FilterNode::convertToOperations(ExecutionSystem *graph, CompositorContext * inputImageSocket->relinkConnections(operation->getInputSocket(0), 1, graph); inputSocket->relinkConnections(operation->getInputSocket(1), 0, graph); outputSocket->relinkConnections(operation->getOutputSocket()); - addPreviewOperation(graph, operation->getOutputSocket(0)); + addPreviewOperation(graph, context, operation->getOutputSocket(0)); graph->addOperation(operation); } diff --git a/source/blender/compositor/nodes/COM_ImageNode.cpp b/source/blender/compositor/nodes/COM_ImageNode.cpp index 2d13ffb82b6..4ebd28d710a 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.cpp +++ b/source/blender/compositor/nodes/COM_ImageNode.cpp @@ -111,7 +111,7 @@ void ImageNode::convertToOperations(ExecutionSystem *graph, CompositorContext *c break; } if (index == 0 && operation) { - addPreviewOperation(graph, operation->getOutputSocket()); + addPreviewOperation(graph, context, operation->getOutputSocket()); } } } @@ -134,7 +134,7 @@ void ImageNode::convertToOperations(ExecutionSystem *graph, CompositorContext *c operation->setImageUser(imageuser); operation->setFramenumber(framenumber); graph->addOperation(operation); - addPreviewOperation(graph, operation->getOutputSocket()); + addPreviewOperation(graph, context, operation->getOutputSocket()); } if (numberOfOutputs > 1) { diff --git a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cpp b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cpp index be949e2cacb..4656e00b006 100644 --- a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cpp +++ b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cpp @@ -55,7 +55,7 @@ void LuminanceMatteNode::convertToOperations(ExecutionSystem *graph, CompositorC addLink(graph, rgbToYUV->getInputSocket(0)->getConnection()->getFromSocket(), operation->getInputSocket(0)); addLink(graph, operationSet->getOutputSocket(), operation->getInputSocket(1)); graph->addOperation(operation); - addPreviewOperation(graph, operation->getOutputSocket()); + addPreviewOperation(graph, context, operation->getOutputSocket()); if (outputSocketImage->isConnected()) { outputSocketImage->relinkConnections(operation->getOutputSocket()); diff --git a/source/blender/compositor/nodes/COM_MixNode.cpp b/source/blender/compositor/nodes/COM_MixNode.cpp index 7aeaaf56c98..ab4e464327d 100644 --- a/source/blender/compositor/nodes/COM_MixNode.cpp +++ b/source/blender/compositor/nodes/COM_MixNode.cpp @@ -128,7 +128,7 @@ void MixNode::convertToOperations(ExecutionSystem *graph, CompositorContext *con color1Socket->relinkConnections(convertProg->getInputSocket(1), 1, graph); color2Socket->relinkConnections(convertProg->getInputSocket(2), 2, graph); outputSocket->relinkConnections(convertProg->getOutputSocket(0)); - addPreviewOperation(graph, convertProg->getOutputSocket(0)); + addPreviewOperation(graph, context, convertProg->getOutputSocket(0)); convertProg->getInputSocket(2)->setResizeMode(color2Socket->getResizeMode()); diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cpp b/source/blender/compositor/nodes/COM_MovieClipNode.cpp index 6d5b9bba8aa..5d5e68fba37 100644 --- a/source/blender/compositor/nodes/COM_MovieClipNode.cpp +++ b/source/blender/compositor/nodes/COM_MovieClipNode.cpp @@ -62,25 +62,9 @@ void MovieClipNode::convertToOperations(ExecutionSystem *graph, CompositorContex // always connect the output image MovieClipOperation *operation = new MovieClipOperation(); - if (ibuf && context->isColorManaged() && ibuf->profile == IB_PROFILE_NONE) { - ConvertColorProfileOperation *converter = new ConvertColorProfileOperation(); - converter->setFromColorProfile(IB_PROFILE_LINEAR_RGB); - converter->setToColorProfile(IB_PROFILE_SRGB); - addLink(graph, operation->getOutputSocket(), converter->getInputSocket(0)); - addPreviewOperation(graph, converter->getOutputSocket()); - if (outputMovieClip->isConnected()) { - outputMovieClip->relinkConnections(converter->getOutputSocket()); - } - graph->addOperation(converter); - if (ibuf) { - converter->setPredivided(ibuf->flags & IB_cm_predivide); - } - } - else { - addPreviewOperation(graph, operation->getOutputSocket()); - if (outputMovieClip->isConnected()) { - outputMovieClip->relinkConnections(operation->getOutputSocket()); - } + addPreviewOperation(graph, context, operation->getOutputSocket()); + if (outputMovieClip->isConnected()) { + outputMovieClip->relinkConnections(operation->getOutputSocket()); } operation->setMovieClip(movieClip); diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cpp b/source/blender/compositor/nodes/COM_OutputFileNode.cpp index b8cee29fee6..921b9e63a73 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cpp +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cpp @@ -62,7 +62,7 @@ void OutputFileNode::convertToOperations(ExecutionSystem *graph, CompositorConte input->relinkConnections(outputOperation->getInputSocket(i)); } } - if (hasConnections) addPreviewOperation(graph, outputOperation->getInputSocket(0)); + if (hasConnections) addPreviewOperation(graph, context, outputOperation->getInputSocket(0)); graph->addOperation(outputOperation); } @@ -80,11 +80,12 @@ void OutputFileNode::convertToOperations(ExecutionSystem *graph, CompositorConte BLI_join_dirfile(path, FILE_MAX, storage->base_path, sockdata->path); OutputSingleLayerOperation *outputOperation = new OutputSingleLayerOperation( - context->getRenderData(), context->getbNodeTree(), input->getDataType(), format, path); + context->getRenderData(), context->getbNodeTree(), input->getDataType(), format, path, + context->getViewSettings(), context->getDisplaySettings()); input->relinkConnections(outputOperation->getInputSocket(0)); graph->addOperation(outputOperation); if (!previewAdded) { - addPreviewOperation(graph, outputOperation->getInputSocket(0)); + addPreviewOperation(graph, context, outputOperation->getInputSocket(0)); previewAdded = true; } } diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cpp b/source/blender/compositor/nodes/COM_RenderLayersNode.cpp index dfb46af841c..b57e99754d6 100644 --- a/source/blender/compositor/nodes/COM_RenderLayersNode.cpp +++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cpp @@ -52,7 +52,7 @@ RenderLayersNode::RenderLayersNode(bNode *editorNode) : Node(editorNode) /* pass */ } -void RenderLayersNode::testSocketConnection(ExecutionSystem *system, int outputSocketNumber, RenderLayersBaseProg *operation) +void RenderLayersNode::testSocketConnection(ExecutionSystem *system, CompositorContext *context, int outputSocketNumber, RenderLayersBaseProg *operation) { OutputSocket *outputSocket = this->getOutputSocket(outputSocketNumber); Scene *scene = (Scene *)this->getbNode()->id; @@ -64,7 +64,7 @@ void RenderLayersNode::testSocketConnection(ExecutionSystem *system, int outputS outputSocket->relinkConnections(operation->getOutputSocket()); system->addOperation(operation); if (outputSocketNumber == 0) { // only do for image socket if connected - addPreviewOperation(system, operation->getOutputSocket()); + addPreviewOperation(system, context, operation->getOutputSocket()); } } else { @@ -72,7 +72,7 @@ void RenderLayersNode::testSocketConnection(ExecutionSystem *system, int outputS system->addOperation(operation); operation->setScene(scene); operation->setLayerId(layerId); - addPreviewOperation(system, operation->getOutputSocket()); + addPreviewOperation(system, context, operation->getOutputSocket()); } else { delete operation; @@ -82,34 +82,34 @@ void RenderLayersNode::testSocketConnection(ExecutionSystem *system, int outputS void RenderLayersNode::convertToOperations(ExecutionSystem *graph, CompositorContext *context) { - testSocketConnection(graph, 0, new RenderLayersColorProg()); - testSocketConnection(graph, 1, new RenderLayersAlphaProg()); - testSocketConnection(graph, 2, new RenderLayersDepthProg()); - testSocketConnection(graph, 3, new RenderLayersNormalOperation()); - testSocketConnection(graph, 4, new RenderLayersUVOperation()); - testSocketConnection(graph, 5, new RenderLayersSpeedOperation()); - testSocketConnection(graph, 6, new RenderLayersColorOperation()); - testSocketConnection(graph, 7, new RenderLayersDiffuseOperation()); - testSocketConnection(graph, 8, new RenderLayersSpecularOperation()); - testSocketConnection(graph, 9, new RenderLayersShadowOperation()); - testSocketConnection(graph, 10, new RenderLayersAOOperation()); - testSocketConnection(graph, 11, new RenderLayersReflectionOperation()); - testSocketConnection(graph, 12, new RenderLayersRefractionOperation()); - testSocketConnection(graph, 13, new RenderLayersIndirectOperation()); - testSocketConnection(graph, 14, new RenderLayersObjectIndexOperation()); - testSocketConnection(graph, 15, new RenderLayersMaterialIndexOperation()); - testSocketConnection(graph, 16, new RenderLayersMistOperation()); - testSocketConnection(graph, 17, new RenderLayersEmitOperation()); - testSocketConnection(graph, 18, new RenderLayersEnvironmentOperation()); + testSocketConnection(graph, context, 0, new RenderLayersColorProg()); + testSocketConnection(graph, context, 1, new RenderLayersAlphaProg()); + testSocketConnection(graph, context, 2, new RenderLayersDepthProg()); + testSocketConnection(graph, context, 3, new RenderLayersNormalOperation()); + testSocketConnection(graph, context, 4, new RenderLayersUVOperation()); + testSocketConnection(graph, context, 5, new RenderLayersSpeedOperation()); + testSocketConnection(graph, context, 6, new RenderLayersColorOperation()); + testSocketConnection(graph, context, 7, new RenderLayersDiffuseOperation()); + testSocketConnection(graph, context, 8, new RenderLayersSpecularOperation()); + testSocketConnection(graph, context, 9, new RenderLayersShadowOperation()); + testSocketConnection(graph, context, 10, new RenderLayersAOOperation()); + testSocketConnection(graph, context, 11, new RenderLayersReflectionOperation()); + testSocketConnection(graph, context, 12, new RenderLayersRefractionOperation()); + testSocketConnection(graph, context, 13, new RenderLayersIndirectOperation()); + testSocketConnection(graph, context, 14, new RenderLayersObjectIndexOperation()); + testSocketConnection(graph, context, 15, new RenderLayersMaterialIndexOperation()); + testSocketConnection(graph, context, 16, new RenderLayersMistOperation()); + testSocketConnection(graph, context, 17, new RenderLayersEmitOperation()); + testSocketConnection(graph, context, 18, new RenderLayersEnvironmentOperation()); // cycles passes - testSocketConnection(graph, 19, new RenderLayersCyclesOperation(SCE_PASS_DIFFUSE_DIRECT)); - testSocketConnection(graph, 20, new RenderLayersCyclesOperation(SCE_PASS_DIFFUSE_INDIRECT)); - testSocketConnection(graph, 21, new RenderLayersCyclesOperation(SCE_PASS_DIFFUSE_COLOR)); - testSocketConnection(graph, 22, new RenderLayersCyclesOperation(SCE_PASS_GLOSSY_DIRECT)); - testSocketConnection(graph, 23, new RenderLayersCyclesOperation(SCE_PASS_GLOSSY_INDIRECT)); - testSocketConnection(graph, 24, new RenderLayersCyclesOperation(SCE_PASS_GLOSSY_COLOR)); - testSocketConnection(graph, 25, new RenderLayersCyclesOperation(SCE_PASS_TRANSM_DIRECT)); - testSocketConnection(graph, 26, new RenderLayersCyclesOperation(SCE_PASS_TRANSM_INDIRECT)); - testSocketConnection(graph, 27, new RenderLayersCyclesOperation(SCE_PASS_TRANSM_COLOR)); + testSocketConnection(graph, context, 19, new RenderLayersCyclesOperation(SCE_PASS_DIFFUSE_DIRECT)); + testSocketConnection(graph, context, 20, new RenderLayersCyclesOperation(SCE_PASS_DIFFUSE_INDIRECT)); + testSocketConnection(graph, context, 21, new RenderLayersCyclesOperation(SCE_PASS_DIFFUSE_COLOR)); + testSocketConnection(graph, context, 22, new RenderLayersCyclesOperation(SCE_PASS_GLOSSY_DIRECT)); + testSocketConnection(graph, context, 23, new RenderLayersCyclesOperation(SCE_PASS_GLOSSY_INDIRECT)); + testSocketConnection(graph, context, 24, new RenderLayersCyclesOperation(SCE_PASS_GLOSSY_COLOR)); + testSocketConnection(graph, context, 25, new RenderLayersCyclesOperation(SCE_PASS_TRANSM_DIRECT)); + testSocketConnection(graph, context, 26, new RenderLayersCyclesOperation(SCE_PASS_TRANSM_INDIRECT)); + testSocketConnection(graph, context, 27, new RenderLayersCyclesOperation(SCE_PASS_TRANSM_COLOR)); } diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.h b/source/blender/compositor/nodes/COM_RenderLayersNode.h index 32672c8ba2d..9bd74624b0c 100644 --- a/source/blender/compositor/nodes/COM_RenderLayersNode.h +++ b/source/blender/compositor/nodes/COM_RenderLayersNode.h @@ -33,5 +33,5 @@ public: RenderLayersNode(bNode *editorNode); void convertToOperations(ExecutionSystem *graph, CompositorContext *context); private: - void testSocketConnection(ExecutionSystem *graph, int outputSocketNumber, RenderLayersBaseProg *operation); + void testSocketConnection(ExecutionSystem *graph, CompositorContext *context, int outputSocketNumber, RenderLayersBaseProg *operation); }; diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.cpp b/source/blender/compositor/nodes/COM_SplitViewerNode.cpp index 388466cee3c..37b888becca 100644 --- a/source/blender/compositor/nodes/COM_SplitViewerNode.cpp +++ b/source/blender/compositor/nodes/COM_SplitViewerNode.cpp @@ -39,13 +39,14 @@ void SplitViewerNode::convertToOperations(ExecutionSystem *graph, CompositorCont ImageUser *imageUser = (ImageUser *) this->getbNode()->storage; if (image1Socket->isConnected() && image2Socket->isConnected()) { SplitViewerOperation *splitViewerOperation = new SplitViewerOperation(); - splitViewerOperation->setColorManagement(context->getRenderData()->color_mgt_flag & R_COLOR_MANAGEMENT); - splitViewerOperation->setColorPredivide(context->getRenderData()->color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE); splitViewerOperation->setImage(image); splitViewerOperation->setImageUser(imageUser); splitViewerOperation->setActive((this->getbNode()->flag & NODE_DO_OUTPUT) && this->isInActiveGroup()); splitViewerOperation->setSplitPercentage(this->getbNode()->custom1); + splitViewerOperation->setViewSettings(context->getViewSettings()); + splitViewerOperation->setDisplaySettings(context->getDisplaySettings()); + /* defaults - the viewer node has these options but not exposed for split view * we could use the split to define an area of interest on one axis at least */ splitViewerOperation->setChunkOrder(COM_ORDER_OF_CHUNKS_DEFAULT); @@ -55,7 +56,7 @@ void SplitViewerNode::convertToOperations(ExecutionSystem *graph, CompositorCont splitViewerOperation->setXSplit(!this->getbNode()->custom2); image1Socket->relinkConnections(splitViewerOperation->getInputSocket(0), 0, graph); image2Socket->relinkConnections(splitViewerOperation->getInputSocket(1), 1, graph); - addPreviewOperation(graph, splitViewerOperation->getInputSocket(0)); + addPreviewOperation(graph, context, splitViewerOperation->getInputSocket(0)); graph->addOperation(splitViewerOperation); } } diff --git a/source/blender/compositor/nodes/COM_TextureNode.cpp b/source/blender/compositor/nodes/COM_TextureNode.cpp index b035e0a392c..5b386a504e4 100644 --- a/source/blender/compositor/nodes/COM_TextureNode.cpp +++ b/source/blender/compositor/nodes/COM_TextureNode.cpp @@ -40,7 +40,7 @@ void TextureNode::convertToOperations(ExecutionSystem *system, CompositorContext operation->setTexture(texture); operation->setRenderData(context->getRenderData()); system->addOperation(operation); - addPreviewOperation(system, operation->getOutputSocket()); + addPreviewOperation(system, context, operation->getOutputSocket()); if (this->getOutputSocket(0)->isConnected()) { TextureAlphaOperation *alphaOperation = new TextureAlphaOperation(); diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cpp b/source/blender/compositor/nodes/COM_ViewerNode.cpp index 88ce0ff2016..d67e9e274b0 100644 --- a/source/blender/compositor/nodes/COM_ViewerNode.cpp +++ b/source/blender/compositor/nodes/COM_ViewerNode.cpp @@ -40,8 +40,6 @@ void ViewerNode::convertToOperations(ExecutionSystem *graph, CompositorContext * ImageUser *imageUser = (ImageUser *) this->getbNode()->storage; bNode *editorNode = this->getbNode(); ViewerOperation *viewerOperation = new ViewerOperation(); - viewerOperation->setColorManagement(context->getRenderData()->color_mgt_flag & R_COLOR_MANAGEMENT); - viewerOperation->setColorPredivide(context->getRenderData()->color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE); viewerOperation->setbNodeTree(context->getbNodeTree()); viewerOperation->setImage(image); viewerOperation->setImageUser(imageUser); @@ -50,6 +48,9 @@ void ViewerNode::convertToOperations(ExecutionSystem *graph, CompositorContext * viewerOperation->setCenterX(editorNode->custom3); viewerOperation->setCenterY(editorNode->custom4); + viewerOperation->setViewSettings(context->getViewSettings()); + viewerOperation->setDisplaySettings(context->getDisplaySettings()); + viewerOperation->setResolutionInputSocketIndex(0); if (!imageSocket->isConnected()) { @@ -62,5 +63,5 @@ void ViewerNode::convertToOperations(ExecutionSystem *graph, CompositorContext * alphaSocket->relinkConnections(viewerOperation->getInputSocket(1)); depthSocket->relinkConnections(viewerOperation->getInputSocket(2)); graph->addOperation(viewerOperation); - addPreviewOperation(graph, viewerOperation->getInputSocket(0)); + addPreviewOperation(graph, context, viewerOperation->getInputSocket(0)); } diff --git a/source/blender/compositor/operations/COM_ImageOperation.cpp b/source/blender/compositor/operations/COM_ImageOperation.cpp index 4ec5643c0bb..fb3efbb67ed 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cpp +++ b/source/blender/compositor/operations/COM_ImageOperation.cpp @@ -33,6 +33,7 @@ extern "C" { #include "RE_render_ext.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" + #include "IMB_colormanagement.h" } BaseImageOperation::BaseImageOperation() : NodeOperation() diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cpp b/source/blender/compositor/operations/COM_OutputFileOperation.cpp index b72875ab2f9..21ecfdb5272 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cpp +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cpp @@ -31,9 +31,12 @@ #include "BKE_global.h" #include "BKE_main.h" +#include "DNA_color_types.h" + extern "C" { #include "MEM_guardedalloc.h" #include "IMB_imbuf.h" + #include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" } @@ -91,7 +94,8 @@ static void write_buffer_rect(rcti *rect, const bNodeTree *tree, OutputSingleLayerOperation::OutputSingleLayerOperation( - const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path) + const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings) { this->m_rd = rd; this->m_tree = tree; @@ -104,6 +108,9 @@ OutputSingleLayerOperation::OutputSingleLayerOperation( this->m_format = format; BLI_strncpy(this->m_path, path, sizeof(this->m_path)); + + this->m_viewSettings = viewSettings; + this->m_displaySettings = displaySettings; } void OutputSingleLayerOperation::initExecution() @@ -131,8 +138,7 @@ void OutputSingleLayerOperation::deinitExecution() ibuf->mall |= IB_rectfloat; ibuf->dither = this->m_rd->dither_intensity; - if (this->m_rd->color_mgt_flag & R_COLOR_MANAGEMENT) - ibuf->profile = IB_PROFILE_LINEAR_RGB; + IMB_display_buffer_to_imbuf_rect(ibuf, m_viewSettings, m_displaySettings); BKE_makepicstring(filename, this->m_path, bmain->name, this->m_rd->cfra, this->m_format->imtype, (this->m_rd->scemode & R_EXTENSION), true); diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index 532242c97ce..69d1ad48ced 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -28,6 +28,8 @@ #include "BLI_rect.h" #include "BLI_path_util.h" +#include "DNA_color_types.h" + #include "intern/openexr/openexr_multi.h" /* Writes the image to a single-layer file. */ @@ -43,8 +45,11 @@ private: DataType m_datatype; SocketReader *m_imageInput; + const ColorManagedViewSettings *m_viewSettings; + const ColorManagedDisplaySettings *m_displaySettings; public: - OutputSingleLayerOperation(const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path); + OutputSingleLayerOperation(const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings); void executeRegion(rcti *rect, unsigned int tileNumber); bool isOutputOperation(bool rendering) const { return true; } diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cpp b/source/blender/compositor/operations/COM_PreviewOperation.cpp index aff374cdded..6e58b277f66 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.cpp +++ b/source/blender/compositor/operations/COM_PreviewOperation.cpp @@ -35,16 +35,19 @@ extern "C" { #include "MEM_guardedalloc.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" + #include "IMB_colormanagement.h" } -PreviewOperation::PreviewOperation() : NodeOperation() +PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings) : NodeOperation() { this->addInputSocket(COM_DT_COLOR, COM_SC_NO_RESIZE); this->m_outputBuffer = NULL; this->m_input = NULL; this->m_divider = 1.0f; this->m_node = NULL; + this->m_viewSettings = viewSettings; + this->m_displaySettings = displaySettings; } void PreviewOperation::initExecution() @@ -82,6 +85,10 @@ void PreviewOperation::executeRegion(rcti *rect, unsigned int tileNumber) { int offset; float color[4]; + struct ColormanageProcessor *cm_processor; + + cm_processor = IMB_colormanagement_display_processor_new(this->m_viewSettings, this->m_displaySettings); + for (int y = rect->ymin; y < rect->ymax; y++) { offset = (y * getWidth() + rect->xmin) * 4; for (int x = rect->xmin; x < rect->xmax; x++) { @@ -93,11 +100,13 @@ void PreviewOperation::executeRegion(rcti *rect, unsigned int tileNumber) color[2] = 0.0f; color[3] = 1.0f; this->m_input->read(color, rx, ry, COM_PS_NEAREST); - linearrgb_to_srgb_v4(color, color); + IMB_colormanagement_processor_apply_v4(cm_processor, color); F4TOCHAR4(color, this->m_outputBuffer + offset); offset += 4; } } + + IMB_colormanagement_processor_free(cm_processor); } bool PreviewOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) { diff --git a/source/blender/compositor/operations/COM_PreviewOperation.h b/source/blender/compositor/operations/COM_PreviewOperation.h index ffd80ff27af..9e774d0e41b 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.h +++ b/source/blender/compositor/operations/COM_PreviewOperation.h @@ -24,6 +24,7 @@ #define _COM_PreviewOperation_h #include "COM_NodeOperation.h" #include "DNA_image_types.h" +#include "DNA_color_types.h" #include "BLI_rect.h" class PreviewOperation : public NodeOperation { @@ -37,8 +38,10 @@ protected: SocketReader *m_input; float m_divider; + const ColorManagedViewSettings *m_viewSettings; + const ColorManagedDisplaySettings *m_displaySettings; public: - PreviewOperation(); + PreviewOperation(const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings); bool isOutputOperation(bool rendering) const { return true; } void initExecution(); void deinitExecution(); diff --git a/source/blender/compositor/operations/COM_SplitViewerOperation.cpp b/source/blender/compositor/operations/COM_SplitViewerOperation.cpp index d59d1f9f10d..7325e32a863 100644 --- a/source/blender/compositor/operations/COM_SplitViewerOperation.cpp +++ b/source/blender/compositor/operations/COM_SplitViewerOperation.cpp @@ -62,7 +62,6 @@ void SplitViewerOperation::deinitExecution() void SplitViewerOperation::executeRegion(rcti *rect, unsigned int tileNumber) { float *buffer = this->m_outputBuffer; - unsigned char *bufferDisplay = this->m_outputBufferDisplay; if (!buffer) return; int x1 = rect->xmin; @@ -76,7 +75,6 @@ void SplitViewerOperation::executeRegion(rcti *rect, unsigned int tileNumber) for (y = y1; y < y2; y++) { for (x = x1; x < x2; x++) { bool image1; - float srgb[4]; image1 = this->m_xSplit ? x > perc : y > perc; if (image1) { this->m_image1Input->read(&(buffer[offset]), x, y, COM_PS_NEAREST); @@ -84,21 +82,7 @@ void SplitViewerOperation::executeRegion(rcti *rect, unsigned int tileNumber) else { this->m_image2Input->read(&(buffer[offset]), x, y, COM_PS_NEAREST); } - /// @todo: linear conversion only when scene color management is selected, also check predivide. - if (this->m_doColorManagement) { - if (this->m_doColorPredivide) { - linearrgb_to_srgb_predivide_v4(srgb, buffer + offset); - } - else { - linearrgb_to_srgb_v4(srgb, buffer + offset); - } - } - else { - copy_v4_v4(srgb, buffer + offset); - } - - rgba_float_to_uchar(bufferDisplay + offset, srgb); - + offset += 4; } offset += (this->getWidth() - (x2 - x1)) * 4; diff --git a/source/blender/compositor/operations/COM_ViewerBaseOperation.cpp b/source/blender/compositor/operations/COM_ViewerBaseOperation.cpp index f443c33cd54..d9ca131721f 100644 --- a/source/blender/compositor/operations/COM_ViewerBaseOperation.cpp +++ b/source/blender/compositor/operations/COM_ViewerBaseOperation.cpp @@ -34,6 +34,7 @@ extern "C" { #include "MEM_guardedalloc.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" + #include "IMB_colormanagement.h" } @@ -43,10 +44,10 @@ ViewerBaseOperation::ViewerBaseOperation() : NodeOperation() this->setImageUser(NULL); this->m_outputBuffer = NULL; this->m_depthBuffer = NULL; - this->m_outputBufferDisplay = NULL; this->m_active = false; - this->m_doColorManagement = true; this->m_doDepthBuffer = false; + this->m_viewSettings = NULL; + this->m_displaySettings = NULL; } void ViewerBaseOperation::initExecution() @@ -60,7 +61,7 @@ void ViewerBaseOperation::initImage() { Image *anImage = this->m_image; ImBuf *ibuf = BKE_image_acquire_ibuf(anImage, this->m_imageUser, &this->m_lock); - + if (!ibuf) return; BLI_lock_thread(LOCK_DRAW_IMAGE); if (ibuf->x != (int)getWidth() || ibuf->y != (int)getHeight()) { @@ -70,30 +71,44 @@ void ViewerBaseOperation::initImage() IMB_freezbuffloatImBuf(ibuf); ibuf->x = getWidth(); ibuf->y = getHeight(); - imb_addrectImBuf(ibuf); imb_addrectfloatImBuf(ibuf); anImage->ok = IMA_OK_LOADED; + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + + BLI_unlock_thread(LOCK_DRAW_IMAGE); } + if (m_doDepthBuffer) { addzbuffloatImBuf(ibuf); } BLI_unlock_thread(LOCK_DRAW_IMAGE); - - + /* now we combine the input with ibuf */ this->m_outputBuffer = ibuf->rect_float; - this->m_outputBufferDisplay = (unsigned char *)ibuf->rect; + + /* needed for display buffer update + * + * no need to lock / reference the image buffer because it's seems + * to be the single place which changes buffers of viewer image + * which is this node + */ + this->m_ibuf = ibuf; + if (m_doDepthBuffer) { this->m_depthBuffer = ibuf->zbuf_float; } - + BKE_image_release_ibuf(this->m_image, this->m_lock); } void ViewerBaseOperation:: updateImage(rcti *rect) { + IMB_partial_display_buffer_update(this->m_ibuf, this->m_outputBuffer, NULL, getWidth(), 0, 0, + this->m_viewSettings, this->m_displaySettings, + rect->xmin, rect->ymin, rect->xmax, rect->ymax); + WM_main_add_notifier(NC_WINDOW | ND_DRAW, NULL); } diff --git a/source/blender/compositor/operations/COM_ViewerBaseOperation.h b/source/blender/compositor/operations/COM_ViewerBaseOperation.h index d90eb343f6c..f7d479eb3b8 100644 --- a/source/blender/compositor/operations/COM_ViewerBaseOperation.h +++ b/source/blender/compositor/operations/COM_ViewerBaseOperation.h @@ -30,7 +30,6 @@ class ViewerBaseOperation : public NodeOperation { protected: float *m_outputBuffer; float *m_depthBuffer; - unsigned char *m_outputBufferDisplay; Image *m_image; ImageUser *m_imageUser; void *m_lock; @@ -38,9 +37,11 @@ protected: float m_centerX; float m_centerY; OrderOfChunks m_chunkOrder; - bool m_doColorManagement; - bool m_doColorPredivide; bool m_doDepthBuffer; + ImBuf *m_ibuf; + + const ColorManagedViewSettings *m_viewSettings; + const ColorManagedDisplaySettings *m_displaySettings; public: bool isOutputOperation(bool rendering) const { return isActiveViewerOutput(); } @@ -57,10 +58,10 @@ public: float getCenterY() { return this->m_centerY; } OrderOfChunks getChunkOrder() { return this->m_chunkOrder; } const CompositorPriority getRenderPriority() const; - void setColorManagement(bool doColorManagement) { this->m_doColorManagement = doColorManagement; } - void setColorPredivide(bool doColorPredivide) { this->m_doColorPredivide = doColorPredivide; } bool isViewerOperation() { return true; } - + + void setViewSettings(const ColorManagedViewSettings *viewSettings) { this->m_viewSettings = viewSettings; } + void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings) { this->m_displaySettings = displaySettings; } protected: ViewerBaseOperation(); void updateImage(rcti *rect); diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cpp b/source/blender/compositor/operations/COM_ViewerOperation.cpp index b85b86bddc3..576d5da4d74 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cpp +++ b/source/blender/compositor/operations/COM_ViewerOperation.cpp @@ -72,7 +72,6 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int tileNumber) { float *buffer = this->m_outputBuffer; float *depthbuffer = this->m_depthBuffer; - unsigned char *bufferDisplay = this->m_outputBufferDisplay; if (!buffer) return; const int x1 = rect->xmin; const int y1 = rect->ymin; @@ -82,7 +81,7 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int tileNumber) const int offsetadd4 = offsetadd * 4; int offset = (y1 * this->getWidth() + x1); int offset4 = offset * 4; - float alpha[4], srgb[4], depth[4]; + float alpha[4], depth[4]; int x; int y; bool breaked = false; @@ -98,19 +97,6 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int tileNumber) this->m_depthInput->read(depth, x, y, COM_PS_NEAREST); depthbuffer[offset] = depth[0]; } - if (this->m_doColorManagement) { - if (this->m_doColorPredivide) { - linearrgb_to_srgb_predivide_v4(srgb, buffer + offset4); - } - else { - linearrgb_to_srgb_v4(srgb, buffer + offset4); - } - } - else { - copy_v4_v4(srgb, buffer + offset4); - } - - rgba_float_to_uchar(bufferDisplay + offset4, srgb); offset ++; offset4 += 4; diff --git a/source/blender/editors/include/ED_clip.h b/source/blender/editors/include/ED_clip.h index 13c3b180230..28727c913cb 100644 --- a/source/blender/editors/include/ED_clip.h +++ b/source/blender/editors/include/ED_clip.h @@ -83,7 +83,7 @@ void ED_space_clip_set_mask(struct bContext *C, struct SpaceClip *sc, struct Mas /* textures buffer */ int ED_space_clip_texture_buffer_supported(struct SpaceClip *sc); -int ED_space_clip_load_movieclip_buffer(struct SpaceClip *sc, struct ImBuf *ibuf); +int ED_space_clip_load_movieclip_buffer(struct SpaceClip *sc, struct ImBuf *ibuf, const unsigned char *display_buffer); void ED_space_clip_unload_movieclip_buffer(struct SpaceClip *sc); void ED_space_clip_free_texture_buffer(struct SpaceClip *sc); diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index 87f12b8ac9c..a50b33966c6 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -39,6 +39,7 @@ struct ToolSettings; struct uiBlock; struct wmWindowManager; struct ARegion; +struct Scene; /* image_edit.c, exported for transform */ struct Image *ED_space_image(struct SpaceImage *sima); @@ -77,7 +78,7 @@ int ED_space_image_maskedit_mask_poll(struct bContext *C); /* UI level image (texture) updating... render calls own stuff (too) */ void ED_image_update_frame(const struct Main *mainp, int cfra); -void ED_image_draw_info(struct ARegion *ar, int color_manage, int channels, int x, int y, +void ED_image_draw_info(struct Scene *scene, struct ARegion *ar, int color_manage, int use_default_view, int channels, int x, int y, const unsigned char cp[4], const float fp[4], int *zp, float *zpf); #endif /* __ED_IMAGE_H__ */ diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 7f40024a056..b1bccfa32c5 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -282,10 +282,12 @@ int ED_view3d_scene_layer_set(int lay, const int *values, int *active); int ED_view3d_context_activate(struct bContext *C); void ED_view3d_draw_offscreen(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, - int winx, int winy, float viewmat[][4], float winmat[][4], int do_bgpic); + int winx, int winy, float viewmat[][4], float winmat[][4], int do_bgpic, int colormanage_background); -struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int sizex, int sizey, unsigned int flag, int draw_background, char err_out[256]); -struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(struct Scene *scene, struct Object *camera, int width, int height, unsigned int flag, int drawtype, int draw_background, char err_out[256]); +struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int sizex, int sizey, unsigned int flag, + int draw_background, int colormanage_background, char err_out[256]); +struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(struct Scene *scene, struct Object *camera, int width, int height, unsigned int flag, int drawtype, + int draw_background, int colormanage_background, char err_out[256]); struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index c6f58fdd70b..917153c9199 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -811,7 +811,7 @@ void uiTemplateColorWheel(uiLayout *layout, struct PointerRNA *ptr, const char * void uiTemplateLayers(uiLayout *layout, struct PointerRNA *ptr, const char *propname, PointerRNA *used_ptr, const char *used_propname, int active_layer); void uiTemplateImage(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, struct PointerRNA *userptr, int compact); -void uiTemplateImageSettings(uiLayout *layout, struct PointerRNA *imfptr); +void uiTemplateImageSettings(uiLayout *layout, struct PointerRNA *imfptr, int color_management); void uiTemplateImageLayers(uiLayout *layout, struct bContext *C, struct Image *ima, struct ImageUser *iuser); void uiTemplateRunningJobs(uiLayout *layout, struct bContext *C); void uiTemplateOperatorSearch(uiLayout *layout); @@ -831,6 +831,9 @@ void uiTemplateMovieClip(struct uiLayout *layout, struct bContext *C, struct Poi void uiTemplateTrack(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname); void uiTemplateMarker(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, PointerRNA *userptr, PointerRNA *trackptr, int cmpact); +void uiTemplateColorspaceSettings(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname); +void uiTemplateColormanagedViewSettings(struct uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname); + /* items */ void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname); void uiItemEnumO_ptr(uiLayout *layout, struct wmOperatorType *ot, const char *name, int icon, const char *propname, int value); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 823f8d7be39..742aed4fbb1 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -75,6 +75,8 @@ #include "BPY_extern.h" +#include "IMB_colormanagement.h" + #include "interface_intern.h" #define MENU_WIDTH 120 @@ -2138,7 +2140,14 @@ uiBlock *uiBeginBlock(const bContext *C, ARegion *region, const char *name, shor block->evil_C = (void *)C; /* XXX */ if (scn) { - block->color_profile = (scn->r.color_mgt_flag & R_COLOR_MANAGEMENT); + block->color_profile = TRUE; + + /* store display device name, don't lookup for transformations yet + * block could be used for non-color displays where looking up for transformation + * would slow down redraw, so only lookup for actual transform when it's indeed + * needed + */ + block->display_device = scn->display_settings.display_device; /* copy to avoid crash when scene gets deleted with ui still open */ block->unit = MEM_mallocN(sizeof(scn->unit), "UI UnitSettings"); @@ -2575,6 +2584,25 @@ void ui_block_do_align(uiBlock *block) } } +struct ColorManagedDisplay *ui_block_display_get(uiBlock *block) +{ + return IMB_colormanagement_display_get_named(block->display_device); +} + +void ui_block_to_display_space_v3(uiBlock *block, float pixel[3]) +{ + struct ColorManagedDisplay *display = ui_block_display_get(block); + + IMB_colormanagement_scene_linear_to_display_v3(pixel, display); +} + +void ui_block_to_scene_linear_v3(uiBlock *block, float pixel[3]) +{ + struct ColorManagedDisplay *display = ui_block_display_get(block); + + IMB_colormanagement_display_to_scene_linear_v3(pixel, display); +} + /** * \brief ui_def_but is the function that draws many button types * diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 77dbbc878a7..f535c3c3e2a 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -48,6 +48,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_colormanagement.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -1110,10 +1111,14 @@ void ui_draw_but_COLORBAND(uiBut *but, uiWidgetColors *UNUSED(wcol), rcti *rect) float v3[2], v1[2], v2[2], v1a[2], v2a[2]; int a; float pos, colf[4] = {0, 0, 0, 0}; /* initialize in case the colorband isn't valid */ - + struct ColorManagedDisplay *display = NULL; + coba = (ColorBand *)(but->editcoba ? but->editcoba : but->poin); if (coba == NULL) return; - + + if (but->block->color_profile) + display = ui_block_display_get(but->block); + x1 = rect->xmin; y1 = rect->ymin; sizex = rect->xmax - x1; @@ -1146,8 +1151,8 @@ void ui_draw_but_COLORBAND(uiBut *but, uiWidgetColors *UNUSED(wcol), rcti *rect) for (a = 1; a <= sizex; a++) { pos = ((float)a) / (sizex - 1); do_colorband(coba, pos, colf); - if (but->block->color_profile != BLI_PR_NONE) - linearrgb_to_srgb_v3_v3(colf, colf); + if (display) + IMB_colormanagement_scene_linear_to_display_v3(colf, display); v1[0] = v2[0] = x1 + a; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index b8a84e1638a..f7b22098835 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3162,13 +3162,15 @@ static int ui_numedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, int mx, if (but->rnaprop) { if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) - color_profile = BLI_PR_NONE; + color_profile = FALSE; } ui_get_but_vectorf(but, rgb); - rgb_to_hsv_compat_v(rgb, hsv); + if (color_profile && (int)but->a1) + ui_block_to_display_space_v3(but->block, rgb); + rgb_to_hsv_compat_v(rgb, hsv); /* relative position within box */ x = ((float)mx_fl - but->rect.xmin) / BLI_RCT_SIZE_X(&but->rect); @@ -3204,17 +3206,16 @@ static int ui_numedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, int mx, /* exception only for value strip - use the range set in but->min/max */ hsv[2] = y * (but->softmax - but->softmin) + but->softmin; - if (color_profile) - hsv[2] = srgb_to_linearrgb(hsv[2]); - - if (hsv[2] > but->softmax) - hsv[2] = but->softmax; break; default: assert(!"invalid hsv type"); } hsv_to_rgb_v(hsv, rgb); + + if (color_profile && (int)but->a1) + ui_block_to_scene_linear_v3(but->block, rgb); + copy_v3_v3(data->vec, rgb); data->draglastx = mx; @@ -3233,10 +3234,14 @@ static void ui_ndofedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, wmNDOF if (but->rnaprop) { if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) - color_profile = BLI_PR_NONE; + color_profile = FALSE; } ui_get_but_vectorf(but, rgb); + + if (color_profile && (int)but->a1) + ui_block_to_display_space_v3(but->block, rgb); + rgb_to_hsv_compat_v(rgb, hsv); switch ((int)but->a1) { @@ -3267,15 +3272,16 @@ static void ui_ndofedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, wmNDOF /* exception only for value strip - use the range set in but->min/max */ hsv[2] += ndof->rx * sensitivity; - if (color_profile) - hsv[2] = srgb_to_linearrgb(hsv[2]); - CLAMP(hsv[2], but->softmin, but->softmax); default: assert(!"invalid hsv type"); } - + hsv_to_rgb_v(hsv, rgb); + + if (color_profile && (int)but->a1) + ui_block_to_scene_linear_v3(but->block, rgb); + copy_v3_v3(data->vec, rgb); ui_set_but_vectorf(but, data->vec); } diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index f6f12f98181..0921107b8e6 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -466,7 +466,7 @@ static void init_brush_icons(void) { \ bbuf = IMB_ibImageFromMemory((unsigned char *)datatoc_ ##name## _png, \ datatoc_ ##name## _png_size, \ - IB_rect, "<brush icon>"); \ + IB_rect, NULL, "<brush icon>"); \ def_internal_icon(bbuf, icon_id, 0, 0, w, ICON_TYPE_BUFFER); \ IMB_freeImBuf(bbuf); \ } (void)0 @@ -520,7 +520,7 @@ static void init_internal_icons(void) char *icondir = BLI_get_folder(BLENDER_DATAFILES, "icons"); if (icondir) { BLI_join_dirfile(iconfilestr, sizeof(iconfilestr), icondir, btheme->tui.iconfile); - bbuf = IMB_loadiffname(iconfilestr, IB_rect); /* if the image is missing bbuf will just be NULL */ + bbuf = IMB_loadiffname(iconfilestr, IB_rect, NULL); /* if the image is missing bbuf will just be NULL */ if (bbuf && (bbuf->x < ICON_IMAGE_W || bbuf->y < ICON_IMAGE_H)) { printf("\n***WARNING***\nIcons file %s too small.\nUsing built-in Icons instead\n", iconfilestr); IMB_freeImBuf(bbuf); @@ -533,7 +533,7 @@ static void init_internal_icons(void) } if (bbuf == NULL) bbuf = IMB_ibImageFromMemory((unsigned char *)datatoc_blender_icons_png, - datatoc_blender_icons_png_size, IB_rect, "<blender icons>"); + datatoc_blender_icons_png_size, IB_rect, NULL, "<blender icons>"); if (bbuf) { /* free existing texture if any */ diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 73e6427d1cb..928e1671cee 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -342,7 +342,12 @@ struct uiBlock { struct UnitSettings *unit; /* unit system, used a lot for numeric buttons so include here rather then fetching through the scene every time. */ float _hsv[3]; /* XXX, only access via ui_block_hsv_get() */ + char color_profile; /* color profile for correcting linear colors for display */ + + char *display_device; /* display devide name used to display this block, + * used by color widgets to transform colors from/to scene linear + */ }; typedef struct uiSafetyRct { @@ -392,6 +397,10 @@ extern void ui_bounds_block(uiBlock *block); extern void ui_block_translate(uiBlock *block, int x, int y); extern void ui_block_do_align(uiBlock *block); +extern struct ColorManagedDisplay *ui_block_display_get(uiBlock *block); +void ui_block_to_display_space_v3(uiBlock *block, float pixel[3]); +void ui_block_to_scene_linear_v3(uiBlock *block, float pixel[3]); + /* interface_regions.c */ struct uiPopupBlockHandle { diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index bf77e605d6e..ded5887f7f2 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -58,6 +58,8 @@ #include "UI_interface.h" +#include "IMB_colormanagement.h" + #include "interface_intern.h" #include "WM_api.h" @@ -75,7 +77,7 @@ /* ********************************************************** */ typedef struct Eyedropper { - short do_color_management; + struct ColorManagedDisplay *display; PointerRNA ptr; PropertyRNA *prop; @@ -89,8 +91,6 @@ typedef struct Eyedropper { static int eyedropper_init(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - const int color_manage = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT; - Eyedropper *eye; op->customdata = eye = MEM_callocN(sizeof(Eyedropper), "Eyedropper"); @@ -106,7 +106,12 @@ static int eyedropper_init(bContext *C, wmOperator *op) return FALSE; } - eye->do_color_management = (color_manage && RNA_property_subtype(eye->prop) == PROP_COLOR); + if (RNA_property_subtype(eye->prop) == PROP_COLOR) { + const char *display_device; + + display_device = scene->display_settings.display_device; + eye->display = IMB_colormanagement_display_get_named(display_device); + } return TRUE; } @@ -194,9 +199,10 @@ static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3 /* to maintain alpha */ RNA_property_float_get_array(&eye->ptr, eye->prop, col_conv); - /* convert from screen (srgb) space to linear rgb space */ - if (eye->do_color_management) { - srgb_to_linearrgb_v3_v3(col_conv, col); + /* convert from display space to linear rgb space */ + if (eye->display) { + copy_v3_v3(col_conv, col); + IMB_colormanagement_display_to_scene_linear_v3(col_conv, eye->display); } else { copy_v3_v3(col_conv, col); diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 59fa66b12af..dc9347b3852 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -66,6 +66,8 @@ #include "ED_screen.h" +#include "IMB_colormanagement.h" + #include "interface_intern.h" #define MENU_SEPR_HEIGHT 6 @@ -1893,11 +1895,15 @@ static void ui_update_block_buts_rgb(uiBlock *block, const float rgb[3]) { uiBut *bt; float *hsv = ui_block_hsv_get(block); + struct ColorManagedDisplay *display = NULL; /* this is to keep the H and S value when V is equal to zero * and we are working in HSV mode, of course! */ rgb_to_hsv_compat_v(rgb, hsv); + + if (block->color_profile) + display = ui_block_display_get(block); /* this updates button strings, is hackish... but button pointers are on stack of caller function */ for (bt = block->buttons.first; bt; bt = bt->next) { @@ -1913,12 +1919,11 @@ static void ui_update_block_buts_rgb(uiBlock *block, const float rgb[3]) /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */ - if (block->color_profile == BLI_PR_NONE) { - copy_v3_v3(rgb_gamma, rgb); - } - else { - /* make an sRGB version, for Hex code */ - linearrgb_to_srgb_v3_v3(rgb_gamma, rgb); + copy_v3_v3(rgb_gamma, rgb); + + if (display) { + /* make a display version, for Hex code */ + IMB_colormanagement_scene_linear_to_display_v3(rgb_gamma, display); } if (rgb_gamma[0] > 1.0f) rgb_gamma[0] = modf(rgb_gamma[0], &intpart); @@ -1997,9 +2002,9 @@ static void do_hex_rna_cb(bContext *UNUSED(C), void *bt1, void *hexcl) hex_to_rgb(hexcol, rgb, rgb + 1, rgb + 2); /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */ - if (but->block->color_profile != BLI_PR_NONE) { + if (but->block->color_profile) { /* so we need to linearise it for Blender */ - srgb_to_linearrgb_v3_v3(rgb, rgb); + ui_block_to_scene_linear_v3(but->block, rgb); } ui_update_block_buts_rgb(but->block, rgb); @@ -2107,14 +2112,16 @@ static void uiBlockPicker(uiBlock *block, float rgba[4], PointerRNA *ptr, Proper /* existence of profile means storage is in linear color space, with display correction */ /* XXX That tip message is not use anywhere! */ - if (block->color_profile == BLI_PR_NONE) { + if (!block->color_profile) { BLI_strncpy(tip, N_("Value in Display Color Space"), sizeof(tip)); copy_v3_v3(rgb_gamma, rgba); } else { BLI_strncpy(tip, N_("Value in Linear RGB Color Space"), sizeof(tip)); - /* make an sRGB version, for Hex code */ - linearrgb_to_srgb_v3_v3(rgb_gamma, rgba); + + /* make a display version, for Hex code */ + copy_v3_v3(rgb_gamma, rgba); + ui_block_to_display_space_v3(block, rgb_gamma); } /* sneaky way to check for alpha */ @@ -2239,7 +2246,7 @@ uiBlock *ui_block_func_COLOR(bContext *C, uiPopupBlockHandle *handle, void *arg_ if (but->rnaprop) { if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { - block->color_profile = BLI_PR_NONE; + block->color_profile = FALSE; } } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 6a63e2b571f..79b9d313775 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2801,3 +2801,56 @@ void uiTemplateKeymapItemProperties(uiLayout *layout, PointerRNA *ptr) } } +/********************************* Color management *************************************/ + +void uiTemplateColorspaceSettings(uiLayout *layout, PointerRNA *ptr, const char *propname) +{ + PropertyRNA *prop; + PointerRNA colorspace_settings_ptr; + + prop = RNA_struct_find_property(ptr, propname); + + if (!prop) { + printf("%s: property not found: %s.%s\n", + __func__, RNA_struct_identifier(ptr->type), propname); + return; + } + + colorspace_settings_ptr = RNA_property_pointer_get(ptr, prop); + + uiItemL(layout, "Color Space:", ICON_NONE); + uiItemR(layout, &colorspace_settings_ptr, "name", 0, "", ICON_NONE); +} + +void uiTemplateColormanagedViewSettings(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr, const char *propname) +{ + PropertyRNA *prop; + PointerRNA view_transform_ptr; + uiLayout *col, *row; + ColorManagedViewSettings *view_settings; + + prop = RNA_struct_find_property(ptr, propname); + + if (!prop) { + printf("%s: property not found: %s.%s\n", + __func__, RNA_struct_identifier(ptr->type), propname); + return; + } + + view_transform_ptr = RNA_property_pointer_get(ptr, prop); + view_settings = view_transform_ptr.data; + + col = uiLayoutColumn(layout, FALSE); + + row = uiLayoutRow(col, FALSE); + uiItemR(row, &view_transform_ptr, "view_transform", UI_ITEM_R_EXPAND, IFACE_("View"), ICON_NONE); + + col = uiLayoutColumn(layout, FALSE); + uiItemR(col, &view_transform_ptr, "exposure", 0, NULL, ICON_NONE); + uiItemR(col, &view_transform_ptr, "gamma", 0, NULL, ICON_NONE); + + col = uiLayoutColumn(layout, FALSE); + uiItemR(col, &view_transform_ptr, "use_curve_mapping", 0, NULL, ICON_NONE); + if (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) + uiTemplateCurveMapping(col, &view_transform_ptr, "curve_mapping", 'c', TRUE, 0); +} diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 026f02d1089..8a7f2d9ca47 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1874,7 +1874,7 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, rcti *rect) int color_profile = but->block->color_profile; if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) - color_profile = BLI_PR_NONE; + color_profile = FALSE; radstep = 2.0f * (float)M_PI / (float)tot; centx = BLI_RCT_CENTER_X_FL(rect); @@ -1887,17 +1887,20 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, rcti *rect) /* color */ ui_get_but_vectorf(but, rgb); - copy_v3_v3(hsv, ui_block_hsv_get(but->block)); + /* copy_v3_v3(hsv, ui_block_hsv_get(but->block)); */ /* UNUSED */ + + rgb_to_hsv_compat_v(rgb, hsvo); + + if (color_profile) + ui_block_to_display_space_v3(but->block, rgb); + rgb_to_hsv_compat_v(rgb, hsv); - copy_v3_v3(hsvo, hsv); /* exception: if 'lock' is set * lock the value of the color wheel to 1. * Useful for color correction tools where you're only interested in hue. */ if (but->flag & UI_BUT_COLOR_LOCK) hsv[2] = 1.f; - else if (color_profile) - hsv[2] = linearrgb_to_srgb(hsv[2]); hsv_to_rgb(0.f, 0.f, hsv[2], colcent, colcent + 1, colcent + 2); @@ -2139,15 +2142,16 @@ static void ui_draw_but_HSV_v(uiBut *but, rcti *rect) int color_profile = but->block->color_profile; if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) - color_profile = BLI_PR_NONE; + color_profile = FALSE; ui_get_but_vectorf(but, rgb); + + if (color_profile) + ui_block_to_display_space_v3(but->block, rgb); + rgb_to_hsv_v(rgb, hsv); v = hsv[2]; - if (color_profile) - v = linearrgb_to_srgb(v); - /* map v from property range to [0,1] */ range = but->softmax - but->softmin; v = (v - but->softmin) / range; @@ -2531,7 +2535,7 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat if (but->rnaprop) { if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) - color_profile = BLI_PR_NONE; + color_profile = FALSE; if (RNA_property_array_length(&but->rnapoin, but->rnaprop) == 4) { col[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3); @@ -2559,7 +2563,7 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat } if (color_profile) - linearrgb_to_srgb_v3_v3(col, col); + ui_block_to_display_space_v3(but->block, col); rgba_float_to_uchar((unsigned char *)wcol->inner, col); diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c index f9b73a56403..f03cb312e3e 100644 --- a/source/blender/editors/object/object_bake.c +++ b/source/blender/editors/object/object_bake.c @@ -69,6 +69,7 @@ #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" +#include "IMB_colormanagement.h" #include "GPU_draw.h" /* GPU_free_image */ @@ -685,7 +686,7 @@ static void apply_heights_data(void *bake_data) } } - ibuf->userflags = IB_RECT_INVALID; + ibuf->userflags = IB_RECT_INVALID | IB_DISPLAY_BUFFER_INVALID; } static void free_heights_data(void *bake_data) @@ -769,6 +770,8 @@ static void apply_heights_callback(DerivedMesh *lores_dm, DerivedMesh *hires_dm, char *rrgb = (char *)ibuf->rect + pixel * 4; rrgb[3] = 255; } + + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; } /* MultiresBake callback for normals' baking @@ -826,6 +829,8 @@ static void apply_tangmat_callback(DerivedMesh *lores_dm, DerivedMesh *hires_dm, rgb_float_to_uchar(rrgb, vec); rrgb[3] = 255; } + + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; } static void count_images(MultiresBakeRender *bkr) @@ -895,7 +900,7 @@ static void finish_images(MultiresBakeRender *bkr) RE_bake_ibuf_filter(ibuf, (char *)ibuf->userdata, bkr->bake_filter); - ibuf->userflags |= IB_BITMAPDIRTY; + ibuf->userflags |= IB_BITMAPDIRTY | IB_DISPLAY_BUFFER_INVALID;; if (ibuf->rect_float) ibuf->userflags |= IB_RECT_INVALID; @@ -1369,6 +1374,7 @@ static void finish_bake_internal(BakeRender *bkr) if (ima->ok == IMA_OK_LOADED) { if (ibuf) { if (ibuf->userflags & IB_BITMAPDIRTY) { + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; GPU_free_image(ima); imb_freemipmapImBuf(ibuf); } diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index b236b555850..ebacac917e8 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -61,6 +61,7 @@ #include "ED_object.h" #include "RE_pipeline.h" +#include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -78,7 +79,7 @@ void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volat { float *rectf = NULL; int ymin, ymax, xmin, xmax; - int rymin, rxmin, predivide, profile_from; + int rymin, rxmin; unsigned char *rectc; /* if renrect argument, we only refresh scanlines */ @@ -138,18 +139,9 @@ void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volat rectf += 4 * (rr->rectx * ymin + xmin); rectc = (unsigned char *)(ibuf->rect + ibuf->x * rymin + rxmin); - if (scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) { - profile_from = IB_PROFILE_LINEAR_RGB; - predivide = (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE); - } - else { - profile_from = IB_PROFILE_SRGB; - predivide = 0; - } - - IMB_buffer_byte_from_float(rectc, rectf, - 4, ibuf->dither, IB_PROFILE_SRGB, profile_from, predivide, - xmax, ymax, ibuf->x, rr->rectx); + IMB_partial_display_buffer_update(ibuf, rectf, NULL, rr->rectx, rxmin, rymin, + &scene->view_settings, &scene->display_settings, + rxmin, rymin, rxmin + xmax, rymin + ymax); } /* ****************************** render invoking ***************** */ diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 55df1caf3eb..d9618e89b68 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -63,6 +63,7 @@ #include "RE_pipeline.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" +#include "IMB_colormanagement.h" #include "RNA_access.h" #include "RNA_define.h" @@ -143,22 +144,30 @@ static void screen_opengl_render_apply(OGLRender *oglrender) ibuf = BKE_sequencer_give_ibuf(context, CFRA, chanshown); if (ibuf) { + ImBuf *linear_ibuf; + BLI_assert((oglrender->sizex == ibuf->x) && (oglrender->sizey == ibuf->y)); - if (ibuf->rect_float == NULL) { - /* internally sequencer working in sRGB space and stores both bytes and float - * buffers in sRGB space, but if byte->float onversion doesn't happen in sequencer - * (e.g. when adding image sequence/movie into sequencer) there'll be only - * byte buffer and profile will still indicate sRGB->linear space conversion is needed - * here we're ensure there'll be no conversion happen and float buffer would store - * linear frame (sergey) */ - ibuf->profile = IB_PROFILE_NONE; - IMB_float_from_rect(ibuf); + linear_ibuf = IMB_dupImBuf(ibuf); + IMB_freeImBuf(ibuf); + + if (linear_ibuf->rect_float == NULL) { + /* internally sequencer working in display space and stores both bytes and float buffers in that space. + * It is possible that byte->float onversion didn't happen in sequencer (e.g. when adding image sequence/movie + * into sequencer) there'll be only byte buffer. Create float buffer from existing byte buffer, making it linear + */ + + IMB_float_from_rect(linear_ibuf); + } + else { + /* ensure float buffer is in linear space, not in display space */ + BKE_sequencer_imbuf_from_sequencer_space(scene, linear_ibuf); } - memcpy(rr->rectf, ibuf->rect_float, sizeof(float) * 4 * oglrender->sizex * oglrender->sizey); - IMB_freeImBuf(ibuf); + memcpy(rr->rectf, linear_ibuf->rect_float, sizeof(float) * 4 * oglrender->sizex * oglrender->sizey); + + IMB_freeImBuf(linear_ibuf); } } else if (view_context) { @@ -181,7 +190,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) } if ((scene->r.mode & R_OSA) == 0) { - ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat, TRUE); + ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat, TRUE, FALSE); GPU_offscreen_read_pixels(oglrender->ofs, GL_FLOAT, rr->rectf); } else { @@ -195,7 +204,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) BLI_jitter_init(jit_ofs[0], scene->r.osa); /* first sample buffer, also initializes 'rv3d->persmat' */ - ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat, TRUE); + ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat, TRUE, FALSE); GPU_offscreen_read_pixels(oglrender->ofs, GL_FLOAT, accum_buffer); /* skip the first sample */ @@ -205,7 +214,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) (jit_ofs[j][0] * 2.0f) / sizex, (jit_ofs[j][1] * 2.0f) / sizey); - ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat_jitter, TRUE); + ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat_jitter, TRUE, FALSE); GPU_offscreen_read_pixels(oglrender->ofs, GL_FLOAT, accum_tmp); add_vn_vn(accum_buffer, accum_tmp, sizex * sizey * sizeof(float)); } @@ -221,7 +230,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) else { /* shouldnt suddenly give errors mid-render but possible */ char err_out[256] = "unknown"; - ImBuf *ibuf_view = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera, oglrender->sizex, oglrender->sizey, IB_rectfloat, OB_SOLID, TRUE, err_out); + ImBuf *ibuf_view = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera, oglrender->sizex, oglrender->sizey, IB_rectfloat, OB_SOLID, TRUE, FALSE, err_out); camera = scene->camera; if (ibuf_view) { @@ -233,27 +242,27 @@ static void screen_opengl_render_apply(OGLRender *oglrender) } } - /* rr->rectf is now filled with image data */ - - if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) - BKE_stamp_buf(scene, camera, NULL, rr->rectf, rr->rectx, rr->recty, 4); - /* note on color management: * * OpenGL renders into sRGB colors, but render buffers are expected to be - * linear if color management is enabled. So we convert to linear here, so - * the conversion back to bytes using the color management flag can make it - * sRGB again, and so that e.g. openexr saving also saves the correct linear - * float buffer. */ + * linear So we convert to linear here, so the conversion back to bytes can make it + * sRGB (or other display space) again, and so that e.g. openexr saving also saves the + * correct linear float buffer. + */ - if (oglrender->scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) { - int predivide = 0; /* no alpha */ + if (!oglrender->is_sequencer) { + /* sequencer has got tricker ocnversion happened above */ IMB_buffer_float_from_float(rr->rectf, rr->rectf, - 4, IB_PROFILE_LINEAR_RGB, IB_PROFILE_SRGB, predivide, + 4, IB_PROFILE_LINEAR_RGB, IB_PROFILE_SRGB, FALSE, oglrender->sizex, oglrender->sizey, oglrender->sizex, oglrender->sizex); } + /* rr->rectf is now filled with image data */ + + if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) + BKE_stamp_buf(scene, camera, NULL, rr->rectf, rr->rectx, rr->recty, 4); + RE_ReleaseResult(oglrender->re); /* update byte from float buffer */ @@ -536,12 +545,28 @@ static int screen_opengl_render_anim_step(bContext *C, wmOperator *op) ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock); if (ibuf) { + int needs_free = FALSE; + + if (is_movie || !BKE_imtype_supports_float(scene->r.im_format.imtype)) { + ImBuf *colormanage_ibuf = IMB_dupImBuf(ibuf); + + IMB_display_buffer_to_imbuf_rect(colormanage_ibuf, &scene->view_settings, &scene->display_settings); + imb_freerectfloatImBuf(colormanage_ibuf); + + // IMB_freeImBuf(ibuf); /* owned by the image */ + ibuf = colormanage_ibuf; + needs_free = TRUE; + } + /* color -> grayscale */ /* editing directly would alter the render view */ if (scene->r.im_format.planes == R_IMF_PLANES_BW) { - ImBuf *ibuf_bw = IMB_dupImBuf(ibuf); + ImBuf *ibuf_bw = IMB_dupImBuf(ibuf); IMB_color_to_bw(ibuf_bw); - // IMB_freeImBuf(ibuf); /* owned by the image */ + + if (needs_free) + IMB_freeImBuf(ibuf); + ibuf = ibuf_bw; } else { @@ -551,6 +576,13 @@ static int screen_opengl_render_anim_step(bContext *C, wmOperator *op) ibuf_cpy->rect = ibuf->rect; ibuf_cpy->rect_float = ibuf->rect_float; ibuf_cpy->zbuf_float = ibuf->zbuf_float; + + if (needs_free) { + ibuf_cpy->mall = ibuf->mall; + ibuf->mall = 0; + IMB_freeImBuf(ibuf); + } + ibuf = ibuf_cpy; } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 068a7aaa50a..a829ef57b85 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -64,6 +64,7 @@ #include "BKE_brush.h" #include "BKE_context.h" +#include "BKE_colortools.h" #include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_idprop.h" @@ -75,11 +76,13 @@ #include "BKE_material.h" #include "BKE_node.h" #include "BKE_object.h" +#include "BKE_scene.h" #include "BKE_texture.h" #include "BKE_world.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_colormanagement.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -115,7 +118,8 @@ ImBuf *get_brush_icon(Brush *brush) BLI_strncpy(path, brush->icon_filepath, sizeof(brush->icon_filepath)); BLI_path_abs(path, G.main->name); - brush->icon_imbuf = IMB_loadiffname(path, flags); + /* use default colorspaces for brushes */ + brush->icon_imbuf = IMB_loadiffname(path, flags, NULL); // otherwise lets try to find it in other directories if (!(brush->icon_imbuf)) { @@ -123,8 +127,10 @@ ImBuf *get_brush_icon(Brush *brush) BLI_make_file_string(G.main->name, path, folder, brush->icon_filepath); - if (path[0]) - brush->icon_imbuf = IMB_loadiffname(path, flags); + if (path[0]) { + /* use fefault color spaces */ + brush->icon_imbuf = IMB_loadiffname(path, flags, NULL); + } } if (brush->icon_imbuf) @@ -254,6 +260,10 @@ static Scene *preview_prepare_scene(Scene *scene, ID *id, int id_type, ShaderPre } sce->r.color_mgt_flag = scene->r.color_mgt_flag; + BKE_color_managed_display_settings_copy(&sce->display_settings, &scene->display_settings); + + BKE_color_managed_view_settings_free(&sce->view_settings); + BKE_color_managed_view_settings_copy(&sce->view_settings, &scene->view_settings); /* prevent overhead for small renders and icons (32) */ if (id && sp->sizex < 40) @@ -263,7 +273,7 @@ static Scene *preview_prepare_scene(Scene *scene, ID *id, int id_type, ShaderPre /* exception: don't color manage texture previews or icons */ if ((id && sp->pr_method == PR_ICON_RENDER) || id_type == ID_TE) - sce->r.color_mgt_flag &= ~R_COLOR_MANAGEMENT; + BKE_scene_disable_color_management(sce); if ((id && sp->pr_method == PR_ICON_RENDER) && id_type != ID_WO) sce->r.alphamode = R_ALPHAPREMUL; @@ -481,7 +491,7 @@ static int ed_preview_draw_rect(ScrArea *sa, Scene *sce, ID *id, int split, int if (id && GS(id->name) != ID_TE) { /* exception: don't color manage texture previews - show the raw values */ if (sce) { - do_gamma_correct = sce->r.color_mgt_flag & R_COLOR_MANAGEMENT; + do_gamma_correct = TRUE; do_predivide = sce->r.color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE; } } @@ -514,15 +524,25 @@ static int ed_preview_draw_rect(ScrArea *sa, Scene *sce, ID *id, int split, int /* temporary conversion to byte for drawing */ float fx = rect->xmin + offx; float fy = rect->ymin; - int profile_from = (do_gamma_correct) ? IB_PROFILE_LINEAR_RGB : IB_PROFILE_SRGB; int dither = 0; unsigned char *rect_byte; rect_byte = MEM_mallocN(rres.rectx * rres.recty * sizeof(int), "ed_preview_draw_rect"); - IMB_buffer_byte_from_float(rect_byte, rres.rectf, - 4, dither, IB_PROFILE_SRGB, profile_from, do_predivide, - rres.rectx, rres.recty, rres.rectx, rres.rectx); + if (do_gamma_correct) { + IMB_display_buffer_transform_apply(rect_byte, rres.rectf, rres.rectx, rres.recty, 4, + &sce->view_settings, &sce->display_settings, do_predivide); + + } + else { + /* OCIO_TODO: currently seems an exception for textures (came fro mlegacish time), + * but is it indeed expected behavior, or textures should be + * color managed as well? + */ + IMB_buffer_byte_from_float(rect_byte, rres.rectf, + 4, dither, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB, do_predivide, + rres.rectx, rres.recty, rres.rectx, rres.rectx); + } glaDrawPixelsSafe(fx, fy, rres.rectx, rres.recty, rres.rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect_byte); diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index 4e71c49465f..5e54119118a 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -245,7 +245,7 @@ static void screenshot_draw(bContext *UNUSED(C), wmOperator *op) /* image template */ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &scd->im_format, &ptr); - uiTemplateImageSettings(layout, &ptr); + uiTemplateImageSettings(layout, &ptr, TRUE); /* main draw call */ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 101235e9c99..64430a263f0 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -104,6 +104,8 @@ #include "GPU_draw.h" #include "GPU_extensions.h" +#include "IMB_colormanagement.h" + #include "paint_intern.h" /* Defines and Structs */ @@ -139,7 +141,7 @@ #define IMAPAINT_TILE_SIZE (1 << IMAPAINT_TILE_BITS) #define IMAPAINT_TILE_NUMBER(size) (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS) -static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint); +static void imapaint_image_update(Scene *scene, SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint); typedef struct ImagePaintState { @@ -153,7 +155,6 @@ typedef struct ImagePaintState { Image *image; ImBuf *canvas; ImBuf *clonecanvas; - short clonefreefloat; char *warnpackedfile; char *warnmultifile; @@ -510,7 +511,7 @@ static void image_undo_restore(bContext *C, ListBase *lb) ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ if (ibuf->mipmap[0]) ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ - + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; } IMB_freeImBuf(tmpibuf); @@ -3615,7 +3616,7 @@ static int project_image_refresh_tagged(ProjPaintState *ps) pr = &(projIma->partRedrawRect[i]); if (pr->x2 != -1) { /* TODO - use 'enabled' ? */ imapaintpartial = *pr; - imapaint_image_update(NULL, projIma->ima, projIma->ibuf, 1); /*last 1 is for texpaint*/ + imapaint_image_update(NULL, NULL, projIma->ima, projIma->ibuf, 1); /*last 1 is for texpaint*/ redraw = 1; } } @@ -3945,7 +3946,7 @@ static void *do_projectpaint_thread(void *ph_v) last_projIma->touch = 1; is_floatbuf = last_projIma->ibuf->rect_float ? 1 : 0; - use_color_correction = (last_projIma->ibuf->profile == IB_PROFILE_LINEAR_RGB) ? 1 : 0; + use_color_correction = TRUE; } /* end copy */ @@ -4028,7 +4029,7 @@ static void *do_projectpaint_thread(void *ph_v) last_projIma->touch = 1; is_floatbuf = last_projIma->ibuf->rect_float ? 1 : 0; - use_color_correction = (last_projIma->ibuf->profile == IB_PROFILE_LINEAR_RGB) ? 1 : 0; + use_color_correction = TRUE; } /* end copy */ @@ -4244,10 +4245,17 @@ static void imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, IMB_freeImBuf(tmpibuf); } -static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint) +static void imapaint_image_update(Scene *scene, SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint) { - if (ibuf->rect_float) - ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ + if (scene) { + IMB_partial_display_buffer_update(ibuf, ibuf->rect_float, (unsigned char *) ibuf->rect, ibuf->x, 0, 0, + &scene->view_settings, &scene->display_settings, + imapaintpartial.x1, imapaintpartial.y1, + imapaintpartial.x2, imapaintpartial.y2); + } + else { + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + } if (ibuf->mipmap[0]) ibuf->userflags |= IB_MIPMAP_INVALID; @@ -4571,15 +4579,7 @@ static int imapaint_canvas_set(ImagePaintState *s, Image *ima) /* temporarily add float rect for cloning */ if (s->canvas->rect_float && !s->clonecanvas->rect_float) { - short profile = IB_PROFILE_NONE; - - /* Don't want to color manage, but don't disturb existing profiles */ - SWAP(short, s->clonecanvas->profile, profile); - IMB_float_from_rect(s->clonecanvas); - s->clonefreefloat = 1; - - SWAP(short, s->clonecanvas->profile, profile); } else if (!s->canvas->rect_float && !s->clonecanvas->rect) IMB_rect_from_float(s->clonecanvas); @@ -4588,10 +4588,8 @@ static int imapaint_canvas_set(ImagePaintState *s, Image *ima) return 1; } -static void imapaint_canvas_free(ImagePaintState *s) +static void imapaint_canvas_free(ImagePaintState *UNUSED(s)) { - if (s->clonefreefloat) - imb_freerectfloatImBuf(s->clonecanvas); } static int imapaint_paint_sub_stroke(ImagePaintState *s, BrushPainter *painter, Image *image, short texpaint, float *uv, double time, int update, float pressure) @@ -4607,9 +4605,12 @@ static int imapaint_paint_sub_stroke(ImagePaintState *s, BrushPainter *painter, BKE_brush_painter_require_imbuf(painter, ((ibuf->rect_float) ? 1 : 0), 0, 0); - if (BKE_brush_painter_paint(painter, imapaint_paint_op, pos, time, pressure, s, ibuf->profile == IB_PROFILE_LINEAR_RGB)) { + /* OCIO_TODO: float buffers are now always linear, so always use color correction + * this should probably be changed when texture painting color space is supported + */ + if (BKE_brush_painter_paint(painter, imapaint_paint_op, pos, time, pressure, s, TRUE)) { if (update) - imapaint_image_update(s->sima, image, ibuf, texpaint); + imapaint_image_update(s->scene, s->sima, image, ibuf, texpaint); return 1; } else return 0; @@ -5884,7 +5885,7 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) if (w > maxsize) w = maxsize; if (h > maxsize) h = maxsize; - ibuf = ED_view3d_draw_offscreen_imbuf(CTX_data_scene(C), CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect, FALSE, err_out); + ibuf = ED_view3d_draw_offscreen_imbuf(CTX_data_scene(C), CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect, FALSE, FALSE, err_out); if (!ibuf) { /* Mostly happens when OpenGL offscreen buffer was failed to create, */ /* but could be other reasons. Should be handled in the future. nazgul */ diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index e66faa88b93..0a1e1d75098 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -119,6 +119,8 @@ void uiTemplateMovieClip(uiLayout *layout, bContext *C, PointerRNA *ptr, const c uiTemplateID(layout, C, ptr, propname, NULL, "CLIP_OT_open", NULL); if (clip) { + uiLayout *col; + row = uiLayoutRow(layout, FALSE); block = uiLayoutGetBlock(row); uiDefBut(block, LABEL, 0, "File Path:", 0, 19, 145, 19, NULL, 0, 0, 0, 0, ""); @@ -129,6 +131,9 @@ void uiTemplateMovieClip(uiLayout *layout, bContext *C, PointerRNA *ptr, const c uiItemR(row, &clipptr, "filepath", 0, "", ICON_NONE); uiItemO(row, "", ICON_FILE_REFRESH, "clip.reload"); + + col = uiLayoutColumn(layout, FALSE); + uiTemplateColorspaceSettings(col, &clipptr, "colorspace_settings"); } } diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index 2353f8f8c12..d486caeacc2 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -42,6 +42,7 @@ #include "BKE_tracking.h" #include "BKE_mask.h" +#include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" @@ -246,14 +247,7 @@ static void draw_movieclip_notes(SpaceClip *sc, ARegion *ar) ED_region_info_draw(ar, str, block, 0.6f); } -static void verify_buffer_float(ImBuf *ibuf) -{ - if (ibuf->rect_float && (ibuf->rect == NULL || (ibuf->userflags & IB_RECT_INVALID))) { - IMB_rect_from_float(ibuf); - } -} - -static void draw_movieclip_buffer(SpaceClip *sc, ARegion *ar, ImBuf *ibuf, +static void draw_movieclip_buffer(const bContext *C, SpaceClip *sc, ARegion *ar, ImBuf *ibuf, int width, int height, float zoomx, float zoomy) { int x, y; @@ -267,13 +261,16 @@ static void draw_movieclip_buffer(SpaceClip *sc, ARegion *ar, ImBuf *ibuf, glRectf(x, y, x + zoomx * width, y + zoomy * height); } else { - verify_buffer_float(ibuf); + unsigned char *display_buffer; + void *cache_handle; + + display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle); - if (ibuf->rect) { + if (display_buffer) { int need_fallback = 1; if (ED_space_clip_texture_buffer_supported(sc)) { - if (ED_space_clip_load_movieclip_buffer(sc, ibuf)) { + if (ED_space_clip_load_movieclip_buffer(sc, ibuf, display_buffer)) { glPushMatrix(); glTranslatef(x, y, 0.0f); glScalef(zoomx, zoomy, 1.0f); @@ -299,12 +296,14 @@ static void draw_movieclip_buffer(SpaceClip *sc, ARegion *ar, ImBuf *ibuf, /* set zoom */ glPixelZoom(zoomx * width / ibuf->x, zoomy * height / ibuf->y); - glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, display_buffer); /* reset zoom */ glPixelZoom(1.0f, 1.0f); } } + + IMB_display_buffer_release(cache_handle); } /* draw boundary border for frame if stabilization is enabled */ @@ -1461,7 +1460,7 @@ void clip_draw_main(const bContext *C, SpaceClip *sc, ARegion *ar) } if (ibuf) { - draw_movieclip_buffer(sc, ar, ibuf, width, height, zoomx, zoomy); + draw_movieclip_buffer(C, sc, ar, ibuf, width, height, zoomx, zoomy); IMB_freeImBuf(ibuf); } else { diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index 08ad6aac970..f2a48f299cf 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -277,13 +277,7 @@ int ED_space_clip_color_sample(SpaceClip *sc, ARegion *ar, int mval[2], float r_ if (ibuf->rect_float) { fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); - /* IB_PROFILE_NONE is default but infact its linear */ - if (ELEM(ibuf->profile, IB_PROFILE_LINEAR_RGB, IB_PROFILE_NONE)) { - linearrgb_to_srgb_v3_v3(r_col, fp); - } - else { - copy_v3_v3(r_col, fp); - } + linearrgb_to_srgb_v3_v3(r_col, fp); ret = TRUE; } else if (ibuf->rect) { @@ -583,12 +577,15 @@ typedef struct SpaceClipDrawContext { GLuint texture; /* OGL texture ID */ short texture_allocated; /* flag if texture was allocated by glGenTextures */ struct ImBuf *texture_ibuf; /* image buffer for which texture was created */ + const unsigned char *display_buffer; /* display buffer for which texture was created */ int image_width, image_height; /* image width and height for which texture was created */ unsigned last_texture; /* ID of previously used texture, so it'll be restored after clip drawing */ /* fields to check if cache is still valid */ int framenr, start_frame, frame_offset; short render_size, render_flag; + + char colorspace[64]; } SpaceClipDrawContext; int ED_space_clip_texture_buffer_supported(SpaceClip *sc) @@ -613,7 +610,7 @@ int ED_space_clip_texture_buffer_supported(SpaceClip *sc) return context->buffers_supported; } -int ED_space_clip_load_movieclip_buffer(SpaceClip *sc, ImBuf *ibuf) +int ED_space_clip_load_movieclip_buffer(SpaceClip *sc, ImBuf *ibuf, const unsigned char *display_buffer) { SpaceClipDrawContext *context = sc->draw_context; MovieClip *clip = ED_space_clip_get_clip(sc); @@ -625,12 +622,22 @@ int ED_space_clip_load_movieclip_buffer(SpaceClip *sc, ImBuf *ibuf) * assuming displaying happens of footage frames only on which painting doesn't heppen. * so not changed image buffer pointer means unchanged image content */ need_rebind |= context->texture_ibuf != ibuf; + need_rebind |= context->display_buffer != display_buffer; need_rebind |= context->framenr != sc->user.framenr; need_rebind |= context->render_size != sc->user.render_size; need_rebind |= context->render_flag != sc->user.render_flag; need_rebind |= context->start_frame != clip->start_frame; need_rebind |= context->frame_offset != clip->frame_offset; + if (!need_rebind) { + /* OCIO_TODO: not entirely nice, but currently it seems to be easiest way + * to deal with changing input color space settings + * pointer-based check could fail due to new buffers could be + * be allocated on on old memory + */ + need_rebind = strcmp(context->colorspace, clip->colorspace_settings.name) != 0; + } + if (need_rebind) { int width = ibuf->x, height = ibuf->y; int need_recreate = 0; @@ -670,16 +677,12 @@ int ED_space_clip_load_movieclip_buffer(SpaceClip *sc, ImBuf *ibuf) glBindTexture(GL_TEXTURE_2D, context->texture); } - if (ibuf->rect_float) { - if (ibuf->rect == NULL) - IMB_rect_from_float(ibuf); - } - - if (ibuf->rect) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + if (display_buffer) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, display_buffer); /* store settings */ context->texture_allocated = 1; + context->display_buffer = display_buffer; context->texture_ibuf = ibuf; context->image_width = ibuf->x; context->image_height = ibuf->y; @@ -688,6 +691,8 @@ int ED_space_clip_load_movieclip_buffer(SpaceClip *sc, ImBuf *ibuf) context->render_flag = sc->user.render_flag; context->start_frame = clip->start_frame; context->frame_offset = clip->frame_offset; + + strcpy(context->colorspace, clip->colorspace_settings.name); } else { /* displaying exactly the same image which was loaded t oa texture, diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 337e13f7785..d3b4df05aa9 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -389,7 +389,7 @@ void filelist_init_icons(void) #ifdef WITH_HEADLESS bbuf = NULL; #else - bbuf = IMB_ibImageFromMemory((unsigned char *)datatoc_prvicons_png, datatoc_prvicons_png_size, IB_rect, "<splash>"); + bbuf = IMB_ibImageFromMemory((unsigned char *)datatoc_prvicons_png, datatoc_prvicons_png_size, IB_rect, NULL, "<splash>"); #endif if (bbuf) { for (y = 0; y < SPECIAL_IMG_ROWS; y++) { diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 40f0683a852..c372973b7b0 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -169,47 +169,6 @@ struct ImageUser *ntree_get_active_iuser(bNodeTree *ntree) /* ************ panel stuff ************* */ -/* is used for both read and write... */ - -static int image_panel_poll(const bContext *C, PanelType *UNUSED(pt)) -{ - SpaceImage *sima = CTX_wm_space_image(C); - ImBuf *ibuf; - void *lock; - int result; - - ibuf = ED_space_image_acquire_buffer(sima, &lock); - result = ibuf && ibuf->rect_float; - ED_space_image_release_buffer(sima, lock); - - return result; -} - -static void image_panel_curves(const bContext *C, Panel *pa) -{ - bScreen *sc = CTX_wm_screen(C); - SpaceImage *sima = CTX_wm_space_image(C); - ImBuf *ibuf; - PointerRNA simaptr; - int levels; - void *lock; - - ibuf = ED_space_image_acquire_buffer(sima, &lock); - - if (ibuf) { - if (sima->cumap == NULL) - sima->cumap = curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f); - - /* curvemap black/white levels only works for RGBA */ - levels = (ibuf->channels == 4); - - RNA_pointer_create(&sc->id, &RNA_SpaceImageEditor, sima, &simaptr); - uiTemplateCurveMapping(pa->layout, &simaptr, "curve", 'c', levels, 0); - } - - ED_space_image_release_buffer(sima, lock); -} - #if 0 /* 0: disable preview * otherwise refresh preview @@ -234,6 +193,7 @@ void image_preview_event(int event) } if (exec && G.scene->nodetree) { + Scene *scene = G.scene; /* should work when no node editor in screen..., so we execute right away */ ntreeCompositTagGenerators(G.scene->nodetree); @@ -244,7 +204,7 @@ void image_preview_event(int event) BIF_store_spare(); - ntreeCompositExecTree(G.scene->nodetree, &G.scene->r, 1); /* 1 is do_previews */ + ntreeCompositExecTree(scene->nodetree, &scene->r, 1, &scene->view_settings, &scene->display_settings); /* 1 is do_previews */ G.scene->nodetree->timecursor = NULL; G.scene->nodetree->test_break = NULL; @@ -611,6 +571,7 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char uiLayout *row, *split, *col; uiBlock *block; char str[128]; + void *lock; if (!ptr->data) @@ -728,7 +689,11 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char uiItemL(layout, str, ICON_NONE); } } - + + col = uiLayoutColumn(layout, FALSE); + uiTemplateColorspaceSettings(col, &imaptr, "colorspace_settings"); + uiItemR(col, &imaptr, "view_as_render", 0, NULL, ICON_NONE); + if (ima->source != IMA_SRC_GENERATED) { if (compact == 0) { /* background image view doesnt need these */ uiItemS(layout); @@ -794,15 +759,18 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char MEM_freeN(cb); } -void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr) +void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, int color_management) { ImageFormatData *imf = imfptr->data; ID *id = imfptr->id.data; + PointerRNA display_settings_ptr; + PropertyRNA *prop; const int depth_ok = BKE_imtype_valid_depths(imf->imtype); /* some settings depend on this being a scene thats rendered */ const short is_render_out = (id && GS(id->name) == ID_SCE); uiLayout *col, *row, *split, *sub; + int show_preview = FALSE; col = uiLayoutColumn(layout, FALSE); @@ -843,6 +811,7 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr) } if (is_render_out && (imf->imtype == R_IMF_IMTYPE_OPENEXR)) { + show_preview = TRUE; uiItemR(row, imfptr, "use_preview", 0, NULL, ICON_NONE); } @@ -864,6 +833,22 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "cineon_gamma", 0, NULL, ICON_NONE); #endif } + + /* color management */ + if (color_management && + (!BKE_imtype_supports_float(imf->imtype) || + (show_preview && imf->flag & R_IMF_FLAG_PREVIEW_JPG))) + { + prop = RNA_struct_find_property(imfptr, "display_settings"); + display_settings_ptr = RNA_property_pointer_get(imfptr, prop); + + col = uiLayoutColumn(layout, FALSE); + uiItemL(col, IFACE_("Color Management"), ICON_NONE); + + uiItemR(col, &display_settings_ptr, "display_device", 0, NULL, ICON_NONE); + + uiTemplateColormanagedViewSettings(col, NULL, imfptr, "view_settings"); + } } void uiTemplateImageLayers(uiLayout *layout, bContext *C, Image *ima, ImageUser *iuser) @@ -886,14 +871,6 @@ void image_buttons_register(ARegionType *art) { PanelType *pt; - pt = MEM_callocN(sizeof(PanelType), "spacetype image panel curves"); - strcpy(pt->idname, "IMAGE_PT_curves"); - strcpy(pt->label, "Curves"); - pt->draw = image_panel_curves; - pt->poll = image_panel_poll; - pt->flag |= PNL_DEFAULT_CLOSED; - BLI_addtail(&art->paneltypes, pt); - pt = MEM_callocN(sizeof(PanelType), "spacetype image panel gpencil"); strcpy(pt->idname, "IMAGE_PT_gpencil"); strcpy(pt->label, "Grease Pencil"); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index e293264c021..638a2ac6681 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -51,6 +51,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_colormanagement.h" #include "BKE_context.h" #include "BKE_global.h" @@ -70,6 +71,8 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "WM_api.h" +#include "WM_types.h" #include "RE_pipeline.h" @@ -77,27 +80,6 @@ #define HEADER_HEIGHT 18 -static void image_verify_buffer_float(Image *ima, ImBuf *ibuf, int color_manage) -{ - /* detect if we need to redo the curve map. - * ibuf->rect is zero for compositor and render results after change - * convert to 32 bits always... drawing float rects isn't supported well (atis) - * - * NOTE: if float buffer changes, we have to manually remove the rect - */ - - if (ibuf->rect_float && (ibuf->rect == NULL || (ibuf->userflags & IB_RECT_INVALID)) ) { - if (color_manage) { - if (ima && ima->source == IMA_SRC_VIEWER) - ibuf->profile = IB_PROFILE_LINEAR_RGB; - } - else - ibuf->profile = IB_PROFILE_NONE; - - IMB_rect_from_float(ibuf); - } -} - static void draw_render_info(Scene *scene, Image *ima, ARegion *ar) { RenderResult *rr; @@ -112,7 +94,7 @@ static void draw_render_info(Scene *scene, Image *ima, ARegion *ar) } /* used by node view too */ -void ED_image_draw_info(ARegion *ar, int color_manage, int channels, int x, int y, +void ED_image_draw_info(Scene *scene, ARegion *ar, int color_manage, int use_default_view, int channels, int x, int y, const unsigned char cp[4], const float fp[4], int *zp, float *zpf) { char str[256]; @@ -209,6 +191,20 @@ void ED_image_draw_info(ARegion *ar, int color_manage, int channels, int x, int BLF_draw_ascii(blf_mono_font, str, sizeof(str)); dx += BLF_width(blf_mono_font, str); } + + if (color_manage && channels == 4) { + float pixel[4]; + + if (use_default_view) + IMB_colormanagement_pixel_to_display_space_v4(pixel, fp, NULL, &scene->display_settings); + else + IMB_colormanagement_pixel_to_display_space_v4(pixel, fp, &scene->view_settings, &scene->display_settings); + + BLI_snprintf(str, sizeof(str), " | CM R:%-.4f G:%-.4f B:%-.4f", pixel[0], pixel[1], pixel[2]); + BLF_position(blf_mono_font, dx, 6, 0); + BLF_draw_ascii(blf_mono_font, str, sizeof(str)); + dx += BLF_width(blf_mono_font, str); + } } /* color rectangle */ @@ -252,11 +248,15 @@ void ED_image_draw_info(ARegion *ar, int color_manage, int channels, int x, int } if (color_manage) { - linearrgb_to_srgb_v4(finalcol, col); + if (use_default_view) + IMB_colormanagement_pixel_to_display_space_v4(finalcol, col, NULL, &scene->display_settings); + else + IMB_colormanagement_pixel_to_display_space_v4(finalcol, col, &scene->view_settings, &scene->display_settings); } else { copy_v4_v4(finalcol, col); } + glDisable(GL_BLEND); glColor3fv(finalcol); dx += 5; @@ -418,10 +418,9 @@ static void sima_draw_zbuffloat_pixels(Scene *scene, float x1, float y1, int rec MEM_freeN(rectf); } -static void draw_image_buffer(SpaceImage *sima, ARegion *ar, Scene *scene, Image *ima, ImBuf *ibuf, float fx, float fy, float zoomx, float zoomy) +static void draw_image_buffer(const bContext *C, SpaceImage *sima, ARegion *ar, Scene *scene, ImBuf *ibuf, float fx, float fy, float zoomx, float zoomy) { int x, y; - int color_manage = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT; /* set zoom */ glPixelZoom(zoomx, zoomy); @@ -445,6 +444,9 @@ static void draw_image_buffer(SpaceImage *sima, ARegion *ar, Scene *scene, Image sima_draw_zbuffloat_pixels(scene, x, y, ibuf->x, ibuf->y, ibuf->rect_float); } else { + unsigned char *display_buffer; + void *cache_handle; + if (sima->flag & SI_USE_ALPHA) { fdrawcheckerboard(x, y, x + ibuf->x * zoomx, y + ibuf->y * zoomy); @@ -452,17 +454,17 @@ static void draw_image_buffer(SpaceImage *sima, ARegion *ar, Scene *scene, Image glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } - /* we don't draw floats buffers directly but - * convert them, and optionally apply curves */ - image_verify_buffer_float(ima, ibuf, color_manage); + display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle); - if (ibuf->rect) - glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + if (display_buffer) + glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, display_buffer); #if 0 else glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_FLOAT, ibuf->rect_float); #endif - + + IMB_display_buffer_release(cache_handle); + if (sima->flag & SI_USE_ALPHA) glDisable(GL_BLEND); } @@ -471,14 +473,14 @@ static void draw_image_buffer(SpaceImage *sima, ARegion *ar, Scene *scene, Image glPixelZoom(1.0f, 1.0f); } -static unsigned int *get_part_from_ibuf(ImBuf *ibuf, short startx, short starty, short endx, short endy) +static unsigned int *get_part_from_buffer(unsigned int *buffer, int width, short startx, short starty, short endx, short endy) { unsigned int *rt, *rp, *rectmain; short y, heigth, len; /* the right offset in rectot */ - rt = ibuf->rect + (starty * ibuf->x + startx); + rt = buffer + (starty * width + startx); len = (endx - startx); heigth = (endy - starty); @@ -487,7 +489,7 @@ static unsigned int *get_part_from_ibuf(ImBuf *ibuf, short startx, short starty, for (y = 0; y < heigth; y++) { memcpy(rp, rt, len * 4); - rt += ibuf->x; + rt += width; rp += len; } return rectmain; @@ -495,28 +497,34 @@ static unsigned int *get_part_from_ibuf(ImBuf *ibuf, short startx, short starty, static void draw_image_buffer_tiled(SpaceImage *sima, ARegion *ar, Scene *scene, Image *ima, ImBuf *ibuf, float fx, float fy, float zoomx, float zoomy) { + unsigned char *display_buffer; unsigned int *rect; int dx, dy, sx, sy, x, y; - int color_manage = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT; + void *cache_handle; /* verify valid values, just leave this a while */ if (ima->xrep < 1) return; if (ima->yrep < 1) return; - + + if (ima->flag & IMA_VIEW_AS_RENDER) + display_buffer = IMB_display_buffer_acquire(ibuf, &scene->view_settings, &scene->display_settings, &cache_handle); + else + display_buffer = IMB_display_buffer_acquire(ibuf, NULL, &scene->display_settings, &cache_handle); + + if (!display_buffer) + return; + glPixelZoom(zoomx, zoomy); if (sima->curtile >= ima->xrep * ima->yrep) sima->curtile = ima->xrep * ima->yrep - 1; - /* create char buffer from float if needed */ - image_verify_buffer_float(ima, ibuf, color_manage); - /* retrieve part of image buffer */ dx = ibuf->x / ima->xrep; dy = ibuf->y / ima->yrep; sx = (sima->curtile % ima->xrep) * dx; sy = (sima->curtile / ima->xrep) * dy; - rect = get_part_from_ibuf(ibuf, sx, sy, sx + dx, sy + dy); + rect = get_part_from_buffer((unsigned int*)display_buffer, ibuf->x, sx, sy, sx + dx, sy + dy); /* draw repeated */ for (sy = 0; sy + dy <= ibuf->y; sy += dy) { @@ -529,10 +537,12 @@ static void draw_image_buffer_tiled(SpaceImage *sima, ARegion *ar, Scene *scene, glPixelZoom(1.0f, 1.0f); + IMB_display_buffer_release(cache_handle); + MEM_freeN(rect); } -static void draw_image_buffer_repeated(SpaceImage *sima, ARegion *ar, Scene *scene, Image *ima, ImBuf *ibuf, float zoomx, float zoomy) +static void draw_image_buffer_repeated(const bContext *C, SpaceImage *sima, ARegion *ar, Scene *scene, Image *ima, ImBuf *ibuf, float zoomx, float zoomy) { const double time_current = PIL_check_seconds_timer(); @@ -549,7 +559,7 @@ static void draw_image_buffer_repeated(SpaceImage *sima, ARegion *ar, Scene *sce if (ima && (ima->tpageflag & IMA_TILES)) draw_image_buffer_tiled(sima, ar, scene, ima, ibuf, x, y, zoomx, zoomy); else - draw_image_buffer(sima, ar, scene, ima, ibuf, x, y, zoomx, zoomy); + draw_image_buffer(C, sima, ar, scene, ibuf, x, y, zoomx, zoomy); /* only draw until running out of time */ if ((PIL_check_seconds_timer() - time_current) > 0.25) @@ -632,22 +642,35 @@ static void draw_image_view_tool(Scene *scene) } #endif -static unsigned char *get_alpha_clone_image(Scene *scene, int *width, int *height) +static unsigned char *get_alpha_clone_image(const bContext *C, Scene *scene, int *width, int *height) { Brush *brush = paint_brush(&scene->toolsettings->imapaint.paint); ImBuf *ibuf; unsigned int size, alpha; + unsigned char *display_buffer; unsigned char *rect, *cp; + void *cache_handle; if (!brush || !brush->clone.image) return NULL; ibuf = BKE_image_get_ibuf(brush->clone.image, NULL); - if (!ibuf || !ibuf->rect) + if (!ibuf) return NULL; - rect = MEM_dupallocN(ibuf->rect); + display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle); + + if (!display_buffer) { + IMB_display_buffer_release(cache_handle); + + return NULL; + } + + rect = MEM_dupallocN(display_buffer); + + IMB_display_buffer_release(cache_handle); + if (!rect) return NULL; @@ -666,7 +689,7 @@ static unsigned char *get_alpha_clone_image(Scene *scene, int *width, int *heigh return rect; } -static void draw_image_paint_helpers(ARegion *ar, Scene *scene, float zoomx, float zoomy) +static void draw_image_paint_helpers(const bContext *C, ARegion *ar, Scene *scene, float zoomx, float zoomy) { Brush *brush; int x, y, w, h; @@ -677,7 +700,7 @@ static void draw_image_paint_helpers(ARegion *ar, Scene *scene, float zoomx, flo if (brush && (brush->imagepaint_tool == PAINT_TOOL_CLONE)) { /* this is not very efficient, but glDrawPixels doesn't allow * drawing with alpha */ - clonerect = get_alpha_clone_image(scene, &w, &h); + clonerect = get_alpha_clone_image(C, scene, &w, &h); if (clonerect) { UI_view2d_to_region_no_clip(&ar->v2d, brush->clone.offset[0], brush->clone.offset[1], &x, &y); @@ -698,8 +721,10 @@ static void draw_image_paint_helpers(ARegion *ar, Scene *scene, float zoomx, flo /* draw main image area */ -void draw_image_main(SpaceImage *sima, ARegion *ar, Scene *scene) +void draw_image_main(const bContext *C, ARegion *ar) { + SpaceImage *sima = CTX_wm_space_image(C); + Scene *scene = CTX_data_scene(C); Image *ima; ImBuf *ibuf; float zoomx, zoomy; @@ -748,15 +773,15 @@ void draw_image_main(SpaceImage *sima, ARegion *ar, Scene *scene) if (ibuf == NULL) ED_region_grid_draw(ar, zoomx, zoomy); else if (sima->flag & SI_DRAW_TILE) - draw_image_buffer_repeated(sima, ar, scene, ima, ibuf, zoomx, zoomy); + draw_image_buffer_repeated(C, sima, ar, scene, ima, ibuf, zoomx, zoomy); else if (ima && (ima->tpageflag & IMA_TILES)) draw_image_buffer_tiled(sima, ar, scene, ima, ibuf, 0.0f, 0.0, zoomx, zoomy); else - draw_image_buffer(sima, ar, scene, ima, ibuf, 0.0f, 0.0f, zoomx, zoomy); + draw_image_buffer(C, sima, ar, scene, ibuf, 0.0f, 0.0f, zoomx, zoomy); /* paint helpers */ if (sima->mode == SI_MODE_PAINT) - draw_image_paint_helpers(ar, scene, zoomx, zoomy); + draw_image_paint_helpers(C, ar, scene, zoomx, zoomy); /* XXX integrate this code */ diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index 0d3a7614f10..f86e59c41a8 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -52,7 +52,7 @@ struct ARegion *image_has_scope_region(struct ScrArea *sa); extern const char *image_context_dir[]; /* doc access */ /* image_draw.c */ -void draw_image_main(struct SpaceImage *sima, struct ARegion *ar, struct Scene *scene); +void draw_image_main(const struct bContext *C, struct ARegion *ar); void draw_image_grease_pencil(struct bContext *C, short onlyv2d); void draw_image_sample_line(struct SpaceImage *sima); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 52a2bf22d4b..16804c5a1c6 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -55,6 +55,7 @@ #include "BKE_report.h" #include "BKE_screen.h" +#include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -1168,6 +1169,10 @@ static int save_image_options_init(SaveImageOptions *simopts, SpaceImage *sima, } BLI_path_abs(simopts->filepath, G.main->name); } + + /* color management */ + BKE_color_managed_display_settings_copy(&simopts->im_format.display_settings, &scene->display_settings); + BKE_color_managed_view_settings_copy(&simopts->im_format.view_settings, &scene->view_settings); } ED_space_image_release_buffer(sima, lock); @@ -1178,6 +1183,8 @@ static int save_image_options_init(SaveImageOptions *simopts, SpaceImage *sima, static void save_image_options_from_op(SaveImageOptions *simopts, wmOperator *op) { if (op->customdata) { + BKE_color_managed_view_settings_free(&simopts->im_format.view_settings); + simopts->im_format = *(ImageFormatData *)op->customdata; } @@ -1190,12 +1197,47 @@ static void save_image_options_from_op(SaveImageOptions *simopts, wmOperator *op static void save_image_options_to_op(SaveImageOptions *simopts, wmOperator *op) { if (op->customdata) { + BKE_color_managed_view_settings_free(&((ImageFormatData *)op->customdata)->view_settings); + *(ImageFormatData *)op->customdata = simopts->im_format; } RNA_string_set(op->ptr, "filepath", simopts->filepath); } +static ImBuf *save_image_colormanaged_imbuf_acquire(ImBuf *ibuf, SaveImageOptions *simopts, int save_as_render, void **cache_handle) +{ + ImageFormatData *imf = &simopts->im_format; + ImBuf *colormanaged_ibuf; + int do_colormanagement; + + *cache_handle = NULL; + do_colormanagement = save_as_render && !BKE_imtype_supports_float(imf->imtype); + + if (do_colormanagement) { + unsigned char *display_buffer; + + display_buffer = IMB_display_buffer_acquire(ibuf, &imf->view_settings, &imf->display_settings, cache_handle); + + if (*cache_handle) { + colormanaged_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0); + colormanaged_ibuf->rect = (unsigned int *) display_buffer; + } + else { + /* no cache handle means color management didn't run transformation + * or performed transformation to image's byte buffer which doesn't + * require allocating new image buffer + */ + colormanaged_ibuf = ibuf; + } + } + else { + colormanaged_ibuf = ibuf; + } + + return colormanaged_ibuf; +} + /* assumes name is FILE_MAX */ /* ima->name and ibuf->name should end up the same */ static void save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveImageOptions *simopts, int do_newpath) @@ -1205,9 +1247,12 @@ static void save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock); if (ibuf) { + void *cache_handle; + ImBuf *colormanaged_ibuf; const char *relbase = ID_BLEND_PATH(CTX_data_main(C), &ima->id); const short relative = (RNA_struct_find_property(op->ptr, "relative_path") && RNA_boolean_get(op->ptr, "relative_path")); const short save_copy = (RNA_struct_find_property(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy")); + const short save_as_render = (RNA_struct_find_property(op->ptr, "save_as_render") && RNA_boolean_get(op->ptr, "save_as_render")); short ok = FALSE; /* old global to ensure a 2nd save goes to same dir */ @@ -1231,7 +1276,9 @@ static void save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI ibuf->planes = BKE_imbuf_alpha_test(ibuf) ? 32 : 24; } } - + + colormanaged_ibuf = save_image_colormanaged_imbuf_acquire(ibuf, simopts, save_as_render, &cache_handle); + if (simopts->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { Scene *scene = CTX_data_scene(C); RenderResult *rr = BKE_image_acquire_renderresult(scene, ima); @@ -1245,7 +1292,8 @@ static void save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI BKE_image_release_renderresult(scene, ima); } else { - if (BKE_imbuf_write_as(ibuf, simopts->filepath, &simopts->im_format, save_copy)) { + if (BKE_imbuf_write_as(colormanaged_ibuf, simopts->filepath, &simopts->im_format, save_copy)) + { ok = TRUE; } } @@ -1284,6 +1332,8 @@ static void save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI if (relative) { BLI_path_rel(ima->name, relbase); /* only after saving */ } + + IMB_colormanagment_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf); } } else { @@ -1294,6 +1344,13 @@ static void save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image); WM_cursor_wait(0); + + if (cache_handle) { + colormanaged_ibuf->rect = NULL; + IMB_freeImBuf(colormanaged_ibuf); + + IMB_display_buffer_release(cache_handle); + } } ED_space_image_release_buffer(sima, lock); @@ -1302,6 +1359,9 @@ static void save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI static void image_save_as_free(wmOperator *op) { if (op->customdata) { + ImageFormatData *im_format = (ImageFormatData *)op->customdata; + BKE_color_managed_view_settings_free(&im_format->view_settings); + MEM_freeN(op->customdata); op->customdata = NULL; } @@ -1354,6 +1414,11 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(eve RNA_boolean_set(op->ptr, "copy", TRUE); } + if (ima->source == IMA_SRC_VIEWER) + RNA_boolean_set(op->ptr, "save_as_render", TRUE); + else + RNA_boolean_set(op->ptr, "save_as_render", FALSE); + op->customdata = MEM_mallocN(sizeof(simopts.im_format), __func__); memcpy(op->customdata, &simopts.im_format, sizeof(simopts.im_format)); @@ -1389,7 +1454,7 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) /* image template */ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &ptr); - uiTemplateImageSettings(layout, &ptr); + uiTemplateImageSettings(layout, &ptr, FALSE); /* main draw call */ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); @@ -1417,6 +1482,7 @@ void IMAGE_OT_save_as(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ + RNA_def_boolean(ot->srna, "save_as_render", 0, "Save As Render", "Apply render part of display transform when saving byte image"); RNA_def_boolean(ot->srna, "copy", 0, "Copy", "Create a new image file without modifying the current image in blender"); WM_operator_properties_filesel(ot, FOLDERFILE | IMAGEFILE | MOVIEFILE, FILE_SPECIAL, FILE_SAVE, @@ -1601,8 +1667,10 @@ static int image_new_exec(bContext *C, wmOperator *op) RNA_float_get_array(op->ptr, "color", color); alpha = RNA_boolean_get(op->ptr, "alpha"); - if (!floatbuf && scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) + if (!floatbuf) { + /* OCIO_TODO: perhaps we need to convert to display space, not just to sRGB */ linearrgb_to_srgb_v3_v3(color, color); + } if (!alpha) color[3] = 1.0f; @@ -1938,14 +2006,18 @@ typedef struct ImageSampleInfo { float *zfp; int draw; + int color_manage; + int use_default_view; } ImageSampleInfo; -static void image_sample_draw(const bContext *UNUSED(C), ARegion *ar, void *arg_info) +static void image_sample_draw(const bContext *C, ARegion *ar, void *arg_info) { ImageSampleInfo *info = arg_info; if (info->draw) { - /* no color management needed for images (color_manage=0) */ - ED_image_draw_info(ar, 0, info->channels, info->x, info->y, info->colp, info->colfp, info->zp, info->zfp); + Scene *scene = CTX_data_scene(C); + + ED_image_draw_info(scene, ar, info->color_manage, info->use_default_view, info->channels, + info->x, info->y, info->colp, info->colfp, info->zp, info->zfp); } } @@ -1975,13 +2047,7 @@ int ED_space_image_color_sample(SpaceImage *sima, ARegion *ar, int mval[2], floa if (ibuf->rect_float) { fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); - - if (ELEM(ibuf->profile, IB_PROFILE_LINEAR_RGB, IB_PROFILE_NONE)) { - linearrgb_to_srgb_v3_v3(r_col, fp); - } - else { - copy_v3_v3(r_col, fp); - } + linearrgb_to_srgb_v3_v3(r_col, fp); ret = TRUE; } else if (ibuf->rect) { @@ -2003,7 +2069,9 @@ static void image_sample_apply(bContext *C, wmOperator *op, wmEvent *event) ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock); ImageSampleInfo *info = op->customdata; float fx, fy; - + Scene *scene = CTX_data_scene(C); + CurveMapping *curve_mapping = scene->view_settings.curve_mapping; + if (ibuf == NULL) { ED_space_image_release_buffer(sima, lock); info->draw = 0; @@ -2016,6 +2084,7 @@ static void image_sample_apply(bContext *C, wmOperator *op, wmEvent *event) float *fp; unsigned char *cp; int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y); + Image *image = ED_space_image(sima); CLAMP(x, 0, ibuf->x - 1); CLAMP(y, 0, ibuf->y - 1); @@ -2029,7 +2098,9 @@ static void image_sample_apply(bContext *C, wmOperator *op, wmEvent *event) info->colfp = NULL; info->zp = NULL; info->zfp = NULL; - + + info->use_default_view = (image->flag & IMA_VIEW_AS_RENDER) ? FALSE : TRUE; + if (ibuf->rect) { cp = (unsigned char *)(ibuf->rect + y * ibuf->x + x); @@ -2044,6 +2115,8 @@ static void image_sample_apply(bContext *C, wmOperator *op, wmEvent *event) info->colf[2] = (float)cp[2] / 255.0f; info->colf[3] = (float)cp[3] / 255.0f; info->colfp = info->colf; + + info->color_manage = FALSE; } if (ibuf->rect_float) { fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); @@ -2053,6 +2126,8 @@ static void image_sample_apply(bContext *C, wmOperator *op, wmEvent *event) info->colf[2] = fp[2]; info->colf[3] = fp[3]; info->colfp = info->colf; + + info->color_manage = TRUE; } if (ibuf->zbuf) { @@ -2063,25 +2138,21 @@ static void image_sample_apply(bContext *C, wmOperator *op, wmEvent *event) info->zf = ibuf->zbuf_float[y * ibuf->x + x]; info->zfp = &info->zf; } - - if (sima->cumap && ibuf->channels == 4) { + + if (curve_mapping && ibuf->channels == 4) { /* we reuse this callback for set curves point operators */ if (RNA_struct_find_property(op->ptr, "point")) { int point = RNA_enum_get(op->ptr, "point"); if (point == 1) { - curvemapping_set_black_white(sima->cumap, NULL, info->colfp); - if (ibuf->rect_float) - curvemapping_do_ibuf(sima->cumap, ibuf); + curvemapping_set_black_white(curve_mapping, NULL, info->colfp); } else if (point == 0) { - curvemapping_set_black_white(sima->cumap, info->colfp, NULL); - if (ibuf->rect_float) - curvemapping_do_ibuf(sima->cumap, ibuf); + curvemapping_set_black_white(curve_mapping, info->colfp, NULL); } } } - + // XXX node curve integration .. #if 0 { @@ -2211,7 +2282,7 @@ static int image_sample_line_exec(bContext *C, wmOperator *op) hist->co[1][0] = x2f; hist->co[1][1] = y2f; - BKE_histogram_update_sample_line(hist, ibuf, (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) != 0); + BKE_histogram_update_sample_line(hist, ibuf, &scene->view_settings, &scene->display_settings); /* reset y zoom */ hist->ymax = 1.0f; @@ -2306,7 +2377,8 @@ static int image_record_composite_apply(bContext *C, wmOperator *op) BKE_image_all_free_anim_ibufs(scene->r.cfra); ntreeCompositTagAnimated(scene->nodetree); - ntreeCompositExecTree(scene->nodetree, &scene->r, 0, scene->r.cfra != rcd->old_cfra); /* 1 is no previews */ + ntreeCompositExecTree(scene->nodetree, &scene->r, 0, scene->r.cfra != rcd->old_cfra, + &scene->view_settings, &scene->display_settings); /* 1 is no previews */ ED_area_tag_redraw(CTX_wm_area(C)); diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 91a0cba2555..5b288bd6149 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -195,9 +195,7 @@ static SpaceLink *image_new(const bContext *UNUSED(C)) static void image_free(SpaceLink *sl) { SpaceImage *simage = (SpaceImage *) sl; - - if (simage->cumap) - curvemapping_free(simage->cumap); + scopes_free(&simage->scopes); } @@ -217,8 +215,6 @@ static SpaceLink *image_duplicate(SpaceLink *sl) SpaceImage *simagen = MEM_dupallocN(sl); /* clear or remove stuff from old */ - if (simagen->cumap) - simagen->cumap = curvemapping_copy(simagen->cumap); scopes_new(&simagen->scopes); @@ -443,6 +439,7 @@ static void image_listener(ScrArea *sa, wmNotifier *wmn) break; case ND_MODE: case ND_RENDER_RESULT: + case ND_RENDER_OPTIONS: case ND_COMPO_RESULT: if (ED_space_image_show_render(sima)) image_scopes_tag_refresh(sa); @@ -652,7 +649,7 @@ static void image_main_area_draw(const bContext *C, ARegion *ar) image_main_area_set_view2d(sima, ar); /* we draw image in pixelspace */ - draw_image_main(sima, ar, scene); + draw_image_main(C, ar); /* and uvs in 0.0-1.0 space */ UI_view2d_view_ortho(v2d); @@ -776,9 +773,9 @@ static void image_scope_area_draw(const bContext *C, ARegion *ar) ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock); if (ibuf) { if (!sima->scopes.ok) { - BKE_histogram_update_sample_line(&sima->sample_line_hist, ibuf, scene->r.color_mgt_flag & R_COLOR_MANAGEMENT); + BKE_histogram_update_sample_line(&sima->sample_line_hist, ibuf, &scene->view_settings, &scene->display_settings); } - scopes_update(&sima->scopes, ibuf, scene->r.color_mgt_flag & R_COLOR_MANAGEMENT); + scopes_update(&sima->scopes, ibuf, &scene->view_settings, &scene->display_settings); } ED_space_image_release_buffer(sima, lock); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 4c46f5c4c62..1f266c98020 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -63,6 +63,7 @@ #include "UI_resources.h" +#include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -2023,7 +2024,7 @@ static void node_composit_buts_file_output_details(uiLayout *layout, bContext *C int multilayer = (RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER); node_composit_buts_file_output(layout, C, ptr); - uiTemplateImageSettings(layout, &imfptr); + uiTemplateImageSettings(layout, &imfptr, TRUE); uiItemS(layout); @@ -2082,7 +2083,7 @@ static void node_composit_buts_file_output_details(uiLayout *layout, bContext *C col = uiLayoutColumn(layout, FALSE); uiLayoutSetActive(col, RNA_boolean_get(&active_input_ptr, "use_node_format") == FALSE); - uiTemplateImageSettings(col, &imfptr); + uiTemplateImageSettings(col, &imfptr, TRUE); } } } @@ -3051,7 +3052,7 @@ void ED_node_init_butfuncs(void) /* ************** Generic drawing ************** */ -void draw_nodespace_back_pix(ARegion *ar, SpaceNode *snode, int color_manage) +void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode) { if ((snode->flag & SNODE_BACKDRAW) && snode->treetype == NTREE_COMPOSIT) { @@ -3059,7 +3060,10 @@ void draw_nodespace_back_pix(ARegion *ar, SpaceNode *snode, int color_manage) void *lock; ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); if (ibuf) { + SpaceNode *snode = CTX_wm_space_node(C); float x, y; + unsigned char *display_buffer; + void *cache_handle; glMatrixMode(GL_PROJECTION); glPushMatrix(); @@ -3078,15 +3082,10 @@ void draw_nodespace_back_pix(ARegion *ar, SpaceNode *snode, int color_manage) x = (ar->winx - snode->zoom * ibuf->x) / 2 + snode->xof; y = (ar->winy - snode->zoom * ibuf->y) / 2 + snode->yof; - if (!ibuf->rect) { - if (color_manage) - ibuf->profile = IB_PROFILE_LINEAR_RGB; - else - ibuf->profile = IB_PROFILE_NONE; - IMB_rect_from_float(ibuf); - } - if (ibuf->rect) { + display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle); + + if (display_buffer) { if (snode->flag & (SNODE_SHOW_R | SNODE_SHOW_G | SNODE_SHOW_B)) { int ofs; @@ -3114,7 +3113,7 @@ void draw_nodespace_back_pix(ARegion *ar, SpaceNode *snode, int color_manage) #ifdef __BIG_ENDIAN__ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); #endif - glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_LUMINANCE, GL_UNSIGNED_INT, ibuf->rect); + glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_LUMINANCE, GL_UNSIGNED_INT, display_buffer); #ifdef __BIG_ENDIAN__ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0); @@ -3126,7 +3125,7 @@ void draw_nodespace_back_pix(ARegion *ar, SpaceNode *snode, int color_manage) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glPixelZoom(snode->zoom, snode->zoom); - glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, display_buffer); glPixelZoom(1.0f, 1.0f); glDisable(GL_BLEND); @@ -3134,12 +3133,14 @@ void draw_nodespace_back_pix(ARegion *ar, SpaceNode *snode, int color_manage) else { glPixelZoom(snode->zoom, snode->zoom); - glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, display_buffer); glPixelZoom(1.0f, 1.0f); } } + IMB_display_buffer_release(cache_handle); + /** @note draw selected info on backdrop */ if (snode->edittree) { bNode *node = snode->edittree->nodes.first; diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index f1a91df6f53..7d35f566140 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -1091,8 +1091,6 @@ void drawnodespace(const bContext *C, ARegion *ar, View2D *v2d) { View2DScrollers *scrollers; SpaceNode *snode = CTX_wm_space_node(C); - Scene *scene = CTX_data_scene(C); - int color_manage = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT; bNodeLinkDrag *nldrag; LinkData *linkdata; @@ -1118,7 +1116,7 @@ void drawnodespace(const bContext *C, ARegion *ar, View2D *v2d) UI_view2d_multi_grid_draw(v2d, 25.0f, 5, 2); /* backdrop */ - draw_nodespace_back_pix(ar, snode, color_manage); + draw_nodespace_back_pix(C, ar, snode); /* nodes */ snode_set_context(snode, CTX_data_scene(C)); diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 9e04bd36a1f..cc386da2e93 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -148,8 +148,9 @@ static void compo_startjob(void *cjv, short *stop, short *do_update, float *prog { CompoJob *cj = cjv; bNodeTree *ntree = cj->localtree; + Scene *scene = cj->scene; - if (cj->scene->use_nodes == FALSE) + if (scene->use_nodes == FALSE) return; cj->stop = stop; @@ -165,7 +166,7 @@ static void compo_startjob(void *cjv, short *stop, short *do_update, float *prog // XXX BIF_store_spare(); - ntreeCompositExecTree(ntree, &cj->scene->r, 0, 1); /* 1 is do_previews */ + ntreeCompositExecTree(ntree, &cj->scene->r, 0, 1, &scene->view_settings, &scene->display_settings); /* 1 is do_previews */ ntree->test_break = NULL; ntree->stats_draw = NULL; diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index 2bb550d1a63..36ebddc8d22 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -125,7 +125,7 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link); void node_draw_link_bezier(View2D *v2d, SpaceNode *snode, bNodeLink *link, int th_col1, int do_shaded, int th_col2, int do_triple, int th_col3); int node_link_bezier_points(View2D * v2d, SpaceNode * snode, bNodeLink * link, float coord_array[][2], int resol); // void node_draw_link_straight(View2D *v2d, SpaceNode *snode, bNodeLink *link, int th_col1, int do_shaded, int th_col2, int do_triple, int th_col3 ); -void draw_nodespace_back_pix(ARegion *ar, SpaceNode *snode, int color_manage); +void draw_nodespace_back_pix(const struct bContext *C, ARegion *ar, SpaceNode *snode); /* node_add.c */ diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c index e89e798a6fa..8e38247348f 100644 --- a/source/blender/editors/space_node/node_view.c +++ b/source/blender/editors/space_node/node_view.c @@ -327,12 +327,12 @@ typedef struct ImageSampleInfo { void *draw_handle; int x, y; int channels; - int color_manage; unsigned char col[4]; float colf[4]; int draw; + int color_manage; } ImageSampleInfo; static void sample_draw(const bContext *C, ARegion *ar, void *arg_info) @@ -341,7 +341,7 @@ static void sample_draw(const bContext *C, ARegion *ar, void *arg_info) ImageSampleInfo *info = arg_info; if (info->draw) { - ED_image_draw_info(ar, (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT), info->channels, + ED_image_draw_info(scene, ar, info->color_manage, FALSE, info->channels, info->x, info->y, info->col, info->colf, NULL, NULL /* zbuf - unused for nodes */ ); @@ -381,12 +381,7 @@ int ED_space_node_color_sample(SpaceNode *snode, ARegion *ar, int mval[2], float if (ibuf->rect_float) { fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); /* IB_PROFILE_NONE is default but infact its linear */ - if (ELEM(ibuf->profile, IB_PROFILE_LINEAR_RGB, IB_PROFILE_NONE)) { - linearrgb_to_srgb_v3_v3(r_col, fp); - } - else { - copy_v3_v3(r_col, fp); - } + linearrgb_to_srgb_v3_v3(r_col, fp); ret = TRUE; } else if (ibuf->rect) { @@ -419,10 +414,6 @@ static void sample_apply(bContext *C, wmOperator *op, wmEvent *event) } if (!ibuf->rect) { - if (info->color_manage) - ibuf->profile = IB_PROFILE_LINEAR_RGB; - else - ibuf->profile = IB_PROFILE_NONE; IMB_rect_from_float(ibuf); } @@ -457,6 +448,8 @@ static void sample_apply(bContext *C, wmOperator *op, wmEvent *event) info->colf[1] = (float)cp[1] / 255.0f; info->colf[2] = (float)cp[2] / 255.0f; info->colf[3] = (float)cp[3] / 255.0f; + + info->color_manage = FALSE; } if (ibuf->rect_float) { fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); @@ -465,6 +458,8 @@ static void sample_apply(bContext *C, wmOperator *op, wmEvent *event) info->colf[1] = fp[1]; info->colf[2] = fp[2]; info->colf[3] = fp[3]; + + info->color_manage = TRUE; } ED_node_sample_set(info->colf); diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 0c316450c29..7363bf2fbd8 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -52,6 +52,7 @@ #include "BKE_sound.h" +#include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "BIF_gl.h" @@ -68,6 +69,9 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "WM_api.h" +#include "WM_types.h" + /* own include */ #include "sequencer_intern.h" @@ -846,6 +850,50 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int return ibuf; } +static void sequencer_check_scopes(SequencerScopes *scopes, ImBuf *ibuf) +{ + if (scopes->reference_ibuf != ibuf) { + if (scopes->zebra_ibuf) { + IMB_freeImBuf(scopes->zebra_ibuf); + scopes->zebra_ibuf = NULL; + } + + if (scopes->waveform_ibuf) { + IMB_freeImBuf(scopes->waveform_ibuf); + scopes->waveform_ibuf = NULL; + } + + if (scopes->sep_waveform_ibuf) { + IMB_freeImBuf(scopes->sep_waveform_ibuf); + scopes->sep_waveform_ibuf = NULL; + } + + if (scopes->vector_ibuf) { + IMB_freeImBuf(scopes->vector_ibuf); + scopes->vector_ibuf = NULL; + } + + if (scopes->histogram_ibuf) { + IMB_freeImBuf(scopes->histogram_ibuf); + scopes->histogram_ibuf = NULL; + } + } +} + +static ImBuf *sequencer_make_scope(Scene *scene, ImBuf *ibuf, ImBuf *(*make_scope_cb) (ImBuf *ibuf)) +{ + ImBuf *display_ibuf = IMB_dupImBuf(ibuf); + ImBuf *scope; + + IMB_colormanagement_imbuf_make_display_space(display_ibuf, &scene->view_settings, &scene->display_settings); + + scope = make_scope_cb(display_ibuf); + + IMB_freeImBuf(display_ibuf); + + return scope; +} + void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq, int cfra, int frame_ofs, int draw_overlay) { struct Main *bmain = CTX_data_main(C); @@ -859,6 +907,8 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq float col[3]; GLuint texid; GLuint last_texid; + unsigned char *display_buffer; + void *cache_handle = NULL; render_size = sseq->render_size; if (render_size == 0) { @@ -906,38 +956,64 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq if (ibuf->rect == NULL && ibuf->rect_float == NULL) return; - - switch (sseq->mainb) { - case SEQ_DRAW_IMG_IMBUF: - if (sseq->zebra != 0) { - scope = make_zebra_view_from_ibuf(ibuf, sseq->zebra); - } - break; - case SEQ_DRAW_IMG_WAVEFORM: - if ((sseq->flag & SEQ_DRAW_COLOR_SEPARATED) != 0) { - scope = make_sep_waveform_view_from_ibuf(ibuf); - } - else { - scope = make_waveform_view_from_ibuf(ibuf); - } - break; - case SEQ_DRAW_IMG_VECTORSCOPE: - scope = make_vectorscope_view_from_ibuf(ibuf); - break; - case SEQ_DRAW_IMG_HISTOGRAM: - scope = make_histogram_view_from_ibuf(ibuf); - break; + + if (sseq->mainb != SEQ_DRAW_IMG_IMBUF || sseq->zebra != 0) { + SequencerScopes *scopes = &sseq->scopes; + + sequencer_check_scopes(scopes, ibuf); + + switch (sseq->mainb) { + case SEQ_DRAW_IMG_IMBUF: + if (!scopes->zebra_ibuf) { + ImBuf *display_ibuf = IMB_dupImBuf(ibuf); + + IMB_colormanagement_imbuf_make_display_space(display_ibuf, &scene->view_settings, &scene->display_settings); + scopes->zebra_ibuf = make_zebra_view_from_ibuf(display_ibuf, sseq->zebra); + IMB_freeImBuf(display_ibuf); + } + scope = scopes->zebra_ibuf; + break; + case SEQ_DRAW_IMG_WAVEFORM: + if ((sseq->flag & SEQ_DRAW_COLOR_SEPARATED) != 0) { + if (!scopes->sep_waveform_ibuf) + scopes->sep_waveform_ibuf = sequencer_make_scope(scene, ibuf, make_sep_waveform_view_from_ibuf); + scope = scopes->sep_waveform_ibuf; + } + else { + if (!scopes->waveform_ibuf) + scopes->waveform_ibuf = sequencer_make_scope(scene, ibuf, make_waveform_view_from_ibuf); + scope = scopes->waveform_ibuf; + } + break; + case SEQ_DRAW_IMG_VECTORSCOPE: + if (!scopes->vector_ibuf) + scopes->vector_ibuf = sequencer_make_scope(scene, ibuf, make_vectorscope_view_from_ibuf); + scope = scopes->vector_ibuf; + break; + case SEQ_DRAW_IMG_HISTOGRAM: + if (!scopes->histogram_ibuf) + scopes->histogram_ibuf = sequencer_make_scope(scene, ibuf, make_histogram_view_from_ibuf); + scope = scopes->histogram_ibuf; + break; + } + + scopes->reference_ibuf = ibuf; } if (scope) { IMB_freeImBuf(ibuf); ibuf = scope; - } - if (ibuf->rect_float && ibuf->rect == NULL) { - IMB_rect_from_float(ibuf); + if (ibuf->rect_float && ibuf->rect == NULL) { + IMB_rect_from_float(ibuf); + } + + display_buffer = (unsigned char *)ibuf->rect; } - + else { + display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle); + } + /* setting up the view - actual drawing starts here */ UI_view2d_view_ortho(v2d); @@ -950,7 +1026,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ibuf->x, ibuf->y, 0, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ibuf->x, ibuf->y, 0, GL_RGBA, GL_UNSIGNED_BYTE, display_buffer); glBegin(GL_QUADS); if (draw_overlay) { @@ -1030,7 +1106,8 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq /* draw grease-pencil (image aligned) */ draw_gpencil_2dimage(C); - IMB_freeImBuf(ibuf); + if (!scope) + IMB_freeImBuf(ibuf); /* ortho at pixel level */ UI_view2d_view_restore(C); @@ -1065,6 +1142,8 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq } } + if (cache_handle) + IMB_display_buffer_release(cache_handle); } #if 0 diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c index 1655b3ec7bc..d09d6a29c10 100644 --- a/source/blender/editors/space_sequencer/sequencer_scopes.c +++ b/source/blender/editors/space_sequencer/sequencer_scopes.c @@ -460,12 +460,28 @@ static ImBuf *make_histogram_view_from_ibuf_byte(ImBuf *ibuf) memset(bins, 0, sizeof(bins)); + #pragma omp parallel for shared(bins, src, ibuf) private(x, y) if (ibuf->y >= 256) for (y = 0; y < ibuf->y; y++) { + unsigned int cur_bins[3][512]; + + memset(cur_bins, 0, sizeof(cur_bins)); + for (x = 0; x < ibuf->x; x++) { - bins[0][*src++]++; - bins[1][*src++]++; - bins[2][*src++]++; - src++; + unsigned char *pixel = src + (y * ibuf->x + x) * 4; + + cur_bins[0][pixel[0]]++; + cur_bins[1][pixel[1]]++; + cur_bins[2][pixel[2]]++; + } + + #pragma omp critical + { + int i; + for (i = 0; i < 512; i++) { + bins[0][i] += cur_bins[0][i]; + bins[1][i] += cur_bins[1][i]; + bins[2][i] += cur_bins[2][i]; + } } } @@ -490,7 +506,7 @@ static ImBuf *make_histogram_view_from_ibuf_byte(ImBuf *ibuf) return rval; } -static int get_bin_float(float f) +BLI_INLINE int get_bin_float(float f) { if (f < -0.25f) { return 0; @@ -512,12 +528,28 @@ static ImBuf *make_histogram_view_from_ibuf_float(ImBuf *ibuf) memset(bins, 0, sizeof(bins)); + #pragma omp parallel for shared(bins, src, ibuf) private(x, y) if (ibuf->y >= 256) for (y = 0; y < ibuf->y; y++) { + unsigned int cur_bins[3][512]; + + memset(cur_bins, 0, sizeof(cur_bins)); + for (x = 0; x < ibuf->x; x++) { - bins[0][get_bin_float(*src++)]++; - bins[1][get_bin_float(*src++)]++; - bins[2][get_bin_float(*src++)]++; - src++; + float *pixel = src + (y * ibuf->x + x) * 4; + + cur_bins[0][get_bin_float(pixel[0])]++; + cur_bins[1][get_bin_float(pixel[1])]++; + cur_bins[2][get_bin_float(pixel[2])]++; + } + + #pragma omp critical + { + int i; + for (i = 0; i < 512; i++) { + bins[0][i] += cur_bins[0][i]; + bins[1][i] += cur_bins[1][i]; + bins[2][i] += cur_bins[2][i]; + } } } diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index f5a13e4efb7..3c3489115a4 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -49,6 +49,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_colormanagement.h" #include "UI_view2d.h" @@ -70,6 +71,7 @@ typedef struct ImageSampleInfo { float *colfp; int draw; +int color_manage; } ImageSampleInfo; static void sample_draw(const bContext *C, ARegion *ar, void *arg_info) @@ -78,8 +80,8 @@ static void sample_draw(const bContext *C, ARegion *ar, void *arg_info) ImageSampleInfo *info = arg_info; if (info->draw) { - ED_image_draw_info(ar, (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT), info->channels, - info->x, info->y, info->col, info->colf, NULL, NULL); + ED_image_draw_info(scene, ar, info->color_manage, FALSE, info->channels, + info->x, info->y, info->colp, info->colfp, NULL, NULL); } } @@ -131,6 +133,8 @@ static void sample_apply(bContext *C, wmOperator *op, wmEvent *event) info->colf[2] = (float)cp[2] / 255.0f; info->colf[3] = (float)cp[3] / 255.0f; info->colfp = info->colf; + + info->color_manage = FALSE; } if (ibuf->rect_float) { fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); @@ -140,6 +144,11 @@ static void sample_apply(bContext *C, wmOperator *op, wmEvent *event) info->colf[2] = fp[2]; info->colf[3] = fp[3]; info->colfp = info->colf; + + /* sequencer's image buffers are in non-linear space, need to make them linear */ + BKE_sequencer_pixel_from_sequencer_space_v4(scene, info->colf); + + info->color_manage = TRUE; } } else { diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index f7362aab7aa..8ac50a57b10 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -57,8 +57,19 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "IMB_imbuf.h" + #include "sequencer_intern.h" // own include +/**************************** common state *****************************/ + +static void sequencer_scopes_tag_refresh(ScrArea *sa) +{ + SpaceSeq *sseq = (SpaceSeq *)sa->spacedata.first; + + sseq->scopes.reference_ibuf = NULL; +} + /* ******************** manage regions ********************* */ ARegion *sequencer_has_buttons_region(ScrArea *sa) @@ -183,12 +194,27 @@ static SpaceLink *sequencer_new(const bContext *C) } /* not spacelink itself */ -static void sequencer_free(SpaceLink *UNUSED(sl)) +static void sequencer_free(SpaceLink *sl) { -// SpaceSeq *sseq= (SpaceSequencer*) sl; - + SpaceSeq *sseq= (SpaceSeq *) sl; + SequencerScopes *scopes = &sseq->scopes; + // XXX if (sseq->gpd) BKE_gpencil_free(sseq->gpd); + if (scopes->zebra_ibuf) + IMB_freeImBuf(scopes->zebra_ibuf); + + if (scopes->waveform_ibuf) + IMB_freeImBuf(scopes->waveform_ibuf); + + if (scopes->sep_waveform_ibuf) + IMB_freeImBuf(scopes->sep_waveform_ibuf); + + if (scopes->vector_ibuf) + IMB_freeImBuf(scopes->vector_ibuf); + + if (scopes->histogram_ibuf) + IMB_freeImBuf(scopes->histogram_ibuf); } @@ -290,7 +316,24 @@ static SpaceLink *sequencer_duplicate(SpaceLink *sl) return (SpaceLink *)sseqn; } - +static void sequencer_listener(ScrArea *sa, wmNotifier *wmn) +{ + /* context changes */ + switch (wmn->category) { + case NC_SCENE: + switch (wmn->data) { + case ND_FRAME: + case ND_SEQUENCER: + sequencer_scopes_tag_refresh(sa); + break; + } + break; + case NC_SPACE: + if (wmn->data == ND_SPACE_SEQUENCER) + sequencer_scopes_tag_refresh(sa); + break; + } +} /* *********************** sequencer (main) region ************************ */ /* add handlers, stuff you only do once or on area/region changes */ @@ -610,6 +653,7 @@ void ED_spacetype_sequencer(void) st->context = sequencer_context; st->dropboxes = sequencer_dropboxes; st->refresh = sequencer_refresh; + st->listener = sequencer_listener; /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer region"); diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index 895b256ab84..ca4f00be2d5 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -372,7 +372,15 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O Gtexdraw.ob = ob; Gtexdraw.is_tex = is_tex; - Gtexdraw.color_profile = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT; + + /* OCIO_TODO: for now assume OpenGL is always doing color management and working in sRGB space + * supporting for real display conversion could be nice here, but it's a bit challenging + * since all the shaders should be aware of such a transform + * perhaps this flag could be completely removed before release in separated commit and + * be re-implemented if real display transform would be needed + */ + Gtexdraw.color_profile = TRUE; + memcpy(Gtexdraw.obcol, obcol, sizeof(obcol)); set_draw_settings_cached(1, NULL, NULL, Gtexdraw); glShadeModel(GL_SMOOTH); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 8a334423ab0..5fbf1971349 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -69,6 +69,7 @@ #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" +#include "IMB_colormanagement.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -2375,7 +2376,7 @@ static void gpu_update_lamps_shadows(Scene *scene, View3D *v3d) mult_m4_m4m4(rv3d.persmat, rv3d.winmat, rv3d.viewmat); invert_m4_m4(rv3d.persinv, rv3d.viewinv); - ED_view3d_draw_offscreen(scene, v3d, &ar, winsize, winsize, viewmat, winmat, FALSE); + ED_view3d_draw_offscreen(scene, v3d, &ar, winsize, winsize, viewmat, winmat, FALSE, FALSE); GPU_lamp_shadow_buffer_unbind(shadow->lamp); v3d->drawtype = drawtype; @@ -2514,7 +2515,7 @@ static void view3d_main_area_setup_view(Scene *scene, View3D *v3d, ARegion *ar, void ED_view3d_draw_offscreen(Scene *scene, View3D *v3d, ARegion *ar, int winx, int winy, float viewmat[][4], float winmat[][4], - int do_bgpic) + int do_bgpic, int colormanage_background) { RegionView3D *rv3d = ar->regiondata; Base *base; @@ -2553,10 +2554,25 @@ void ED_view3d_draw_offscreen(Scene *scene, View3D *v3d, ARegion *ar, /* set background color, fallback on the view background color * (if active clip is set but frame is failed to load fallback to horizon color as background) */ if (scene->world) { - if (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) + /* NOTE: currently OpenGL is supposed to always work in sRGB space and do not + * apply any tonemaps since it's really tricky to support for all features (GLSL, textures, etc) + * but due to compatibility issues background is being affected display transform, so we can + * emulate behavior of disabled colro management + * but this function is also used for sequencer's scene strips which shouldn't be affected by + * tonemaps now and should be purely sRGB, that's why we've got this colormanage_background + * we can drop this flag in cost of some compatibility loss -- background wouldn't be + * color managed in 3d viewport + * same goes to opengl rendering, where color profile should be applied as very final step + */ + + if (colormanage_background) { + IMB_colormanagement_pixel_to_display_space_v3(backcol, &scene->world->horr, &scene->view_settings, + &scene->display_settings); + } + else { linearrgb_to_srgb_v3_v3(backcol, &scene->world->horr); - else - copy_v3_v3(backcol, &scene->world->horr); + } + glClearColor(backcol[0], backcol[1], backcol[2], 0.0); } else { @@ -2661,7 +2677,8 @@ void ED_view3d_draw_offscreen(Scene *scene, View3D *v3d, ARegion *ar, /* utility func for ED_view3d_draw_offscreen */ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, - int sizex, int sizey, unsigned int flag, int draw_background, char err_out[256]) + int sizex, int sizey, unsigned int flag, int draw_background, + int colormanage_background, char err_out[256]) { RegionView3D *rv3d = ar->regiondata; ImBuf *ibuf; @@ -2686,10 +2703,10 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, BKE_camera_params_compute_viewplane(¶ms, sizex, sizey, scene->r.xasp, scene->r.yasp); BKE_camera_params_compute_matrix(¶ms); - ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, params.winmat, draw_background); + ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, params.winmat, draw_background, colormanage_background); } else { - ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, NULL, draw_background); + ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, NULL, draw_background, colormanage_background); } /* read in pixels & stamp */ @@ -2714,7 +2731,8 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, /* creates own 3d views, used by the sequencer */ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int width, int height, - unsigned int flag, int drawtype, int draw_background, char err_out[256]) + unsigned int flag, int drawtype, int draw_background, + int colormanage_background, char err_out[256]) { View3D v3d = {NULL}; ARegion ar = {NULL}; @@ -2753,7 +2771,8 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int w mult_m4_m4m4(rv3d.persmat, rv3d.winmat, rv3d.viewmat); invert_m4_m4(rv3d.persinv, rv3d.viewinv); - return ED_view3d_draw_offscreen_imbuf(scene, &v3d, &ar, width, height, flag, draw_background, err_out); + return ED_view3d_draw_offscreen_imbuf(scene, &v3d, &ar, width, height, flag, + draw_background, colormanage_background, err_out); // seq_view3d_cb(scene, cfra, render_size, seqrectx, seqrecty); } @@ -2936,10 +2955,9 @@ static void view3d_main_area_draw_objects(const bContext *C, ARegion *ar, const /* clear background */ if ((v3d->flag2 & V3D_RENDER_OVERRIDE) && scene->world) { - if (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) - linearrgb_to_srgb_v3_v3(backcol, &scene->world->horr); - else - copy_v3_v3(backcol, &scene->world->horr); + IMB_colormanagement_pixel_to_display_space_v3(backcol, &scene->world->horr, &scene->view_settings, + &scene->display_settings); + glClearColor(backcol[0], backcol[1], backcol[2], 0.0); } else @@ -3163,6 +3181,17 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar) const char *grid_unit = NULL; int draw_border = (rv3d->persp == RV3D_CAMOB && (scene->r.mode & R_BORDER)); + /* --- until we get a clue and make viewport threadsafe (temp mango change for stability) */ + if (G.is_rendering) { + ED_region_pixelspace(ar); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + UI_ThemeClearColor(TH_BACK); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + BLF_draw_default(10,10,0, "do do da da.. da da.. da da.. can't touch this! it's render time", 512); + return; + } + /* --- end temp mango change */ + /* draw viewport using opengl */ if (v3d->drawtype != OB_RENDER || !view3d_main_area_do_render_draw(C) || draw_border) { view3d_main_area_draw_objects(C, ar, &grid_unit); diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index ab3ab0a133e..9b027f7906d 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -492,7 +492,7 @@ int GPU_verify_image(Image *ima, ImageUser *iuser, int tftile, int compare, int } /* TODO unneeded when float images are correctly treated as linear always */ - if (!ncd && ibuf->profile == IB_PROFILE_LINEAR_RGB) + if (!ncd) do_color_management = TRUE; if (ibuf->rect==NULL) @@ -535,7 +535,7 @@ int GPU_verify_image(Image *ima, ImageUser *iuser, int tftile, int compare, int if (do_color_management) { srgb_frect = MEM_mallocN(ibuf->x*ibuf->y*sizeof(float)*4, "floar_buf_col_cor"); IMB_buffer_float_from_float(srgb_frect, ibuf->rect_float, - ibuf->channels, IB_PROFILE_SRGB, ibuf->profile, 0, + ibuf->channels, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB, 0, ibuf->x, ibuf->y, ibuf->x, ibuf->x); /* clamp buffer colors to 1.0 to avoid artifacts due to glu for hdr images */ IMB_buffer_float_clamp(srgb_frect, ibuf->x, ibuf->y); @@ -560,7 +560,7 @@ int GPU_verify_image(Image *ima, ImageUser *iuser, int tftile, int compare, int if (do_color_management) { frect = srgb_frect = MEM_mallocN(ibuf->x*ibuf->y*sizeof(*srgb_frect)*4, "floar_buf_col_cor"); IMB_buffer_float_from_float(srgb_frect, ibuf->rect_float, - ibuf->channels, IB_PROFILE_SRGB, ibuf->profile, 0, + ibuf->channels, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB, 0, ibuf->x, ibuf->y, ibuf->x, ibuf->x); /* clamp buffer colors to 1.0 to avoid artifacts due to glu for hdr images */ IMB_buffer_float_clamp(srgb_frect, ibuf->x, ibuf->y); @@ -901,7 +901,7 @@ void GPU_paint_update_image(Image *ima, int x, int y, int w, int h, int mipmap) glGetIntegerv(GL_UNPACK_SKIP_ROWS, &skip_rows); /* if color correction is needed, we must update the part that needs updating. */ - if (ibuf->rect_float && (!U.use_16bit_textures || (ibuf->profile == IB_PROFILE_LINEAR_RGB))) { + if (ibuf->rect_float) { float *buffer = MEM_mallocN(w*h*sizeof(float)*4, "temp_texpaint_float_buf"); IMB_partial_rect_from_float(ibuf, buffer, x, y, w, h); @@ -1197,7 +1197,12 @@ void GPU_begin_object_materials(View3D *v3d, RegionView3D *rv3d, Scene *scene, O GPUMaterial *gpumat; GPUBlendMode alphablend; int a; - int gamma = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT; + + /* OCIO_TODO: assume color management is always enabled. could be nice to support real display transform here, + * but that's not so important and could be done later + */ + int gamma = TRUE; + int new_shading_nodes = BKE_scene_use_new_shading_nodes(scene); /* initialize state */ diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 61be4057843..dd92b561235 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -389,8 +389,12 @@ void gpu_material_add_node(GPUMaterial *material, GPUNode *node) int GPU_material_do_color_management(GPUMaterial *mat) { - return ((mat->scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) && - !((mat->scene->gm.flag & GAME_GLSL_NO_COLOR_MANAGEMENT))); + /* OCIO_TODO: for now assume scene always does color management. probably could be + * improved in the future to support real display transform + * also probably we'll need to get rid ofgame engine's color management flag + */ + + return !((mat->scene->gm.flag & GAME_GLSL_NO_COLOR_MANAGEMENT)); } static GPUNodeLink *lamp_get_visibility(GPUMaterial *mat, GPULamp *lamp, GPUNodeLink **lv, GPUNodeLink **dist) diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index aa4454562b4..8849265679a 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -30,6 +30,7 @@ set(INC ../blenlib ../blenloader ../makesdna + ../makesrna ../../../intern/guardedalloc ../../../intern/memutil ) @@ -45,6 +46,7 @@ set(SRC intern/anim_movie.c intern/bmp.c intern/cache.c + intern/colormanagement.c intern/divers.c intern/filetype.c intern/filter.c @@ -67,6 +69,7 @@ set(SRC intern/util.c intern/writeimage.c + IMB_colormanagement.h IMB_imbuf.h IMB_imbuf_types.h IMB_moviecache.h @@ -180,6 +183,10 @@ if(WITH_IMAGE_HDR) add_definitions(-DWITH_HDR) endif() +list(APPEND INC + ../../../intern/opencolorio +) + if(WIN32) list(APPEND INC ../../../intern/utfconv diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h new file mode 100644 index 00000000000..d09007985b0 --- /dev/null +++ b/source/blender/imbuf/IMB_colormanagement.h @@ -0,0 +1,155 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 by Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Xavier Thomas, + * Lukas Toenne, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifndef IMB_COLORMANAGEMENT_H +#define IMB_COLORMANAGEMENT_H + +#define BCM_CONFIG_FILE "config.ocio" + +struct bContext; +struct ColorManagedColorspaceSettings; +struct ColorManagedDisplaySettings; +struct ColorManagedViewSettings; +struct ColormanageProcessor; +struct EnumPropertyItem; +struct ImBuf; +struct Main; +struct rcti; +struct PartialBufferUpdateContext; +struct wmWindow; +struct Scene; + +struct ColorSpace; +struct ColorManagedDisplay; + +/* ** Initialization / De-initialization ** */ + +void IMB_colormanagement_init(void); +void IMB_colormanagement_exit(void); + +/* ** Generic functions ** */ + +void IMB_colormanagement_check_file_config(struct Main *bmain); + +void IMB_colormanagement_validate_settings(struct ColorManagedDisplaySettings *display_settings, + struct ColorManagedViewSettings *view_settings); + +const char *IMB_colormanagement_role_colorspace_name_get(int role); + +/* ** Color space transformation functions ** */ +void IMB_colormanagement_transform(float *buffer, int width, int height, int channels, + const char *from_colorspace, const char *to_colorspace, int predivide); +void IMB_colormanagement_transform_threaded(float *buffer, int width, int height, int channels, + const char *from_colorspace, const char *to_colorspace, int predivide); +void IMB_colormanagement_transform_v4(float pixel[4], const char *from_colorspace, const char *to_colorspace); + +void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], struct ColorSpace *colorspace); +void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], struct ColorSpace *colorspace); + +void IMB_colormanagement_colorspace_to_scene_linear(float *buffer, int width, int height, int channels, struct ColorSpace *colorspace, int predivide); + +void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], struct ColorManagedDisplay *display); +void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], struct ColorManagedDisplay *display); + +void IMB_colormanagement_pixel_to_display_space_v4(float result[4], const float pixel[4], const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings); + +void IMB_colormanagement_pixel_to_display_space_v3(float result[3], const float pixel[3], const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings); + +void IMB_colormanagement_imbuf_assign_float_space(struct ImBuf *ibuf, struct ColorManagedColorspaceSettings *colorspace_settings); + +void IMB_colormanagement_imbuf_make_display_space(struct ImBuf *ibuf, const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings); + +/* ** Public display buffers interfaces ** */ + +unsigned char *IMB_display_buffer_acquire(struct ImBuf *ibuf, const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings, void **cache_handle); +unsigned char *IMB_display_buffer_acquire_ctx(const struct bContext *C, struct ImBuf *ibuf, void **cache_handle); + +void IMB_display_buffer_to_imbuf_rect(struct ImBuf *ibuf, const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings); + +void IMB_display_buffer_transform_apply(unsigned char *display_buffer, float *linear_buffer, int width, int height, + int channels, const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings, int predivide); + +void IMB_display_buffer_release(void *cache_handle); + +/* ** Display funcrions ** */ +int IMB_colormanagement_display_get_named_index(const char *name); +const char *IMB_colormanagement_display_get_indexed_name(int index); +const char *IMB_colormanagement_display_get_default_name(void); +struct ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name); + +/* ** View funcrions ** */ +int IMB_colormanagement_view_get_named_index(const char *name); +const char *IMB_colormanagement_view_get_indexed_name(int index); + +/* ** Color space functions ** */ +int IMB_colormanagement_colorspace_get_named_index(const char *name); +const char *IMB_colormanagement_colorspace_get_indexed_name(int index); +const char *IMB_colormanagement_view_get_default_name(const char *display_name); + +void IMB_colormanagment_colorspace_from_ibuf_ftype(struct ColorManagedColorspaceSettings *colorspace_settings, struct ImBuf *ibuf); + +/* ** RNA helper functions ** */ +void IMB_colormanagement_display_items_add(struct EnumPropertyItem **items, int *totitem); +void IMB_colormanagement_view_items_add(struct EnumPropertyItem **items, int *totitem, const char *display_name); +void IMB_colormanagement_colorspace_items_add(struct EnumPropertyItem **items, int *totitem); + +/* ** Tile-based buffer management ** */ +void IMB_partial_display_buffer_update(struct ImBuf *ibuf, const float *linear_buffer, const unsigned char *buffer_byte, + 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); + +/* ** Pixel processor functions ** */ +struct ColormanageProcessor *IMB_colormanagement_display_processor_new(const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings); +struct ColormanageProcessor *IMB_colormanagement_colorspace_processor_new(const char *from_colorspace, const char *to_colorspace); +void IMB_colormanagement_processor_apply_v4(struct ColormanageProcessor *cm_processor, float pixel[4]); +void IMB_colormanagement_processor_apply_v3(struct ColormanageProcessor *cm_processor, float pixel[3]); +void IMB_colormanagement_processor_apply(struct ColormanageProcessor *cm_processor, float *buffer, int width, int height, + int channels, int predivide); +void IMB_colormanagement_processor_free(struct ColormanageProcessor *cm_processor); + +/* Roles */ +enum { + COLOR_ROLE_SCENE_LINEAR = 0, + COLOR_ROLE_COLOR_PICKING, + COLOR_ROLE_TEXTURE_PAINTING, + COLOR_ROLE_DEFAULT_SEQUENCER, + COLOR_ROLE_DEFAULT_BYTE, + COLOR_ROLE_DEFAULT_FLOAT, +}; + +#endif /* IMB_COLORMANAGEMENT_H */ diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index d23d42ddd0c..1503b9f6f67 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -70,6 +70,8 @@ #ifndef __IMB_IMBUF_H__ #define __IMB_IMBUF_H__ +#define IM_MAX_SPACE 64 + /** * * \attention defined in ??? @@ -82,6 +84,8 @@ struct ImBuf; */ struct anim; +struct ColorManagedDisplay; + /** * * \attention Defined in allocimbuf.c @@ -93,20 +97,19 @@ void IMB_exit(void); * * \attention Defined in readimage.c */ -struct ImBuf *IMB_ibImageFromMemory(unsigned char *mem, size_t size, - int flags, const char *descr); +struct ImBuf *IMB_ibImageFromMemory(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE], const char *descr); /** * * \attention Defined in readimage.c */ -struct ImBuf *IMB_testiffname(const char *filepath, int flags); +struct ImBuf *IMB_testiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]); /** * * \attention Defined in readimage.c */ -struct ImBuf *IMB_loadiffname(const char *filepath, int flags); +struct ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]); /** * @@ -245,7 +248,7 @@ int IMB_anim_get_fps(struct anim * anim, * * \attention Defined in anim_movie.c */ -struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex); +struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex, char colorspace[IM_MAX_SPACE]); void IMB_close_anim(struct anim *anim); void IMB_close_anim_proxies(struct anim *anim); @@ -372,7 +375,6 @@ void IMB_partial_rect_from_float(struct ImBuf *ibuf, float *buffer, int x, int y void IMB_float_from_rect(struct ImBuf *ibuf); void IMB_float_from_rect_simple(struct ImBuf *ibuf); /* no profile conversion */ /* note, check that the conversion exists, only some are supported */ -void IMB_convert_profile(struct ImBuf *ibuf, int profile); float *IMB_float_profile_ensure(struct ImBuf *ibuf, int profile, int *alloc); void IMB_color_to_bw(struct ImBuf *ibuf); void IMB_saturation(struct ImBuf *ibuf, float sat); @@ -417,7 +419,7 @@ void bilinear_interpolation_color_wrap(struct ImBuf *in, unsigned char *col, flo * * \attention defined in readimage.c */ -struct ImBuf *IMB_loadifffile(int file, int flags, const char *descr); +struct ImBuf *IMB_loadifffile(int file, int flags, char colorspace[IM_MAX_SPACE], const char *descr); /** * @@ -478,12 +480,12 @@ void IMB_freezbuffloatImBuf(struct ImBuf *ibuf); * \attention Defined in rectop.c */ void IMB_rectfill(struct ImBuf *drect, const float col[4]); -void IMB_rectfill_area(struct ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2); +void IMB_rectfill_area(struct ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2, struct ColorManagedDisplay *display); void IMB_rectfill_alpha(struct ImBuf *ibuf, const float value); /* this should not be here, really, we needed it for operating on render data, IMB_rectfill_area calls it */ void buf_rectfill_area(unsigned char *rect, float *rectf, int width, int height, - const float col[4], const int do_color_management, + const float col[4], struct ColorManagedDisplay *display, int x1, int y1, int x2, int y2); /* defined in metadata.c */ diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index be93d51d7d7..93e213dc486 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -101,7 +101,6 @@ typedef struct ImBuf { /* parameters used by conversion between byte and float */ float dither; /* random dither value, for conversion from float -> byte rect */ - short profile; /* color space/profile preset that the byte rect buffer represents */ /* mipmapping */ struct ImBuf *mipmap[IB_MIPMAP_LEVELS]; /* MipMap levels, a series of halved images */ @@ -127,6 +126,12 @@ typedef struct ImBuf { unsigned int encodedsize; /* Size of data written to encodedbuffer */ unsigned int encodedbuffersize; /* Size of encodedbuffer */ + /* color management */ + struct ColorSpace *rect_colorspace; /* color space of byte buffer */ + struct ColorSpace *float_colorspace; /* color space of float buffer, used by sequencer only */ + unsigned int *display_buffer_flags; /* array of per-display display buffers dirty flags */ + struct ColormanageCache *colormanage_cache; /* cache used by color management */ + /* information for compressed textures */ struct DDSData dds_data; } ImBuf; @@ -136,10 +141,11 @@ typedef struct ImBuf { * \brief userflags: Flags used internally by blender for imagebuffers */ -#define IB_BITMAPFONT (1 << 0) /* this image is a font */ -#define IB_BITMAPDIRTY (1 << 1) /* image needs to be saved is not the same as filename */ -#define IB_MIPMAP_INVALID (1 << 2) /* image mipmaps are invalid, need recreate */ -#define IB_RECT_INVALID (1 << 3) /* float buffer changed, needs recreation of byte rect */ +#define IB_BITMAPFONT (1 << 0) /* this image is a font */ +#define IB_BITMAPDIRTY (1 << 1) /* image needs to be saved is not the same as filename */ +#define IB_MIPMAP_INVALID (1 << 2) /* image mipmaps are invalid, need recreate */ +#define IB_RECT_INVALID (1 << 3) /* float buffer changed, needs recreation of byte rect */ +#define IB_DISPLAY_BUFFER_INVALID (1 << 4) /* either float or byte buffer changed, need to re-calculate display buffers */ /** * \name Imbuf Component flags diff --git a/source/blender/imbuf/SConscript b/source/blender/imbuf/SConscript index a1030112f4b..976108cd84e 100644 --- a/source/blender/imbuf/SConscript +++ b/source/blender/imbuf/SConscript @@ -4,9 +4,9 @@ Import ('env') sources = env.Glob('intern/*.c') -incs = '. ../makesdna #/intern/guardedalloc #/intern/memutil ../blenlib' +incs = '. ../makesdna ../makesrna #/intern/guardedalloc #/intern/memutil ../blenlib' incs += ' ../avi ../blenkernel ../blenloader' -incs += ' #/intern/ffmpeg' +incs += ' #/intern/ffmpeg #intern/opencolorio' incs += ' ' + env['BF_JPEG_INC'] incs += ' ' + env['BF_PNG_INC'] diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index 571c54b9cb3..d5cc4929aed 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -191,6 +191,7 @@ struct anim { struct anim *proxy_anim[IMB_PROXY_MAX_SLOT]; struct anim_index *curr_idx[IMB_TC_MAX_SLOT]; + char colorspace[64]; }; #endif diff --git a/source/blender/imbuf/intern/IMB_colormanagement_intern.h b/source/blender/imbuf/intern/IMB_colormanagement_intern.h new file mode 100644 index 00000000000..0c002b78848 --- /dev/null +++ b/source/blender/imbuf/intern/IMB_colormanagement_intern.h @@ -0,0 +1,92 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 by Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Xavier Thomas, + * Lukas Toenne, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifndef IMB_COLORMANAGEMENT_INTERN_H +#define IMB_COLORMANAGEMENT_INTERN_H + +#include "DNA_listBase.h" + +#define BCM_CONFIG_FILE "config.ocio" + +struct ConstProcessorRcPtr; +struct ImBuf; + +typedef struct ColorSpace { + struct ColorSpace *next, *prev; + int index; + char name[64]; + char description[64]; + + struct ConstProcessorRcPtr *to_scene_linear; + struct ConstProcessorRcPtr *from_scene_linear; + + int is_invertible; +} ColorSpace; + +typedef struct ColorManagedDisplay { + struct ColorManagedDisplay *next, *prev; + int index; + char name[64]; + ListBase views; + + struct ConstProcessorRcPtr *to_scene_linear; + struct ConstProcessorRcPtr *from_scene_linear; +} ColorManagedDisplay; + +typedef struct ColorManagedView { + struct ColorManagedView *next, *prev; + int index; + char name[64]; +} ColorManagedView; + +void colormanage_cache_free(struct ImBuf *ibuf); + +struct ColorManagedDisplay *colormanage_display_get_default(void); +struct ColorManagedDisplay *colormanage_display_add(const char *name); +struct ColorManagedDisplay *colormanage_display_get_named(const char *name); +struct ColorManagedDisplay *colormanage_display_get_indexed(int index); + +const char *colormanage_view_get_default_name(const ColorManagedDisplay *display); +struct ColorManagedView *colormanage_view_get_default(const ColorManagedDisplay *display); +struct ColorManagedView *colormanage_view_add(const char *name); +struct ColorManagedView *colormanage_view_get_indexed(int index); +struct ColorManagedView *colormanage_view_get_named(const char *name); + +struct ColorSpace *colormanage_colorspace_add(const char *name, const char *description, int is_invertible); +struct ColorSpace *colormanage_colorspace_get_named(const char *name); +struct ColorSpace *colormanage_colorspace_get_roled(int role); +struct ColorSpace *colormanage_colorspace_get_indexed(int index); + +void colorspace_set_default_role(char *colorspace, int size, int role); + +void colormanage_imbuf_set_default_spaces(struct ImBuf *ibuf); +void colormanage_imbuf_make_linear(struct ImBuf *ibuf, const char *from_colorspace); + +#endif /* IMB_COLORMANAGEMENT_INTERN_H */ diff --git a/source/blender/imbuf/intern/IMB_filetype.h b/source/blender/imbuf/intern/IMB_filetype.h index 74d0a92f26b..56a03121409 100644 --- a/source/blender/imbuf/intern/IMB_filetype.h +++ b/source/blender/imbuf/intern/IMB_filetype.h @@ -40,12 +40,13 @@ typedef struct ImFileType { int (*is_a)(unsigned char *buf); int (*ftype)(struct ImFileType *type, struct ImBuf *ibuf); - struct ImBuf *(*load)(unsigned char *mem, size_t size, int flags); + struct ImBuf *(*load)(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); int (*save)(struct ImBuf *ibuf, const char *name, int flags); void (*load_tile)(struct ImBuf *ibuf, unsigned char *mem, size_t size, int tx, int ty, unsigned int *rect); int flag; int filetype; + int default_save_role; } ImFileType; extern ImFileType IMB_FILE_TYPES[]; @@ -63,57 +64,57 @@ void imb_tile_cache_tile_free(struct ImBuf *ibuf, int tx, int ty); /* png */ int imb_is_a_png(unsigned char *buf); -struct ImBuf *imb_loadpng(unsigned char *mem, size_t size, int flags); +struct ImBuf *imb_loadpng(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); int imb_savepng(struct ImBuf *ibuf, const char *name, int flags); /* targa */ int imb_is_a_targa(unsigned char *buf); -struct ImBuf *imb_loadtarga(unsigned char *mem, size_t size, int flags); +struct ImBuf *imb_loadtarga(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); int imb_savetarga(struct ImBuf * ibuf, const char *name, int flags); /* iris */ int imb_is_a_iris(unsigned char *mem); -struct ImBuf *imb_loadiris(unsigned char *mem, size_t size, int flags); +struct ImBuf *imb_loadiris(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); int imb_saveiris(struct ImBuf * ibuf, const char *name, int flags); /* jp2 */ int imb_is_a_jp2(unsigned char *buf); -struct ImBuf *imb_jp2_decode(unsigned char *mem, size_t size, int flags); +struct ImBuf *imb_jp2_decode(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); int imb_savejp2(struct ImBuf *ibuf, const char *name, int flags); /* jpeg */ int imb_is_a_jpeg(unsigned char *mem); int imb_savejpeg(struct ImBuf *ibuf, const char *name, int flags); -struct ImBuf * imb_load_jpeg (unsigned char * buffer, size_t size, int flags); +struct ImBuf * imb_load_jpeg (unsigned char * buffer, size_t size, int flags, char colorspace[IM_MAX_SPACE]); /* bmp */ int imb_is_a_bmp(unsigned char *buf); -struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags); +struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); int imb_savebmp(struct ImBuf *ibuf, const char *name, int flags); /* cocoa */ -struct ImBuf *imb_cocoaLoadImage(unsigned char *mem, size_t size, int flags); +struct ImBuf *imb_cocoaLoadImage(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); short imb_cocoaSaveImage(struct ImBuf *ibuf, const char *name, int flags); /* cineon */ int imb_savecineon(struct ImBuf *buf, const char *name, int flags); -struct ImBuf *imb_loadcineon(unsigned char *mem, size_t size, int flags); +struct ImBuf *imb_loadcineon(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); int imb_is_cineon(unsigned char *buf); /* dpx */ int imb_save_dpx(struct ImBuf *buf, const char *name, int flags); -struct ImBuf *imb_loaddpx(unsigned char *mem, size_t size, int flags); +struct ImBuf *imb_loaddpx(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); int imb_is_dpx(unsigned char *buf); /* hdr */ int imb_is_a_hdr(unsigned char *buf); -struct ImBuf *imb_loadhdr(unsigned char *mem, size_t size, int flags); +struct ImBuf *imb_loadhdr(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); int imb_savehdr(struct ImBuf * ibuf, const char *name, int flags); /* tiff */ void imb_inittiff(void); int imb_is_a_tiff(unsigned char *buf); -struct ImBuf *imb_loadtiff(unsigned char *mem, size_t size, int flags); +struct ImBuf *imb_loadtiff(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); void imb_loadtiletiff(struct ImBuf *ibuf, unsigned char *mem, size_t size, int tx, int ty, unsigned int *rect); int imb_savetiff(struct ImBuf *ibuf, const char *name, int flags); diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index 7724f444b2c..69048274104 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -41,6 +41,7 @@ #include "IMB_allocimbuf.h" #include "IMB_filetype.h" #include "IMB_metadata.h" +#include "IMB_colormanagement_intern.h" #include "imbuf.h" @@ -162,8 +163,11 @@ void IMB_freeImBuf(ImBuf *ibuf) IMB_freezbuffloatImBuf(ibuf); freeencodedbufferImBuf(ibuf); IMB_metadata_free(ibuf); - if (ibuf->dds_data.data != NULL) + colormanage_cache_free(ibuf); + + if (ibuf->dds_data.data != NULL) { free(ibuf->dds_data.data); /* dds_data.data is allocated by DirectDrawSurface::readData(), so don't use MEM_freeN! */ + } MEM_freeN(ibuf); } } @@ -354,7 +358,7 @@ ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y, uchar planes, unsigned int ibuf->ftype = TGA; ibuf->channels = 4; /* float option, is set to other values when buffers get assigned */ ibuf->ppm[0] = ibuf->ppm[1] = IMB_DPI_DEFAULT / 0.0254; /* IMB_DPI_DEFAULT -> pixels-per-meter */ - + if (flags & IB_rect) { if (imb_addrectImBuf(ibuf) == FALSE) { IMB_freeImBuf(ibuf); @@ -382,6 +386,9 @@ ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y, uchar planes, unsigned int return NULL; } } + + /* assign default spaces */ + colormanage_imbuf_set_default_spaces(ibuf); } return (ibuf); } @@ -442,8 +449,11 @@ ImBuf *IMB_dupImBuf(ImBuf *ibuf1) /* for now don't duplicate metadata */ tbuf.metadata = NULL; + tbuf.display_buffer_flags = NULL; + tbuf.colormanage_cache = NULL; + *ibuf2 = tbuf; - + return(ibuf2); } diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 16dbd0823da..394f5169046 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -113,6 +113,9 @@ #endif #endif +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + int ismovie(const char *UNUSED(filepath)) { return 0; @@ -263,12 +266,20 @@ void IMB_close_anim_proxies(struct anim *anim) IMB_free_indices(anim); } -struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex) +struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex, char colorspace[IM_MAX_SPACE]) { struct anim *anim; anim = (struct anim *)MEM_callocN(sizeof(struct anim), "anim struct"); if (anim != NULL) { + if (colorspace) { + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + BLI_strncpy(anim->colorspace, colorspace, sizeof(anim->colorspace)); + } + else { + colorspace_set_default_role(anim->colorspace, sizeof(anim->colorspace), COLOR_ROLE_DEFAULT_BYTE); + } + BLI_strncpy(anim->name, name, sizeof(anim->name)); anim->ib_flags = ib_flags; anim->streamindex = streamindex; @@ -404,7 +415,7 @@ static ImBuf *avi_fetchibuf(struct anim *anim, int position) if (anim->pgf) { lpbi = AVIStreamGetFrame(anim->pgf, position + AVIStreamStart(anim->pavi[anim->firstvideo])); if (lpbi) { - ibuf = IMB_ibImageFromMemory((unsigned char *) lpbi, 100, IB_rect, "<avi_fetchibuf>"); + ibuf = IMB_ibImageFromMemory((unsigned char *) lpbi, 100, IB_rect, anim->colorspace, "<avi_fetchibuf>"); //Oh brother... } } @@ -432,8 +443,8 @@ static ImBuf *avi_fetchibuf(struct anim *anim, int position) MEM_freeN(tmp); } - ibuf->profile = IB_PROFILE_SRGB; - + ibuf->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace); + return ibuf; } @@ -634,8 +645,6 @@ static void ffmpeg_postprocess(struct anim *anim) ImBuf *ibuf = anim->last_frame; int filter_y = 0; - ibuf->profile = IB_PROFILE_SRGB; - if (!anim->pFrameComplete) { return; } @@ -1090,6 +1099,7 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_freeImBuf(anim->last_frame); anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect); + anim->last_frame->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace); ffmpeg_postprocess(anim); @@ -1213,7 +1223,7 @@ static ImBuf *anim_getnew(struct anim *anim) switch (anim->curtype) { case ANIM_SEQUENCE: - ibuf = IMB_loadiffname(anim->name, anim->ib_flags); + ibuf = IMB_loadiffname(anim->name, anim->ib_flags, anim->colorspace); if (ibuf) { BLI_strncpy(anim->first, anim->name, sizeof(anim->first)); anim->duration = 1; @@ -1309,7 +1319,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, int position, pic = an_stringdec(anim->first, head, tail, &digits); pic += position; an_stringenc(anim->name, head, tail, digits, pic); - ibuf = IMB_loadiffname(anim->name, IB_rect); + ibuf = IMB_loadiffname(anim->name, IB_rect, anim->colorspace); if (ibuf) { anim->curposition = position; } @@ -1319,7 +1329,6 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, int position, if (ibuf) { anim->curposition = position; IMB_convert_rgba_to_abgr(ibuf); - ibuf->profile = IB_PROFILE_SRGB; } break; case ANIM_AVI: @@ -1330,8 +1339,16 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, int position, #ifdef WITH_QUICKTIME case ANIM_QTIME: ibuf = qtime_fetchibuf(anim, position); - if (ibuf) + if (ibuf) { + if (ibuf->rect) { + /* OCIO_TODO: should happen in quicktime module, but it currently doesn't have access + * to color management's internals + */ + ibuf->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace); + } + anim->curposition = position; + } break; #endif #ifdef WITH_FFMPEG diff --git a/source/blender/imbuf/intern/bmp.c b/source/blender/imbuf/intern/bmp.c index 8fa468949e7..df12f0b703e 100644 --- a/source/blender/imbuf/intern/bmp.c +++ b/source/blender/imbuf/intern/bmp.c @@ -38,6 +38,9 @@ #include "IMB_allocimbuf.h" #include "IMB_filetype.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + /* some code copied from article on microsoft.com, copied * here for enhanced BMP support in the future * http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0197/mfcp1/mfcp1.htm&nav=/msj/0197/newnav.htm @@ -115,7 +118,7 @@ int imb_is_a_bmp(unsigned char *buf) return checkbmp(buf); } -struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags) +struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf *ibuf = NULL; BMPINFOHEADER bmi; @@ -127,6 +130,8 @@ struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags) if (checkbmp(mem) == 0) return(NULL); + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + if ((mem[0] == 'B') && (mem[1] == 'M')) { /* skip fileheader */ mem += BMP_FILEHEADER_SIZE; @@ -195,7 +200,6 @@ struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags) if (ibuf) { ibuf->ftype = BMP; - ibuf->profile = IB_PROFILE_SRGB; } return(ibuf); diff --git a/source/blender/imbuf/intern/cineon/cineon_dpx.c b/source/blender/imbuf/intern/cineon/cineon_dpx.c index 182e0a7e29d..7705af13b1e 100644 --- a/source/blender/imbuf/intern/cineon/cineon_dpx.c +++ b/source/blender/imbuf/intern/cineon/cineon_dpx.c @@ -44,6 +44,9 @@ #include "IMB_imbuf.h" #include "IMB_filetype.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + #include "BKE_global.h" #include "MEM_guardedalloc.h" @@ -63,7 +66,7 @@ static void cineon_conversion_parameters(LogImageByteConversionParameters *param } #endif -static ImBuf *imb_load_dpx_cineon(unsigned char *mem, int use_cineon, int size, int flags) +static ImBuf *imb_load_dpx_cineon(unsigned char *mem, int use_cineon, int size, int flags, char colorspace[IM_MAX_SPACE]) { ImBuf *ibuf; LogImageFile *image; @@ -72,6 +75,8 @@ static ImBuf *imb_load_dpx_cineon(unsigned char *mem, int use_cineon, int size, int width, height, depth; float *frow; + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT); + logImageSetVerbose((G.debug & G_DEBUG) ? 1:0); image = logImageOpenFromMem(mem, size, use_cineon); @@ -202,10 +207,10 @@ int imb_is_cineon(unsigned char *buf) return cineonIsMemFileCineon(buf); } -ImBuf *imb_loadcineon(unsigned char *mem, size_t size, int flags) +ImBuf *imb_loadcineon(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { if (imb_is_cineon(mem)) - return imb_load_dpx_cineon(mem, 1, size, flags); + return imb_load_dpx_cineon(mem, 1, size, flags, colorspace); return NULL; } @@ -219,9 +224,9 @@ int imb_is_dpx(unsigned char *buf) return dpxIsMemFileCineon(buf); } -ImBuf *imb_loaddpx(unsigned char *mem, size_t size, int flags) +ImBuf *imb_loaddpx(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { if (imb_is_dpx(mem)) - return imb_load_dpx_cineon(mem, 0, size, flags); + return imb_load_dpx_cineon(mem, 0, size, flags, colorspace); return NULL; } diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c new file mode 100644 index 00000000000..6db45f0308a --- /dev/null +++ b/source/blender/imbuf/intern/colormanagement.c @@ -0,0 +1,2337 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 by Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Xavier Thomas, + * Lukas Toenne, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + +#include <string.h> +#include <math.h> + +#include "DNA_color_types.h" +#include "DNA_image_types.h" +#include "DNA_movieclip_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_windowmanager_types.h" + +#include "IMB_filter.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" +#include "IMB_filetype.h" +#include "IMB_moviecache.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_math_color.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_threads.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_utildefines.h" +#include "BKE_main.h" + +#include "RNA_define.h" + +#include <ocio_capi.h> + +/*********************** Global declarations *************************/ + +#define MAX_COLORSPACE_NAME 64 + +/* ** list of all supported color spaces, displays and views */ +static char global_role_scene_linear[MAX_COLORSPACE_NAME]; +static char global_role_color_picking[MAX_COLORSPACE_NAME]; +static char global_role_texture_painting[MAX_COLORSPACE_NAME]; +static char global_role_default_byte[MAX_COLORSPACE_NAME]; +static char global_role_default_float[MAX_COLORSPACE_NAME]; +static char global_role_default_sequencer[MAX_COLORSPACE_NAME]; + +static ListBase global_colorspaces = {NULL}; +static ListBase global_displays = {NULL}; +static ListBase global_views = {NULL}; + +static int global_tot_colorspace = 0; +static int global_tot_display = 0; +static int global_tot_view = 0; + +typedef struct ColormanageProcessor { + ConstProcessorRcPtr *processor; + CurveMapping *curve_mapping; +} ColormanageProcessor; + +/*********************** Color managed cache *************************/ + +/* Cache Implementation Notes + * ========================== + * + * All color management cache stuff is stored in two properties of + * image buffers: + * + * 1. display_buffer_flags + * + * This is a bit field which used to mark calculated transformations + * for particular image buffer. Index inside of this array means index + * of a color managed display. Element with given index matches view + * transformations applied for a given display. So if bit B of array + * element B is set to 1, this means display buffer with display index + * of A and view transform of B was ever calculated for this imbuf. + * + * In contrast with indices in global lists of displays and views this + * indices are 0-based, not 1-based. This is needed to save some bytes + * of memory. + * + * 2. colormanage_cache + * + * This is a pointer to a structure which holds all data which is + * needed for color management cache to work. + * + * It contains two parts: + * - data + * - moviecache + * + * Data field is used to store additional information about cached + * buffers which affects on whether cached buffer could be used. + * This data can't go to cache key because changes in this data + * shouldn't lead extra buffers adding to cache, it shall + * invalidate cached images. + * + * Currently such a data contains only exposure and gamma, but + * would likely extended further. + * + * data field is not null only for elements of cache, not used for + * original image buffers. + * + * Color management cache is using generic MovieCache implementation + * to make it easier to deal with memory limitation. + * + * Currently color management is using the same memory limitation + * pool as sequencer and clip editor are using which means color + * managed buffers would be removed from the cache as soon as new + * frames are loading for the movie clip and there's no space in + * cache. + * + * Every image buffer has got own movie cache instance, which + * means keys for color managed buffers could be really simple + * and look up in this cache would be fast and independent from + * overall amount of color managed images. + */ + +/* NOTE: ColormanageCacheViewSettings and ColormanageCacheDisplaySettings are + * quite the same as ColorManagedViewSettings and ColorManageDisplaySettings + * but they holds indexes of all transformations and color spaces, not + * their names. + * + * This helps avoid extra colorsmace / display / view lookup without + * requiring to pass all variables which affects on display buffer + * to color management cache system and keeps calls small and nice. + */ +typedef struct ColormanageCacheViewSettings { + int flag; + int view; + float exposure; + float gamma; + CurveMapping *curve_mapping; +} ColormanageCacheViewSettings; + +typedef struct ColormanageCacheDisplaySettings { + int display; +} ColormanageCacheDisplaySettings; + +typedef struct ColormanageCacheKey { + int view; /* view transformation used for display buffer */ + int display; /* display device name */ +} ColormanageCacheKey; + +typedef struct ColormnaageCacheData { + int flag; /* view flags of cached buffer */ + float exposure; /* exposure value cached buffer is calculated with */ + float gamma; /* gamma value cached buffer is calculated with */ + int predivide; /* predivide flag of cached buffer */ + CurveMapping *curve_mapping; /* curve mapping used for cached buffer */ + int curve_mapping_timestamp; /* time stamp of curve mapping used for cached buffer */ +} ColormnaageCacheData; + +typedef struct ColormanageCache { + struct MovieCache *moviecache; + + ColormnaageCacheData *data; +} ColormanageCache; + +static struct MovieCache *colormanage_moviecache_get(const ImBuf *ibuf) +{ + if (!ibuf->colormanage_cache) + return NULL; + + return ibuf->colormanage_cache->moviecache; +} + +static ColormnaageCacheData *colormanage_cachedata_get(const ImBuf *ibuf) +{ + if (!ibuf->colormanage_cache) + return NULL; + + return ibuf->colormanage_cache->data; +} + +static unsigned int colormanage_hashhash(const void *key_v) +{ + ColormanageCacheKey *key = (ColormanageCacheKey *)key_v; + + unsigned int rval = (key->display << 16) | (key->view % 0xffff); + + return rval; +} + +static int colormanage_hashcmp(const void *av, const void *bv) +{ + const ColormanageCacheKey *a = (ColormanageCacheKey *) av; + const ColormanageCacheKey *b = (ColormanageCacheKey *) bv; + + if (a->view < b->view) + return -1; + else if (a->view > b->view) + return 1; + + if (a->display < b->display) + return -1; + else if (a->display > b->display) + return 1; + + return 0; +} + +static struct MovieCache *colormanage_moviecache_ensure(ImBuf *ibuf) +{ + if (!ibuf->colormanage_cache) + ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage cache"); + + if (!ibuf->colormanage_cache->moviecache) { + struct MovieCache *moviecache; + + moviecache = IMB_moviecache_create("colormanage cache", sizeof(ColormanageCacheKey), + colormanage_hashhash, colormanage_hashcmp); + + ibuf->colormanage_cache->moviecache = moviecache; + } + + return ibuf->colormanage_cache->moviecache; +} + +static void colormanage_cachedata_set(ImBuf *ibuf, ColormnaageCacheData *data) +{ + if (!ibuf->colormanage_cache) + ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage cache"); + + ibuf->colormanage_cache->data = data; +} + +static void colormanage_view_settings_to_cache(ColormanageCacheViewSettings *cache_view_settings, + const ColorManagedViewSettings *view_settings) +{ + int view = IMB_colormanagement_view_get_named_index(view_settings->view_transform); + + cache_view_settings->view = view; + cache_view_settings->exposure = view_settings->exposure; + cache_view_settings->gamma = view_settings->gamma; + cache_view_settings->flag = view_settings->flag; + cache_view_settings->curve_mapping = view_settings->curve_mapping; +} + +static void colormanage_display_settings_to_cache(ColormanageCacheDisplaySettings *cache_display_settings, + const ColorManagedDisplaySettings *display_settings) +{ + int display = IMB_colormanagement_display_get_named_index(display_settings->display_device); + + cache_display_settings->display = display; +} + +static void colormanage_settings_to_key(ColormanageCacheKey *key, + const ColormanageCacheViewSettings *view_settings, + const ColormanageCacheDisplaySettings *display_settings) +{ + key->view = view_settings->view; + key->display = display_settings->display; +} + +static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf, ColormanageCacheKey *key, void **cache_handle) +{ + ImBuf *cache_ibuf; + struct MovieCache *moviecache = colormanage_moviecache_get(ibuf); + + if (!moviecache) { + /* if there's no moviecache it means no color management was applied on given image buffer before */ + + return NULL; + } + + *cache_handle = NULL; + + cache_ibuf = IMB_moviecache_get(moviecache, key); + + *cache_handle = cache_ibuf; + + return cache_ibuf; +} + +static unsigned char *colormanage_cache_get(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings, + const ColormanageCacheDisplaySettings *display_settings, + void **cache_handle) +{ + ColormanageCacheKey key; + ImBuf *cache_ibuf; + int view_flag = 1 << (view_settings->view - 1); + int predivide = ibuf->flags & IB_cm_predivide; + CurveMapping *curve_mapping = view_settings->curve_mapping; + int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0; + + colormanage_settings_to_key(&key, view_settings, display_settings); + + /* check whether image was marked as dirty for requested transform */ + if ((ibuf->display_buffer_flags[display_settings->display - 1] & view_flag) == 0) { + return NULL; + } + + cache_ibuf = colormanage_cache_get_ibuf(ibuf, &key, cache_handle); + + if (cache_ibuf) { + ColormnaageCacheData *cache_data; + + BLI_assert(cache_ibuf->x == ibuf->x && + cache_ibuf->y == ibuf->y && + cache_ibuf->channels == ibuf->channels); + + /* only buffers with different color space conversions are being stored + * in cache separately. buffer which were used only different exposure/gamma + * are re-suing the same cached buffer + * + * check here which exposure/gamma/curve was used for cached buffer and if they're + * different from requested buffer should be re-generated + */ + cache_data = colormanage_cachedata_get(cache_ibuf); + + if (cache_data->exposure != view_settings->exposure || + cache_data->gamma != view_settings->gamma || + cache_data->predivide != predivide || + cache_data->flag != view_settings->flag || + cache_data->curve_mapping != curve_mapping || + cache_data->curve_mapping_timestamp != curve_mapping_timestamp) + { + *cache_handle = NULL; + + IMB_freeImBuf(cache_ibuf); + + return NULL; + } + + return (unsigned char *) cache_ibuf->rect; + } + + return NULL; +} + +static void colormanage_cache_put(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings, + const ColormanageCacheDisplaySettings *display_settings, + unsigned char *display_buffer, void **cache_handle) +{ + ColormanageCacheKey key; + ImBuf *cache_ibuf; + ColormnaageCacheData *cache_data; + int view_flag = 1 << (view_settings->view - 1); + int predivide = ibuf->flags & IB_cm_predivide; + struct MovieCache *moviecache = colormanage_moviecache_ensure(ibuf); + CurveMapping *curve_mapping = view_settings->curve_mapping; + int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0; + + colormanage_settings_to_key(&key, view_settings, display_settings); + + /* mark display buffer as valid */ + ibuf->display_buffer_flags[display_settings->display - 1] |= view_flag; + + /* buffer itself */ + cache_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0); + cache_ibuf->rect = (unsigned int *) display_buffer; + + cache_ibuf->mall |= IB_rect; + cache_ibuf->flags |= IB_rect; + + /* store data which is needed to check whether cached buffer could be used for color managed display settings */ + cache_data = MEM_callocN(sizeof(ColormnaageCacheData), "color manage cache imbuf data"); + cache_data->exposure = view_settings->exposure; + cache_data->gamma = view_settings->gamma; + cache_data->predivide = predivide; + cache_data->flag = view_settings->flag; + cache_data->curve_mapping = curve_mapping; + cache_data->curve_mapping_timestamp = curve_mapping_timestamp; + + colormanage_cachedata_set(cache_ibuf, cache_data); + + *cache_handle = cache_ibuf; + + IMB_moviecache_put(moviecache, &key, cache_ibuf); +} + +static void colormanage_cache_handle_release(void *cache_handle) +{ + ImBuf *cache_ibuf = cache_handle; + + IMB_freeImBuf(cache_ibuf); +} + +/*********************** Initialization / De-initialization *************************/ + +static void colormanage_role_color_space_name_get(ConstConfigRcPtr *config, char *colorspace_name, const char *role) +{ + ConstColorSpaceRcPtr *ociocs; + + ociocs = OCIO_configGetColorSpace(config, role); + + if (ociocs) { + const char *name = OCIO_colorSpaceGetName(ociocs); + + /* assume function was called with buffer properly allocated to MAX_COLORSPACE_NAME chars */ + BLI_strncpy(colorspace_name, name, MAX_COLORSPACE_NAME); + OCIO_colorSpaceRelease(ociocs); + } + else { + printf("Color management: Error could not find role %s role.\n", role); + } +} + +static void colormanage_load_config(ConstConfigRcPtr *config) +{ + int tot_colorspace, tot_display, tot_display_view, index, viewindex, viewindex2; + const char *name; + + /* get roles */ + colormanage_role_color_space_name_get(config, global_role_scene_linear, OCIO_ROLE_SCENE_LINEAR); + colormanage_role_color_space_name_get(config, global_role_color_picking, OCIO_ROLE_COLOR_PICKING); + colormanage_role_color_space_name_get(config, global_role_texture_painting, OCIO_ROLE_TEXTURE_PAINT); + colormanage_role_color_space_name_get(config, global_role_default_sequencer, OCIO_ROLE_DEFAULT_SEQUENCER); + colormanage_role_color_space_name_get(config, global_role_default_byte, OCIO_ROLE_DEFAULT_BYTE); + colormanage_role_color_space_name_get(config, global_role_default_float, OCIO_ROLE_DEFAULT_FLOAT); + + /* load colorspaces */ + tot_colorspace = OCIO_configGetNumColorSpaces(config); + for (index = 0 ; index < tot_colorspace; index++) { + ConstColorSpaceRcPtr *ocio_colorspace; + const char *description; + int is_invertible; + + name = OCIO_configGetColorSpaceNameByIndex(config, index); + + ocio_colorspace = OCIO_configGetColorSpace(config, name); + description = OCIO_colorSpaceGetDescription(ocio_colorspace); + is_invertible = OCIO_colorSpaceIsInvertible(ocio_colorspace); + + colormanage_colorspace_add(name, description, is_invertible); + + OCIO_colorSpaceRelease(ocio_colorspace); + } + + /* load displays */ + viewindex2 = 0; + tot_display = OCIO_configGetNumDisplays(config); + + for (index = 0 ; index < tot_display; index++) { + const char *displayname; + ColorManagedDisplay *display; + + displayname = OCIO_configGetDisplay(config, index); + + display = colormanage_display_add(displayname); + + /* load views */ + tot_display_view = OCIO_configGetNumViews(config, displayname); + for (viewindex = 0 ; viewindex < tot_display_view; viewindex++, viewindex2++) { + const char *viewname; + ColorManagedView *view; + LinkData *display_view; + + viewname = OCIO_configGetView(config, displayname, viewindex); + + /* first check if view transform with given name was already loaded */ + view = colormanage_view_get_named(viewname); + + if (!view) { + view = colormanage_view_add(viewname); + } + + display_view = BLI_genericNodeN(view); + + BLI_addtail(&display->views, display_view); + } + } + + global_tot_display = tot_display; +} + +static void colormanage_free_config(void) +{ + ColorSpace *colorspace; + ColorManagedDisplay *display; + + /* free color spaces */ + colorspace = global_colorspaces.first; + while (colorspace) { + ColorSpace *colorspace_next = colorspace->next; + + /* free precomputer processors */ + if (colorspace->to_scene_linear) + OCIO_processorRelease((ConstProcessorRcPtr *) colorspace->to_scene_linear); + + if (colorspace->from_scene_linear) + OCIO_processorRelease((ConstProcessorRcPtr *) colorspace->from_scene_linear); + + /* free color space itself */ + MEM_freeN(colorspace); + + colorspace = colorspace_next; + } + + /* free displays */ + display = global_displays.first; + while (display) { + ColorManagedDisplay *display_next = display->next; + + /* free precomputer processors */ + if (display->to_scene_linear) + OCIO_processorRelease((ConstProcessorRcPtr *) display->to_scene_linear); + + if (display->from_scene_linear) + OCIO_processorRelease((ConstProcessorRcPtr *) display->from_scene_linear); + + /* free list of views */ + BLI_freelistN(&display->views); + + MEM_freeN(display); + display = display_next; + } + + /* free views */ + BLI_freelistN(&global_views); +} + +void IMB_colormanagement_init(void) +{ + const char *ocio_env; + const char *configdir; + char configfile[FILE_MAX]; + ConstConfigRcPtr *config = NULL; + + ocio_env = getenv("OCIO"); + + if (ocio_env && ocio_env[0] != '\0') + config = OCIO_configCreateFromEnv(); + + if (config == NULL) { + configdir = BLI_get_folder(BLENDER_DATAFILES, "colormanagement"); + + if (configdir) { + BLI_join_dirfile(configfile, sizeof(configfile), configdir, BCM_CONFIG_FILE); + + config = OCIO_configCreateFromFile(configfile); + } + } + + if (config) { + OCIO_setCurrentConfig(config); + + colormanage_load_config(config); + + OCIO_configRelease(config); + } + + BLI_init_srgb_conversion(); +} + +void IMB_colormanagement_exit(void) +{ + colormanage_free_config(); +} + +/*********************** Internal functions *************************/ + +void colormanage_cache_free(ImBuf *ibuf) +{ + if (ibuf->display_buffer_flags) { + MEM_freeN(ibuf->display_buffer_flags); + + ibuf->display_buffer_flags = NULL; + } + + if (ibuf->colormanage_cache) { + ColormnaageCacheData *cache_data = colormanage_cachedata_get(ibuf); + struct MovieCache *moviecache = colormanage_moviecache_get(ibuf); + + if (cache_data) { + MEM_freeN(cache_data); + } + + if (moviecache) { + IMB_moviecache_free(moviecache); + } + + MEM_freeN(ibuf->colormanage_cache); + + ibuf->colormanage_cache = NULL; + } +} + +static void display_transform_get_from_ctx(const bContext *C, ColorManagedViewSettings **view_settings_r, + ColorManagedDisplaySettings **display_settings_r) +{ + Scene *scene = CTX_data_scene(C); + SpaceImage *sima = CTX_wm_space_image(C); + + *view_settings_r = &scene->view_settings; + *display_settings_r = &scene->display_settings; + + if (sima) { + if ((sima->image->flag & IMA_VIEW_AS_RENDER) == 0) + *view_settings_r = NULL; + } +} + +static ConstProcessorRcPtr *create_display_buffer_processor(const char *view_transform, const char *display, + float exposure, float gamma) +{ + ConstConfigRcPtr *config = OCIO_getCurrentConfig(); + DisplayTransformRcPtr *dt; + ConstProcessorRcPtr *processor; + + if (!config) { + /* there's no valid OCIO configuration, can't create processor */ + + return NULL; + } + + dt = OCIO_createDisplayTransform(); + + /* assuming handling buffer was already converted to scene linear space */ + OCIO_displayTransformSetInputColorSpaceName(dt, global_role_scene_linear); + OCIO_displayTransformSetView(dt, view_transform); + OCIO_displayTransformSetDisplay(dt, display); + + /* fstop exposure control */ + if (exposure != 0.0f) { + MatrixTransformRcPtr *mt; + float gain = powf(2.0f, exposure); + const float scale4f[] = {gain, gain, gain, gain}; + float m44[16], offset4[4]; + + OCIO_matrixTransformScale(m44, offset4, scale4f); + mt = OCIO_createMatrixTransform(); + OCIO_matrixTransformSetValue(mt, m44, offset4); + OCIO_displayTransformSetLinearCC(dt, (ConstTransformRcPtr *) mt); + + OCIO_matrixTransformRelease(mt); + } + + /* post-display gamma transform */ + if (gamma != 1.0f) { + ExponentTransformRcPtr *et; + float exponent = 1.0f / MAX2(FLT_EPSILON, gamma); + const float exponent4f[] = {exponent, exponent, exponent, exponent}; + + et = OCIO_createExponentTransform(); + OCIO_exponentTransformSetValue(et, exponent4f); + OCIO_displayTransformSetDisplayCC(dt, (ConstTransformRcPtr *) et); + + OCIO_exponentTransformRelease(et); + } + + processor = OCIO_configGetProcessor(config, (ConstTransformRcPtr *) dt); + + OCIO_displayTransformRelease(dt); + OCIO_configRelease(config); + + return processor; +} + +static ConstProcessorRcPtr *create_colorspace_transform_processor(const char *from_colorspace, + const char *to_colorspace) +{ + ConstConfigRcPtr *config = OCIO_getCurrentConfig(); + ConstProcessorRcPtr *processor; + + if (!config) { + /* there's no valid OCIO configuration, can't create processor */ + + return NULL; + } + + processor = OCIO_configGetProcessorWithNames(config, from_colorspace, to_colorspace); + + OCIO_configRelease(config); + + return processor; +} + +static ConstProcessorRcPtr *colorspace_to_scene_linear_processor(ColorSpace *colorspace) +{ + if (colorspace->to_scene_linear == NULL) { + BLI_lock_thread(LOCK_COLORMANAGE); + + if (colorspace->to_scene_linear == NULL) { + ConstProcessorRcPtr *to_scene_linear; + to_scene_linear = create_colorspace_transform_processor(colorspace->name, global_role_scene_linear); + colorspace->to_scene_linear = (struct ConstProcessorRcPtr *) to_scene_linear; + } + + BLI_unlock_thread(LOCK_COLORMANAGE); + } + + return (ConstProcessorRcPtr *) colorspace->to_scene_linear; +} + +static ConstProcessorRcPtr *colorspace_from_scene_linear_processor(ColorSpace *colorspace) +{ + if (colorspace->from_scene_linear == NULL) { + BLI_lock_thread(LOCK_COLORMANAGE); + + if (colorspace->from_scene_linear == NULL) { + ConstProcessorRcPtr *from_scene_linear; + from_scene_linear = create_colorspace_transform_processor(global_role_scene_linear, colorspace->name); + colorspace->from_scene_linear = (struct ConstProcessorRcPtr *) from_scene_linear; + } + + BLI_unlock_thread(LOCK_COLORMANAGE); + } + + return (ConstProcessorRcPtr *) colorspace->from_scene_linear; +} + +static ConstProcessorRcPtr *display_from_scene_linear_processor(ColorManagedDisplay *display) +{ + if (display->from_scene_linear == NULL) { + BLI_lock_thread(LOCK_COLORMANAGE); + + if (display->from_scene_linear == NULL) { + const char *view_name = colormanage_view_get_default_name(display); + ConstConfigRcPtr *config = OCIO_getCurrentConfig(); + ConstProcessorRcPtr *processor = NULL; + + if (view_name && config) { + const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(config, display->name, view_name); + processor = OCIO_configGetProcessorWithNames(config, global_role_scene_linear, view_colorspace); + + OCIO_configRelease(config); + } + + display->from_scene_linear = (struct ConstProcessorRcPtr *) processor; + } + + BLI_unlock_thread(LOCK_COLORMANAGE); + } + + return (ConstProcessorRcPtr *) display->from_scene_linear; +} + +static ConstProcessorRcPtr *display_to_scene_linear_processor(ColorManagedDisplay *display) +{ + if (display->to_scene_linear == NULL) { + BLI_lock_thread(LOCK_COLORMANAGE); + + if (display->to_scene_linear == NULL) { + const char *view_name = colormanage_view_get_default_name(display); + ConstConfigRcPtr *config = OCIO_getCurrentConfig(); + ConstProcessorRcPtr *processor = NULL; + + if (view_name && config) { + const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(config, display->name, view_name); + processor = OCIO_configGetProcessorWithNames(config, view_colorspace, global_role_scene_linear); + + OCIO_configRelease(config); + } + + display->to_scene_linear = (struct ConstProcessorRcPtr *) processor; + } + + BLI_unlock_thread(LOCK_COLORMANAGE); + } + + return (ConstProcessorRcPtr *) display->to_scene_linear; +} + +static void init_default_view_settings(const ColorManagedDisplaySettings *display_settings, + ColorManagedViewSettings *view_settings) +{ + ColorManagedDisplay *display; + ColorManagedView *default_view; + + display = colormanage_display_get_named(display_settings->display_device); + default_view = colormanage_view_get_default(display); + + if (default_view) + BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform)); + else + view_settings->view_transform[0] = '\0'; + + view_settings->flag = 0; + view_settings->gamma = 1.0f; + view_settings->exposure = 0.0f; + view_settings->curve_mapping = NULL; +} + +static void curve_mapping_apply_pixel(CurveMapping *curve_mapping, float *pixel, int channels) +{ + if (channels == 1) { + pixel[0] = curvemap_evaluateF(curve_mapping->cm, pixel[0]); + } + else if (channels == 2) { + pixel[0] = curvemap_evaluateF(curve_mapping->cm, pixel[0]); + pixel[1] = curvemap_evaluateF(curve_mapping->cm, pixel[1]); + } + else { + curvemapping_evaluate_premulRGBF(curve_mapping, pixel, pixel); + } +} + +void colorspace_set_default_role(char *colorspace, int size, int role) +{ + if (colorspace && colorspace[0] == '\0') { + const char *role_colorspace; + + role_colorspace = IMB_colormanagement_role_colorspace_name_get(role); + + BLI_strncpy(colorspace, role_colorspace, size); + } +} + +void colormanage_imbuf_set_default_spaces(ImBuf *ibuf) +{ + ibuf->rect_colorspace = colormanage_colorspace_get_named(global_role_default_byte); +} + +void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace) +{ + if (ibuf->rect_float) { + const char *to_colorspace = global_role_scene_linear; + int predivide = ibuf->flags & IB_cm_predivide; + + if (ibuf->rect) + imb_freerectImBuf(ibuf); + + IMB_colormanagement_transform(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels, + from_colorspace, to_colorspace, predivide); + } +} + +/*********************** Generic functions *************************/ + +static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings, const char *what, + const ColorManagedDisplay *default_display) +{ + if (display_settings->display_device[0] == '\0') { + BLI_strncpy(display_settings->display_device, default_display->name, sizeof(display_settings->display_device)); + } + else { + ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device); + + if (!display) { + printf("Color management: display \"%s\" used by %s not found, setting to default (\"%s\").\n", + display_settings->display_device, what, default_display->name); + + BLI_strncpy(display_settings->display_device, default_display->name, + sizeof(display_settings->display_device)); + } + } +} + +static void colormanage_check_view_settings(ColorManagedDisplaySettings *display_settings, + ColorManagedViewSettings *view_settings, const char *what) +{ + ColorManagedDisplay *display; + ColorManagedView *default_view; + + if (view_settings->view_transform[0] == '\0') { + display = colormanage_display_get_named(display_settings->display_device); + default_view = colormanage_view_get_default(display); + + if (default_view) + BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform)); + } + else { + ColorManagedView *view = colormanage_view_get_named(view_settings->view_transform); + + if (!view) { + display = colormanage_display_get_named(display_settings->display_device); + default_view = colormanage_view_get_default(display); + + if (default_view) { + printf("Color management: %s view \"%s\" not found, setting default \"%s\".\n", + what, view_settings->view_transform, default_view->name); + + BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform)); + } + } + } + + /* OCIO_TODO: move to do_versions() */ + if (view_settings->exposure == 0.0f && view_settings->gamma == 0.0f) { + view_settings->exposure = 0.0f; + view_settings->gamma = 1.0f; + } +} + +static void colormanage_check_colorspace_settings(ColorManagedColorspaceSettings *colorspace_settings, const char *what) +{ + if (colorspace_settings->name[0] == '\0') { + /* pass */ + } + else { + ColorSpace *colorspace = colormanage_colorspace_get_named(colorspace_settings->name); + + if (!colorspace) { + printf("Color management: %s colorspace \"%s\" not found, will use default instead.\n", + what, colorspace_settings->name); + + BLI_strncpy(colorspace_settings->name, "", sizeof(colorspace_settings->name)); + } + } + + (void) what; +} + +void IMB_colormanagement_check_file_config(Main *bmain) +{ + Scene *scene; + Image *image; + MovieClip *clip; + + ColorManagedDisplay *default_display; + + default_display = colormanage_display_get_default(); + + if (!default_display) { + /* happens when OCIO configuration is incorrect */ + return; + } + + for (scene = bmain->scene.first; scene; scene = scene->id.next) { + ColorManagedColorspaceSettings *sequencer_colorspace_settings; + + colormanage_check_display_settings(&scene->display_settings, "scene", default_display); + colormanage_check_view_settings(&scene->display_settings, &scene->view_settings, "scene"); + + sequencer_colorspace_settings = &scene->sequencer_colorspace_settings; + + colormanage_check_colorspace_settings(sequencer_colorspace_settings, "sequencer"); + + if (sequencer_colorspace_settings->name[0] == '\0') { + BLI_strncpy(sequencer_colorspace_settings->name, global_role_default_sequencer, MAX_COLORSPACE_NAME); + } + } + + /* ** check input color space settings ** */ + + for (image = bmain->image.first; image; image = image->id.next) { + colormanage_check_colorspace_settings(&image->colorspace_settings, "image"); + } + + for (clip = bmain->movieclip.first; clip; clip = clip->id.next) { + colormanage_check_colorspace_settings(&clip->colorspace_settings, "clip"); + } +} + +void IMB_colormanagement_validate_settings(ColorManagedDisplaySettings *display_settings, + ColorManagedViewSettings *view_settings) +{ + ColorManagedDisplay *display; + ColorManagedView *default_view; + LinkData *view_link; + + display = colormanage_display_get_named(display_settings->display_device); + default_view = colormanage_view_get_default(display); + + for (view_link = display->views.first; view_link; view_link = view_link->next) { + ColorManagedView *view = view_link->data; + + if (!strcmp(view->name, view_settings->view_transform)) + break; + } + + if (view_link == NULL) + BLI_strncpy(view_settings->view_transform, default_view->name, sizeof(view_settings->view_transform)); +} + +const char *IMB_colormanagement_role_colorspace_name_get(int role) +{ + switch (role) { + case COLOR_ROLE_SCENE_LINEAR: + return global_role_scene_linear; + break; + case COLOR_ROLE_COLOR_PICKING: + return global_role_color_picking; + break; + case COLOR_ROLE_TEXTURE_PAINTING: + return global_role_texture_painting; + break; + case COLOR_ROLE_DEFAULT_SEQUENCER: + return global_role_default_sequencer; + break; + case COLOR_ROLE_DEFAULT_FLOAT: + return global_role_default_float; + break; + case COLOR_ROLE_DEFAULT_BYTE: + return global_role_default_byte; + break; + default: + printf("Unknown role was passed to %s\n", __func__); + BLI_assert(0); + } + + return NULL; +} + +/*********************** Threaded display buffer transform routines *************************/ + +typedef struct DisplayBufferThread { + ColormanageProcessor *cm_processor; + + float *buffer; + unsigned char *byte_buffer; + + float *display_buffer; + unsigned char *display_buffer_byte; + + int width; + int start_line; + int tot_line; + + int channels; + float dither; + int predivide; + + const char *byte_colorspace; + const char *float_colorspace; +} DisplayBufferThread; + +typedef struct DisplayBufferInitData { + ImBuf *ibuf; + ColormanageProcessor *cm_processor; + float *buffer; + unsigned char *byte_buffer; + + float *display_buffer; + unsigned char *display_buffer_byte; + + int width; + + const char *byte_colorspace; + const char *float_colorspace; +} DisplayBufferInitData; + +static void display_buffer_init_handle(void *handle_v, int start_line, int tot_line, void *init_data_v) +{ + DisplayBufferThread *handle = (DisplayBufferThread *) handle_v; + DisplayBufferInitData *init_data = (DisplayBufferInitData *) init_data_v; + ImBuf *ibuf = init_data->ibuf; + + int predivide = ibuf->flags & IB_cm_predivide; + int channels = ibuf->channels; + float dither = ibuf->dither; + + int offset = channels * start_line * ibuf->x; + + memset(handle, 0, sizeof(DisplayBufferThread)); + + handle->cm_processor = init_data->cm_processor; + + if (init_data->buffer) + handle->buffer = init_data->buffer + offset; + + if (init_data->byte_buffer) + handle->byte_buffer = init_data->byte_buffer + offset; + + if (init_data->display_buffer) + handle->display_buffer = init_data->display_buffer + offset; + + if (init_data->display_buffer_byte) + handle->display_buffer_byte = init_data->display_buffer_byte + offset; + + handle->width = ibuf->x; + + handle->start_line = start_line; + handle->tot_line = tot_line; + + handle->channels = channels; + handle->dither = dither; + handle->predivide = predivide; + + handle->byte_colorspace = init_data->byte_colorspace; + handle->float_colorspace = init_data->float_colorspace; +} + +static void *display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle) +{ + float *linear_buffer = NULL; + + int channels = handle->channels; + int width = handle->width; + int height = handle->tot_line; + + int buffer_size = channels * width * height; + + int predivide = handle->predivide; + + linear_buffer = MEM_callocN(buffer_size * sizeof(float), "color conversion linear buffer"); + + if (!handle->buffer) { + unsigned char *byte_buffer = handle->byte_buffer; + + const char *from_colorspace = handle->byte_colorspace; + const char *to_colorspace = global_role_scene_linear; + + float *fp; + unsigned char *cp; + int i; + + /* first convert byte buffer to float, keep in image space */ + for (i = 0, fp = linear_buffer, cp = byte_buffer; + i < channels * width * height; + i++, fp++, cp++) + { + *fp = (float)(*cp) / 255.0f; + } + + /* convert float buffer to scene linear space */ + IMB_colormanagement_transform(linear_buffer, width, height, channels, + from_colorspace, to_colorspace, predivide); + } + else if (handle->float_colorspace) { + /* currently float is non-linear only in sequencer, which is working + * in it's own color space even to handle float buffers. + * This color space is the same for byte and float images. + * Need to convert float buffer to linear space before applying display transform + */ + + const char *from_colorspace = handle->float_colorspace; + const char *to_colorspace = global_role_scene_linear; + + memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float)); + + IMB_colormanagement_transform(linear_buffer, width, height, channels, + from_colorspace, to_colorspace, predivide); + } + else { + /* some processors would want to modify float original buffer + * before converting it into display byte buffer, so we need to + * make sure original's ImBuf buffers wouldn't be modified by + * using duplicated buffer here + * + * NOTE: MEM_dupallocN can't be used because buffer could be + * specified as an offset inside allocated buffer + */ + + memcpy(linear_buffer, handle->buffer, buffer_size * sizeof(float)); + } + + return linear_buffer; +} + +static void *do_display_buffer_apply_thread(void *handle_v) +{ + DisplayBufferThread *handle = (DisplayBufferThread *) handle_v; + ColormanageProcessor *cm_processor = handle->cm_processor; + float *buffer = handle->buffer; + float *display_buffer = handle->display_buffer; + unsigned char *display_buffer_byte = handle->display_buffer_byte; + int channels = handle->channels; + int width = handle->width; + int height = handle->tot_line; + float dither = handle->dither; + int predivide = handle->predivide; + + float *linear_buffer = display_buffer_apply_get_linear_buffer(handle); + + /* apply processor */ + IMB_colormanagement_processor_apply(cm_processor, linear_buffer, width, height, channels, predivide); + + /* copy result to output buffers */ + if (display_buffer_byte) { + /* do conversion */ + IMB_buffer_byte_from_float(display_buffer_byte, linear_buffer, + channels, dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB, + predivide, width, height, width, width); + } + + if (display_buffer) + memcpy(display_buffer, linear_buffer, width * height * channels * sizeof(float)); + + if (linear_buffer != buffer) + MEM_freeN(linear_buffer); + + return NULL; +} + +static void display_buffer_apply_threaded(ImBuf *ibuf, float *buffer, unsigned char *byte_buffer, float *display_buffer, + unsigned char *display_buffer_byte, ColormanageProcessor *cm_processor) +{ + DisplayBufferInitData init_data; + + init_data.ibuf = ibuf; + init_data.cm_processor = cm_processor; + init_data.buffer = buffer; + init_data.byte_buffer = byte_buffer; + init_data.display_buffer = display_buffer; + init_data.display_buffer_byte = display_buffer_byte; + + if (ibuf->rect_colorspace != NULL) { + init_data.byte_colorspace = ibuf->rect_colorspace->name; + } + else { + /* happens for viewer images, which are not so simple to determine where to + * set image buffer's color spaces + */ + init_data.byte_colorspace = global_role_default_byte; + } + + if (ibuf->float_colorspace != NULL) { + /* sequencer stores float buffers in non-linear space */ + init_data.float_colorspace = ibuf->float_colorspace->name; + } + else { + init_data.float_colorspace = NULL; + } + + IMB_processor_apply_threaded(ibuf->y, sizeof(DisplayBufferThread), &init_data, + display_buffer_init_handle, do_display_buffer_apply_thread); +} + +static void colormanage_display_buffer_process_ex(ImBuf *ibuf, float *display_buffer, unsigned char *display_buffer_byte, + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings) +{ + ColormanageProcessor *cm_processor; + + cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); + + display_buffer_apply_threaded(ibuf, ibuf->rect_float, (unsigned char *) ibuf->rect, + display_buffer, display_buffer_byte, cm_processor); + + IMB_colormanagement_processor_free(cm_processor); +} + +static void colormanage_display_buffer_process(ImBuf *ibuf, unsigned char *display_buffer, + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings) +{ + colormanage_display_buffer_process_ex(ibuf, NULL, display_buffer, view_settings, display_settings); +} + +/*********************** Threaded processor transform routines *************************/ + +typedef struct ProcessorTransformThread { + ColormanageProcessor *cm_processor; + float *buffer; + int width; + int start_line; + int tot_line; + int channels; + int predivide; +} ProcessorTransformThread; + +typedef struct ProcessorTransformInit { + ColormanageProcessor *cm_processor; + float *buffer; + int width; + int height; + int channels; + int predivide; +} ProcessorTransformInitData; + +static void processor_transform_init_handle(void *handle_v, int start_line, int tot_line, void *init_data_v) +{ + ProcessorTransformThread *handle = (ProcessorTransformThread *) handle_v; + ProcessorTransformInitData *init_data = (ProcessorTransformInitData *) init_data_v; + + int channels = init_data->channels; + int width = init_data->width; + int predivide = init_data->predivide; + + int offset = channels * start_line * width; + + memset(handle, 0, sizeof(ProcessorTransformThread)); + + handle->cm_processor = init_data->cm_processor; + + handle->buffer = init_data->buffer + offset; + + handle->width = width; + + handle->start_line = start_line; + handle->tot_line = tot_line; + + handle->channels = channels; + handle->predivide = predivide; +} + +static void *do_processor_transform_thread(void *handle_v) +{ + ProcessorTransformThread *handle = (ProcessorTransformThread *) handle_v; + float *buffer = handle->buffer; + int channels = handle->channels; + int width = handle->width; + int height = handle->tot_line; + int predivide = handle->predivide; + + IMB_colormanagement_processor_apply(handle->cm_processor, buffer, width, height, channels, predivide); + + return NULL; +} + +static void processor_transform_apply_threaded(float *buffer, int width, int height, int channels, + ColormanageProcessor *cm_processor, int predivide) +{ + ProcessorTransformInitData init_data; + + init_data.cm_processor = cm_processor; + init_data.buffer = buffer; + init_data.width = width; + init_data.height = height; + init_data.channels = channels; + init_data.predivide = predivide; + + IMB_processor_apply_threaded(height, sizeof(ProcessorTransformThread), &init_data, + processor_transform_init_handle, do_processor_transform_thread); +} + +/*********************** Color space transformation functions *************************/ + +/* convert the whole buffer from specified by name color space to another - internal implementation */ +static void colormanagement_transform_ex(float *buffer, int width, int height, int channels, const char *from_colorspace, + const char *to_colorspace, int predivide, int do_threaded) +{ + ColormanageProcessor *cm_processor; + + if (from_colorspace[0] == '\0') { + return; + } + + if (!strcmp(from_colorspace, to_colorspace)) { + /* if source and destination color spaces are identical, skip + * threading overhead and simply do nothing + */ + return; + } + + cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace); + + if (do_threaded) + processor_transform_apply_threaded(buffer, width, height, channels, cm_processor, predivide); + else + IMB_colormanagement_processor_apply(cm_processor, buffer, width, height, channels, predivide); + + IMB_colormanagement_processor_free(cm_processor); +} + +/* convert the whole buffer from specified by name color space to another */ +void IMB_colormanagement_transform(float *buffer, int width, int height, int channels, + const char *from_colorspace, const char *to_colorspace, int predivide) +{ + colormanagement_transform_ex(buffer, width, height, channels, from_colorspace, to_colorspace, predivide, FALSE); +} + +/* convert the whole buffer from specified by name color space to another + * will do threaded conversion + */ +void IMB_colormanagement_transform_threaded(float *buffer, int width, int height, int channels, + const char *from_colorspace, const char *to_colorspace, int predivide) +{ + colormanagement_transform_ex(buffer, width, height, channels, from_colorspace, to_colorspace, predivide, TRUE); +} + +void IMB_colormanagement_transform_v4(float pixel[4], const char *from_colorspace, const char *to_colorspace) +{ + ColormanageProcessor *cm_processor; + + if (from_colorspace[0] == '\0') { + return; + } + + if (!strcmp(from_colorspace, to_colorspace)) { + /* if source and destination color spaces are identical, skip + * threading overhead and simply do nothing + */ + return; + } + + cm_processor = IMB_colormanagement_colorspace_processor_new(from_colorspace, to_colorspace); + + IMB_colormanagement_processor_apply_v4(cm_processor, pixel); + + IMB_colormanagement_processor_free(cm_processor); +} + +/* convert pixel from specified by descriptor color space to scene linear + * used by performance-critical areas such as renderer and baker + */ +void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], ColorSpace *colorspace) +{ + ConstProcessorRcPtr *processor; + + if (!colorspace) { + /* OCIO_TODO: make sure it never happens */ + + printf("%s: perform conversion from unknown color space\n", __func__); + + return; + } + + processor = colorspace_to_scene_linear_processor(colorspace); + + if (processor) + OCIO_processorApplyRGB(processor, pixel); +} + +/* same as above, but converts colors in opposite direction */ +void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], ColorSpace *colorspace) +{ + ConstProcessorRcPtr *processor; + + if (!colorspace) { + /* OCIO_TODO: make sure it never happens */ + + printf("%s: perform conversion from unknown color space\n", __func__); + + return; + } + + processor = colorspace_from_scene_linear_processor(colorspace); + + if (processor) + OCIO_processorApplyRGB(processor, pixel); +} + +void IMB_colormanagement_colorspace_to_scene_linear(float *buffer, int width, int height, int channels, struct ColorSpace *colorspace, int predivide) +{ + ConstProcessorRcPtr *processor; + + if (!colorspace) { + /* OCIO_TODO: make sure it never happens */ + + printf("%s: perform conversion from unknown color space\n", __func__); + + return; + } + + processor = colorspace_to_scene_linear_processor(colorspace); + + if (processor) { + PackedImageDesc *img; + + img = OCIO_createPackedImageDesc(buffer, width, height, channels, sizeof(float), + channels * sizeof(float), channels * sizeof(float) * width); + + if (predivide) + OCIO_processorApply_predivide(processor, img); + else + OCIO_processorApply(processor, img); + + OCIO_packedImageDescRelease(img); + } +} + +/* convert pixel from scene linear to display space using default view + * used by performance-critical areas such as color-related widgets where we want to reduce + * amount of per-widget allocations + */ +void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManagedDisplay *display) +{ + ConstProcessorRcPtr *processor; + + processor = display_from_scene_linear_processor(display); + + if (processor) + OCIO_processorApplyRGB(processor, pixel); +} + +/* same as above, but converts color in opposite direction */ +void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], ColorManagedDisplay *display) +{ + ConstProcessorRcPtr *processor; + + processor = display_to_scene_linear_processor(display); + + if (processor) + OCIO_processorApplyRGB(processor, pixel); +} + +void IMB_colormanagement_pixel_to_display_space_v4(float result[4], const float pixel[4], + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings) +{ + ColormanageProcessor *cm_processor; + + copy_v4_v4(result, pixel); + + cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); + IMB_colormanagement_processor_apply_v4(cm_processor, result); + IMB_colormanagement_processor_free(cm_processor); +} + +void IMB_colormanagement_pixel_to_display_space_v3(float result[3], const float pixel[3], + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings) +{ + ColormanageProcessor *cm_processor; + + copy_v3_v3(result, pixel); + + cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); + IMB_colormanagement_processor_apply_v3(cm_processor, result); + IMB_colormanagement_processor_free(cm_processor); +} + +void IMB_colormanagement_imbuf_assign_float_space(ImBuf *ibuf, ColorManagedColorspaceSettings *colorspace_settings) +{ + ibuf->float_colorspace = colormanage_colorspace_get_named(colorspace_settings->name); +} + +void IMB_colormanagement_imbuf_make_display_space(ImBuf *ibuf, const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings) +{ + /* OCIO_TODO: byte buffer management is not supported here yet */ + if (!ibuf->rect_float) + return; + + if (global_tot_display == 0 || global_tot_view == 0) { + IMB_buffer_float_from_float(ibuf->rect_float, ibuf->rect_float, ibuf->channels, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB, + ibuf->flags & IB_cm_predivide, ibuf->x, ibuf->y, ibuf->x, ibuf->x); + } + else { + colormanage_display_buffer_process_ex(ibuf, ibuf->rect_float, NULL, view_settings, display_settings); + } +} + +static void imbuf_verify_float(ImBuf *ibuf) +{ + /* multiple threads could request for display buffer at once and in case + * view transform is not used it'll lead to display buffer calculated + * several times + * it is harmless, but would take much more time (assuming thread lock + * happens faster than running float->byte conversion for average image) + */ + BLI_lock_thread(LOCK_COLORMANAGE); + + if (ibuf->rect_float && (ibuf->rect == NULL || (ibuf->userflags & IB_RECT_INVALID))) { + IMB_rect_from_float(ibuf); + + ibuf->userflags &= ~IB_RECT_INVALID; + } + + BLI_unlock_thread(LOCK_COLORMANAGE); +} + +/*********************** Public display buffers interfaces *************************/ + +/* acquire display buffer for given image buffer using specified view and display settings */ +unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, void **cache_handle) +{ + *cache_handle = NULL; + + if (!ibuf->x || !ibuf->y) + return NULL; + + if (global_tot_display == 0 || global_tot_view == 0) { + /* if there's no view transform or display transforms, fallback to standard sRGB/linear conversion + * the same logic would be used if OCIO is disabled + */ + + imbuf_verify_float(ibuf); + + return (unsigned char *) ibuf->rect; + } + else { + unsigned char *display_buffer; + int buffer_size; + ColormanageCacheViewSettings cache_view_settings; + ColormanageCacheDisplaySettings cache_display_settings; + ColorManagedViewSettings default_view_settings; + const ColorManagedViewSettings *applied_view_settings; + + if (view_settings) { + applied_view_settings = view_settings; + } + else { + /* if no view settings were specified, use default display transformation + * this happens for images which don't want to be displayed with render settings + */ + + init_default_view_settings(display_settings, &default_view_settings); + applied_view_settings = &default_view_settings; + } + + colormanage_view_settings_to_cache(&cache_view_settings, applied_view_settings); + colormanage_display_settings_to_cache(&cache_display_settings, display_settings); + + BLI_lock_thread(LOCK_COLORMANAGE); + + /* ensure color management bit fields exists */ + if (!ibuf->display_buffer_flags) { + if (global_tot_display) + ibuf->display_buffer_flags = MEM_callocN(sizeof(unsigned int) * global_tot_display, "imbuf display_buffer_flags"); + } + else if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) { + /* all display buffers were marked as invalid from other areas, + * now propagate this flag to internal color management routines + */ + memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int)); + + ibuf->userflags &= ~IB_DISPLAY_BUFFER_INVALID; + } + + display_buffer = colormanage_cache_get(ibuf, &cache_view_settings, &cache_display_settings, cache_handle); + + if (display_buffer) { + BLI_unlock_thread(LOCK_COLORMANAGE); + return display_buffer; + } + + buffer_size = ibuf->channels * ibuf->x * ibuf->y * sizeof(float); + display_buffer = MEM_callocN(buffer_size, "imbuf display buffer"); + + colormanage_display_buffer_process(ibuf, display_buffer, applied_view_settings, display_settings); + + colormanage_cache_put(ibuf, &cache_view_settings, &cache_display_settings, display_buffer, cache_handle); + + BLI_unlock_thread(LOCK_COLORMANAGE); + + return display_buffer; + } +} + +/* same as IMB_display_buffer_acquire but gets view and display settings from context */ +unsigned char *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle) +{ + ColorManagedViewSettings *view_settings; + ColorManagedDisplaySettings *display_settings; + + display_transform_get_from_ctx(C, &view_settings, &display_settings); + + return IMB_display_buffer_acquire(ibuf, view_settings, display_settings, cache_handle); +} + +/* covert float buffer to display space and store it in image buffer's byte array */ +void IMB_display_buffer_to_imbuf_rect(ImBuf *ibuf, const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings) +{ + if (global_tot_display == 0 || global_tot_view == 0) { + imbuf_verify_float(ibuf); + } + else { + if (!ibuf->rect) { + imb_addrectImBuf(ibuf); + } + + colormanage_display_buffer_process(ibuf, (unsigned char *) ibuf->rect, view_settings, display_settings); + } +} + +void 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, int predivide) +{ + if (global_tot_display == 0 || global_tot_view == 0) { + IMB_buffer_byte_from_float(display_buffer, linear_buffer, 4, 0.0f, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB, FALSE, + width, height, width, width); + } + else { + float *buffer; + ColormanageProcessor *cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); + + buffer = MEM_callocN(channels * width * height * sizeof(float), "display transform temp buffer"); + memcpy(buffer, linear_buffer, channels * width * height * sizeof(float)); + + IMB_colormanagement_processor_apply(cm_processor, buffer, width, height, channels, predivide); + + IMB_colormanagement_processor_free(cm_processor); + + IMB_buffer_byte_from_float(display_buffer, buffer, channels, 0.0f, IB_PROFILE_SRGB, IB_PROFILE_SRGB, + FALSE, width, height, width, width); + + MEM_freeN(buffer); + } +} + +void IMB_display_buffer_release(void *cache_handle) +{ + if (cache_handle) { + BLI_lock_thread(LOCK_COLORMANAGE); + + colormanage_cache_handle_release(cache_handle); + + BLI_unlock_thread(LOCK_COLORMANAGE); + } +} + +/*********************** Display functions *************************/ + +ColorManagedDisplay *colormanage_display_get_default(void) +{ + ConstConfigRcPtr *config = OCIO_getCurrentConfig(); + const char *display; + + if (!config) { + /* no valid OCIO configuration, can't get default display */ + + return NULL; + } + + display = OCIO_configGetDefaultDisplay(config); + + OCIO_configRelease(config); + + if (display[0] == '\0') + return NULL; + + return colormanage_display_get_named(display); +} + +ColorManagedDisplay *colormanage_display_add(const char *name) +{ + ColorManagedDisplay *display; + int index = 0; + + if (global_displays.last) { + ColorManagedDisplay *last_display = global_displays.last; + + index = last_display->index; + } + + display = MEM_callocN(sizeof(ColorManagedDisplay), "ColorManagedDisplay"); + + display->index = index + 1; + + BLI_strncpy(display->name, name, sizeof(display->name)); + + BLI_addtail(&global_displays, display); + + return display; +} + +ColorManagedDisplay *colormanage_display_get_named(const char *name) +{ + ColorManagedDisplay *display; + + for (display = global_displays.first; display; display = display->next) { + if (!strcmp(display->name, name)) + return display; + } + + return NULL; +} + +ColorManagedDisplay *colormanage_display_get_indexed(int index) +{ + /* display indices are 1-based */ + return BLI_findlink(&global_displays, index - 1); +} + +int IMB_colormanagement_display_get_named_index(const char *name) +{ + ColorManagedDisplay *display; + + display = colormanage_display_get_named(name); + + if (display) { + return display->index; + } + + return 0; +} + +const char *IMB_colormanagement_display_get_indexed_name(int index) +{ + ColorManagedDisplay *display; + + display = colormanage_display_get_indexed(index); + + if (display) { + return display->name; + } + + return NULL; +} + +const char *IMB_colormanagement_display_get_default_name(void) +{ + ColorManagedDisplay *display = colormanage_display_get_default(); + + return display->name; +} + +/* used by performance-critical pixel processing areas, such as color widgets */ +ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name) +{ + return colormanage_display_get_named(name); +} + +/*********************** View functions *************************/ + +const char *colormanage_view_get_default_name(const ColorManagedDisplay *display) +{ + ConstConfigRcPtr *config = OCIO_getCurrentConfig(); + const char *name; + + if (!config) { + /* no valid OCIO configuration, can't get default view */ + + return NULL; + } + + name = OCIO_configGetDefaultView(config, display->name); + + OCIO_configRelease(config); + + return name; +} + +ColorManagedView *colormanage_view_get_default(const ColorManagedDisplay *display) +{ + const char *name = colormanage_view_get_default_name(display); + + if (!name || name[0] == '\0') + return NULL; + + return colormanage_view_get_named(name); +} + +ColorManagedView *colormanage_view_add(const char *name) +{ + ColorManagedView *view; + int index = global_tot_view; + + view = MEM_callocN(sizeof(ColorManagedView), "ColorManagedView"); + view->index = index + 1; + BLI_strncpy(view->name, name, sizeof(view->name)); + + BLI_addtail(&global_views, view); + + global_tot_view++; + + return view; +} + +ColorManagedView *colormanage_view_get_named(const char *name) +{ + ColorManagedView *view; + + for (view = global_views.first; view; view = view->next) { + if (!strcmp(view->name, name)) + return view; + } + + return NULL; +} + +ColorManagedView *colormanage_view_get_indexed(int index) +{ + /* view transform indices are 1-based */ + return BLI_findlink(&global_views, index - 1); +} + +int IMB_colormanagement_view_get_named_index(const char *name) +{ + ColorManagedView *view = colormanage_view_get_named(name); + + if (view) { + return view->index; + } + + return 0; +} + +const char *IMB_colormanagement_view_get_indexed_name(int index) +{ + ColorManagedView *view = colormanage_view_get_indexed(index); + + if (view) { + return view->name; + } + + return NULL; +} + +const char *IMB_colormanagement_view_get_default_name(const char *display_name) +{ + ColorManagedDisplay *display = colormanage_display_get_named(display_name); + ColorManagedView *view = colormanage_view_get_default(display); + + if (view) { + return view->name; + } + + return NULL; +} + +/*********************** Color space functions *************************/ + +static void colormanage_description_strip(char *description) +{ + int i, n; + + for (i = strlen(description) - 1; i >= 0; i--) { + if (ELEM(description[i], '\r', '\n')) { + description[i] = '\0'; + } + else { + break; + } + } + + for (i = 0, n = strlen(description); i < n; i++) { + if (ELEM(description[i], '\r', '\n')) { + description[i] = ' '; + } + } +} + +ColorSpace *colormanage_colorspace_add(const char *name, const char *description, int is_invertible) +{ + ColorSpace *colorspace, *prev_space; + int counter = 1; + + colorspace = MEM_callocN(sizeof(ColorSpace), "ColorSpace"); + + BLI_strncpy(colorspace->name, name, sizeof(colorspace->name)); + + if (description) { + BLI_strncpy(colorspace->description, description, sizeof(colorspace->description)); + + colormanage_description_strip(colorspace->description); + } + + colorspace->is_invertible = is_invertible; + + for (prev_space = global_colorspaces.first; prev_space; prev_space = prev_space->next) { + if (BLI_strcasecmp(prev_space->name, colorspace->name) > 0) + break; + + prev_space->index = counter++; + } + + if (!prev_space) + BLI_addtail(&global_colorspaces, colorspace); + else + BLI_insertlinkbefore(&global_colorspaces, prev_space, colorspace); + + colorspace->index = counter++; + for (; prev_space; prev_space = prev_space->next) { + prev_space->index = counter++; + } + + global_tot_colorspace++; + + return colorspace; +} + +ColorSpace *colormanage_colorspace_get_named(const char *name) +{ + ColorSpace *colorspace; + + for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) { + if (!strcmp(colorspace->name, name)) + return colorspace; + } + + return NULL; +} + +ColorSpace *colormanage_colorspace_get_roled(int role) +{ + const char *role_colorspace = IMB_colormanagement_role_colorspace_name_get(role); + + return colormanage_colorspace_get_named(role_colorspace); +} + +ColorSpace *colormanage_colorspace_get_indexed(int index) +{ + /* display indices are 1-based */ + return BLI_findlink(&global_colorspaces, index - 1); +} + +int IMB_colormanagement_colorspace_get_named_index(const char *name) +{ + ColorSpace *colorspace; + + colorspace = colormanage_colorspace_get_named(name); + + if (colorspace) { + return colorspace->index; + } + + return 0; +} + +const char *IMB_colormanagement_colorspace_get_indexed_name(int index) +{ + ColorSpace *colorspace; + + colorspace = colormanage_colorspace_get_indexed(index); + + if (colorspace) { + return colorspace->name; + } + + return ""; +} + +void IMB_colormanagment_colorspace_from_ibuf_ftype(ColorManagedColorspaceSettings *colorspace_settings, ImBuf *ibuf) +{ + ImFileType *type; + + for (type = IMB_FILE_TYPES; type->is_a; type++) { + if (type->save && type->ftype(type, ibuf)) { + const char *role_colorspace; + + role_colorspace = IMB_colormanagement_role_colorspace_name_get(type->default_save_role); + + BLI_strncpy(colorspace_settings->name, role_colorspace, sizeof(colorspace_settings->name)); + } + } +} + +/*********************** RNA helper functions *************************/ + +void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totitem) +{ + ColorManagedDisplay *display; + + for (display = global_displays.first; display; display = display->next) { + EnumPropertyItem item; + + item.value = display->index; + item.name = display->name; + item.identifier = display->name; + item.icon = 0; + item.description = ""; + + RNA_enum_item_add(items, totitem, &item); + } +} + +static void colormanagement_view_item_add(EnumPropertyItem **items, int *totitem, ColorManagedView *view) +{ + EnumPropertyItem item; + + item.value = view->index; + item.name = view->name; + item.identifier = view->name; + item.icon = 0; + item.description = ""; + + RNA_enum_item_add(items, totitem, &item); +} + +void IMB_colormanagement_view_items_add(EnumPropertyItem **items, int *totitem, const char *display_name) +{ + ColorManagedDisplay *display = colormanage_display_get_named(display_name); + ColorManagedView *view; + + if (display) { + LinkData *display_view; + + for (display_view = display->views.first; display_view; display_view = display_view->next) { + view = display_view->data; + + colormanagement_view_item_add(items, totitem, view); + } + } +} + +void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *totitem) +{ + ColorSpace *colorspace; + + for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) { + EnumPropertyItem item; + + if (!colorspace->is_invertible) + continue; + + item.value = colorspace->index; + item.name = colorspace->name; + item.identifier = colorspace->name; + item.icon = 0; + + if (colorspace->description) + item.description = colorspace->description; + else + item.description = ""; + + RNA_enum_item_add(items, totitem, &item); + } +} + +/*********************** Partial display buffer update *************************/ + +/* + * Partial display update is supposed to be used by such areas as + * compositor and renderer, This areas are calculating tiles of the + * images and because of performance reasons only this tiles should + * be color managed. + * This gives nice visual feedback without slowing things down. + * + * Updating happens for active display transformation only, all + * the rest buffers would be marked as dirty + */ + +static void 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, + int xmin, int ymin, int xmax, int ymax) +{ + int x, y; + int channels = ibuf->channels; + int predivide = ibuf->flags & IB_cm_predivide; + float dither = ibuf->dither; + ColorSpace *rect_colorspace = ibuf->rect_colorspace; + float *display_buffer_float = NULL; + int width = xmax - xmin; + int height = ymax - ymin; + + if (dither != 0.0f) { + display_buffer_float = MEM_callocN(channels * width * height * sizeof(float), "display buffer for dither"); + } + + for (y = ymin; y < ymax; y++) { + for (x = xmin; x < xmax; x++) { + int display_index = (y * display_stride + x) * channels; + int linear_index = ((y - linear_offset_y) * linear_stride + (x - linear_offset_x)) * channels; + float pixel[4]; + + if (linear_buffer) { + copy_v4_v4(pixel, (float *) linear_buffer + linear_index); + } + else if (byte_buffer) { + rgba_uchar_to_float(pixel, byte_buffer + linear_index); + + IMB_colormanagement_colorspace_to_scene_linear_v3(pixel, rect_colorspace); + } + + if (predivide) + IMB_colormanagement_processor_apply_v4(cm_processor, pixel); + else + IMB_colormanagement_processor_apply_v4(cm_processor, pixel); + + if (display_buffer_float) { + int index = ((y - ymin) * width + (x - xmin)) * channels; + + copy_v4_v4(display_buffer_float + index, pixel); + } + else { + rgba_float_to_uchar(display_buffer + display_index, pixel); + } + } + } + + if (display_buffer_float) { + int display_index = (ymin * display_stride + xmin) * channels; + + IMB_buffer_byte_from_float(display_buffer + display_index, display_buffer_float, channels, dither, + IB_PROFILE_SRGB, IB_PROFILE_SRGB, FALSE, width, height, display_stride, width); + + MEM_freeN(display_buffer_float); + } +} + +void 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) +{ + if (ibuf->rect && ibuf->rect_float) { + /* update byte buffer created by legacy color management */ + + unsigned char *rect = (unsigned char *) ibuf->rect; + int predivide = ibuf->flags & IB_cm_predivide; + int channels = ibuf->channels; + int width = xmax - xmin; + int height = ymax - ymin; + int rect_index = (ymin * ibuf->x + xmin) * channels; + int linear_index = ((ymin - offset_y) * stride + (xmin - offset_x)) * channels; + + IMB_buffer_byte_from_float(rect + rect_index, linear_buffer + linear_index, channels, ibuf->dither, + IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB, predivide, width, height, ibuf->x, stride); + } + + if (ibuf->display_buffer_flags) { + ColormanageCacheViewSettings cache_view_settings; + ColormanageCacheDisplaySettings cache_display_settings; + void *cache_handle = NULL; + unsigned char *display_buffer = NULL; + int view_flag, display_index, buffer_width; + + colormanage_view_settings_to_cache(&cache_view_settings, view_settings); + colormanage_display_settings_to_cache(&cache_display_settings, display_settings); + + view_flag = 1 << (cache_view_settings.view - 1); + display_index = cache_display_settings.display - 1; + + BLI_lock_thread(LOCK_COLORMANAGE); + if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) + display_buffer = colormanage_cache_get(ibuf, &cache_view_settings, &cache_display_settings, &cache_handle); + + /* in some rare cases buffer's dimension could be changing directly from + * different thread + * this i.e. happens when image editor acquires render result + */ + buffer_width = ibuf->x; + + /* mark all other buffers as invalid */ + memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int)); + ibuf->display_buffer_flags[display_index] |= view_flag; + + BLI_unlock_thread(LOCK_COLORMANAGE); + + if (display_buffer) { + ColormanageProcessor *cm_processor; + + cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); + + partial_buffer_update_rect(ibuf, display_buffer, linear_buffer, byte_buffer, buffer_width, stride, + offset_x, offset_y, cm_processor, xmin, ymin, xmax, ymax); + + IMB_colormanagement_processor_free(cm_processor); + + IMB_display_buffer_release(cache_handle); + } + } +} + +/*********************** Pixel processor functions *************************/ + +ColormanageProcessor *IMB_colormanagement_display_processor_new(const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings) +{ + ColormanageProcessor *cm_processor; + + cm_processor = MEM_callocN(sizeof(ColormanageProcessor), "colormanagement processor"); + + { + ColorManagedViewSettings default_view_settings; + const ColorManagedViewSettings *applied_view_settings; + + if (view_settings) { + applied_view_settings = view_settings; + } + else { + init_default_view_settings(display_settings, &default_view_settings); + applied_view_settings = &default_view_settings; + } + + cm_processor->processor = create_display_buffer_processor(applied_view_settings->view_transform, display_settings->display_device, + applied_view_settings->exposure, applied_view_settings->gamma); + + if (applied_view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) { + cm_processor->curve_mapping = curvemapping_copy(applied_view_settings->curve_mapping); + curvemapping_premultiply(cm_processor->curve_mapping, FALSE); + } + } + + return cm_processor; +} + +ColormanageProcessor *IMB_colormanagement_colorspace_processor_new(const char *from_colorspace, const char *to_colorspace) +{ + ColormanageProcessor *cm_processor; + + cm_processor = MEM_callocN(sizeof(ColormanageProcessor), "colormanagement processor"); + + cm_processor->processor = create_colorspace_transform_processor(from_colorspace, to_colorspace); + + return cm_processor; +} + +void IMB_colormanagement_processor_apply_v4(ColormanageProcessor *cm_processor, float pixel[4]) +{ + if (cm_processor->curve_mapping) + curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel); + + OCIO_processorApplyRGBA(cm_processor->processor, pixel); +} + +void IMB_colormanagement_processor_apply_v3(ColormanageProcessor *cm_processor, float pixel[3]) +{ + if (cm_processor->curve_mapping) + curvemapping_evaluate_premulRGBF(cm_processor->curve_mapping, pixel, pixel); + + OCIO_processorApplyRGB(cm_processor->processor, pixel); +} + +void IMB_colormanagement_processor_apply(ColormanageProcessor *cm_processor, float *buffer, int width, int height, + int channels, int predivide) +{ + /* apply curve mapping */ + if (cm_processor->curve_mapping) { + int x, y; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + float *pixel = buffer + channels * (y * width + x); + + curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, channels); + } + } + } + + { + PackedImageDesc *img; + + /* apply OCIO processor */ + img = OCIO_createPackedImageDesc(buffer, width, height, channels, sizeof(float), + channels * sizeof(float), channels * sizeof(float) * width); + + if (predivide) + OCIO_processorApply_predivide(cm_processor->processor, img); + else + OCIO_processorApply(cm_processor->processor, img); + + OCIO_packedImageDescRelease(img); + } +} + +void IMB_colormanagement_processor_free(ColormanageProcessor *cm_processor) +{ + if (cm_processor->curve_mapping) + curvemapping_free(cm_processor->curve_mapping); + + OCIO_processorRelease(cm_processor->processor); + + MEM_freeN(cm_processor); +} diff --git a/source/blender/imbuf/intern/dds/dds_api.cpp b/source/blender/imbuf/intern/dds/dds_api.cpp index 71313d4438f..7aacbf20bae 100644 --- a/source/blender/imbuf/intern/dds/dds_api.cpp +++ b/source/blender/imbuf/intern/dds/dds_api.cpp @@ -43,6 +43,9 @@ extern "C" { #include "IMB_imbuf.h" #include "IMB_allocimbuf.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + int imb_save_dds(struct ImBuf * ibuf, const char *name, int flags) { return(0); /* todo: finish this function */ @@ -79,7 +82,7 @@ int imb_is_a_dds(unsigned char *mem) // note: use at most first 32 bytes return(1); } -struct ImBuf *imb_load_dds(unsigned char *mem, size_t size, int flags) +struct ImBuf *imb_load_dds(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf * ibuf = 0; DirectDrawSurface dds(mem, size); /* reads header */ @@ -92,6 +95,12 @@ struct ImBuf *imb_load_dds(unsigned char *mem, size_t size, int flags) Color32 pixel; Color32 *pixels = 0; + /* OCIO_TODO: never was able to save DDS, so can'ttest loading + * but profile used to be set to sRGB and can't see rect_float here, so + * default byte space should work fine + */ + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + if (!imb_is_a_dds(mem)) return (0); @@ -133,7 +142,6 @@ struct ImBuf *imb_load_dds(unsigned char *mem, size_t size, int flags) if (ibuf == 0) return(0); /* memory allocation failed */ ibuf->ftype = DDS; - ibuf->profile = IB_PROFILE_SRGB; ibuf->dds_data.fourcc = dds.fourCC(); ibuf->dds_data.nummipmaps = dds.mipmapCount(); diff --git a/source/blender/imbuf/intern/dds/dds_api.h b/source/blender/imbuf/intern/dds/dds_api.h index 589257816e0..2316fefce69 100644 --- a/source/blender/imbuf/intern/dds/dds_api.h +++ b/source/blender/imbuf/intern/dds/dds_api.h @@ -32,9 +32,11 @@ extern "C" { #endif +#include "../../IMB_imbuf.h" + int imb_save_dds(struct ImBuf *ibuf, const char *name, int flags); int imb_is_a_dds(unsigned char *mem); /* use only first 32 bytes of mem */ -struct ImBuf *imb_load_dds(unsigned char *mem, size_t size, int flags); +struct ImBuf *imb_load_dds(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); #ifdef __cplusplus } diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index 8acceb77b36..9ce5d0e30da 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -40,6 +40,9 @@ #include "IMB_imbuf.h" #include "IMB_allocimbuf.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + #include "MEM_guardedalloc.h" /**************************** Interlace/Deinterlace **************************/ @@ -522,32 +525,34 @@ void IMB_buffer_byte_from_byte(uchar *rect_to, const uchar *rect_from, void IMB_rect_from_float(ImBuf *ibuf) { int predivide = (ibuf->flags & IB_cm_predivide); - int profile_from; + float *buffer; + const char *from_colorspace; /* verify we have a float buffer */ if (ibuf->rect_float == NULL) return; /* create byte rect if it didn't exist yet */ - if (ibuf->rect == NULL) - imb_addrectImBuf(ibuf); - - /* determine profiles */ - if (ibuf->profile == IB_PROFILE_LINEAR_RGB) { - profile_from = IB_PROFILE_LINEAR_RGB; - } - else if (ELEM(ibuf->profile, IB_PROFILE_SRGB, IB_PROFILE_NONE)) { - profile_from = IB_PROFILE_SRGB; - } - else { - profile_from = IB_PROFILE_SRGB; /* should never happen */ - BLI_assert(0); + if (ibuf->rect == NULL) { + if (imb_addrectImBuf(ibuf) == 0) + return; } - /* do conversion */ - IMB_buffer_byte_from_float((uchar *)ibuf->rect, ibuf->rect_float, - ibuf->channels, ibuf->dither, IB_PROFILE_SRGB, profile_from, predivide, - ibuf->x, ibuf->y, ibuf->x, ibuf->x); + if (ibuf->float_colorspace == NULL) + from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); + else + from_colorspace = ibuf->float_colorspace->name; + + buffer = MEM_dupallocN(ibuf->rect_float); + + /* first make float buffer in byte space */ + IMB_colormanagement_transform(buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, ibuf->rect_colorspace->name, predivide); + + /* convert float to byte */ + IMB_buffer_byte_from_float((unsigned char *) ibuf->rect, buffer, ibuf->channels, ibuf->dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB, + FALSE, ibuf->x, ibuf->y, ibuf->x, ibuf->x); + + MEM_freeN(buffer); /* ensure user flag is reset */ ibuf->userflags &= ~IB_RECT_INVALID; @@ -559,7 +564,7 @@ void IMB_partial_rect_from_float(ImBuf *ibuf, float *buffer, int x, int y, int w float *rect_float; uchar *rect_byte; int predivide = (ibuf->flags & IB_cm_predivide); - int profile_from; + int profile_from = IB_PROFILE_LINEAR_RGB; /* verify we have a float buffer */ if (ibuf->rect_float == NULL || buffer == NULL) @@ -569,18 +574,6 @@ void IMB_partial_rect_from_float(ImBuf *ibuf, float *buffer, int x, int y, int w if (ibuf->rect == NULL) imb_addrectImBuf(ibuf); - /* determine profiles */ - if (ibuf->profile == IB_PROFILE_LINEAR_RGB) { - profile_from = IB_PROFILE_LINEAR_RGB; - } - else if (ELEM(ibuf->profile, IB_PROFILE_SRGB, IB_PROFILE_NONE)) { - profile_from = IB_PROFILE_SRGB; - } - else { - profile_from = IB_PROFILE_SRGB; /* should never happen */ - BLI_assert(0); - } - /* do conversion */ rect_float = ibuf->rect_float + (x + y * ibuf->x) * ibuf->channels; rect_byte = (uchar *)ibuf->rect + (x + y * ibuf->x) * 4; @@ -589,6 +582,7 @@ void IMB_partial_rect_from_float(ImBuf *ibuf, float *buffer, int x, int y, int w ibuf->channels, IB_PROFILE_SRGB, profile_from, predivide, w, h, w, ibuf->x); + /* XXX: need to convert to image buffer's rect space */ IMB_buffer_byte_from_float(rect_byte, buffer, 4, ibuf->dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB, 0, w, h, ibuf->x, w); @@ -600,26 +594,23 @@ void IMB_partial_rect_from_float(ImBuf *ibuf, float *buffer, int x, int y, int w void IMB_float_from_rect(ImBuf *ibuf) { int predivide = (ibuf->flags & IB_cm_predivide); - int profile_from; /* verify if we byte and float buffers */ if (ibuf->rect == NULL) return; - if (ibuf->rect_float == NULL) + if (ibuf->rect_float == NULL) { if (imb_addrectfloatImBuf(ibuf) == 0) return; - - /* determine profiles */ - if (ibuf->profile == IB_PROFILE_NONE) - profile_from = IB_PROFILE_LINEAR_RGB; - else - profile_from = IB_PROFILE_SRGB; - - /* do conversion */ - IMB_buffer_float_from_byte(ibuf->rect_float, (uchar *)ibuf->rect, - IB_PROFILE_LINEAR_RGB, profile_from, predivide, - ibuf->x, ibuf->y, ibuf->x, ibuf->x); + } + + /* first, create float buffer in non-linear space */ + IMB_buffer_float_from_byte(ibuf->rect_float, (unsigned char *) ibuf->rect, IB_PROFILE_SRGB, IB_PROFILE_SRGB, + FALSE, ibuf->x, ibuf->y, ibuf->x, ibuf->x); + + /* then make float be in linear space */ + IMB_colormanagement_colorspace_to_scene_linear(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels, + ibuf->rect_colorspace, predivide); } /* no profile conversion */ @@ -635,63 +626,19 @@ void IMB_float_from_rect_simple(ImBuf *ibuf) ibuf->x, ibuf->y, ibuf->x, ibuf->x); } -void IMB_convert_profile(ImBuf *ibuf, int profile) -{ - int predivide = (ibuf->flags & IB_cm_predivide); - int profile_from, profile_to; - - if (ibuf->profile == profile) - return; - - /* determine profiles */ - if (ibuf->profile == IB_PROFILE_LINEAR_RGB) - profile_from = IB_PROFILE_LINEAR_RGB; - else if (ELEM(ibuf->profile, IB_PROFILE_SRGB, IB_PROFILE_NONE)) - profile_from = IB_PROFILE_SRGB; - else { - BLI_assert(0); - profile_from = IB_PROFILE_SRGB; /* dummy, should never happen */ - } - - if (profile == IB_PROFILE_LINEAR_RGB) - profile_to = IB_PROFILE_LINEAR_RGB; - else if (ELEM(profile, IB_PROFILE_SRGB, IB_PROFILE_NONE)) - profile_to = IB_PROFILE_SRGB; - else { - BLI_assert(0); - profile_to = IB_PROFILE_SRGB; /* dummy, should never happen */ - } - - /* do conversion */ - if (ibuf->rect_float) { - IMB_buffer_float_from_float(ibuf->rect_float, ibuf->rect_float, - 4, profile_to, profile_from, predivide, - ibuf->x, ibuf->y, ibuf->x, ibuf->x); - } - - if (ibuf->rect) { - IMB_buffer_byte_from_byte((uchar *)ibuf->rect, (uchar *)ibuf->rect, - profile_to, profile_from, predivide, - ibuf->x, ibuf->y, ibuf->x, ibuf->x); - } - - /* set new profile */ - ibuf->profile = profile; -} - /* use when you need to get a buffer with a certain profile * if the return */ + +/* OCIO_TODO: used only by Cineon/DPX exporter which is still broken, so can not guarantee + * this function is working properly + */ float *IMB_float_profile_ensure(ImBuf *ibuf, int profile, int *alloc) { int predivide = (ibuf->flags & IB_cm_predivide); - int profile_from, profile_to; - - /* determine profiles */ - if (ibuf->profile == IB_PROFILE_NONE) - profile_from = IB_PROFILE_LINEAR_RGB; - else - profile_from = IB_PROFILE_SRGB; + int profile_from = IB_PROFILE_LINEAR_RGB; + int profile_to; + /* determine profile */ if (profile == IB_PROFILE_NONE) profile_to = IB_PROFILE_LINEAR_RGB; else diff --git a/source/blender/imbuf/intern/filetype.c b/source/blender/imbuf/intern/filetype.c index d67de3be68b..ed4db50035d 100644 --- a/source/blender/imbuf/intern/filetype.c +++ b/source/blender/imbuf/intern/filetype.c @@ -30,6 +30,8 @@ #include "IMB_imbuf_types.h" #include "IMB_filetype.h" +#include "IMB_colormanagement.h" + #ifdef WITH_OPENEXR #include "openexr/openexr_api.h" #endif @@ -66,34 +68,34 @@ void quicktime_exit(void); #endif ImFileType IMB_FILE_TYPES[] = { - {NULL, NULL, imb_is_a_jpeg, imb_ftype_default, imb_load_jpeg, imb_savejpeg, NULL, 0, JPG}, - {NULL, NULL, imb_is_a_png, imb_ftype_default, imb_loadpng, imb_savepng, NULL, 0, PNG}, - {NULL, NULL, imb_is_a_bmp, imb_ftype_default, imb_bmp_decode, imb_savebmp, NULL, 0, BMP}, - {NULL, NULL, imb_is_a_targa, imb_ftype_default, imb_loadtarga, imb_savetarga, NULL, 0, TGA}, - {NULL, NULL, imb_is_a_iris, imb_ftype_iris, imb_loadiris, imb_saveiris, NULL, 0, IMAGIC}, + {NULL, NULL, imb_is_a_jpeg, imb_ftype_default, imb_load_jpeg, imb_savejpeg, NULL, 0, JPG, COLOR_ROLE_DEFAULT_BYTE}, + {NULL, NULL, imb_is_a_png, imb_ftype_default, imb_loadpng, imb_savepng, NULL, 0, PNG, COLOR_ROLE_DEFAULT_BYTE}, + {NULL, NULL, imb_is_a_bmp, imb_ftype_default, imb_bmp_decode, imb_savebmp, NULL, 0, BMP, COLOR_ROLE_DEFAULT_BYTE}, + {NULL, NULL, imb_is_a_targa, imb_ftype_default, imb_loadtarga, imb_savetarga, NULL, 0, TGA, COLOR_ROLE_DEFAULT_BYTE}, + {NULL, NULL, imb_is_a_iris, imb_ftype_iris, imb_loadiris, imb_saveiris, NULL, 0, IMAGIC, COLOR_ROLE_DEFAULT_BYTE}, #ifdef WITH_CINEON - {NULL, NULL, imb_is_dpx, imb_ftype_default, imb_loaddpx, imb_save_dpx, NULL, IM_FTYPE_FLOAT, DPX}, - {NULL, NULL, imb_is_cineon, imb_ftype_default, imb_loadcineon, imb_savecineon, NULL, IM_FTYPE_FLOAT, CINEON}, + {NULL, NULL, imb_is_dpx, imb_ftype_default, imb_loaddpx, imb_save_dpx, NULL, IM_FTYPE_FLOAT, DPX, COLOR_ROLE_DEFAULT_FLOAT}, + {NULL, NULL, imb_is_cineon, imb_ftype_default, imb_loadcineon, imb_savecineon, NULL, IM_FTYPE_FLOAT, CINEON, COLOR_ROLE_DEFAULT_FLOAT}, #endif #ifdef WITH_TIFF - {imb_inittiff, NULL, imb_is_a_tiff, imb_ftype_default, imb_loadtiff, imb_savetiff, imb_loadtiletiff, 0, TIF}, + {imb_inittiff, NULL, imb_is_a_tiff, imb_ftype_default, imb_loadtiff, imb_savetiff, imb_loadtiletiff, 0, TIF, COLOR_ROLE_DEFAULT_BYTE}, #endif #ifdef WITH_HDR - {NULL, NULL, imb_is_a_hdr, imb_ftype_default, imb_loadhdr, imb_savehdr, NULL, IM_FTYPE_FLOAT, RADHDR}, + {NULL, NULL, imb_is_a_hdr, imb_ftype_default, imb_loadhdr, imb_savehdr, NULL, IM_FTYPE_FLOAT, RADHDR, COLOR_ROLE_DEFAULT_FLOAT}, #endif #ifdef WITH_OPENEXR - {imb_initopenexr, NULL, imb_is_a_openexr, imb_ftype_default, imb_load_openexr, imb_save_openexr, NULL, IM_FTYPE_FLOAT, OPENEXR}, + {imb_initopenexr, NULL, imb_is_a_openexr, imb_ftype_default, imb_load_openexr, imb_save_openexr, NULL, IM_FTYPE_FLOAT, OPENEXR, COLOR_ROLE_DEFAULT_FLOAT}, #endif #ifdef WITH_OPENJPEG - {NULL, NULL, imb_is_a_jp2, imb_ftype_default, imb_jp2_decode, imb_savejp2, NULL, IM_FTYPE_FLOAT, JP2}, + {NULL, NULL, imb_is_a_jp2, imb_ftype_default, imb_jp2_decode, imb_savejp2, NULL, IM_FTYPE_FLOAT, JP2, COLOR_ROLE_DEFAULT_BYTE}, #endif #ifdef WITH_DDS - {NULL, NULL, imb_is_a_dds, imb_ftype_default, imb_load_dds, NULL, NULL, 0, DDS}, + {NULL, NULL, imb_is_a_dds, imb_ftype_default, imb_load_dds, NULL, NULL, 0, DDS, COLOR_ROLE_DEFAULT_BYTE}, #endif #ifdef WITH_QUICKTIME - {quicktime_init, quicktime_exit, imb_is_a_quicktime, imb_ftype_quicktime, imb_quicktime_decode, NULL, NULL, 0, QUICKTIME}, + {quicktime_init, quicktime_exit, imb_is_a_quicktime, imb_ftype_quicktime, imb_quicktime_decode, NULL, NULL, 0, QUICKTIME, COLOR_ROLE_DEFAULT_BYTE}, #endif - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0} + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0} }; void imb_filetypes_init(void) diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index ea493e277f3..97316c48621 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -1254,7 +1254,8 @@ struct anim *IMB_anim_open_proxy( get_proxy_filename(anim, preview_size, fname, FALSE); - anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0); + /* proxies are generated in default color space */ + anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0, NULL); anim->proxies_tried |= preview_size; diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c index 13b0fc1b55a..3fd25fff92f 100644 --- a/source/blender/imbuf/intern/iris.c +++ b/source/blender/imbuf/intern/iris.c @@ -42,6 +42,9 @@ #include "IMB_allocimbuf.h" #include "IMB_filetype.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + typedef struct { unsigned short imagic; /* stuff saved on disk . . */ unsigned short type; @@ -247,7 +250,7 @@ int imb_is_a_iris(unsigned char *mem) * */ -struct ImBuf *imb_loadiris(unsigned char *mem, size_t size, int flags) +struct ImBuf *imb_loadiris(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { unsigned int *base, *lptr = NULL; float *fbase, *fptr = NULL; @@ -265,6 +268,9 @@ struct ImBuf *imb_loadiris(unsigned char *mem, size_t size, int flags) if (!imb_is_a_iris(mem)) return NULL; + /* OCIO_TODO: only tested with 1 byte per pixel, not sure how to test with other settings */ + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + /*printf("new iris\n");*/ file_data = mem; @@ -523,7 +529,6 @@ struct ImBuf *imb_loadiris(unsigned char *mem, size_t size, int flags) } ibuf->ftype = IMAGIC; - ibuf->profile = IB_PROFILE_SRGB; test_endian_zbuf(ibuf); diff --git a/source/blender/imbuf/intern/jp2.c b/source/blender/imbuf/intern/jp2.c index 1fe9a5ab522..dd559c55402 100644 --- a/source/blender/imbuf/intern/jp2.c +++ b/source/blender/imbuf/intern/jp2.c @@ -36,6 +36,9 @@ #include "IMB_allocimbuf.h" #include "IMB_filetype.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + #include "openjpeg.h" #define JP2_FILEHEADER_SIZE 14 @@ -109,7 +112,7 @@ static void info_callback(const char *msg, void *client_data) } \ } (void)0 \ -struct ImBuf *imb_jp2_decode(unsigned char *mem, size_t size, int flags) +struct ImBuf *imb_jp2_decode(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf *ibuf = NULL; int use_float = FALSE; /* for precision higher then 8 use float */ @@ -137,6 +140,9 @@ struct ImBuf *imb_jp2_decode(unsigned char *mem, size_t size, int flags) if (!is_jp2 && !is_j2k) return(NULL); + /* both 8, 12 and 16 bit JP2Ks are default to standard byte colorspace */ + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + /* configure the event callbacks (not required) */ memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); event_mgr.error_handler = error_callback; @@ -641,136 +647,67 @@ static opj_image_t *ibuftoimage(ImBuf *ibuf, opj_cparameters_t *parameters) switch (prec) { case 8: /* Convert blenders float color channels to 8, 12 or 16bit ints */ if (numcomps == 4) { - if (ibuf->profile == IB_PROFILE_LINEAR_RGB) { - PIXEL_LOOPER_BEGIN(rect_float) - { - r[i] = DOWNSAMPLE_FLOAT_TO_8BIT(linearrgb_to_srgb(rect_float[0])); - g[i] = DOWNSAMPLE_FLOAT_TO_8BIT(linearrgb_to_srgb(rect_float[1])); - b[i] = DOWNSAMPLE_FLOAT_TO_8BIT(linearrgb_to_srgb(rect_float[2])); - a[i] = DOWNSAMPLE_FLOAT_TO_8BIT(rect_float[3]); - } - PIXEL_LOOPER_END; - } - else { - PIXEL_LOOPER_BEGIN(rect_float) - { - r[i] = DOWNSAMPLE_FLOAT_TO_8BIT(rect_float[0]); - g[i] = DOWNSAMPLE_FLOAT_TO_8BIT(rect_float[1]); - b[i] = DOWNSAMPLE_FLOAT_TO_8BIT(rect_float[2]); - a[i] = DOWNSAMPLE_FLOAT_TO_8BIT(rect_float[3]); - } - PIXEL_LOOPER_END; + PIXEL_LOOPER_BEGIN(rect_float) + { + r[i] = DOWNSAMPLE_FLOAT_TO_8BIT(linearrgb_to_srgb(rect_float[0])); + g[i] = DOWNSAMPLE_FLOAT_TO_8BIT(linearrgb_to_srgb(rect_float[1])); + b[i] = DOWNSAMPLE_FLOAT_TO_8BIT(linearrgb_to_srgb(rect_float[2])); + a[i] = DOWNSAMPLE_FLOAT_TO_8BIT(rect_float[3]); } + PIXEL_LOOPER_END; } else { - if (ibuf->profile == IB_PROFILE_LINEAR_RGB) { - PIXEL_LOOPER_BEGIN(rect_float) - { - r[i] = DOWNSAMPLE_FLOAT_TO_8BIT(linearrgb_to_srgb(rect_float[0])); - g[i] = DOWNSAMPLE_FLOAT_TO_8BIT(linearrgb_to_srgb(rect_float[1])); - b[i] = DOWNSAMPLE_FLOAT_TO_8BIT(linearrgb_to_srgb(rect_float[2])); - } - PIXEL_LOOPER_END; - } - else { - PIXEL_LOOPER_BEGIN(rect_float) - { - r[i] = DOWNSAMPLE_FLOAT_TO_8BIT(rect_float[0]); - g[i] = DOWNSAMPLE_FLOAT_TO_8BIT(rect_float[1]); - b[i] = DOWNSAMPLE_FLOAT_TO_8BIT(rect_float[2]); - } - PIXEL_LOOPER_END; + PIXEL_LOOPER_BEGIN(rect_float) + { + r[i] = DOWNSAMPLE_FLOAT_TO_8BIT(linearrgb_to_srgb(rect_float[0])); + g[i] = DOWNSAMPLE_FLOAT_TO_8BIT(linearrgb_to_srgb(rect_float[1])); + b[i] = DOWNSAMPLE_FLOAT_TO_8BIT(linearrgb_to_srgb(rect_float[2])); } + PIXEL_LOOPER_END; } break; case 12: if (numcomps == 4) { - if (ibuf->profile == IB_PROFILE_LINEAR_RGB) { - PIXEL_LOOPER_BEGIN(rect_float) - { - r[i] = DOWNSAMPLE_FLOAT_TO_12BIT(linearrgb_to_srgb(rect_float[0])); - g[i] = DOWNSAMPLE_FLOAT_TO_12BIT(linearrgb_to_srgb(rect_float[1])); - b[i] = DOWNSAMPLE_FLOAT_TO_12BIT(linearrgb_to_srgb(rect_float[2])); - a[i] = DOWNSAMPLE_FLOAT_TO_12BIT(rect_float[3]); - } - PIXEL_LOOPER_END; - } - else { - PIXEL_LOOPER_BEGIN(rect_float) - { - r[i] = DOWNSAMPLE_FLOAT_TO_12BIT(rect_float[0]); - g[i] = DOWNSAMPLE_FLOAT_TO_12BIT(rect_float[1]); - b[i] = DOWNSAMPLE_FLOAT_TO_12BIT(rect_float[2]); - a[i] = DOWNSAMPLE_FLOAT_TO_12BIT(rect_float[3]); - } - PIXEL_LOOPER_END; + PIXEL_LOOPER_BEGIN(rect_float) + { + r[i] = DOWNSAMPLE_FLOAT_TO_12BIT(linearrgb_to_srgb(rect_float[0])); + g[i] = DOWNSAMPLE_FLOAT_TO_12BIT(linearrgb_to_srgb(rect_float[1])); + b[i] = DOWNSAMPLE_FLOAT_TO_12BIT(linearrgb_to_srgb(rect_float[2])); + a[i] = DOWNSAMPLE_FLOAT_TO_12BIT(rect_float[3]); } + PIXEL_LOOPER_END; } else { - if (ibuf->profile == IB_PROFILE_LINEAR_RGB) { - PIXEL_LOOPER_BEGIN(rect_float) - { - r[i] = DOWNSAMPLE_FLOAT_TO_12BIT(linearrgb_to_srgb(rect_float[0])); - g[i] = DOWNSAMPLE_FLOAT_TO_12BIT(linearrgb_to_srgb(rect_float[1])); - b[i] = DOWNSAMPLE_FLOAT_TO_12BIT(linearrgb_to_srgb(rect_float[2])); - } - PIXEL_LOOPER_END; - } - else { - PIXEL_LOOPER_BEGIN(rect_float) - { - r[i] = DOWNSAMPLE_FLOAT_TO_12BIT(rect_float[0]); - g[i] = DOWNSAMPLE_FLOAT_TO_12BIT(rect_float[1]); - b[i] = DOWNSAMPLE_FLOAT_TO_12BIT(rect_float[2]); - } - PIXEL_LOOPER_END; + PIXEL_LOOPER_BEGIN(rect_float) + { + r[i] = DOWNSAMPLE_FLOAT_TO_12BIT(linearrgb_to_srgb(rect_float[0])); + g[i] = DOWNSAMPLE_FLOAT_TO_12BIT(linearrgb_to_srgb(rect_float[1])); + b[i] = DOWNSAMPLE_FLOAT_TO_12BIT(linearrgb_to_srgb(rect_float[2])); } + PIXEL_LOOPER_END; } break; case 16: if (numcomps == 4) { - if (ibuf->profile == IB_PROFILE_LINEAR_RGB) { - PIXEL_LOOPER_BEGIN(rect_float) - { - r[i] = DOWNSAMPLE_FLOAT_TO_16BIT(linearrgb_to_srgb(rect_float[0])); - g[i] = DOWNSAMPLE_FLOAT_TO_16BIT(linearrgb_to_srgb(rect_float[1])); - b[i] = DOWNSAMPLE_FLOAT_TO_16BIT(linearrgb_to_srgb(rect_float[2])); - a[i] = DOWNSAMPLE_FLOAT_TO_16BIT(rect_float[3]); - } - PIXEL_LOOPER_END; - } - else { - PIXEL_LOOPER_BEGIN(rect_float) - { - r[i] = DOWNSAMPLE_FLOAT_TO_16BIT(rect_float[0]); - g[i] = DOWNSAMPLE_FLOAT_TO_16BIT(rect_float[1]); - b[i] = DOWNSAMPLE_FLOAT_TO_16BIT(rect_float[2]); - a[i] = DOWNSAMPLE_FLOAT_TO_16BIT(rect_float[3]); - } - PIXEL_LOOPER_END; + PIXEL_LOOPER_BEGIN(rect_float) + { + r[i] = DOWNSAMPLE_FLOAT_TO_16BIT(linearrgb_to_srgb(rect_float[0])); + g[i] = DOWNSAMPLE_FLOAT_TO_16BIT(linearrgb_to_srgb(rect_float[1])); + b[i] = DOWNSAMPLE_FLOAT_TO_16BIT(linearrgb_to_srgb(rect_float[2])); + a[i] = DOWNSAMPLE_FLOAT_TO_16BIT(rect_float[3]); } + PIXEL_LOOPER_END; } else { - if (ibuf->profile == IB_PROFILE_LINEAR_RGB) { - PIXEL_LOOPER_BEGIN(rect_float) - { - r[i] = DOWNSAMPLE_FLOAT_TO_16BIT(linearrgb_to_srgb(rect_float[0])); - g[i] = DOWNSAMPLE_FLOAT_TO_16BIT(linearrgb_to_srgb(rect_float[1])); - b[i] = DOWNSAMPLE_FLOAT_TO_16BIT(linearrgb_to_srgb(rect_float[2])); - } - PIXEL_LOOPER_END; - } - else { - PIXEL_LOOPER_BEGIN(rect_float) - { - r[i] = DOWNSAMPLE_FLOAT_TO_16BIT(rect_float[0]); - g[i] = DOWNSAMPLE_FLOAT_TO_16BIT(rect_float[1]); - b[i] = DOWNSAMPLE_FLOAT_TO_16BIT(rect_float[2]); - } - PIXEL_LOOPER_END; + PIXEL_LOOPER_BEGIN(rect_float) + { + r[i] = DOWNSAMPLE_FLOAT_TO_16BIT(linearrgb_to_srgb(rect_float[0])); + g[i] = DOWNSAMPLE_FLOAT_TO_16BIT(linearrgb_to_srgb(rect_float[1])); + b[i] = DOWNSAMPLE_FLOAT_TO_16BIT(linearrgb_to_srgb(rect_float[2])); } + PIXEL_LOOPER_END; } break; } diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 364950b99cb..691db96989d 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -48,6 +48,9 @@ #include "jpeglib.h" #include "jerror.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + #define IS_jpg(x) (x->ftype & JPG) #define IS_stdjpg(x) ((x->ftype & JPG_MSK) == JPG_STD) #define IS_vidjpg(x) ((x->ftype & JPG_MSK) == JPG_VID) @@ -435,21 +438,22 @@ next_stamp_marker: jpeg_destroy((j_common_ptr) cinfo); if (ibuf) { ibuf->ftype = ibuf_ftype; - ibuf->profile = IB_PROFILE_SRGB; } } return(ibuf); } -ImBuf *imb_load_jpeg(unsigned char *buffer, size_t size, int flags) +ImBuf *imb_load_jpeg(unsigned char *buffer, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct jpeg_decompress_struct _cinfo, *cinfo = &_cinfo; struct my_error_mgr jerr; ImBuf *ibuf; if (!imb_is_a_jpeg(buffer)) return NULL; - + + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + cinfo->err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = jpeg_error; diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 26aab29b8dd..13078921d1c 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -64,6 +64,9 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void) #include "IMB_allocimbuf.h" #include "IMB_metadata.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + #include "openexr_multi.h" } @@ -379,30 +382,15 @@ static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags else { unsigned char *from; - if (ibuf->profile == IB_PROFILE_LINEAR_RGB) { - for (int i = ibuf->y - 1; i >= 0; i--) { - from = (unsigned char *)ibuf->rect + channels * i * width; + for (int i = ibuf->y - 1; i >= 0; i--) { + from = (unsigned char *)ibuf->rect + channels * i * width; - for (int j = ibuf->x; j > 0; j--) { - to->r = (float)(from[0]) / 255.0f; - to->g = (float)(from[1]) / 255.0f; - to->b = (float)(from[2]) / 255.0f; - to->a = (float)(channels >= 4) ? from[3] / 255.0f : 1.0f; - to++; from += 4; - } - } - } - else { - for (int i = ibuf->y - 1; i >= 0; i--) { - from = (unsigned char *)ibuf->rect + channels * i * width; - - for (int j = ibuf->x; j > 0; j--) { - to->r = srgb_to_linearrgb((float)from[0] / 255.0f); - to->g = srgb_to_linearrgb((float)from[1] / 255.0f); - to->b = srgb_to_linearrgb((float)from[2] / 255.0f); - to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f; - to++; from += 4; - } + for (int j = ibuf->x; j > 0; j--) { + to->r = srgb_to_linearrgb((float)from[0] / 255.0f); + to->g = srgb_to_linearrgb((float)from[1] / 255.0f); + to->b = srgb_to_linearrgb((float)from[2] / 255.0f); + to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f; + to++; from += 4; } } } @@ -1129,13 +1117,15 @@ static int exr_is_multilayer(InputFile *file) return 0; } -struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags) +struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf *ibuf = NULL; InputFile *file = NULL; if (imb_is_a_openexr(mem) == 0) return(NULL); + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT); + try { Mem_IStream *membuf = new Mem_IStream(mem, size); @@ -1164,9 +1154,6 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags) ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0); ibuf->ftype = OPENEXR; - /* openEXR is linear as per EXR spec */ - ibuf->profile = IB_PROFILE_LINEAR_RGB; - if (!(flags & IB_test)) { if (is_multi) { /* only enters with IB_multilayer flag set */ /* constructs channels for reading, allocates memory in channels */ diff --git a/source/blender/imbuf/intern/openexr/openexr_api.h b/source/blender/imbuf/intern/openexr/openexr_api.h index d12fe2fc49f..3135795fb3f 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.h +++ b/source/blender/imbuf/intern/openexr/openexr_api.h @@ -50,7 +50,7 @@ int imb_is_a_openexr (unsigned char *mem); int imb_save_openexr (struct ImBuf *ibuf, const char *name, int flags); -struct ImBuf *imb_load_openexr (unsigned char *mem, size_t size, int flags); +struct ImBuf *imb_load_openexr (unsigned char *mem, size_t size, int flags, char *colorspace); #ifdef __cplusplus } diff --git a/source/blender/imbuf/intern/png.c b/source/blender/imbuf/intern/png.c index 92cd9622849..6310d8e105f 100644 --- a/source/blender/imbuf/intern/png.c +++ b/source/blender/imbuf/intern/png.c @@ -47,6 +47,9 @@ #include "IMB_metadata.h" #include "IMB_filetype.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + typedef struct PNGReadStruct { unsigned char *data; unsigned int size; @@ -302,7 +305,7 @@ int imb_savepng(struct ImBuf *ibuf, const char *name, int flags) return(1); } -ImBuf *imb_loadpng(unsigned char *mem, size_t size, int flags) +ImBuf *imb_loadpng(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf *ibuf = NULL; png_structp png_ptr; @@ -317,11 +320,13 @@ ImBuf *imb_loadpng(unsigned char *mem, size_t size, int flags) unsigned char *from, *to; unsigned short *from16; float *to_float; - float tmp[4]; int i, bytesperpixel; if (imb_is_a_png(mem) == 0) return(NULL); + /* both 8 and 16 bit PNGs are default to standard byte colorspace */ + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { @@ -389,10 +394,6 @@ ImBuf *imb_loadpng(unsigned char *mem, size_t size, int flags) if (ibuf) { ibuf->ftype = PNG; - if (bit_depth == 16) - ibuf->profile = IB_PROFILE_LINEAR_RGB; - else - ibuf->profile = IB_PROFILE_SRGB; if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) { int unit_type; @@ -443,37 +444,33 @@ ImBuf *imb_loadpng(unsigned char *mem, size_t size, int flags) switch (bytesperpixel) { case 4: for (i = ibuf->x * ibuf->y; i > 0; i--) { - tmp[0] = from16[0] / 65535.0; - tmp[1] = from16[1] / 65535.0; - tmp[2] = from16[2] / 65535.0; - tmp[3] = from16[3] / 65535.0; - srgb_to_linearrgb_v4(to_float, tmp); + to_float[0] = from16[0] / 65535.0; + to_float[1] = from16[1] / 65535.0; + to_float[2] = from16[2] / 65535.0; + to_float[3] = from16[3] / 65535.0; to_float += 4; from16 += 4; } break; case 3: for (i = ibuf->x * ibuf->y; i > 0; i--) { - tmp[0] = from16[0] / 65535.0; - tmp[1] = from16[1] / 65535.0; - tmp[2] = from16[2] / 65535.0; - tmp[3] = 1.0; - srgb_to_linearrgb_v4(to_float, tmp); + to_float[0] = from16[0] / 65535.0; + to_float[1] = from16[1] / 65535.0; + to_float[2] = from16[2] / 65535.0; + to_float[3] = 1.0; to_float += 4; from16 += 3; } break; case 2: for (i = ibuf->x * ibuf->y; i > 0; i--) { - tmp[0] = tmp[1] = tmp[2] = from16[0] / 65535.0; - tmp[3] = from16[1] / 65535.0; - srgb_to_linearrgb_v4(to_float, tmp); + to_float[0] = to_float[1] = to_float[2] = from16[0] / 65535.0; + to_float[3] = from16[1] / 65535.0; to_float += 4; from16 += 2; } break; case 1: for (i = ibuf->x * ibuf->y; i > 0; i--) { - tmp[0] = tmp[1] = tmp[2] = from16[0] / 65535.0; - tmp[3] = 1.0; - srgb_to_linearrgb_v4(to_float, tmp); + to_float[0] = to_float[1] = to_float[2] = from16[0] / 65535.0; + to_float[3] = 1.0; to_float += 4; from16++; } break; diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c index 4cd50484a64..5add372cd4e 100644 --- a/source/blender/imbuf/intern/radiance_hdr.c +++ b/source/blender/imbuf/intern/radiance_hdr.c @@ -53,6 +53,9 @@ #include "IMB_allocimbuf.h" #include "IMB_filetype.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + /* needed constants */ #define MINELEN 8 #define MAXELEN 0x7fff @@ -171,7 +174,7 @@ int imb_is_a_hdr(unsigned char *buf) return 0; } -struct ImBuf *imb_loadhdr(unsigned char *mem, size_t size, int flags) +struct ImBuf *imb_loadhdr(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf *ibuf; RGBE *sline; @@ -184,6 +187,8 @@ struct ImBuf *imb_loadhdr(unsigned char *mem, size_t size, int flags) char oriY[80], oriX[80]; if (imb_is_a_hdr((void *)mem)) { + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT); + /* find empty line, next line is resolution info */ for (x = 1; x < size; x++) { if ((mem[x - 1] == '\n') && (mem[x] == '\n')) { @@ -207,7 +212,6 @@ struct ImBuf *imb_loadhdr(unsigned char *mem, size_t size, int flags) if (ibuf == NULL) return NULL; ibuf->ftype = RADHDR; - ibuf->profile = IB_PROFILE_LINEAR_RGB; if (flags & IB_test) return ibuf; diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c index 4bde71588b7..a1fa05d1098 100644 --- a/source/blender/imbuf/intern/readimage.c +++ b/source/blender/imbuf/intern/readimage.c @@ -54,20 +54,42 @@ #include "IMB_imbuf.h" #include "IMB_filetype.h" -ImBuf *IMB_ibImageFromMemory(unsigned char *mem, size_t size, int flags, const char *descr) +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + +ImBuf *IMB_ibImageFromMemory(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE], const char *descr) { ImBuf *ibuf; ImFileType *type; + char effective_colorspace[IM_MAX_SPACE] = ""; if (mem == NULL) { fprintf(stderr, "%s: NULL pointer\n", __func__); return NULL; } + if (colorspace) + BLI_strncpy(effective_colorspace, colorspace, sizeof(effective_colorspace)); + for (type = IMB_FILE_TYPES; type->is_a; type++) { if (type->load) { - ibuf = type->load(mem, size, flags); + ibuf = type->load(mem, size, flags, effective_colorspace); if (ibuf) { + if (colorspace) { + if (ibuf->rect) { + /* byte buffer is never internally converted to some standard space, + * store pointer to it's color space descriptor instead + */ + ibuf->rect_colorspace = colormanage_colorspace_get_named(effective_colorspace); + } + + BLI_strncpy(colorspace, effective_colorspace, IM_MAX_SPACE); + } + + /* OCIO_TODO: in some cases it's faster to do threaded conversion, + * but how to distinguish such cases */ + colormanage_imbuf_make_linear(ibuf, effective_colorspace); + if (flags & IB_premul) { IMB_premultiply_alpha(ibuf); ibuf->flags |= IB_premul; @@ -83,7 +105,7 @@ ImBuf *IMB_ibImageFromMemory(unsigned char *mem, size_t size, int flags, const c return NULL; } -ImBuf *IMB_loadifffile(int file, int flags, const char *descr) +ImBuf *IMB_loadifffile(int file, int flags, char colorspace[IM_MAX_SPACE], const char *descr) { ImBuf *ibuf; unsigned char *mem; @@ -99,7 +121,7 @@ ImBuf *IMB_loadifffile(int file, int flags, const char *descr) return NULL; } - ibuf = IMB_ibImageFromMemory(mem, size, flags, descr); + ibuf = IMB_ibImageFromMemory(mem, size, flags, colorspace, descr); if (munmap(mem, size)) fprintf(stderr, "%s: couldn't unmap file %s\n", __func__, descr); @@ -122,7 +144,7 @@ static void imb_cache_filename(char *filename, const char *name, int flags) BLI_strncpy(filename, name, IB_FILENAME_SIZE); } -ImBuf *IMB_loadiffname(const char *filepath, int flags) +ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]) { ImBuf *ibuf; int file, a; @@ -133,7 +155,7 @@ ImBuf *IMB_loadiffname(const char *filepath, int flags) file = BLI_open(filepath_tx, O_BINARY | O_RDONLY, 0); if (file < 0) return NULL; - ibuf = IMB_loadifffile(file, flags, filepath_tx); + ibuf = IMB_loadifffile(file, flags, colorspace, filepath_tx); if (ibuf) { BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name)); @@ -148,7 +170,7 @@ ImBuf *IMB_loadiffname(const char *filepath, int flags) return ibuf; } -ImBuf *IMB_testiffname(const char *filepath, int flags) +ImBuf *IMB_testiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]) { ImBuf *ibuf; int file; @@ -159,7 +181,7 @@ ImBuf *IMB_testiffname(const char *filepath, int flags) file = BLI_open(filepath_tx, O_BINARY | O_RDONLY, 0); if (file < 0) return NULL; - ibuf = IMB_loadifffile(file, flags | IB_test | IB_multilayer, filepath_tx); + ibuf = IMB_loadifffile(file, flags | IB_test | IB_multilayer, colorspace, filepath_tx); if (ibuf) { BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name)); diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c index c3e23246638..a11f16d5669 100644 --- a/source/blender/imbuf/intern/rectop.c +++ b/source/blender/imbuf/intern/rectop.c @@ -42,6 +42,7 @@ #include "IMB_imbuf.h" #include "IMB_allocimbuf.h" +#include "IMB_colormanagement.h" /* blend modes */ @@ -486,7 +487,7 @@ void IMB_rectfill(struct ImBuf *drect, const float col[4]) void buf_rectfill_area(unsigned char *rect, float *rectf, int width, int height, - const float col[4], const int do_color_management, + const float col[4], struct ColorManagedDisplay *display, int x1, int y1, int x2, int y2) { int i, j; @@ -553,11 +554,12 @@ void buf_rectfill_area(unsigned char *rect, float *rectf, int width, int height, float col_conv[4]; float *pixel; - if (do_color_management) { - srgb_to_linearrgb_v4(col_conv, col); + if (display) { + copy_v4_v4(col_conv, col); + IMB_colormanagement_display_to_scene_linear_v3(col_conv, display); } else { - copy_v4_v4(col_conv, col); + srgb_to_linearrgb_v4(col_conv, col); } for (j = 0; j < y2 - y1; j++) { @@ -581,12 +583,10 @@ void buf_rectfill_area(unsigned char *rect, float *rectf, int width, int height, } } -void IMB_rectfill_area(struct ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2) +void IMB_rectfill_area(struct ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2, struct ColorManagedDisplay *display) { - int do_color_management; if (!ibuf) return; - do_color_management = (ibuf->profile == IB_PROFILE_LINEAR_RGB); - buf_rectfill_area((unsigned char *) ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, col, do_color_management, + buf_rectfill_area((unsigned char *) ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, col, display, x1, y1, x2, y2); } diff --git a/source/blender/imbuf/intern/targa.c b/source/blender/imbuf/intern/targa.c index 2f7452772ef..253680e4bea 100644 --- a/source/blender/imbuf/intern/targa.c +++ b/source/blender/imbuf/intern/targa.c @@ -46,6 +46,8 @@ #include "IMB_allocimbuf.h" #include "IMB_filetype.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" /* this one is only def-ed once, strangely... related to GS? */ #define GSS(x) (((uchar *)(x))[1] << 8 | ((uchar *)(x))[0]) @@ -546,22 +548,23 @@ partial_load: } -ImBuf *imb_loadtarga(unsigned char *mem, size_t mem_size, int flags) +ImBuf *imb_loadtarga(unsigned char *mem, size_t mem_size, int flags, char colorspace[IM_MAX_SPACE]) { TARGA tga; struct ImBuf *ibuf; int col, count, size; unsigned int *rect, *cmap = NULL /*, mincol = 0*/, maxcol = 0; uchar *cp = (uchar *) &col; - + if (checktarga(&tga, mem) == 0) return(NULL); + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + if (flags & IB_test) ibuf = IMB_allocImBuf(tga.xsize, tga.ysize, tga.pixsize, 0); else ibuf = IMB_allocImBuf(tga.xsize, tga.ysize, (tga.pixsize + 0x7) & ~0x7, IB_rect); if (ibuf == NULL) return(NULL); ibuf->ftype = TGA; - ibuf->profile = IB_PROFILE_SRGB; mem = mem + 18 + tga.numid; cp[0] = 0xff; diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 2a2aedb49ff..ff7218d649c 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -313,7 +313,7 @@ ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, Im img = IMB_loadblend_thumb(path); } else { - img = IMB_loadiffname(path, IB_rect | IB_metadata); + img = IMB_loadiffname(path, IB_rect | IB_metadata, NULL); } } @@ -326,7 +326,7 @@ ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, Im } else if (THB_SOURCE_MOVIE == source) { struct anim *anim = NULL; - anim = IMB_open_anim(path, IB_rect | IB_metadata, 0); + anim = IMB_open_anim(path, IB_rect | IB_metadata, 0, NULL); if (anim != NULL) { img = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); if (img == NULL) { @@ -376,6 +376,7 @@ ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, Im } img->ftype = PNG; img->planes = 32; + if (IMB_saveiff(img, temp, IB_rect | IB_metadata)) { #ifndef WIN32 chmod(temp, S_IRUSR | S_IWUSR); @@ -401,7 +402,7 @@ ImBuf *IMB_thumb_read(const char *path, ThumbSize size) return NULL; } if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) { - img = IMB_loadiffname(thumb, IB_rect | IB_metadata); + img = IMB_loadiffname(thumb, IB_rect | IB_metadata, NULL); } return img; @@ -456,10 +457,10 @@ ImBuf *IMB_thumb_manage(const char *path, ThumbSize size, ThumbSource source) if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) { if (BLI_path_ncmp(path, thumb, sizeof(thumb)) == 0) { - img = IMB_loadiffname(path, IB_rect); + img = IMB_loadiffname(path, IB_rect, NULL); } else { - img = IMB_loadiffname(thumb, IB_rect | IB_metadata); + img = IMB_loadiffname(thumb, IB_rect | IB_metadata, NULL); if (img) { char mtime[40]; if (!IMB_metadata_get_field(img, "Thumb::MTime", mtime, 40)) { diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index ac5f5da8a67..932a4941a0a 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -57,6 +57,9 @@ #include "IMB_filetype.h" #include "IMB_filter.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + #include "tiffio.h" #ifdef WIN32 @@ -464,8 +467,6 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image, int premul) _TIFFfree(sbuf); if (success) { - ibuf->profile = (bitspersample == 32) ? IB_PROFILE_LINEAR_RGB : IB_PROFILE_SRGB; - /* Code seems to be not needed for 16 bits tif, on PPC G5 OSX (ton) */ if (bitspersample < 16) if (ENDIAN_ORDER == B_ENDIAN) @@ -508,7 +509,7 @@ void imb_inittiff(void) * * \return: A newly allocated ImBuf structure if successful, otherwise NULL. */ -ImBuf *imb_loadtiff(unsigned char *mem, size_t size, int flags) +ImBuf *imb_loadtiff(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { TIFF *image = NULL; ImBuf *ibuf = NULL, *hbuf; @@ -527,6 +528,9 @@ ImBuf *imb_loadtiff(unsigned char *mem, size_t size, int flags) if (imb_is_a_tiff(mem) == 0) return NULL; + /* both 8 and 16 bit PNGs are default to standard byte colorspace */ + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + image = imb_tiff_client_open(&memFile, mem, size); if (image == NULL) { @@ -786,10 +790,7 @@ int imb_savetiff(ImBuf *ibuf, const char *name, int flags) /* convert from float source */ float rgb[4]; - if (ibuf->profile == IB_PROFILE_LINEAR_RGB) - linearrgb_to_srgb_v3_v3(rgb, &fromf[from_i]); - else - copy_v3_v3(rgb, &fromf[from_i]); + linearrgb_to_srgb_v3_v3(rgb, &fromf[from_i]); rgb[3] = fromf[from_i + 3]; diff --git a/source/blender/imbuf/intern/writeimage.c b/source/blender/imbuf/intern/writeimage.c index 1c182e9784e..5d785e08212 100644 --- a/source/blender/imbuf/intern/writeimage.c +++ b/source/blender/imbuf/intern/writeimage.c @@ -38,8 +38,28 @@ #include "IMB_imbuf.h" #include "IMB_filetype.h" +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" + #include "imbuf.h" +static ImBuf *prepare_write_imbuf(ImFileType *type, ImBuf *ibuf) +{ + ImBuf *write_ibuf = ibuf; + + if (type->flag & IM_FTYPE_FLOAT) { + /* pass */ + } + else { + if (ibuf->rect == NULL && ibuf->rect_float) { + ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE); + IMB_rect_from_float(ibuf); + } + } + + return write_ibuf; +} + short IMB_saveiff(struct ImBuf *ibuf, const char *name, int flags) { ImFileType *type; @@ -49,11 +69,17 @@ short IMB_saveiff(struct ImBuf *ibuf, const char *name, int flags) for (type = IMB_FILE_TYPES; type->is_a; type++) { if (type->save && type->ftype(type, ibuf)) { - if (!(type->flag & IM_FTYPE_FLOAT)) { - if (ibuf->rect == NULL && ibuf->rect_float) - IMB_rect_from_float(ibuf); - } - return type->save(ibuf, name, flags); + ImBuf *write_ibuf; + short result = FALSE; + + write_ibuf = prepare_write_imbuf(type, ibuf); + + result = type->save(write_ibuf, name, flags); + + if (write_ibuf != ibuf) + IMB_freeImBuf(write_ibuf); + + return result; } } diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h index 4ead26c04f3..32a0629c338 100644 --- a/source/blender/makesdna/DNA_color_types.h +++ b/source/blender/makesdna/DNA_color_types.h @@ -160,6 +160,26 @@ typedef struct Scopes { #define SCOPES_WAVEFRM_YCC_709 3 #define SCOPES_WAVEFRM_YCC_JPEG 4 +typedef struct ColorManagedViewSettings { + int flag, pad; + char view_transform[64]; /* view transform which is being applied when displaying buffer on the screen */ + float exposure; /* fstop exposure */ + float gamma; /* post-display gamma transform */ + struct CurveMapping *curve_mapping; /* pre-display RGB curves transform */ + void *pad2; +} ColorManagedViewSettings; + +typedef struct ColorManagedDisplaySettings { + char display_device[64]; +} ColorManagedDisplaySettings; + +typedef struct ColorManagedColorspaceSettings { + char name[64]; /* MAX_COLORSPACE_NAME */ +} ColorManagedColorspaceSettings; + +/* ColorManagedViewSettings->flag */ +enum { + COLORMANAGE_VIEW_USE_CURVES = (1 << 0) +}; #endif - diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 4a4b21017db..38058dbb699 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -33,6 +33,7 @@ #define __DNA_IMAGE_TYPES_H__ #include "DNA_ID.h" +#include "DNA_color_types.h" /* for color management */ struct PackedFile; struct Scene; @@ -107,6 +108,9 @@ typedef struct Image { /* display aspect - for UV editing images resized for faster openGL display */ float aspx, aspy; + + /* color management */ + ColorManagedColorspaceSettings colorspace_settings; } Image; @@ -123,6 +127,7 @@ typedef struct Image { #define IMA_CM_PREDIVIDE 256 #define IMA_USED_FOR_RENDER 512 #define IMA_USER_FRAME_IN_RANGE 1024 /* for image user, but these flags are mixed */ +#define IMA_VIEW_AS_RENDER 2048 /* Image.tpageflag */ #define IMA_TILES 1 diff --git a/source/blender/makesdna/DNA_movieclip_types.h b/source/blender/makesdna/DNA_movieclip_types.h index d8bba4a3bf5..cda51779528 100644 --- a/source/blender/makesdna/DNA_movieclip_types.h +++ b/source/blender/makesdna/DNA_movieclip_types.h @@ -37,6 +37,7 @@ #include "DNA_ID.h" #include "DNA_tracking_types.h" +#include "DNA_color_types.h" /* for color management */ struct anim; struct AnimData; @@ -94,6 +95,9 @@ typedef struct MovieClip { /* from a file. affects only a way how scene frame is mapping */ /* to a file name and not touches other data associated with */ /* a clip */ + + /* color management */ + ColorManagedColorspaceSettings colorspace_settings; } MovieClip; typedef struct MovieClipScopes { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 23a8cb098f4..e3a8863714a 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -41,6 +41,7 @@ extern "C" { #endif +#include "DNA_color_types.h" /* color management */ #include "DNA_vec_types.h" #include "DNA_listBase.h" #include "DNA_ID.h" @@ -273,6 +274,9 @@ typedef struct ImageFormatData { char pad[7]; + /* color management */ + ColorManagedViewSettings view_settings; + ColorManagedDisplaySettings display_settings; } ImageFormatData; @@ -1133,6 +1137,11 @@ typedef struct Scene { uint64_t customdata_mask; /* XXX. runtime flag for drawing, actually belongs in the window, only used by BKE_object_handle_update() */ uint64_t customdata_mask_modal; /* XXX. same as above but for temp operator use (gl renders) */ + + /* Color Management */ + ColorManagedViewSettings view_settings; + ColorManagedDisplaySettings display_settings; + ColorManagedColorspaceSettings sequencer_colorspace_settings; } Scene; @@ -1254,7 +1263,7 @@ typedef struct Scene { #define R_ALPHAKEY 2 /* color_mgt_flag */ -#define R_COLOR_MANAGEMENT (1 << 0) +#define R_COLOR_MANAGEMENT (1 << 0) /* deprecated, should only be used in versioning code only */ #define R_COLOR_MANAGEMENT_PREDIVIDE (1 << 1) /* subimtype, flag options for imtype */ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index d094c1d6255..4d259fad246 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -275,6 +275,18 @@ typedef struct BrightContrastModifierData { float contrast; } BrightContrastModifierData; +/* ***************** Scopes ****************** */ + +typedef struct SequencerScopes { + struct ImBuf *reference_ibuf; + + struct ImBuf *zebra_ibuf; + struct ImBuf *waveform_ibuf; + struct ImBuf *sep_waveform_ibuf; + struct ImBuf *vector_ibuf; + struct ImBuf *histogram_ibuf; +} SequencerScopes; + #define MAXSEQ 32 #define SELECT 1 @@ -317,6 +329,7 @@ typedef struct BrightContrastModifierData { #define SEQ_USE_PROXY_CUSTOM_FILE (1 << 21) #define SEQ_USE_EFFECT_DEFAULT_FADE (1 << 22) +#define SEQ_USE_LINEAR_MODIFIERS (1 << 23) // flags for whether those properties are animated or not #define SEQ_AUDIO_VOLUME_ANIMATED (1 << 24) diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index b828247c816..6cc4541f19e 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -40,6 +40,7 @@ #include "DNA_outliner_types.h" /* for TreeStoreElem */ #include "DNA_image_types.h" /* ImageUser */ #include "DNA_movieclip_types.h" /* MovieClipUser */ +#include "DNA_sequence_types.h" /* SequencerScopes */ /* Hum ... Not really nice... but needed for spacebuts. */ #include "DNA_view2d_types.h" @@ -473,6 +474,8 @@ typedef struct SpaceSeq { int overlay_type; struct bGPdata *gpd; /* grease-pencil data */ + + struct SequencerScopes scopes; /* different scoped displayed in space */ } SpaceSeq; @@ -685,8 +688,9 @@ typedef struct SpaceImage { struct Image *image; struct ImageUser iuser; - struct CurveMapping *cumap; - + + struct CurveMapping *cumap DNA_DEPRECATED; /* was switched to scene's color management settings */ + struct Scopes scopes; /* histogram waveform and vectorscope */ struct Histogram sample_line_hist; /* sample line histogram */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index a32272e2a7f..8e387dd4eba 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -104,6 +104,9 @@ extern StructRNA RNA_CollectionProperty; extern StructRNA RNA_CollisionModifier; extern StructRNA RNA_CollisionSensor; extern StructRNA RNA_CollisionSettings; +extern StructRNA RNA_ColorManagedColorspaceSettings; +extern StructRNA RNA_ColorManagedDisplaySettings; +extern StructRNA RNA_ColorManagedViewSettings; extern StructRNA RNA_ColorRamp; extern StructRNA RNA_ColorRampElement; extern StructRNA RNA_ColorSequence; diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 0bd42c2f5a0..0edcad59577 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -34,25 +34,31 @@ #include "DNA_color_types.h" #include "DNA_texture_types.h" +#include "WM_api.h" +#include "WM_types.h" + #ifdef RNA_RUNTIME #include "RNA_access.h" +#include "DNA_image_types.h" #include "DNA_material_types.h" +#include "DNA_movieclip_types.h" #include "DNA_node_types.h" #include "MEM_guardedalloc.h" #include "BKE_colortools.h" #include "BKE_depsgraph.h" +#include "BKE_image.h" +#include "BKE_movieclip.h" #include "BKE_node.h" #include "BKE_texture.h" -#include "WM_api.h" -#include "WM_types.h" - #include "ED_node.h" +#include "IMB_colormanagement.h" + static int rna_CurveMapping_curves_length(PointerRNA *ptr) { CurveMapping *cumap = (CurveMapping *)ptr->data; @@ -337,6 +343,154 @@ static void rna_Scopes_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Pointer s->ok = 0; } +static int rna_ColorManagedDisplaySettings_display_device_get(struct PointerRNA *ptr) +{ + ColorManagedDisplaySettings *display = (ColorManagedDisplaySettings *) ptr->data; + + return IMB_colormanagement_display_get_named_index(display->display_device); +} + +static void rna_ColorManagedDisplaySettings_display_device_set(struct PointerRNA *ptr, int value) +{ + ColorManagedDisplaySettings *display = (ColorManagedDisplaySettings *) ptr->data; + const char *name = IMB_colormanagement_display_get_indexed_name(value); + + if (name) { + BLI_strncpy(display->display_device, name, sizeof(display->display_device)); + } +} + +static EnumPropertyItem *rna_ColorManagedDisplaySettings_display_device_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), int *free) +{ + EnumPropertyItem *items = NULL; + int totitem = 0; + + IMB_colormanagement_display_items_add(&items, &totitem); + RNA_enum_item_end(&items, &totitem); + + *free = TRUE; + + return items; +} + +static void rna_ColorManagedDisplaySettings_display_device_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + ID *id = ptr->id.data; + + if (GS(id->name) == ID_SCE) { + Scene *scene = (Scene *) id; + + IMB_colormanagement_validate_settings(&scene->display_settings, &scene->view_settings); + + WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL); + } +} + +static int rna_ColorManagedViewSettings_view_transform_get(PointerRNA *ptr) +{ + ColorManagedViewSettings *view = (ColorManagedViewSettings *) ptr->data; + + return IMB_colormanagement_view_get_named_index(view->view_transform); +} + +static void rna_ColorManagedViewSettings_view_transform_set(PointerRNA *ptr, int value) +{ + ColorManagedViewSettings *view = (ColorManagedViewSettings *) ptr->data; + + const char *name = IMB_colormanagement_view_get_indexed_name(value); + + if (name) { + BLI_strncpy(view->view_transform, name, sizeof(view->view_transform)); + } +} + +static EnumPropertyItem* rna_ColorManagedViewSettings_view_transform_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), int *free) +{ + Scene *scene = CTX_data_scene(C); + EnumPropertyItem *items = NULL; + ColorManagedDisplaySettings *display_settings = &scene->display_settings; + int totitem = 0; + + IMB_colormanagement_view_items_add(&items, &totitem, display_settings->display_device); + RNA_enum_item_end(&items, &totitem); + + *free = TRUE; + return items; +} + +static void rna_ColorManagedViewSettings_use_curves_set(PointerRNA *ptr, int value) +{ + ColorManagedViewSettings *view_settings = (ColorManagedViewSettings *) ptr->data; + + if (value) { + view_settings->flag |= COLORMANAGE_VIEW_USE_CURVES; + + if (view_settings->curve_mapping == NULL) { + view_settings->curve_mapping = curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f); + } + } + else { + view_settings->flag &= ~COLORMANAGE_VIEW_USE_CURVES; + } +} + +static int rna_ColorManagedColorspaceSettings_colorspace_get(struct PointerRNA *ptr) +{ + ColorManagedColorspaceSettings *colorspace = (ColorManagedColorspaceSettings *) ptr->data; + + return IMB_colormanagement_colorspace_get_named_index(colorspace->name); +} + +static void rna_ColorManagedColorspaceSettings_colorspace_set(struct PointerRNA *ptr, int value) +{ + ColorManagedColorspaceSettings *colorspace = (ColorManagedColorspaceSettings *) ptr->data; + const char *name = IMB_colormanagement_colorspace_get_indexed_name(value); + + if (name) { + BLI_strncpy(colorspace->name, name, sizeof(colorspace->name)); + } +} + +static EnumPropertyItem *rna_ColorManagedColorspaceSettings_colorspace_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), int *free) +{ + EnumPropertyItem *items = NULL; + int totitem = 0; + + IMB_colormanagement_colorspace_items_add(&items, &totitem); + RNA_enum_item_end(&items, &totitem); + + *free = TRUE; + + return items; +} + +static void rna_ColorManagedColorspaceSettings_reload_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + ID *id = ptr->id.data; + + if (GS(id->name) == ID_IM) { + Image *ima = (Image *) id; + + BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD); + WM_main_add_notifier(NC_IMAGE | ND_DISPLAY, &ima->id); + } + else if (GS(id->name) == ID_MC) { + MovieClip *clip = (MovieClip *) id; + + BKE_movieclip_reload(clip); + WM_main_add_notifier(NC_MOVIECLIP | ND_DISPLAY, &clip->id); + } +} + +static void rna_ColorManagement_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + ID *id = ptr->id.data; + + if (GS(id->name) == ID_SCE) { + WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL); + } +} + /* this function only exists because #curvemap_evaluateF uses a 'const' qualifier */ float rna_CurveMap_evaluateF(struct CurveMap *cuma, float value) { @@ -682,6 +836,87 @@ static void rna_def_scopes(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Vectorscope Opacity", "Opacity of the points"); } +static void rna_def_colormanage(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem display_device_items[] = { + {0, "DEFAULT", 0, "Default", ""}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem view_transform_items[] = { + {0, "NONE", 0, "None", "Do not perform any color transform on display, use old non-color managed technique for display"}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem color_space_items[] = { + {0, "NONE", 0, "None", "Do not perform any color transform on load, treat colors as in scene linear space already"}, + {0, NULL, 0, NULL, NULL} + }; + + /* ** Display Settings ** */ + srna = RNA_def_struct(brna, "ColorManagedDisplaySettings", NULL); + RNA_def_struct_ui_text(srna, "ColorManagedDisplaySettings", "Color management specific to display device"); + + prop= RNA_def_property(srna, "display_device", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, display_device_items); + RNA_def_property_enum_funcs(prop, "rna_ColorManagedDisplaySettings_display_device_get", + "rna_ColorManagedDisplaySettings_display_device_set", + "rna_ColorManagedDisplaySettings_display_device_itemf"); + RNA_def_property_ui_text(prop, "Display Device", "Display device name"); + RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagedDisplaySettings_display_device_update"); + + /* ** View Settings ** */ + srna = RNA_def_struct(brna, "ColorManagedViewSettings", NULL); + RNA_def_struct_ui_text(srna, "ColorManagedViewSettings", "Color management settings used for displaying images on the display"); + + prop= RNA_def_property(srna, "view_transform", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, view_transform_items); + RNA_def_property_enum_funcs(prop, "rna_ColorManagedViewSettings_view_transform_get", + "rna_ColorManagedViewSettings_view_transform_set", + "rna_ColorManagedViewSettings_view_transform_itemf"); + RNA_def_property_ui_text(prop, "View Transform", "View used "); + RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update"); + + prop = RNA_def_property(srna, "exposure", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "exposure"); + RNA_def_property_range(prop, -10.0f, 10.0f); + RNA_def_property_float_default(prop, 0.0f); + RNA_def_property_ui_text(prop, "Exposure", "Exposure (stops) applied before display transform"); + RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update"); + + prop = RNA_def_property(srna, "gamma", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "gamma"); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_range(prop, 0.0f, 5.0f); + RNA_def_property_ui_text(prop, "Gamma", "Amount of gamma modification applied after display transform"); + RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update"); + + prop = RNA_def_property(srna, "curve_mapping", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curve_mapping"); + RNA_def_property_ui_text(prop, "Curve", "Color curve mapping applied before display transform"); + RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update"); + + prop = RNA_def_property(srna, "use_curve_mapping", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", COLORMANAGE_VIEW_USE_CURVES); + RNA_def_property_boolean_funcs(prop, NULL, "rna_ColorManagedViewSettings_use_curves_set"); + RNA_def_property_ui_text(prop, "Use Curves", "Use RGB curved for pre-display transformation"); + RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update"); + + /* ** Colorspace ** */ + srna = RNA_def_struct(brna, "ColorManagedColorspaceSettings", NULL); + RNA_def_struct_ui_text(srna, "ColorManagedColorspaceSettings", "Input color space settings"); + + prop= RNA_def_property(srna, "name", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, color_space_items); + RNA_def_property_enum_funcs(prop, "rna_ColorManagedColorspaceSettings_colorspace_get", + "rna_ColorManagedColorspaceSettings_colorspace_set", + "rna_ColorManagedColorspaceSettings_colorspace_itemf"); + RNA_def_property_ui_text(prop, "Color Space", "Input color space name"); + RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagedColorspaceSettings_reload_update"); +} void RNA_def_color(BlenderRNA *brna) { @@ -692,6 +927,7 @@ void RNA_def_color(BlenderRNA *brna) rna_def_color_ramp(brna); rna_def_histogram(brna); rna_def_scopes(brna); + rna_def_colormanage(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 62a06888613..495d60df49c 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -513,6 +513,11 @@ static void rna_def_image(BlenderRNA *brna) "to avoid fringing for images with light backgrounds"); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_free_update"); + prop = RNA_def_property(srna, "view_as_render", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_VIEW_AS_RENDER); + RNA_def_property_ui_text(prop, "View as Render", "Apply render part of display transformation when displaying this image on the screen"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL); + prop = RNA_def_property(srna, "is_dirty", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs(prop, "rna_Image_dirty_get", NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -652,6 +657,11 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_dynamic_array_funcs(prop, "rna_Image_pixels_get_length"); RNA_def_property_float_funcs(prop, "rna_Image_pixels_get", "rna_Image_pixels_set", NULL); + prop = RNA_def_property(srna, "colorspace_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "colorspace_settings"); + RNA_def_property_struct_type(prop, "ColorManagedColorspaceSettings"); + RNA_def_property_ui_text(prop, "Colorspace Settings", "Input color space settings"); + RNA_api_image(srna); } diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index fa3f8d69c47..c66c0085763 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -50,6 +50,7 @@ #include "BKE_global.h" /* grr: G.main->name */ #include "IMB_imbuf.h" +#include "IMB_colormanagement.h" #include "BIF_gl.h" #include "GPU_draw.h" @@ -80,16 +81,17 @@ static void rna_Image_save_render(Image *image, bContext *C, ReportList *reports BKE_reportf(reports, RPT_ERROR, "Couldn't acquire buffer from image"); } else { - /* temp swap out the color */ - const unsigned char imb_planes_back = ibuf->planes; - const float dither_back = ibuf->dither; - ibuf->planes = scene->r.im_format.planes; - ibuf->dither = scene->r.dither_intensity; - if (!BKE_imbuf_write(ibuf, path, &scene->r.im_format)) { + ImBuf *write_ibuf = IMB_dupImBuf(ibuf); + + IMB_display_buffer_to_imbuf_rect(write_ibuf, &scene->view_settings, &scene->display_settings); + + write_ibuf->planes = scene->r.im_format.planes; + write_ibuf->dither = scene->r.dither_intensity; + + if (!BKE_imbuf_write(write_ibuf, path, &scene->r.im_format)) { BKE_reportf(reports, RPT_ERROR, "Couldn't write image: %s", path); } - ibuf->planes = imb_planes_back; - ibuf->dither = dither_back; + IMB_freeImBuf(write_ibuf); } BKE_image_release_ibuf(image, lock); @@ -118,6 +120,8 @@ static void rna_Image_save(Image *image, ReportList *reports) if (image->source == IMA_SRC_GENERATED) image->source = IMA_SRC_FILE; + IMB_colormanagment_colorspace_from_ibuf_ftype(&image->colorspace_settings, ibuf); + ibuf->userflags &= ~IB_BITMAPDIRTY; } else { diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c index 8b2b741b83e..8d1105fd620 100644 --- a/source/blender/makesrna/intern/rna_movieclip.c +++ b/source/blender/makesrna/intern/rna_movieclip.c @@ -299,6 +299,12 @@ static void rna_def_movieclip(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Frame Offset", "Offset of footage first frame relative to it's file name " "(affects only how footage is loading, does not change data associated with a clip)"); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_MovieClip_reload_update"); + + /* color management */ + prop = RNA_def_property(srna, "colorspace_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "colorspace_settings"); + RNA_def_property_struct_type(prop, "ColorManagedColorspaceSettings"); + RNA_def_property_ui_text(prop, "Colorspace Settings", "Input color space settings"); } void RNA_def_movieclip(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index f34c23a463e..0227d9c1554 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1093,34 +1093,6 @@ static void rna_Scene_glsl_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poi DAG_id_tag_update(&scene->id, 0); } -static void rna_RenderSettings_color_management_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) -{ - /* reset image nodes */ - Scene *scene = (Scene *)ptr->id.data; - bNodeTree *ntree = scene->nodetree; - bNode *node; - - if (ntree && scene->use_nodes) { - /* images are freed here, stop render and preview threads, until - * Image is threadsafe. when we are changing this property from a - * python script in the render thread, don't stop own thread */ - if (BLI_thread_is_main()) - WM_jobs_stop_all(bmain->wm.first); - - for (node = ntree->nodes.first; node; node = node->next) { - if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_IMAGE)) { - ED_node_changed_update(&scene->id, node); - WM_main_add_notifier(NC_NODE | NA_EDITED, node); - - if (node->type == CMP_NODE_IMAGE) - BKE_image_signal((Image *)node->id, NULL, IMA_SIGNAL_FREE); - } - } - } - - rna_Scene_glsl_update(bmain, scene, ptr); -} - static void rna_SceneRenderLayer_name_set(PointerRNA *ptr, const char *value) { Scene *scene = (Scene *)ptr->id.data; @@ -2958,6 +2930,17 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 10.0f); RNA_def_property_ui_text(prop, "G", "Log conversion gamma"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + /* color management */ + prop = RNA_def_property(srna, "view_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "view_settings"); + RNA_def_property_struct_type(prop, "ColorManagedViewSettings"); + RNA_def_property_ui_text(prop, "View Settings", "Color management settings applied on image before saving"); + + prop = RNA_def_property(srna, "display_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "display_settings"); + RNA_def_property_struct_type(prop, "ColorManagedDisplaySettings"); + RNA_def_property_ui_text(prop, "Display Settings", "Settings of device saved image would be displayed on"); } static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna) @@ -3698,11 +3681,6 @@ static void rna_def_scene_render_data(BlenderRNA *brna) "editor pipeline, if sequencer strips exist"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - prop = RNA_def_property(srna, "use_color_management", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "color_mgt_flag", R_COLOR_MANAGEMENT); - RNA_def_property_ui_text(prop, "Color Management", "Use linear workflow - gamma corrected imaging pipeline"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_RenderSettings_color_management_update"); - prop = RNA_def_property(srna, "use_color_unpremultiply", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "color_mgt_flag", R_COLOR_MANAGEMENT_PREDIVIDE); RNA_def_property_ui_text(prop, "Color Unpremultiply", @@ -4574,6 +4552,22 @@ void RNA_def_scene(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Active Movie Clip", "Active movie clip used for constraints and viewport drawing"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + /* color management */ + prop = RNA_def_property(srna, "view_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "view_settings"); + RNA_def_property_struct_type(prop, "ColorManagedViewSettings"); + RNA_def_property_ui_text(prop, "View Settings", "Color management settings applied on image before saving"); + + prop = RNA_def_property(srna, "display_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "display_settings"); + RNA_def_property_struct_type(prop, "ColorManagedDisplaySettings"); + RNA_def_property_ui_text(prop, "Display Settings", "Settings of device saved image would be displayed on"); + + prop = RNA_def_property(srna, "sequencer_colorspace_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "sequencer_colorspace_settings"); + RNA_def_property_struct_type(prop, "ColorManagedColorspaceSettings"); + RNA_def_property_ui_text(prop, "Sequencer Colorspace Settings", "Settings of color space sequencer is working in"); + /* Nestled Data */ rna_def_tool_settings(brna); rna_def_unified_paint_settings(brna); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index e3e467a9abb..b27148e964d 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1433,6 +1433,11 @@ static void rna_def_sequence(BlenderRNA *brna) RNA_def_property_int_funcs(prop, NULL, "rna_Sequence_channel_set", NULL); /* overlap test */ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_update"); + prop = RNA_def_property(srna, "use_linear_modifiers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_LINEAR_MODIFIERS); + RNA_def_property_ui_text(prop, "Use Linear Modifiers", "Calculate modifiers in linear space instead of sequencer's space"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_update"); + /* blending */ prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 549868e5ed0..ab1dfbf22b0 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -188,7 +188,8 @@ static Sequence *rna_Sequences_new_movie(ID *id, Editing *ed, ReportList *report Scene *scene = (Scene *)id; Sequence *seq; - struct anim *an = openanim(file, IB_rect, 0); + /* OCIO_TODO: support configurable color spaces for strips */ + struct anim *an = openanim(file, IB_rect, 0, NULL); if (an == NULL) { BKE_report(reports, RPT_ERROR, "Sequences.new_movie: unable to open movie file"); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index a00e8bfea2d..246f9fef98a 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -658,20 +658,6 @@ static void rna_SpaceImageEditor_cursor_location_set(PointerRNA *ptr, const floa } } -static void rna_SpaceImageEditor_curves_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) -{ - SpaceImage *sima = (SpaceImage *)ptr->data; - ImBuf *ibuf; - void *lock; - - ibuf = ED_space_image_acquire_buffer(sima, &lock); - if (ibuf->rect_float) - curvemapping_do_ibuf(sima->cumap, ibuf); - ED_space_image_release_buffer(sima, lock); - - WM_main_add_notifier(NC_IMAGE, sima->image); -} - static void rna_SpaceImageEditor_scopes_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) { SpaceImage *sima = (SpaceImage *)ptr->data; @@ -680,7 +666,7 @@ static void rna_SpaceImageEditor_scopes_update(Main *UNUSED(bmain), Scene *scene ibuf = ED_space_image_acquire_buffer(sima, &lock); if (ibuf) { - scopes_update(&sima->scopes, ibuf, scene->r.color_mgt_flag & R_COLOR_MANAGEMENT); + scopes_update(&sima->scopes, ibuf, &scene->view_settings, &scene->display_settings); WM_main_add_notifier(NC_IMAGE, sima->image); } ED_space_image_release_buffer(sima, lock); @@ -1980,11 +1966,6 @@ static void rna_def_space_image(BlenderRNA *brna) "Parameters defining which layer, pass and frame of the image is displayed"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); - prop = RNA_def_property(srna, "curve", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "cumap"); - RNA_def_property_ui_text(prop, "Curve", "Color curve mapping to use for displaying the image"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, "rna_SpaceImageEditor_curves_update"); - prop = RNA_def_property(srna, "scopes", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "scopes"); RNA_def_property_struct_type(prop, "Scopes"); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index cf43bd74d72..ea38b60b25f 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -415,6 +415,7 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_function_ui_description(func, "User interface for setting image format options"); parm = RNA_def_pointer(func, "image_settings", "ImageFormatSettings", "", ""); RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR | PROP_NEVER_NULL); + RNA_def_boolean(func, "color_management", 0, "", "Show color management settings"); func = RNA_def_function(srna, "template_movieclip", "uiTemplateMovieClip"); RNA_def_function_ui_description(func, "Item(s). User interface for selecting movie clips and their source paths"); @@ -497,6 +498,17 @@ void RNA_api_ui_layout(StructRNA *srna) func = RNA_def_function(srna, "introspect", "uiLayoutIntrospect"); parm = RNA_def_string(func, "string", "", 1024 * 1024, "Descr", "DESCR"); RNA_def_function_return(func, parm); + + /* color management templates */ + func = RNA_def_function(srna, "template_colorspace_settings", "uiTemplateColorspaceSettings"); + RNA_def_function_ui_description(func, "Item. A widget to control input color space settings."); + api_ui_item_rna_common(func); + + func = RNA_def_function(srna, "template_colormanaged_view_settings", "uiTemplateColormanagedViewSettings"); + RNA_def_function_ui_description(func, "Item. A widget to control color managed view settings settings."); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + api_ui_item_rna_common(func); + /* RNA_def_boolean(func, "show_global_settings", 0, "", "Show widgets to control global color management settings"); */ } #endif diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c index e1510f9f813..c9960df1345 100644 --- a/source/blender/nodes/composite/node_composite_tree.c +++ b/source/blender/nodes/composite/node_composite_tree.c @@ -33,6 +33,7 @@ #include <stdio.h> #include "DNA_anim_types.h" +#include "DNA_color_types.h" #include "DNA_scene_types.h" #include "DNA_node_types.h" @@ -693,7 +694,9 @@ static void ntreeCompositExecTreeOld(bNodeTree *ntree, RenderData *rd, int do_pr void *COM_linker_hack = NULL; -void ntreeCompositExecTree(bNodeTree *ntree, RenderData *rd, int rendering, int do_preview) +void ntreeCompositExecTree(bNodeTree *ntree, RenderData *rd, int rendering, int do_preview, + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings) { #ifdef WITH_COMPOSITOR #ifdef WITH_COMPOSITOR_LEGACY @@ -704,10 +707,11 @@ void ntreeCompositExecTree(bNodeTree *ntree, RenderData *rd, int rendering, int else #endif { - COM_execute(rd, ntree, rendering); + COM_execute(rd, ntree, rendering, view_settings, display_settings); } #else (void)ntree, (void)rd, (void)rendering, (void)do_preview; + (void)view_settings, (void)display_settings; #endif (void)do_preview; diff --git a/source/blender/nodes/composite/node_composite_util.c b/source/blender/nodes/composite/node_composite_util.c index 2277c5a2836..fe4eebc9492 100644 --- a/source/blender/nodes/composite/node_composite_util.c +++ b/source/blender/nodes/composite/node_composite_util.c @@ -606,6 +606,9 @@ static CompBuf *generate_procedural_preview(CompBuf *cbuf, int newx, int newy) return outbuf; } +/* OCIO_TODO: this function is only used by legacy compositor system only, which would likely be removed soon, + * keep check for old color management flag for now + */ void generate_preview(void *data, bNode *node, CompBuf *stackbuf) { RenderData *rd= data; diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c index dcc88c7c010..9f3874560ac 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.c +++ b/source/blender/nodes/composite/nodes/node_composite_image.c @@ -292,33 +292,20 @@ float *node_composit_get_float_buffer(RenderData *rd, ImBuf *ibuf, int *alloc) *alloc= FALSE; + /* OCIO_TODO: this is a part of legacy compositor system, don't bother with porting this code + * to new color management system since this code would likely be simply removed soon + */ if (rd->color_mgt_flag & R_COLOR_MANAGEMENT) { - if (ibuf->profile != IB_PROFILE_NONE) { - rect= ibuf->rect_float; - } - else { - rect= MEM_mapallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, "node_composit_get_image"); - - IMB_buffer_float_from_float(rect, ibuf->rect_float, - 4, IB_PROFILE_LINEAR_RGB, IB_PROFILE_SRGB, predivide, - ibuf->x, ibuf->y, ibuf->x, ibuf->x); - - *alloc= TRUE; - } + rect= ibuf->rect_float; } else { - if (ibuf->profile == IB_PROFILE_NONE) { - rect= ibuf->rect_float; - } - else { - rect= MEM_mapallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, "node_composit_get_image"); + rect= MEM_mapallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, "node_composit_get_image"); - IMB_buffer_float_from_float(rect, ibuf->rect_float, - 4, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB, predivide, - ibuf->x, ibuf->y, ibuf->x, ibuf->x); + IMB_buffer_float_from_float(rect, ibuf->rect_float, + 4, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB, predivide, + ibuf->x, ibuf->y, ibuf->x, ibuf->x); *alloc= TRUE; - } } return rect; diff --git a/source/blender/nodes/composite/nodes/node_composite_outputFile.c b/source/blender/nodes/composite/nodes/node_composite_outputFile.c index 2d9b9348668..656e2a72c03 100644 --- a/source/blender/nodes/composite/nodes/node_composite_outputFile.c +++ b/source/blender/nodes/composite/nodes/node_composite_outputFile.c @@ -267,9 +267,6 @@ static void exec_output_file_singlelayer(RenderData *rd, bNode *node, bNodeStack ibuf->rect_float = cbuf->rect; ibuf->dither = rd->dither_intensity; - if (rd->color_mgt_flag & R_COLOR_MANAGEMENT) - ibuf->profile = IB_PROFILE_LINEAR_RGB; - /* get full path */ BLI_join_dirfile(path, FILE_MAX, nimf->base_path, sockdata->path); BKE_makepicstring(filename, path, bmain->name, rd->cfra, format->imtype, (rd->scemode & R_EXTENSION), TRUE); diff --git a/source/blender/quicktime/apple/qtkit_import.m b/source/blender/quicktime/apple/qtkit_import.m index e30b8331fc0..75b9056de1b 100644 --- a/source/blender/quicktime/apple/qtkit_import.m +++ b/source/blender/quicktime/apple/qtkit_import.m @@ -291,7 +291,6 @@ ImBuf * qtime_fetchibuf (struct anim *anim, int position) ibuf = nsImageToiBuf(frameImage,anim->x, anim->y); [pool drain]; - ibuf->profile = IB_PROFILE_SRGB; return ibuf; } diff --git a/source/blender/quicktime/apple/quicktime_import.c b/source/blender/quicktime/apple/quicktime_import.c index 5b13d77a030..e44eba2bfe0 100644 --- a/source/blender/quicktime/apple/quicktime_import.c +++ b/source/blender/quicktime/apple/quicktime_import.c @@ -392,8 +392,6 @@ ImBuf *qtime_fetchibuf(struct anim *anim, int position) } #endif - ibuf->profile = IB_PROFILE_SRGB; - IMB_flipy(ibuf); return ibuf; } diff --git a/source/blender/render/intern/include/render_result.h b/source/blender/render/intern/include/render_result.h index 303d4094f8e..999fb24ba32 100644 --- a/source/blender/render/intern/include/render_result.h +++ b/source/blender/render/intern/include/render_result.h @@ -47,6 +47,8 @@ struct RenderLayer; struct RenderResult; struct Scene; struct rcti; +struct ColorManagedDisplaySettings; +struct ColorManagedViewSettings; /* New */ @@ -90,7 +92,9 @@ void render_result_rect_from_ibuf(struct RenderResult *rr, struct RenderData *rd void render_result_rect_fill_zero(struct RenderResult *rr); void render_result_rect_get_pixels(struct RenderResult *rr, struct RenderData *rd, - unsigned int *rect, int rectx, int recty); + unsigned int *rect, int rectx, int recty, + const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings); #endif /* __RENDER_RESULT_H__ */ diff --git a/source/blender/render/intern/source/envmap.c b/source/blender/render/intern/source/envmap.c index 26bd482af69..03eb21dfa23 100644 --- a/source/blender/render/intern/source/envmap.c +++ b/source/blender/render/intern/source/envmap.c @@ -472,9 +472,6 @@ static void render_envmap(Render *re, EnvMap *env) ibuf = IMB_allocImBuf(envre->rectx, envre->recty, 24, IB_rect | IB_rectfloat); memcpy(ibuf->rect_float, rl->rectf, ibuf->channels * ibuf->x * ibuf->y * sizeof(float)); - if (re->scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) - ibuf->profile = IB_PROFILE_LINEAR_RGB; - /* envmap renders without alpha */ alpha = ibuf->rect_float + 3; for (y = ibuf->x * ibuf->y - 1; y >= 0; y--, alpha += 4) diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 5f110410978..599d91333e6 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -67,6 +67,7 @@ #include "BLI_callbacks.h" #include "PIL_time.h" +#include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -333,7 +334,7 @@ void RE_ResultGet32(Render *re, unsigned int *rect) RenderResult rres; RE_AcquireResultImage(re, &rres); - render_result_rect_get_pixels(&rres, &re->r, rect, re->rectx, re->recty); + render_result_rect_get_pixels(&rres, &re->r, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings); RE_ReleaseResultImage(re); } @@ -1434,7 +1435,7 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) ntreeCompositTagRender(re->scene); ntreeCompositTagAnimated(ntree); - ntreeCompositExecTree(ntree, &re->r, 1, G.background == 0); + ntreeCompositExecTree(ntree, &re->r, 1, G.background == 0, &re->scene->view_settings, &re->scene->display_settings); } /* ensure we get either composited result or the active layer */ @@ -1598,7 +1599,7 @@ static void do_render_composite_fields_blur_3d(Render *re) if (re->r.scemode & R_FULL_SAMPLE) do_merge_fullsample(re, ntree); else { - ntreeCompositExecTree(ntree, &re->r, 1, G.background == 0); + ntreeCompositExecTree(ntree, &re->r, 1, G.background == 0, &re->scene->view_settings, &re->scene->display_settings); } ntree->stats_draw = NULL; @@ -1647,7 +1648,7 @@ int RE_seq_render_active(Scene *scene, RenderData *rd) static void do_render_seq(Render *re) { static int recurs_depth = 0; - struct ImBuf *ibuf; + struct ImBuf *ibuf, *out; RenderResult *rr; /* don't assign re->result here as it might change during give_ibuf_seq */ int cfra = re->r.cfra; SeqRenderData context; @@ -1674,7 +1675,11 @@ static void do_render_seq(Render *re) 100); } - ibuf = BKE_sequencer_give_ibuf(context, cfra, 0); + out = BKE_sequencer_give_ibuf(context, cfra, 0); + + ibuf = IMB_dupImBuf(out); + IMB_freeImBuf(out); + BKE_sequencer_imbuf_from_sequencer_space(re->scene, ibuf); recurs_depth--; @@ -2069,6 +2074,14 @@ void RE_BlenderFrame(Render *re, Main *bmain, Scene *scene, SceneRenderLayer *sr G.is_rendering = FALSE; } +static void colormanage_image_for_write(Scene *scene, ImBuf *ibuf) +{ + IMB_display_buffer_to_imbuf_rect(ibuf, &scene->view_settings, &scene->display_settings); + + if (ibuf) + imb_freerectfloatImBuf(ibuf); +} + static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const char *name_override) { char name[FILE_MAX]; @@ -2081,19 +2094,27 @@ static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovie /* write movie or image */ if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { int do_free = FALSE; - unsigned int *rect32 = (unsigned int *)rres.rect32; + ImBuf *ibuf = render_result_rect_to_ibuf(&rres, &scene->r); + /* note; the way it gets 32 bits rects is weak... */ - if (rres.rect32 == NULL) { - rect32 = MEM_mapallocN(sizeof(int) * rres.rectx * rres.recty, "temp 32 bits rect"); - RE_ResultGet32(re, rect32); + if (ibuf->rect == NULL) { + ibuf->rect = MEM_mapallocN(sizeof(int) * rres.rectx * rres.recty, "temp 32 bits rect"); + RE_ResultGet32(re, ibuf->rect); do_free = TRUE; } - ok = mh->append_movie(&re->r, scene->r.sfra, scene->r.cfra, (int *)rect32, - rres.rectx, rres.recty, re->reports); + colormanage_image_for_write(scene, ibuf); + + ok = mh->append_movie(&re->r, scene->r.sfra, scene->r.cfra, (int *) ibuf->rect, + ibuf->x, ibuf->y, re->reports); if (do_free) { - MEM_freeN(rect32); + MEM_freeN(ibuf->rect); + ibuf->rect = NULL; } + + /* imbuf knows which rects are not part of ibuf */ + IMB_freeImBuf(ibuf); + printf("Append frame %d", scene->r.cfra); } else { @@ -2110,6 +2131,12 @@ static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovie } else { ImBuf *ibuf = render_result_rect_to_ibuf(&rres, &scene->r); + int do_colormanagement; + + do_colormanagement = !BKE_imtype_supports_float(scene->r.im_format.imtype); + + if (do_colormanagement) + colormanage_image_for_write(scene, ibuf); ok = BKE_imbuf_write_stamp(scene, camera, ibuf, name, &scene->r.im_format); @@ -2127,6 +2154,9 @@ static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovie name[strlen(name) - 4] = 0; BKE_add_image_extension(name, R_IMF_IMTYPE_JPEG90); ibuf->planes = 24; + + colormanage_image_for_write(scene, ibuf); + BKE_imbuf_write_stamp(scene, camera, ibuf, name, &imf); printf("\nSaved: %s", name); } @@ -2381,7 +2411,8 @@ void RE_init_threadcount(Render *re) * x/y offsets are only used on a partial copy when dimensions don't match */ void RE_layer_load_from_file(RenderLayer *layer, ReportList *reports, const char *filename, int x, int y) { - ImBuf *ibuf = IMB_loadiffname(filename, IB_rect); + /* OCIO_TODO: assume layer was saved in defaule color space */ + ImBuf *ibuf = IMB_loadiffname(filename, IB_rect, NULL); if (ibuf && (ibuf->rect || ibuf->rect_float)) { if (ibuf->x == layer->rectx && ibuf->y == layer->recty) { @@ -2470,8 +2501,7 @@ int RE_WriteEnvmapResult(struct ReportList *reports, Scene *scene, EnvMap *env, return 0; } - if (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) - ibuf->profile = IB_PROFILE_LINEAR_RGB; + IMB_display_buffer_to_imbuf_rect(ibuf, &scene->view_settings, &scene->display_settings); /* to save, we first get absolute path */ BLI_strncpy(filepath, relpath, sizeof(filepath)); diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c index 2f932b2149e..f11f84c9d3b 100644 --- a/source/blender/render/intern/source/render_result.c +++ b/source/blender/render/intern/source/render_result.c @@ -50,6 +50,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_colormanagement.h" #include "intern/openexr/openexr_multi.h" @@ -1081,16 +1082,12 @@ ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd) /* float factor for random dither, imbuf takes care of it */ ibuf->dither = rd->dither_intensity; - /* prepare to gamma correct to sRGB color space */ - if (rd->color_mgt_flag & R_COLOR_MANAGEMENT) { - /* sequence editor can generate 8bpc render buffers */ - if (ibuf->rect) { - ibuf->profile = IB_PROFILE_SRGB; - if (BKE_imtype_valid_depths(rd->im_format.imtype) & (R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_24 | R_IMF_CHAN_DEPTH_32)) - IMB_float_from_rect(ibuf); - } - else { - ibuf->profile = IB_PROFILE_LINEAR_RGB; + /* prepare to gamma correct to sRGB color space + * note that sequence editor can generate 8bpc render buffers + */ + if (ibuf->rect) { + if (BKE_imtype_valid_depths(rd->im_format.imtype) & (R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_24 | R_IMF_CHAN_DEPTH_32)) { + IMB_float_from_rect(ibuf); } } @@ -1106,22 +1103,14 @@ ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd) return ibuf; } -void render_result_rect_from_ibuf(RenderResult *rr, RenderData *rd, ImBuf *ibuf) +void render_result_rect_from_ibuf(RenderResult *rr, RenderData *UNUSED(rd), ImBuf *ibuf) { if (ibuf->rect_float) { - /* color management: when off ensure rectf is non-lin, since thats what the internal - * render engine delivers */ - int profile_to = (rd->color_mgt_flag & R_COLOR_MANAGEMENT) ? IB_PROFILE_LINEAR_RGB : IB_PROFILE_SRGB; - int profile_from = (ibuf->profile == IB_PROFILE_LINEAR_RGB) ? IB_PROFILE_LINEAR_RGB : IB_PROFILE_SRGB; - int predivide = (rd->color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE); - if (!rr->rectf) rr->rectf = MEM_mallocN(4 * sizeof(float) * rr->rectx * rr->recty, "render_seq rectf"); - IMB_buffer_float_from_float(rr->rectf, ibuf->rect_float, - 4, profile_to, profile_from, predivide, - rr->rectx, rr->recty, rr->rectx, rr->rectx); - + memcpy(rr->rectf, ibuf->rect_float, 4 * sizeof(float) * rr->rectx * rr->recty); + /* TSK! Since sequence render doesn't free the *rr render result, the old rect32 * can hang around when sequence render has rendered a 32 bits one before */ if (rr->rect32) { @@ -1153,19 +1142,17 @@ void render_result_rect_fill_zero(RenderResult *rr) rr->rect32 = MEM_callocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); } -void render_result_rect_get_pixels(RenderResult *rr, RenderData *rd, unsigned int *rect, int rectx, int recty) +void render_result_rect_get_pixels(RenderResult *rr, RenderData *rd, unsigned int *rect, int rectx, int recty, + const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) { if (rr->rect32) { memcpy(rect, rr->rect32, sizeof(int) * rr->rectx * rr->recty); } else if (rr->rectf) { - int profile_from = (rd->color_mgt_flag & R_COLOR_MANAGEMENT) ? IB_PROFILE_LINEAR_RGB : IB_PROFILE_SRGB; int predivide = (rd->color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE); - int dither = 0; - IMB_buffer_byte_from_float((unsigned char *)rect, rr->rectf, - 4, dither, IB_PROFILE_SRGB, profile_from, predivide, - rr->rectx, rr->recty, rr->rectx, rr->rectx); + IMB_display_buffer_transform_apply((unsigned char *) rect, rr->rectf, rr->rectx, rr->recty, 4, + view_settings, display_settings, predivide); } else /* else fill with black */ diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c index 4d3b9c2493e..6d22d8991b4 100644 --- a/source/blender/render/intern/source/render_texture.c +++ b/source/blender/render/intern/source/render_texture.c @@ -50,6 +50,7 @@ #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" +#include "IMB_colormanagement.h" #include "BKE_colortools.h" #include "BKE_image.h" @@ -1226,8 +1227,8 @@ int multitex_nodes(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, ImBuf *ibuf = BKE_image_get_ibuf(tex->ima, &tex->iuser); /* don't linearize float buffers, assumed to be linear */ - if (ibuf && !(ibuf->rect_float) && R.r.color_mgt_flag & R_COLOR_MANAGEMENT) - srgb_to_linearrgb_v3_v3(&texres->tr, &texres->tr); + if (ibuf && !(ibuf->rect_float)) + IMB_colormanagement_colorspace_to_scene_linear_v3(&texres->tr, ibuf->rect_colorspace); } } else { @@ -2379,8 +2380,8 @@ void do_material_tex(ShadeInput *shi, Render *re) ImBuf *ibuf = BKE_image_get_ibuf(ima, &tex->iuser); /* don't linearize float buffers, assumed to be linear */ - if (ibuf && !(ibuf->rect_float) && re->r.color_mgt_flag & R_COLOR_MANAGEMENT) - srgb_to_linearrgb_v3_v3(tcol, tcol); + if (ibuf && !(ibuf->rect_float)) + IMB_colormanagement_colorspace_to_scene_linear_v3(tcol, ibuf->rect_colorspace); } if (mtex->mapto & MAP_COL) { @@ -2891,8 +2892,8 @@ void do_halo_tex(HaloRen *har, float xn, float yn, float col_r[4]) ImBuf *ibuf = BKE_image_get_ibuf(ima, &mtex->tex->iuser); /* don't linearize float buffers, assumed to be linear */ - if (ibuf && !(ibuf->rect_float) && R.r.color_mgt_flag & R_COLOR_MANAGEMENT) - srgb_to_linearrgb_v3_v3(&texres.tr, &texres.tr); + if (ibuf && !(ibuf->rect_float)) + IMB_colormanagement_colorspace_to_scene_linear_v3(&texres.tr, ibuf->rect_colorspace); } fact= texres.tin*mtex->colfac; @@ -3106,8 +3107,8 @@ void do_sky_tex(const float rco[3], float lo[3], const float dxyview[2], float h ImBuf *ibuf = BKE_image_get_ibuf(ima, &tex->iuser); /* don't linearize float buffers, assumed to be linear */ - if (ibuf && !(ibuf->rect_float) && R.r.color_mgt_flag & R_COLOR_MANAGEMENT) - srgb_to_linearrgb_v3_v3(tcol, tcol); + if (ibuf && !(ibuf->rect_float)) + IMB_colormanagement_colorspace_to_scene_linear_v3(tcol, ibuf->rect_colorspace); } if (mtex->mapto & WOMAP_HORIZ) { @@ -3320,8 +3321,8 @@ void do_lamp_tex(LampRen *la, const float lavec[3], ShadeInput *shi, float col_r ImBuf *ibuf = BKE_image_get_ibuf(ima, &tex->iuser); /* don't linearize float buffers, assumed to be linear */ - if (ibuf && !(ibuf->rect_float) && R.r.color_mgt_flag & R_COLOR_MANAGEMENT) - srgb_to_linearrgb_v3_v3(&texres.tr, &texres.tr); + if (ibuf && !(ibuf->rect_float)) + IMB_colormanagement_colorspace_to_scene_linear_v3(&texres.tr, ibuf->rect_colorspace); } /* lamp colors were premultiplied with this */ diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c index c009c7b7394..97a05e6a4c9 100644 --- a/source/blender/render/intern/source/rendercore.c +++ b/source/blender/render/intern/source/rendercore.c @@ -62,6 +62,7 @@ #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" +#include "IMB_colormanagement.h" /* local include */ #include "rayintersection.h" @@ -1993,6 +1994,8 @@ typedef struct BakeShade { float dxco[3], dyco[3]; short *do_update; + + struct ColorSpace *rect_colorspace; } BakeShade; static void bake_set_shade_input(ObjectInstanceRen *obi, VlakRen *vlr, ShadeInput *shi, int quad, int UNUSED(isect), int x, int y, float u, float v) @@ -2168,8 +2171,12 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int UNUSED(qua else { unsigned char *col= (unsigned char *)(bs->rect + bs->rectx*y + x); - if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_TEXTURE) && (R.r.color_mgt_flag & R_COLOR_MANAGEMENT)) { - linearrgb_to_srgb_uchar3(col, shr.combined); + if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_TEXTURE)) { + float rgb[3]; + + copy_v3_v3(rgb, shr.combined); + IMB_colormanagement_scene_linear_to_colorspace_v3(rgb, bs->rect_colorspace); + rgb_float_to_uchar(col, rgb); } else { rgb_float_to_uchar(col, shr.combined); @@ -2503,6 +2510,7 @@ static void shade_tface(BakeShade *bs) bs->rectx= bs->ibuf->x; bs->recty= bs->ibuf->y; bs->rect= bs->ibuf->rect; + bs->rect_colorspace= bs->ibuf->rect_colorspace; bs->rect_float= bs->ibuf->rect_float; bs->quad= 0; @@ -2613,8 +2621,6 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_up ima->flag&= ~IMA_USED_FOR_RENDER; if (ibuf) { ibuf->userdata = NULL; /* use for masking if needed */ - if (ibuf->rect_float) - ibuf->profile = IB_PROFILE_LINEAR_RGB; } } diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c index bbdcfbb5a73..d1d2c66985e 100644 --- a/source/blender/render/intern/source/shadeinput.c +++ b/source/blender/render/intern/source/shadeinput.c @@ -1325,7 +1325,14 @@ void shade_input_initialize(ShadeInput *shi, RenderPart *pa, RenderLayer *rl, in shi->sample = sample; shi->thread = pa->thread; shi->do_preview = (R.r.scemode & R_MATNODE_PREVIEW) != 0; - shi->do_manage = (R.r.color_mgt_flag & R_COLOR_MANAGEMENT); + + /* OCIO_TODO: for now assume color management is always enabled and vertes colors are in sRGB space + * probably would be nice to have this things configurable, but for now it should work + * also probably this flag could be removed (in separated commit) before the release + * since it's not actually meaningful anymore + */ + shi->do_manage = TRUE; + shi->lay = rl->lay; shi->layflag = rl->layflag; shi->passflag = rl->passflag; diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 46a669eb495..eb6fabd03c6 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -705,11 +705,11 @@ static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, int **thumb_pt) if (scene->camera) { ibuf = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, - IB_rect, OB_SOLID, FALSE, err_out); + IB_rect, OB_SOLID, FALSE, FALSE, err_out); } else { ibuf = ED_view3d_draw_offscreen_imbuf(scene, v3d, ar, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, - IB_rect, FALSE, err_out); + IB_rect, FALSE, FALSE, err_out); } if (ibuf) { diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 71d6cf557e1..908f7e27f0b 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -67,7 +67,6 @@ #include "BKE_tracking.h" /* free tracking clipboard */ #include "BLI_listbase.h" -#include "BLI_math_color.h" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -115,6 +114,8 @@ #include "BKE_sound.h" #include "COM_compositor.h" +#include "IMB_colormanagement.h" + static void wm_init_reports(bContext *C) { BKE_reports_init(CTX_wm_reports(C), RPT_STORE); @@ -149,8 +150,8 @@ void WM_init(bContext *C, int argc, const char **argv) BLF_init(11, U.dpi); /* Please update source/gamengine/GamePlayer/GPG_ghost.cpp if you change this */ BLF_lang_init(); - /* initialize color stuff */ - BLI_init_srgb_conversion(); + /* initialize color management stuff */ + IMB_colormanagement_init(); /* get the default database, plus a wm */ WM_homefile_read(C, NULL, G.factory_startup); @@ -370,6 +371,8 @@ void WM_exit_ext(bContext *C, const short do_python) sound_exit(); + IMB_colormanagement_exit(); + /* first wrap up running stuff, we assume only the active WM is running */ /* modal handlers are on window level freed, others too? */ /* note; same code copied in wm_files.c */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index d3d90970b0c..313fc0a819e 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1296,7 +1296,7 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar extern int datatoc_splash_png_size; ImBuf *ibuf = IMB_ibImageFromMemory((unsigned char *)datatoc_splash_png, - datatoc_splash_png_size, IB_rect, "<splash screen>"); + datatoc_splash_png_size, IB_rect, NULL, "<splash screen>"); #else ImBuf *ibuf = NULL; #endif diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 6d245730795..bff40faf1d1 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -281,7 +281,8 @@ static void build_pict_list(char *first, int totframes, int fstep, int fontid) struct anim *anim; if (IMB_isanim(first)) { - anim = IMB_open_anim(first, IB_rect, 0); + /* OCIO_TODO: support different input color space */ + anim = IMB_open_anim(first, IB_rect, 0, NULL); if (anim) { int pic; ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); @@ -380,12 +381,13 @@ static void build_pict_list(char *first, int totframes, int fstep, int fontid) pupdate_time(); if (ptottime > 1.0) { + /* OCIO_TODO: support different input color space */ if (picture->mem) { ibuf = IMB_ibImageFromMemory((unsigned char *)picture->mem, picture->size, - picture->IB_flags, picture->name); + picture->IB_flags, NULL, picture->name); } else { - ibuf = IMB_loadiffname(picture->name, picture->IB_flags); + ibuf = IMB_loadiffname(picture->name, picture->IB_flags, NULL); } if (ibuf) { playanim_toscreen(picture, ibuf, fontid); @@ -826,7 +828,8 @@ void WM_main_playanim(int argc, const char **argv) } if (IMB_isanim(filepath)) { - anim = IMB_open_anim(filepath, IB_rect, 0); + /* OCIO_TODO: support different input color spaces */ + anim = IMB_open_anim(filepath, IB_rect, 0, NULL); if (anim) { ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); IMB_close_anim(anim); @@ -838,7 +841,8 @@ void WM_main_playanim(int argc, const char **argv) } if (ibuf == NULL) { - ibuf = IMB_loadiffname(filepath, IB_rect); + /* OCIO_TODO: support different input color space */ + ibuf = IMB_loadiffname(filepath, IB_rect, NULL); } if (ibuf == NULL) { @@ -950,11 +954,13 @@ void WM_main_playanim(int argc, const char **argv) ibuf = IMB_anim_absolute(ps.picture->anim, ps.picture->frame, IMB_TC_NONE, IMB_PROXY_NONE); } else if (ps.picture->mem) { + /* use correct colorspace here */ ibuf = IMB_ibImageFromMemory((unsigned char *) ps.picture->mem, ps.picture->size, - ps.picture->IB_flags, ps.picture->name); + ps.picture->IB_flags, NULL, ps.picture->name); } else { - ibuf = IMB_loadiffname(ps.picture->name, ps.picture->IB_flags); + /* use correct colorspace here */ + ibuf = IMB_loadiffname(ps.picture->name, ps.picture->IB_flags, NULL); } if (ibuf) { diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt index 44991adcc9d..04d4be05034 100644 --- a/source/blenderplayer/CMakeLists.txt +++ b/source/blenderplayer/CMakeLists.txt @@ -147,6 +147,7 @@ endif() bf_intern_mikktspace extern_recastnavigation bf_intern_raskter + bf_intern_opencolorio ) if(WITH_MOD_CLOTH_ELTOPO) diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index b600c08f4dc..c6383b471f7 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -428,6 +428,8 @@ void uiTemplateMovieClip(struct uiLayout *layout, struct bContext *C, struct Poi void uiTemplateTrack(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname) {} void uiTemplateMarker(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, PointerRNA *userptr, PointerRNA *trackptr, int compact) {} void uiTemplateImageSettings(struct uiLayout *layout, struct PointerRNA *imfptr) {} +void uiTemplateColorspaceSettings(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname) {} +void uiTemplateColormanagedViewSettings(struct uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, int show_global_settings) {} /* rna render */ struct RenderResult *RE_engine_begin_result(struct RenderEngine *engine, int x, int y, int w, int h) {return (struct RenderResult *) NULL;} diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 0229079c13c..3356091b97f 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -301,6 +301,15 @@ if(WITH_INTERNATIONAL) ) endif() +# color management +if(WITH_OPENCOLORIO) + install( + DIRECTORY ${CMAKE_SOURCE_DIR}/release/datafiles/colormanagement + DESTINATION ${TARGETDIR_VER}/datafiles + PATTERN ".svn" EXCLUDE + ) +endif() + # helpful tip when using make if("${CMAKE_GENERATOR}" MATCHES ".*Makefiles.*") # message after building. @@ -636,6 +645,17 @@ elseif(WIN32) endif() endif() + if(WITH_OPENCOLORIO) + if(NOT MINGW) + set_lib_path(OCIOBIN "opencolorio/bin") + install( + FILES + ${OCIOBIN}/OpenColorIO.dll + DESTINATION ${TARGETDIR} + ) + endif() + endif() + elseif(APPLE) # handy install macro to exclude files, we use \$ escape for the "to" @@ -907,6 +927,7 @@ endif() cycles_util cycles_subd bf_intern_raskter + bf_intern_opencolorio ) if(WITH_COMPOSITOR) |