Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/imbuf/intern')
-rw-r--r--source/blender/imbuf/intern/IMB_anim.h1
-rw-r--r--source/blender/imbuf/intern/IMB_colormanagement_intern.h92
-rw-r--r--source/blender/imbuf/intern/IMB_filetype.h25
-rw-r--r--source/blender/imbuf/intern/allocimbuf.c16
-rw-r--r--source/blender/imbuf/intern/anim_movie.c37
-rw-r--r--source/blender/imbuf/intern/bmp.c8
-rw-r--r--source/blender/imbuf/intern/cineon/cineon_dpx.c15
-rw-r--r--source/blender/imbuf/intern/colormanagement.c2337
-rw-r--r--source/blender/imbuf/intern/dds/dds_api.cpp12
-rw-r--r--source/blender/imbuf/intern/dds/dds_api.h4
-rw-r--r--source/blender/imbuf/intern/divers.c137
-rw-r--r--source/blender/imbuf/intern/filetype.c30
-rw-r--r--source/blender/imbuf/intern/indexer.c3
-rw-r--r--source/blender/imbuf/intern/iris.c9
-rw-r--r--source/blender/imbuf/intern/jp2.c155
-rw-r--r--source/blender/imbuf/intern/jpeg.c10
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.cpp41
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.h2
-rw-r--r--source/blender/imbuf/intern/png.c41
-rw-r--r--source/blender/imbuf/intern/radiance_hdr.c8
-rw-r--r--source/blender/imbuf/intern/readimage.c38
-rw-r--r--source/blender/imbuf/intern/rectop.c16
-rw-r--r--source/blender/imbuf/intern/targa.c9
-rw-r--r--source/blender/imbuf/intern/thumbs.c11
-rw-r--r--source/blender/imbuf/intern/tiff.c15
-rw-r--r--source/blender/imbuf/intern/writeimage.c36
26 files changed, 2761 insertions, 347 deletions
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;
}
}