Colour Management

- 1st stage: Linear Workflow

This implements automatic linear workflow in Blender's renderer. With the 
new Colour Management option on in the Render buttons, all inputs to the 
renderer and compositor are converted to linear colour space before 
rendering, and gamma corrected afterwards. In essence, this makes all 
manual gamma correction with nodes, etc unnecessary, since it's done 
automatically through the pipeline.

It's all explained much better in the notes/doc here, so please have a look:
http://wiki.blender.org/index.php/Dev:Source/Blender/Architecture/Colour_Management

And an example of the sort of difference it makes:
http://mke3.net/blender/devel/rendering/b25_colormanagement_test01.jpg

This also enables Colour Management in the default B.blend, and changes the 
default lamp falloff to inverse square, which is more correct, and much 
easier to use now it's all gamma corrected properly.

Next step is to look into profiles/soft proofing for the compositor.

Thanks to brecht for reviewing and fixing some oversights!
This commit is contained in:
Matt Ebb 2009-07-17 02:43:15 +00:00
parent 70f6255433
commit 1ef7293585
31 changed files with 21352 additions and 20500 deletions

Binary file not shown.

@ -118,6 +118,7 @@ class RENDER_PT_shading(RenderButtonsPanel):
col = split.column()
col.itemR(rd, "render_envmaps", text="Environment Map")
col.itemR(rd, "render_raytracing", text="Ray Tracing")
col.itemR(rd, "color_management")
col.itemR(rd, "alpha_mode", text="Alpha")
class RENDER_PT_performance(RenderButtonsPanel):

@ -33,7 +33,16 @@ struct CurveMapping;
struct CurveMap;
struct ImBuf;
struct rctf;
void gamma_correct_rec709(float *c, float gamma);
void gamma_correct(float *c, float gamma);
float srgb_to_linearrgb(float c);
float linearrgb_to_srgb(float c);
void color_manage_linearize(float *col_to, float *col_from);
void floatbuf_to_srgb_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int w);
void floatbuf_to_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int w);
struct CurveMapping *curvemapping_add(int tot, float minx, float miny, float maxx, float maxy);
void curvemapping_free(struct CurveMapping *cumap);
struct CurveMapping *curvemapping_copy(struct CurveMapping *cumap);
@ -60,5 +69,6 @@ void curvemapping_initialize(struct CurveMapping *cumap);
void curvemapping_table_RGBA(struct CurveMapping *cumap, float **array, int *size);
void colorcorrection_do_ibuf(struct ImBuf *ibuf, const char *profile);
#endif

@ -58,6 +58,91 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
/* ********************************* color transforms ********************************* */
/*Transform linear RGB values to nonlinear RGB values. Rec.
709 is ITU-R Recommendation BT. 709 (1990) ``Basic
Parameter Values for the HDTV Standard for the Studio and
for International Programme Exchange'', formerly CCIR Rec.
709.*/
void gamma_correct_rec709(float *c, float gamma)
{
/* Rec. 709 gamma correction. */
float cc = 0.018f;
if (*c < cc)
*c *= ((1.099f * (float)pow(cc, gamma)) - 0.099f) / cc;
else
*c = (1.099f * (float)pow(*c, gamma)) - 0.099f;
}
void gamma_correct(float *c, float gamma)
{
*c = pow((*c), gamma);
}
float srgb_to_linearrgb(float c)
{
if (c < 0.04045f)
return (c < 0.f)?0.f:c / 12.92;
else
return pow((c + 0.055)/1.055, 2.4);
}
float linearrgb_to_srgb(float c)
{
if (c < 0.0031308)
return (c < 0.f)?0.f:c * 12.92;
else
return 1.055 * pow(c, 1.0/2.4) - 0.055;
}
/* utility function convert an RGB triplet from sRGB to linear RGB color space */
void color_manage_linearize(float *col_to, float *col_from)
{
col_to[0] = srgb_to_linearrgb(col_from[0]);
col_to[1] = srgb_to_linearrgb(col_from[1]);
col_to[2] = srgb_to_linearrgb(col_from[2]);
}
void floatbuf_to_srgb_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int w)
{
int x, y;
float *rf= rectf;
float srgb[3];
unsigned char *rc= rectc;
for(y=y1; y<y2; y++) {
for(x=x1; x<x2; x++, rf+=4, rc+=4) {
srgb[0]= linearrgb_to_srgb(rf[0]);
srgb[1]= linearrgb_to_srgb(rf[1]);
srgb[2]= linearrgb_to_srgb(rf[2]);
rc[0]= FTOCHAR(srgb[0]);
rc[1]= FTOCHAR(srgb[1]);
rc[2]= FTOCHAR(srgb[2]);
rc[3]= FTOCHAR(rf[3]);
}
}
}
void floatbuf_to_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int w)
{
int x, y;
float *rf= rectf;
unsigned char *rc= rectc;
for(y=y1; y<y2; y++) {
for(x=x1; x<x2; x++, rf+=4, rc+=4) {
rc[0]= FTOCHAR(rf[0]);
rc[1]= FTOCHAR(rf[1]);
rc[2]= FTOCHAR(rf[2]);
rc[3]= FTOCHAR(rf[3]);
}
}
}
/* ********************************* color curve ********************* */
/* ***************** operations on full struct ************* */

@ -715,7 +715,7 @@ void *add_lamp(char *name)
la->r= la->g= la->b= la->k= 1.0f;
la->haint= la->energy= 1.0f;
la->dist= 20.0f;
la->dist= 25.0f;
la->spotsize= 45.0f;
la->spotblend= 0.15f;
la->att2= 1.0f;
@ -734,7 +734,7 @@ void *add_lamp(char *name)
la->ray_samp_method = LA_SAMP_HALTON;
la->adapt_thresh = 0.001f;
la->preview=NULL;
la->falloff_type = LA_FALLOFF_INVLINEAR;
la->falloff_type = LA_FALLOFF_INVSQUARE;
la->curfalloff = curvemapping_add(1, 0.0f, 1.0f, 1.0f, 0.0f);
la->sun_effect_type = 0;
la->horizon_brightness = 1.0;

@ -223,6 +223,7 @@ Scene *add_scene(char *name)
sce->r.frs_sec= 25;
sce->r.frs_sec_base= 1;
sce->r.ocres = 128;
sce->r.color_mgt_flag |= R_COLOR_MANAGEMENT;
sce->r.bake_mode= 1; /* prevent to include render stuff here */
sce->r.bake_filter= 8;

@ -339,7 +339,6 @@ void rgb_to_ycc(float r, float g, float b, float *ly, float *lcb, float *lcr);
void rgb_to_hsv(float r, float g, float b, float *lh, float *ls, float *lv);
void xyz_to_rgb(float x, float y, float z, float *r, float *g, float *b, int colorspace);
int constrain_rgb(float *r, float *g, float *b);
void gamma_correct_rgb(float *r, float *g, float *b);
unsigned int hsv_to_cpack(float h, float s, float v);
unsigned int rgb_to_cpack(float r, float g, float b);
void cpack_to_rgb(unsigned int col, float *r, float *g, float *b);

@ -3542,33 +3542,10 @@ int constrain_rgb(float *r, float *g, float *b)
if (w > 0) {
*r += w; *g += w; *b += w;
return 1; /* Colour modified to fit RGB gamut */
return 1; /* Color modified to fit RGB gamut */
}
return 0; /* Colour within RGB gamut */
}
/*Transform linear RGB values to nonlinear RGB values. Rec.
709 is ITU-R Recommendation BT. 709 (1990) ``Basic
Parameter Values for the HDTV Standard for the Studio and
for International Programme Exchange'', formerly CCIR Rec.
709.*/
static void gamma_correct(float *c)
{
/* Rec. 709 gamma correction. */
float cc = 0.018f;
if (*c < cc)
*c *= ((1.099f * (float)pow(cc, 0.45)) - 0.099f) / cc;
else
*c = (1.099f * (float)pow(*c, 0.45)) - 0.099f;
}
void gamma_correct_rgb(float *r, float *g, float *b)
{
gamma_correct(r);
gamma_correct(g);
gamma_correct(b);
return 0; /* Color within RGB gamut */
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -132,10 +132,11 @@ void glaDrawPixelsSafe (float x, float y, int img_w, int img_h, int row_w, int
* is expected to be in RGBA byte or float format, and the
* modelview and projection matrices are assumed to define a
* 1-to-1 mapping to screen space.
* @param gamma_correct Optionally gamma correct float sources to sRGB for display
*/
/* only for float rects, converts to 32 bits and draws */
void glaDrawPixelsSafe_to32(float fx, float fy, int img_w, int img_h, int row_w, float *rectf);
void glaDrawPixelsSafe_to32(float fx, float fy, int img_w, int img_h, int row_w, float *rectf, int gamma_correct);
void glaDrawPixelsTex (float x, float y, int img_w, int img_h, int format, void *rect);

@ -293,6 +293,7 @@ static Scene *preview_prepare_scene(Scene *scene, int id_type, ShaderPreview *sp
sce->world->range= scene->world->range;
}
sce->r.color_mgt_flag = scene->r.color_mgt_flag;
sce->r.cfra= scene->r.cfra;
if(id_type==ID_MA) {
@ -353,6 +354,9 @@ static Scene *preview_prepare_scene(Scene *scene, int id_type, ShaderPreview *sp
sce->lay= 1<<MA_TEXTURE;
/* exception: don't color manage texture previews */
sce->r.color_mgt_flag &= ~R_COLOR_MANAGEMENT;
for(base= sce->base.first; base; base= base->next) {
if(base->object->id.name[2]=='t') {
Material *mat= give_current_material(base->object, base->object->actcol);
@ -404,21 +408,27 @@ static Scene *preview_prepare_scene(Scene *scene, int id_type, ShaderPreview *sp
return NULL;
}
/* new UI convention: draw is in pixel space already. */
/* uses ROUNDBOX button in block to get the rect */
void ED_preview_draw(const bContext *C, void *idp, rcti *rect)
{
if(idp) {
ScrArea *sa= CTX_wm_area(C);
Scene *sce = CTX_data_scene(C);
ID *id = (ID *)idp;
SpaceButs *sbuts= sa->spacedata.first;
RenderResult rres;
int newx= rect->xmax-rect->xmin, newy= rect->ymax-rect->ymin;
int ok= 0;
char name[32];
int gamma_correct=0;
if (id && GS(id->name) != ID_TE) {
/* exception: don't color manage texture previews - show the raw values */
if (sce) gamma_correct = sce->r.color_mgt_flag & R_COLOR_MANAGEMENT;
}
sprintf(name, "Preview %p", sa);
BLI_lock_malloc_thread();
RE_GetResultImage(RE_GetRender(name), &rres);
if(rres.rectf) {
@ -428,11 +438,10 @@ void ED_preview_draw(const bContext *C, void *idp, rcti *rect)
rect->xmax= rect->xmin + rres.rectx;
rect->ymax= rect->ymin + rres.recty;
glaDrawPixelsSafe(rect->xmin, rect->ymin, rres.rectx, rres.recty, rres.rectx, GL_RGBA, GL_FLOAT, rres.rectf);
glaDrawPixelsSafe_to32(rect->xmin, rect->ymin, rres.rectx, rres.recty, rres.rectx, rres.rectf, gamma_correct);
ok= 1;
}
}
BLI_unlock_malloc_thread();
/* check for spacetype... */
if(sbuts->spacetype==SPACE_BUTS && sbuts->preview) {
@ -468,7 +477,7 @@ void view3d_previewrender_progress(RenderResult *rr, volatile rcti *renrect)
glDrawBuffer(GL_FRONT);
// glaDefine2DArea(&sa->winrct);
glaDrawPixelsSafe_to32(ofsx, ofsy, rr->rectx, rr->recty, rr->rectx, rl->rectf);
glaDrawPixelsSafe_to32(ofsx, ofsy, rr->rectx, rr->recty, rr->rectx, rl->rectf, 0);
bglFlush();
glDrawBuffer(GL_BACK);

@ -35,6 +35,7 @@
#include "DNA_listBase.h"
#include "BKE_utildefines.h"
#include "BKE_colortools.h"
#include "BLI_arithb.h"
#include "BLI_threads.h"
@ -482,27 +483,20 @@ void glaDrawPixelsTex(float x, float y, int img_w, int img_h, int format, void *
glaDrawPixelsTexScaled(x, y, img_w, img_h, format, rect, 1.0f, 1.0f);
}
void glaDrawPixelsSafe_to32(float fx, float fy, int img_w, int img_h, int row_w, float *rectf)
void glaDrawPixelsSafe_to32(float fx, float fy, int img_w, int img_h, int row_w, float *rectf, int gamma_correct)
{
float *rf;
int x, y;
char *rect32, *rc;
unsigned char *rect32;
/* copy imgw-imgh to a temporal 32 bits rect */
if(img_w<1 || img_h<1) return;
rc= rect32= MEM_mallocN(img_w*img_h*sizeof(int), "temp 32 bits");
rect32= MEM_mallocN(img_w*img_h*sizeof(int), "temp 32 bits");
for(y=0; y<img_h; y++) {
rf= rectf;
for(x=0; x<img_w; x++, rf+=4, rc+=4) {
rc[0]= FTOCHAR(rf[0]);
rc[1]= FTOCHAR(rf[1]);
rc[2]= FTOCHAR(rf[2]);
rc[3]= FTOCHAR(rf[3]);
}
rectf+= 4*row_w;
}
if (gamma_correct) {
floatbuf_to_srgb_byte(rectf, rect32, 0, img_w, 0, img_h, img_w);
} else {
floatbuf_to_byte(rectf, rect32, 0, img_w, 0, img_h, img_w);
}
glaDrawPixelsSafe(fx, fy, img_w, img_h, img_w, GL_RGBA, GL_UNSIGNED_BYTE, rect32);
@ -855,4 +849,3 @@ void bglFlush(void)
#endif
}

@ -41,6 +41,7 @@
#include "DNA_scene_types.h"
#include "BKE_blender.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_global.h"
@ -2575,21 +2576,46 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec
rectf+= 4*(rr->rectx*ymin + xmin);
rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
for(y1= 0; y1<ymax; y1++) {
float *rf= rectf;
char *rc= rectc;
/* XXX temp. because crop offset */
if( rectc >= (char *)(ibuf->rect)) {
for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
rc[0]= FTOCHAR(rf[0]);
rc[1]= FTOCHAR(rf[1]);
rc[2]= FTOCHAR(rf[2]);
rc[3]= FTOCHAR(rf[3]);
/* XXX make nice consistent functions for this */
if (rj->scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) {
for(y1= 0; y1<ymax; y1++) {
float *rf= rectf;
float srgb[3];
char *rc= rectc;
/* XXX temp. because crop offset */
if( rectc >= (char *)(ibuf->rect)) {
for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
srgb[0]= linearrgb_to_srgb(rf[0]);
srgb[1]= linearrgb_to_srgb(rf[1]);
srgb[2]= linearrgb_to_srgb(rf[2]);
rc[0]= FTOCHAR(srgb[0]);
rc[1]= FTOCHAR(srgb[1]);
rc[2]= FTOCHAR(srgb[2]);
rc[3]= FTOCHAR(rf[3]);
}
}
rectf += 4*rr->rectx;
rectc += 4*ibuf->x;
}
} else {
for(y1= 0; y1<ymax; y1++) {
float *rf= rectf;
char *rc= rectc;
/* XXX temp. because crop offset */
if( rectc >= (char *)(ibuf->rect)) {
for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
rc[0]= FTOCHAR(rf[0]);
rc[1]= FTOCHAR(rf[1]);
rc[2]= FTOCHAR(rf[2]);
rc[3]= FTOCHAR(rf[3]);
}
}
rectf += 4*rr->rectx;
rectc += 4*ibuf->x;
}
rectf += 4*rr->rectx;
rectc += 4*ibuf->x;
}
/* make jobs timer to send notifier */

@ -106,7 +106,7 @@ static int image_curves_active(SpaceImage *sima)
return 0;
}
static void image_verify_buffer_float(SpaceImage *sima, ImBuf *ibuf)
static void image_verify_buffer_float(SpaceImage *sima, Image *ima, ImBuf *ibuf, int color_manage)
{
/* detect if we need to redo the curve map.
ibuf->rect is zero for compositor and render results after change
@ -121,6 +121,12 @@ static void image_verify_buffer_float(SpaceImage *sima, ImBuf *ibuf)
curvemapping_do_ibuf(sima->cumap, ibuf);
}
else {
if (color_manage) {
if (ima && ima->source == IMA_SRC_VIEWER)
ibuf->profile = IB_PROFILE_SRGB;
} else {
ibuf->profile = IB_PROFILE_NONE;
}
IMB_rect_from_float(ibuf);
}
}
@ -371,9 +377,10 @@ static void sima_draw_zbuffloat_pixels(Scene *scene, float x1, float y1, int rec
MEM_freeN(rectf);
}
static void draw_image_buffer(SpaceImage *sima, ARegion *ar, Scene *scene, ImBuf *ibuf, float fx, float fy, float zoomx, float zoomy)
static void draw_image_buffer(SpaceImage *sima, ARegion *ar, Scene *scene, Image *ima, ImBuf *ibuf, float fx, float fy, float zoomx, float zoomy)
{
int x, y;
int color_manage = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT;
/* set zoom */
glPixelZoom(zoomx, zoomy);
@ -398,7 +405,7 @@ static void draw_image_buffer(SpaceImage *sima, ARegion *ar, Scene *scene, ImBuf
}
#ifdef WITH_LCMS
else if(sima->flag & SI_COLOR_CORRECTION) {
image_verify_buffer_float(sima, ibuf);
image_verify_buffer_float(sima, ima, ibuf, color_manage);
sima_draw_colorcorrected_pixels(x, y, ibuf);
@ -414,7 +421,7 @@ static void draw_image_buffer(SpaceImage *sima, ARegion *ar, Scene *scene, ImBuf
/* we don't draw floats buffers directly but
* convert them, and optionally apply curves */
image_verify_buffer_float(sima, ibuf);
image_verify_buffer_float(sima, ima, ibuf, color_manage);
if(ibuf->rect)
glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
@ -451,10 +458,11 @@ static unsigned int *get_part_from_ibuf(ImBuf *ibuf, short startx, short starty,
return rectmain;
}
static void draw_image_buffer_tiled(SpaceImage *sima, ARegion *ar, Image *ima, ImBuf *ibuf, float fx, float fy, float zoomx, float zoomy)
static void draw_image_buffer_tiled(SpaceImage *sima, ARegion *ar, Scene *scene, Image *ima, ImBuf *ibuf, float fx, float fy, float zoomx, float zoomy)
{
unsigned int *rect;
int dx, dy, sx, sy, x, y;
int color_manage = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT;
/* verify valid values, just leave this a while */
if(ima->xrep<1) return;
@ -466,7 +474,7 @@ static void draw_image_buffer_tiled(SpaceImage *sima, ARegion *ar, Image *ima, I
sima->curtile = ima->xrep*ima->yrep - 1;
/* create char buffer from float if needed */
image_verify_buffer_float(sima, ibuf);
image_verify_buffer_float(sima, ima, ibuf, color_manage);
/* retrieve part of image buffer */
dx= ibuf->x/ima->xrep;
@ -499,9 +507,9 @@ static void draw_image_buffer_repeated(SpaceImage *sima, ARegion *ar, Scene *sce
for(x=floor(ar->v2d.cur.xmin); x<ar->v2d.cur.xmax; x += 1.0f) {
for(y=floor(ar->v2d.cur.ymin); y<ar->v2d.cur.ymax; y += 1.0f) {
if(ima && (ima->tpageflag & IMA_TILES))
draw_image_buffer_tiled(sima, ar, ima, ibuf, x, y, zoomx, zoomy);
draw_image_buffer_tiled(sima, ar, scene, ima, ibuf, x, y, zoomx, zoomy);
else
draw_image_buffer(sima, ar, scene, ibuf, x, y, zoomx, zoomy);
draw_image_buffer(sima, ar, scene, ima, ibuf, x, y, zoomx, zoomy);
/* only draw until running out of time */
if((PIL_check_seconds_timer() - time_current) > 0.25)
@ -673,9 +681,9 @@ void draw_image_main(SpaceImage *sima, ARegion *ar, Scene *scene)
else if(sima->flag & SI_DRAW_TILE)
draw_image_buffer_repeated(sima, ar, scene, ima, ibuf, zoomx, zoomy);
else if(ima && (ima->tpageflag & IMA_TILES))
draw_image_buffer_tiled(sima, ar, ima, ibuf, 0.0f, 0.0, zoomx, zoomy);
draw_image_buffer_tiled(sima, ar, scene, ima, ibuf, 0.0f, 0.0, zoomx, zoomy);
else
draw_image_buffer(sima, ar, scene, ibuf, 0.0f, 0.0f, zoomx, zoomy);
draw_image_buffer(sima, ar, scene, ima, ibuf, 0.0f, 0.0f, zoomx, zoomy);
/* grease pencil */
draw_image_grease_pencil(sima, ibuf);

@ -119,7 +119,7 @@ void imagewindow_progress(SpaceImage *sima, RenderResult *rr, volatile rcti *ren
if(rect32)
glaDrawPixelsSafe(x1, y1, xmax, ymax, rr->rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect32);
else
glaDrawPixelsSafe_to32(x1, y1, xmax, ymax, rr->rectx, rectf);
glaDrawPixelsSafe_to32(x1, y1, xmax, ymax, rr->rectx, rectf, 0);
glPixelZoom(1.0, 1.0);

@ -98,9 +98,13 @@ typedef struct ImBuf {
unsigned int encodedsize; /**< Size of data written to encodedbuffer */
unsigned int encodedbuffersize; /**< Size of encodedbuffer */
float *rect_float; /**< floating point Rect equivalent */
float *rect_float; /**< floating point Rect equivalent
Linear RGB color space - may need gamma correction to
sRGB when generating 8bit representations */
int channels; /**< amount of channels in rect_float (0 = 4 channel default) */
float dither; /**< random dither value, for conversion from float -> byte rect */
short profile; /** color space/profile preset that the byte rect buffer represents */
char profile_filename[256]; /** to be implemented properly, specific filename for custom profiles */
struct MEM_CacheLimiterHandle_s * c_handle; /**< handle for cache limiter */
struct ImgInfo * img_info;
@ -213,6 +217,18 @@ typedef enum {
#define AN_tanx (Anim | TANX)
/**@}*/
/**
* \name Imbuf preset profile tags
* \brief Some predefined color space profiles that 8 bit imbufs can represent
*/
/**@{*/
#define IB_PROFILE_NONE 0
#define IB_PROFILE_LINEAR_RGB 1
#define IB_PROFILE_SRGB 2
#define IB_PROFILE_CUSTOM 3
/**@}*/
/** \name Imbuf File Type Tests
* \brief These macros test if an ImBuf struct is the corresponding file type.
*/

@ -39,6 +39,7 @@
#include "IMB_allocimbuf.h"
#include "IMB_divers.h"
#include "BKE_utildefines.h"
#include "BKE_colortools.h"
void imb_checkncols(struct ImBuf *ibuf)
{
@ -176,9 +177,11 @@ void IMB_gamwarp(struct ImBuf *ibuf, double gamma)
void IMB_rect_from_float(struct ImBuf *ibuf)
{
/* quick method to convert floatbuf to byte */
float *tof = ibuf->rect_float;
float *tof = (float *)ibuf->rect_float;
float dither= ibuf->dither;
float srgb[3];
int i, channels= ibuf->channels;
short profile= ibuf->profile;
unsigned char *to = (unsigned char *) ibuf->rect;
if(tof==NULL) return;
@ -187,7 +190,24 @@ void IMB_rect_from_float(struct ImBuf *ibuf)
to = (unsigned char *) ibuf->rect;
}
if(dither==0.0f || channels!=4) {
if (profile == IB_PROFILE_SRGB && (channels == 3 || channels == 4)) {
if(channels == 3) {
for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=3) {
srgb[0]= linearrgb_to_srgb(tof[0]);
srgb[1]= linearrgb_to_srgb(tof[1]);
srgb[2]= linearrgb_to_srgb(tof[2]);
to[0] = FTOCHAR(srgb[0]);
to[1] = FTOCHAR(srgb[1]);
to[2] = FTOCHAR(srgb[2]);
to[3] = 255;
}
}
else if (channels == 4) {
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 || channels!=4)) {
if(channels==1) {
for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof++)
to[1]= to[2]= to[3]= to[0] = FTOCHAR(tof[0]);
@ -242,14 +262,28 @@ void IMB_float_from_rect(struct ImBuf *ibuf)
tof = ibuf->rect_float;
}
for (i = ibuf->x * ibuf->y; i > 0; i--)
{
tof[0] = ((float)to[0])*(1.0f/255.0f);
tof[1] = ((float)to[1])*(1.0f/255.0f);
tof[2] = ((float)to[2])*(1.0f/255.0f);
tof[3] = ((float)to[3])*(1.0f/255.0f);
to += 4;
tof += 4;
if (ibuf->profile == IB_PROFILE_SRGB) {
/* convert from srgb to linear rgb */
for (i = ibuf->x * ibuf->y; i > 0; i--)
{
tof[0] = srgb_to_linearrgb(((float)to[0])*(1.0f/255.0f));
tof[1] = srgb_to_linearrgb(((float)to[1])*(1.0f/255.0f));
tof[2] = srgb_to_linearrgb(((float)to[2])*(1.0f/255.0f));
tof[3] = ((float)to[3])*(1.0f/255.0f);
to += 4;
tof += 4;
}
} else {
for (i = ibuf->x * ibuf->y; i > 0; i--)
{
tof[0] = ((float)to[0])*(1.0f/255.0f);
tof[1] = ((float)to[1])*(1.0f/255.0f);
tof[2] = ((float)to[2])*(1.0f/255.0f);
tof[3] = ((float)to[3])*(1.0f/255.0f);
to += 4;
tof += 4;
}
}
}

@ -274,8 +274,12 @@ typedef struct RenderData {
* Value used to define filter size for all filter options */
float gauss;
/* color management settings - color profiles, gamma correction, etc */
int color_mgt_flag;
/** post-production settings. Depricated, but here for upwards compat (initialized to 1) */
float postmul, postgamma, posthue, postsat;
float postgamma, posthue, postsat;
/* Dither noise intensity */
float dither_intensity;
@ -722,6 +726,9 @@ typedef struct Scene {
#define R_PLANES32 32
#define R_PLANESBW 8
/* color_mgt_flag */
#define R_COLOR_MANAGEMENT 1
/* imtype */
#define R_TARGA 0
#define R_IRIS 1

@ -1113,6 +1113,11 @@ void rna_def_scene_render_data(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Sequencer", "Process the render (and composited) result through the video sequence editor pipeline, if sequencer strips exist.");
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
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);
prop= RNA_def_property(srna, "file_extensions", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "scemode", R_EXTENSION);
RNA_def_property_ui_text(prop, "File Extensions", "Add the file format extensions to the rendered file name (eg: filename + .jpg)");

@ -65,9 +65,28 @@ static CompBuf *node_composit_get_image(RenderData *rd, Image *ima, ImageUser *i
if(ibuf==NULL)
return NULL;
if(ibuf->rect_float==NULL)
IMB_float_from_rect(ibuf);
if (rd->color_mgt_flag & R_COLOR_MANAGEMENT) {
if (ibuf->profile == IB_PROFILE_NONE) {
if (ibuf->rect_float != NULL) {
imb_freerectfloatImBuf(ibuf);
}
ibuf->profile = IB_PROFILE_SRGB;
IMB_float_from_rect(ibuf);
}
} 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 (ibuf->rect_float == NULL) {
IMB_float_from_rect(ibuf);
}
type= ibuf->channels;
if(rd->scemode & R_COMP_CROP) {

@ -66,6 +66,7 @@ void shade_input_set_strand(struct ShadeInput *shi, struct StrandRen *strand, st
void shade_input_set_strand_texco(struct ShadeInput *shi, struct StrandRen *strand, struct StrandVert *svert, struct StrandPoint *spoint);
void shade_input_do_shade(struct ShadeInput *shi, struct ShadeResult *shr);
void shade_input_init_material(struct ShadeInput *shi);
void shade_input_initialize(struct ShadeInput *shi, struct RenderPart *pa, struct RenderLayer *rl, int sample);
void shade_sample_initialize(struct ShadeSample *ssamp, struct RenderPart *pa, struct RenderLayer *rl);

@ -4701,6 +4701,7 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view)
extern int slurph_opt; /* key.c */
Scene *sce;
float mat[4][4];
float amb[3];
unsigned int lay;
re->scene= scene;
@ -4747,7 +4748,9 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view)
/* still bad... doing all */
init_render_textures(re);
init_render_materials(re->r.mode, &re->wrld.ambr);
if (re->r.color_mgt_flag & R_COLOR_MANAGEMENT) color_manage_linearize(amb, &re->wrld.ambr);
else VECCOPY(amb, &re->wrld.ambr);
init_render_materials(re->r.mode, amb);
set_node_shader_lamp_loop(shade_material_loop);
/* MAKE RENDER DATA */
@ -5358,6 +5361,7 @@ void RE_Database_FromScene_Vectors(Render *re, Scene *sce)
void RE_Database_Baking(Render *re, Scene *scene, int type, Object *actob)
{
float mat[4][4];
float amb[3];
unsigned int lay;
int onlyselected, nolamps;
@ -5419,9 +5423,13 @@ void RE_Database_Baking(Render *re, Scene *scene, int type, Object *actob)
/* still bad... doing all */
init_render_textures(re);
init_render_materials(re->r.mode, &re->wrld.ambr);
if (re->r.color_mgt_flag & R_COLOR_MANAGEMENT) color_manage_linearize(amb, &re->wrld.ambr);
else VECCOPY(amb, &re->wrld.ambr);
init_render_materials(re->r.mode, amb);
set_node_shader_lamp_loop(shade_material_loop);
/* MAKE RENDER DATA */
nolamps= !ELEM3(type, RE_BAKE_LIGHT, RE_BAKE_ALL, RE_BAKE_SHADOW);
onlyselected= ELEM3(type, RE_BAKE_NORMALS, RE_BAKE_TEXTURE, RE_BAKE_DISPLACEMENT);

@ -2543,13 +2543,16 @@ static void do_write_image_or_movie(Render *re, Scene *scene, bMovieHandle *mh)
ImBuf *ibuf= IMB_allocImBuf(rres.rectx, rres.recty, scene->r.planes, 0, 0);
int ok;
/* if not exists, BKE_write_ibuf makes one */
/* if not exists, BKE_write_ibuf makes one */
ibuf->rect= (unsigned int *)rres.rect32;
ibuf->rect_float= rres.rectf;
ibuf->zbuf_float= rres.rectz;
/* float factor for random dither, imbuf takes care of it */
ibuf->dither= scene->r.dither_intensity;
/* gamma correct to sRGB color space */
if (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)
ibuf->profile = IB_PROFILE_SRGB;
ok= BKE_write_ibuf(scene, ibuf, name, scene->r.imtype, scene->r.subimtype, scene->r.quality);

@ -43,6 +43,7 @@
#include "DNA_texture_types.h"
#include "DNA_lamp_types.h"
#include "BKE_colortools.h"
#include "BKE_image.h"
#include "BKE_global.h"
#include "BKE_material.h"
@ -58,6 +59,7 @@
#include "rendercore.h"
#include "shadbuf.h"
#include "pixelshading.h"
#include "shading.h"
#include "sunsky.h"
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
@ -536,9 +538,15 @@ void shadeSkyView(float *colf, float *rco, float *view, float *dxyview, short th
blend= fabs(0.5+ view[1]);
}
hor[0]= R.wrld.horr; hor[1]= R.wrld.horg; hor[2]= R.wrld.horb;
zen[0]= R.wrld.zenr; zen[1]= R.wrld.zeng; zen[2]= R.wrld.zenb;
if (R.r.color_mgt_flag & R_COLOR_MANAGEMENT) {
color_manage_linearize(hor, &R.wrld.horr);
color_manage_linearize(zen, &R.wrld.zenr);
}
else {
VECCOPY(hor, &R.wrld.horr);
VECCOPY(zen, &R.wrld.zenr);
}
/* Careful: SKYTEX and SKYBLEND are NOT mutually exclusive! If */
/* SKYBLEND is active, the texture and color blend are added. */
if(R.wrld.skytype & WO_SKYTEX) {
@ -625,9 +633,11 @@ void shadeSkyPixel(float *collector, float fx, float fy, short thread)
}
else if((R.wrld.skytype & (WO_SKYBLEND+WO_SKYTEX))==0) {
/* 2. solid color */
collector[0] = R.wrld.horr;
collector[1] = R.wrld.horg;
collector[2] = R.wrld.horb;
if(R.r.color_mgt_flag & R_COLOR_MANAGEMENT)
color_manage_linearize(collector, &R.wrld.horr);
else
VECCOPY(collector, &R.wrld.horr);
collector[3] = 0.0f;
}
else {

@ -225,8 +225,7 @@ static void shade_ray(Isect *is, ShadeInput *shi, ShadeResult *shr)
shi->obr= obi->obr;
shi->vlr= vlr;
shi->mat= vlr->mat;
memcpy(&shi->r, &shi->mat->r, 23*sizeof(float)); // note, keep this synced with render_types.h
shi->har= shi->mat->har;
shade_input_init_material(shi);
// Osa structs we leave unchanged now
SWAP(int, osatex, shi->osatex);

@ -1515,9 +1515,7 @@ static void shade_sample_sss(ShadeSample *ssamp, Material *mat, ObjectInstanceRe
shi->mat= mat;
/* init material vars */
// note, keep this synced with render_types.h
memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));
shi->har= shi->mat->har;
shade_input_init_material(shi);
/* render */
shade_input_set_shade_texco(shi);
@ -1950,10 +1948,7 @@ void RE_shade_external(Render *re, ShadeInput *shi, ShadeResult *shr)
if(shi->mat->nodetree && shi->mat->use_nodes)
ntreeShaderExecTree(shi->mat->nodetree, shi, shr);
else {
/* copy all relevant material vars, note, keep this synced with render_types.h */
memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));
shi->har= shi->mat->har;
shade_input_init_material(shi);
shade_material_loop(shi, shr);
}
}
@ -2104,9 +2099,7 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int quad, int
ShadeResult shr;
VlakRen *vlr= shi->vlr;
/* init material vars */
memcpy(&shi->r, &shi->mat->r, 23*sizeof(float)); // note, keep this synced with render_types.h
shi->har= shi->mat->har;
shade_input_init_material(shi);
if(bs->type==RE_BAKE_AO) {
ambient_occlusion(shi);

@ -39,6 +39,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_material_types.h"
#include "BKE_colortools.h"
#include "BKE_utildefines.h"
#include "BKE_node.h"
@ -84,11 +85,45 @@ extern struct Render R;
*/
/* initialise material variables in shadeinput,
* doing inverse gamma correction where applicable */
void shade_input_init_material(ShadeInput *shi)
{
if (R.r.color_mgt_flag & R_COLOR_MANAGEMENT) {
color_manage_linearize(&shi->r, &shi->mat->r);
color_manage_linearize(&shi->specr, &shi->mat->specr);
color_manage_linearize(&shi->mirr, &shi->mat->mirr);
/* material ambr / ambg / ambb is overwritten from world
color_manage_linearize(shi->ambr, shi->mat->ambr);
*/
/* note, keep this synced with render_types.h */
memcpy(&shi->amb, &shi->mat->amb, 11*sizeof(float));
shi->har= shi->mat->har;
} else {
/* note, keep this synced with render_types.h */
memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));
shi->har= shi->mat->har;
}
}
static void shadeinput_colors_linearize(ShadeInput *shi)
{
color_manage_linearize(&shi->r, &shi->r);
color_manage_linearize(&shi->specr, &shi->specr);
color_manage_linearize(&shi->mirr, &shi->mirr);
}
/* also used as callback for nodes */
/* delivers a fully filled in ShadeResult, for all passes */
void shade_material_loop(ShadeInput *shi, ShadeResult *shr)
{
/* because node materials don't have access to rendering context,
* inverse gamma correction must happen here. evil. */
if (R.r.color_mgt_flag & R_COLOR_MANAGEMENT && shi->nodes == 1)
shadeinput_colors_linearize(shi);
shade_lamp_loop(shi, shr); /* clears shr */
@ -96,9 +131,7 @@ void shade_material_loop(ShadeInput *shi, ShadeResult *shr)
ShadeResult shr_t;
float fac= shi->translucency;
/* gotta copy it again */
memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));
shi->har= shi->mat->har;
shade_input_init_material(shi);
VECCOPY(shi->vn, shi->vno);
VECMUL(shi->vn, -1.0f);
@ -148,8 +181,7 @@ void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr)
}
else {
/* copy all relevant material vars, note, keep this synced with render_types.h */
memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));
shi->har= shi->mat->har;
shade_input_init_material(shi);
shade_material_loop(shi, shr);
}
@ -571,6 +603,13 @@ void shade_input_set_strand_texco(ShadeInput *shi, StrandRen *strand, StrandVert
/* not supported */
}
}
if (R.r.color_mgt_flag & R_COLOR_MANAGEMENT) {
if(mode & (MA_VERTEXCOL|MA_VERTEXCOLP|MA_FACETEXTURE)) {
color_manage_linearize(shi->vcol, shi->vcol);
}
}
}
/* from scanline pixel coordinates to 3d coordinates, requires set_triangle */
@ -1240,6 +1279,12 @@ void shade_input_set_shade_texco(ShadeInput *shi)
} /* else {
Note! For raytracing winco is not set, important because thus means all shader input's need to have their variables set to zero else in-initialized values are used
*/
if (R.r.color_mgt_flag & R_COLOR_MANAGEMENT) {
if(mode & (MA_VERTEXCOL|MA_VERTEXCOLP|MA_FACETEXTURE)) {
color_manage_linearize(shi->vcol, shi->vcol);
}
}
}
/* ****************** ShadeSample ************************************** */

@ -55,6 +55,7 @@
#include "DNA_material_types.h"
#include "BKE_colortools.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_material.h"
@ -916,7 +917,7 @@ static void sss_create_tree_mat(Render *re, Material *mat)
if(!re->test_break(re->tbh)) {
SSSData *sss= MEM_callocN(sizeof(*sss), "SSSData");
float ior= mat->sss_ior, cfac= mat->sss_colfac;
float *col= mat->sss_col, *radius= mat->sss_radius;
float col[3], *radius= mat->sss_radius;
float fw= mat->sss_front, bw= mat->sss_back;
float error = mat->sss_error;
@ -924,6 +925,9 @@ static void sss_create_tree_mat(Render *re, Material *mat)
if((re->r.scemode & R_PREVIEWBUTS) && error < 0.5f)
error= 0.5f;
if (re->r.color_mgt_flag & R_COLOR_MANAGEMENT) color_manage_linearize(col, mat->sss_col);
else VECCOPY(col, mat->sss_col);
sss->ss[0]= scatter_settings_new(col[0], radius[0], ior, cfac, fw, bw);
sss->ss[1]= scatter_settings_new(col[1], radius[1], ior, cfac, fw, bw);
sss->ss[2]= scatter_settings_new(col[2], radius[2], ior, cfac, fw, bw);

@ -281,9 +281,7 @@ void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, Str
shade_input_set_strand_texco(shi, sseg->strand, sseg->v[1], spoint);
/* init material vars */
// note, keep this synced with render_types.h
memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));
shi->har= shi->mat->har;
shade_input_init_material(shi);
/* shade */
shade_samples_do_AO(ssamp);

@ -48,6 +48,7 @@
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
#include "BKE_colortools.h"
#include "BKE_image.h"
#include "BKE_node.h"
#include "BKE_plugin_types.h"
@ -1808,6 +1809,11 @@ void do_material_tex(ShadeInput *shi)
}
else texres.tin= texres.ta;
/* inverse gamma correction */
if (R.r.color_mgt_flag & R_COLOR_MANAGEMENT) {
color_manage_linearize(tcol, tcol);
}
if(mtex->mapto & MAP_COL) {
texture_rgb_blend(&shi->r, tcol, &shi->r, texres.tin, colfac, mtex->blendtype);
}
@ -2127,6 +2133,11 @@ void do_halo_tex(HaloRen *har, float xn, float yn, float *colf)
}
else texres.tin= texres.ta;
/* inverse gamma correction */
if (R.r.color_mgt_flag & R_COLOR_MANAGEMENT) {
color_manage_linearize(&texres.tr, &texres.tr);
}
fact= texres.tin*mtex->colfac;
facm= 1.0-fact;
@ -2314,6 +2325,11 @@ void do_sky_tex(float *rco, float *lo, float *dxyview, float *hor, float *zen, f
tcol[0]= texres.tr; tcol[1]= texres.tg; tcol[2]= texres.tb;
/* inverse gamma correction */
if (R.r.color_mgt_flag & R_COLOR_MANAGEMENT) {
color_manage_linearize(tcol, tcol);
}
if(mtex->mapto & WOMAP_HORIZ) {
texture_rgb_blend(hor, tcol, hor, texres.tin, mtex->colfac, mtex->blendtype);
}
@ -2496,6 +2512,11 @@ void do_lamp_tex(LampRen *la, float *lavec, ShadeInput *shi, float *colf, int ef
}
else texres.tin= texres.ta;
/* inverse gamma correction */
if (R.r.color_mgt_flag & R_COLOR_MANAGEMENT) {
color_manage_linearize(&texres.tr, &texres.tr);
}
/* lamp colors were premultiplied with this */
col[0]= texres.tr*la->energy;
col[1]= texres.tg*la->energy;