Color management fixes

Now it's a bit more robust, tagging images with profiles when they're loaded, 
which then get interpreted later on by conversion functions. Just Linear RGB 
and sRGB profiles at the moment, same as before.

This commit fixes Martin's problem with EXRs and Multilayer images loading/
saving too dark, and it also makes the sequence editor work correctly with it too.

Also fixes:
[#19647] gamma correction with color management is reset when resetting Curve
[#19454] 2.5: Dither does not work when Color management is enabled
This commit is contained in:
Matt Ebb 2010-01-09 00:16:35 +00:00
parent e62e66fe8a
commit 04dec46c6a
23 changed files with 166 additions and 82 deletions

@ -143,6 +143,9 @@ struct RenderPass *BKE_image_multilayer_index(struct RenderResult *rr, struct Im
struct RenderResult *BKE_image_acquire_renderresult(struct Scene *scene, struct Image *ima);
void BKE_image_release_renderresult(struct Scene *scene, struct Image *ima);
/* frees all ibufs used by any image datablocks */
void BKE_image_free_image_ibufs(void);
/* goes over all textures that use images */
void BKE_image_free_all_textures(void);

@ -763,10 +763,16 @@ void colorcorrection_do_ibuf(ImBuf *ibuf, const char *profile)
}
}
/* only used for image editor curves */
void curvemapping_do_ibuf(CurveMapping *cumap, ImBuf *ibuf)
{
ImBuf *tmpbuf;
int pixel;
char *tmpcbuf;
float *pix_in;
float col[3];
int stride= 4;
float *pix_out;
if(ibuf==NULL)
return;
@ -775,35 +781,45 @@ void curvemapping_do_ibuf(CurveMapping *cumap, ImBuf *ibuf)
else if(ibuf->rect==NULL)
imb_addrectImBuf(ibuf);
if (!ibuf->rect || !ibuf->rect_float)
return;
/* work on a temp buffer, so can color manage afterwards.
* No worse off memory wise than comp nodes */
tmpbuf = IMB_dupImBuf(ibuf);
curvemapping_premultiply(cumap, 0);
if(ibuf->rect_float && ibuf->rect) {
float *pixf= ibuf->rect_float;
float col[3];
int stride= 4;
char *pixc= (char *)ibuf->rect;
pix_in= ibuf->rect_float;
pix_out= tmpbuf->rect_float;
// pixc= (char *)ibuf->rect;
if(ibuf->channels)
stride= ibuf->channels;
if(ibuf->channels)
stride= ibuf->channels;
for(pixel= ibuf->x*ibuf->y; pixel>0; pixel--, pixf+=stride, pixc+=4) {
if(stride<3) {
col[0]= curvemap_evaluateF(cumap->cm, *pixf);
pixc[1]= pixc[2]= pixc[3]= pixc[0]= FTOCHAR(col[0]);
}
else {
curvemapping_evaluate_premulRGBF(cumap, col, pixf);
pixc[0]= FTOCHAR(col[0]);
pixc[1]= FTOCHAR(col[1]);
pixc[2]= FTOCHAR(col[2]);
if(stride>3)
pixc[3]= FTOCHAR(pixf[3]);
else
pixc[3]= 255;
}
for(pixel= ibuf->x*ibuf->y; pixel>0; pixel--, pix_in+=stride, pix_out+=4) {
if(stride<3) {
col[0]= curvemap_evaluateF(cumap->cm, *pix_in);
pix_out[1]= pix_out[2]= pix_out[3]= pix_out[0]= col[0];
}
else {
curvemapping_evaluate_premulRGBF(cumap, col, pix_in);
pix_out[0]= col[0];
pix_out[1]= col[1];
pix_out[2]= col[2];
if(stride>3)
pix_out[3]= pix_in[3];
else
pix_out[3]= 1.f;
}
}
IMB_rect_from_float(tmpbuf);
SWAP(char *, tmpbuf->rect, ibuf->rect);
IMB_freeImBuf(tmpbuf);
curvemapping_premultiply(cumap, 1);
}

@ -725,6 +725,17 @@ void BKE_image_print_memlist(void)
}
}
/* frees all ibufs used by any image datablocks */
void BKE_image_free_image_ibufs(void)
{
Image *ima;
for(ima= G.main->image.first; ima; ima= ima->id.next) {
image_free_buffers(ima);
}
}
void BKE_image_free_all_textures(void)
{
Tex *tex;
@ -1898,6 +1909,7 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser)
ibuf->rect_float= rpass->rect;
ibuf->flags |= IB_rectfloat;
ibuf->channels= rpass->channels;
ibuf->profile = IB_PROFILE_LINEAR_RGB;
image_assign_ibuf(ima, ibuf, iuser?iuser->multi_index:IMA_NO_INDEX, 0);
}

@ -1694,7 +1694,19 @@ static void input_preprocess(Scene *scene, Sequence *seq, TStripElem *se, int cf
if(seq->flag & SEQ_MAKE_FLOAT) {
if (!se->ibuf->rect_float) {
IMB_float_from_rect(se->ibuf);
if (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) {
IMB_float_from_rect(se->ibuf);
} else {
int profile = IB_PROFILE_NONE;
/* no color management:
* don't disturb the existing profiles */
SWAP(int, se->ibuf->profile, profile);
IMB_float_from_rect(se->ibuf);
SWAP(int, se->ibuf->profile, profile);
}
}
if (se->ibuf->rect) {
imb_freerectImBuf(se->ibuf);

@ -4081,10 +4081,17 @@ static int imapaint_canvas_set(ImagePaintState *s, Image *ima)
s->clonecanvas= ibuf;
/* temporarily add float rect for cloning */
if(s->canvas->rect_float && !s->clonecanvas->rect_float) {
/* temporarily add float rect for cloning */
int profile = IB_PROFILE_NONE;
/* Don't want to color manage, but don't disturb existing profiles */
SWAP(int, s->clonecanvas->profile, profile);
IMB_float_from_rect(s->clonecanvas);
s->clonefreefloat= 1;
SWAP(int, s->clonecanvas->profile, profile);
}
else if(!s->canvas->rect_float && !s->clonecanvas->rect)
IMB_rect_from_float(s->clonecanvas);

@ -126,7 +126,7 @@ static void image_verify_buffer_float(SpaceImage *sima, Image *ima, ImBuf *ibuf,
else {
if (color_manage) {
if (ima && ima->source == IMA_SRC_VIEWER)
ibuf->profile = IB_PROFILE_SRGB;
ibuf->profile = IB_PROFILE_LINEAR_RGB;
} else {
ibuf->profile = IB_PROFILE_NONE;
}

@ -1269,7 +1269,7 @@ void draw_nodespace_back_pix(ARegion *ar, SpaceNode *snode, int color_manage)
if(!ibuf->rect) {
if(color_manage)
ibuf->profile= IB_PROFILE_SRGB;
ibuf->profile = IB_PROFILE_LINEAR_RGB;
else
ibuf->profile = IB_PROFILE_NONE;
IMB_rect_from_float(ibuf);

@ -699,8 +699,14 @@ void draw_image_seq(Scene *scene, ARegion *ar, SpaceSeq *sseq)
break;
}
if(ibuf->rect_float && ibuf->rect==NULL)
if(ibuf->rect_float && ibuf->rect==NULL) {
if (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) {
ibuf->profile = IB_PROFILE_LINEAR_RGB;
} else {
ibuf->profile = IB_PROFILE_NONE;
}
IMB_rect_from_float(ibuf);
}
/* needed for gla draw */
glaDefine2DArea(&ar->winrct);

@ -452,6 +452,7 @@ struct ImBuf *imb_loadamiga(int *iffmem,int flags)
if (ibuf == 0) return (0);
ibuf->ftype = (ftype | AMI);
ibuf->profile = IB_PROFILE_SRGB;
if (cmap){
ibuf->mincol = 0;

@ -175,6 +175,7 @@ struct ImBuf *imb_bmp_decode(unsigned char *mem, int size, int flags)
if (ibuf) {
ibuf->ftype = BMP;
ibuf->profile = IB_PROFILE_SRGB;
}
return(ibuf);

@ -101,6 +101,7 @@ struct ImBuf *imb_load_dds(unsigned char *mem, int size, int flags)
if (ibuf == 0) return(0); /* memory allocation failed */
ibuf->ftype = DDS;
ibuf->profile = IB_PROFILE_SRGB;
if ((flags & IB_test) == 0) {
if (!imb_addrectImBuf(ibuf)) return(ibuf);

@ -175,12 +175,14 @@ void IMB_gamwarp(struct ImBuf *ibuf, double gamma)
}
/* assume converting from linear float to sRGB byte */
void IMB_rect_from_float(struct ImBuf *ibuf)
{
/* quick method to convert floatbuf to byte */
float *tof = (float *)ibuf->rect_float;
float dither= ibuf->dither;
float srgb[3];
int do_dither = ibuf->dither != 0.f;
float dither= ibuf->dither / 255.0;
float srgb[4];
int i, channels= ibuf->channels;
short profile= ibuf->profile;
unsigned char *to = (unsigned char *) ibuf->rect;
@ -195,7 +197,7 @@ void IMB_rect_from_float(struct ImBuf *ibuf)
for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof++)
to[1]= to[2]= to[3]= to[0] = FTOCHAR(tof[0]);
}
else if (profile == IB_PROFILE_SRGB) {
else if (profile == IB_PROFILE_LINEAR_RGB) {
if(channels == 3) {
for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=3) {
srgb[0]= linearrgb_to_srgb(tof[0]);
@ -209,10 +211,26 @@ void IMB_rect_from_float(struct ImBuf *ibuf)
}
}
else if (channels == 4) {
floatbuf_to_srgb_byte(tof, to, 0, ibuf->x, 0, ibuf->y, ibuf->x);
if (dither != 0.f) {
for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=4) {
const float d = (BLI_frand()-0.5)*dither;
srgb[0]= d + linearrgb_to_srgb(tof[0]);
srgb[1]= d + linearrgb_to_srgb(tof[1]);
srgb[2]= d + linearrgb_to_srgb(tof[2]);
srgb[3]= d + tof[3];
to[0] = FTOCHAR(srgb[0]);
to[1] = FTOCHAR(srgb[1]);
to[2] = FTOCHAR(srgb[2]);
to[3] = FTOCHAR(srgb[3]);
}
} else {
floatbuf_to_srgb_byte(tof, to, 0, ibuf->x, 0, ibuf->y, ibuf->x);
}
}
}
else if(ELEM(profile, IB_PROFILE_NONE, IB_PROFILE_LINEAR_RGB) && dither==0.0f) {
else if(ELEM(profile, IB_PROFILE_NONE, IB_PROFILE_SRGB)) {
if(channels==3) {
for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=3) {
to[0] = FTOCHAR(tof[0]);
@ -222,32 +240,26 @@ void IMB_rect_from_float(struct ImBuf *ibuf)
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=4) {
to[0] = FTOCHAR(tof[0]);
to[1] = FTOCHAR(tof[1]);
to[2] = FTOCHAR(tof[2]);
to[3] = FTOCHAR(tof[3]);
if (dither != 0.f) {
float col[3];
for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=4) {
const float d = (BLI_frand()-0.5)*dither;
const float col[4] = {d+tof[0], d+tof[1], d+tof[2], d+tof[3]};
to[0] = FTOCHAR(col[0]);
to[1] = FTOCHAR(col[1]);
to[2] = FTOCHAR(col[2]);
to[3] = FTOCHAR(col[3]);
}
} else {
for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=4) {
to[0] = FTOCHAR(tof[0]);
to[1] = FTOCHAR(tof[1]);
to[2] = FTOCHAR(tof[2]);
to[3] = FTOCHAR(tof[3]);
}
}
}
}
else {
float dither_value, col;
dither= dither/255.0f;
for (i = ibuf->x * ibuf->y; i > 0; i--) {
dither_value = (BLI_frand()-0.5)*dither;
col= tof[0] + dither_value;
to[0] = FTOCHAR(col);
col= tof[1] + dither_value;
to[1] = FTOCHAR(col);
col= tof[2] + dither_value;
to[2] = FTOCHAR(col);
col= tof[3] + dither_value;
to[3] = FTOCHAR(col);
to += 4;
tof += 4;
}
}
}
void IMB_float_from_rect(struct ImBuf *ibuf)
@ -263,8 +275,11 @@ void IMB_float_from_rect(struct ImBuf *ibuf)
tof = ibuf->rect_float;
}
if (ibuf->profile == IB_PROFILE_SRGB) {
/* convert from srgb to linear rgb */
/* Float bufs should be stored linear */
if (ibuf->profile != IB_PROFILE_NONE) {
/* if the image has been given a profile then we're working
* with color management in mind, so convert it to linear space */
for (i = ibuf->x * ibuf->y; i > 0; i--)
{

@ -387,6 +387,7 @@ struct ImBuf *imb_loadanim(int *iffmem, int flags)
if (ibuf==0) return (0);
ibuf->ftype = (Anim | adat.type);
ibuf->profile = IB_PROFILE_SRGB;
ibuf->xorig = adat.xorig;
ibuf->yorig = adat.yorig;
ibuf->flags = flags;

@ -494,6 +494,7 @@ struct ImBuf *imb_loadiris(unsigned char *mem, int flags)
}
ibuf->ftype = IMAGIC;
ibuf->profile = IB_PROFILE_SRGB;
if (flags & IB_ttob) IMB_flipy(ibuf);
test_endian_zbuf(ibuf);

@ -416,6 +416,7 @@ next_stamp_marker:
jpeg_destroy((j_common_ptr) cinfo);
ibuf->ftype = ibuf_ftype;
ibuf->profile = IB_PROFILE_SRGB;
}
return(ibuf);

@ -934,6 +934,9 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, int size, int flags)
ibuf = IMB_allocImBuf(width, height, 32, 0, 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 */

@ -368,6 +368,7 @@ struct ImBuf *imb_loadpng(unsigned char *mem, int size, int flags)
if (ibuf) {
ibuf->ftype = PNG;
ibuf->profile = IB_PROFILE_SRGB;
} else {
printf("Couldn't allocate memory for PNG image\n");
}

@ -204,6 +204,7 @@ struct ImBuf *imb_loadhdr(unsigned char *mem, int size, int flags)
if (ibuf==NULL) return NULL;
ibuf->ftype = RADHDR;
ibuf->profile = IB_PROFILE_LINEAR_RGB;
ibuf->xorig = ibuf->yorig = 0;
if (flags & IB_test) return ibuf;

@ -161,6 +161,7 @@ ImBuf *IMB_ibImageFromMemory(int *mem, int size, int flags) {
ibuf = imb_cocoaLoadImage((uchar *)mem, size, flags);
if(ibuf) {
ibuf->ftype = TIF;
ibuf->profile = IB_PROFILE_SRGB;
return ibuf;
}
#else

@ -569,6 +569,7 @@ struct ImBuf *imb_loadtarga(unsigned char *mem, int mem_size, int flags)
if (ibuf == 0) return(0);
ibuf->ftype = TGA;
ibuf->profile = IB_PROFILE_SRGB;
ibuf->xorig = tga.xorig;
ibuf->yorig = tga.yorig;
mem = mem + 18 + tga.numid;

@ -347,6 +347,7 @@ struct ImBuf *imb_loadtiff(unsigned char *mem, int size, int flags)
ibuf = IMB_allocImBuf(width, height, 8*bytesperpixel, 0, 0);
if (ibuf) {
ibuf->ftype = TIF;
ibuf->profile = IB_PROFILE_SRGB;
} else {
fprintf(stderr,
"imb_loadtiff: could not allocate memory for TIFF " \

@ -96,6 +96,7 @@ EnumPropertyItem snap_element_items[] = {
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_pointcache.h"
@ -517,6 +518,12 @@ static int rna_SceneRenderData_engine_get(PointerRNA *ptr)
return 0;
}
static void rna_SceneRenderData_color_management_update(Main *bmain, Scene *unused, PointerRNA *ptr)
{
/* reset all generated image block buffers to prevent out-of-date conversions */
BKE_image_free_image_ibufs();
}
static void rna_SceneRenderLayer_name_set(PointerRNA *ptr, const char *value)
{
Scene *scene= (Scene*)ptr->id.data;
@ -2114,7 +2121,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
prop= RNA_def_property(srna, "color_management", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "color_mgt_flag", R_COLOR_MANAGEMENT);
RNA_def_property_ui_text(prop, "Color Management", "Use color profiles and gamma corrected imaging pipeline");
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS|NC_MATERIAL|ND_SHADING, NULL);
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS|NC_MATERIAL|ND_SHADING, "rna_SceneRenderData_color_management_update");
prop= RNA_def_property(srna, "use_file_extension", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "scemode", R_EXTENSION);

@ -65,26 +65,18 @@ static CompBuf *node_composit_get_image(RenderData *rd, Image *ima, ImageUser *i
if(ibuf==NULL)
return NULL;
if (rd->color_mgt_flag & R_COLOR_MANAGEMENT) {
if (ibuf->profile == IB_PROFILE_NONE) {
/* if float buffer already exists = already linear */
/* else ... */
if (ibuf->rect_float == NULL) {
imb_freerectfloatImBuf(ibuf);
ibuf->profile = IB_PROFILE_SRGB;
IMB_float_from_rect(ibuf);
} else {
ibuf->profile = IB_PROFILE_LINEAR_RGB;
}
}
} else {
if (ibuf->profile == IB_PROFILE_SRGB) {
if (ibuf->rect_float != NULL) {
imb_freerectfloatImBuf(ibuf);
}
ibuf->profile = IB_PROFILE_NONE;
IMB_float_from_rect(ibuf);
if (!(rd->color_mgt_flag & R_COLOR_MANAGEMENT)) {
int profile = IB_PROFILE_NONE;
/* temporarily set profile to none to not disturb actual */
SWAP(int, ibuf->profile, profile);
if (ibuf->rect_float != NULL) {
imb_freerectfloatImBuf(ibuf);
}
IMB_float_from_rect(ibuf);
SWAP(int, ibuf->profile, profile);
}
if (ibuf->rect_float == NULL) {