Color management: add "Color Unpremultiply" option for images and render settings.
authorBrecht Van Lommel <brechtvanlommel@pandora.be>
Fri, 30 Dec 2011 14:17:11 +0000 (14:17 +0000)
committerBrecht Van Lommel <brechtvanlommel@pandora.be>
Fri, 30 Dec 2011 14:17:11 +0000 (14:17 +0000)
For premultiplied alpha images, this makes any color space conversion for the image
or render output work on color without alpha multiplied in.

This is typically useful to avoid fringing when the image was or will be composited
over a light background. If the image will be composited over a black background on
the other hand, leaving this option off will give correct results.

In an ideal world, there should never be any color space conversion on images with
alpha, since it's undefined what to do then, but in practice it's useful to have
this option.

Patch by Troy Sobotka, with changes by me.

17 files changed:
release/scripts/startup/bl_ui/properties_render.py
source/blender/blenkernel/intern/image.c
source/blender/editors/include/BIF_glutil.h
source/blender/editors/render/render_internal.c
source/blender/editors/render/render_preview.c
source/blender/editors/screen/glutil.c
source/blender/editors/space_image/image_buttons.c
source/blender/editors/space_node/space_node.c
source/blender/imbuf/IMB_imbuf_types.h
source/blender/imbuf/intern/divers.c
source/blender/makesdna/DNA_image_types.h
source/blender/makesdna/DNA_scene_types.h
source/blender/makesrna/intern/rna_image.c
source/blender/makesrna/intern/rna_scene.c
source/blender/nodes/composite/node_composite_util.c
source/blender/nodes/composite/nodes/node_composite_image.c
source/blender/render/intern/source/pipeline.c

index 6a8439ffaaada054f4ef2bee6743f361b97240d5..7887998b90f460d43b7b3e390b44e4680910ab93 100644 (file)
@@ -316,6 +316,9 @@ class RENDER_PT_shading(RenderButtonsPanel, Panel):
         col = split.column()
         col.prop(rd, "use_raytrace", text="Ray Tracing")
         col.prop(rd, "use_color_management")
+        sub = col.row()
+        sub.active = rd.use_color_management == True
+        sub.prop(rd, "use_color_unpremultiply")
         col.prop(rd, "alpha_mode", text="Alpha")
 
 
index 354a5265f9091324aabe1cc2f3c30bc1e89b02c8..a5c8f5c905d99950cca5e45793943244eb14a4c9 100644 (file)
@@ -285,6 +285,10 @@ static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int frame)
                                break;
 
                ibuf->index= index;
+               if(ima->flag & IMA_CM_PREDIVIDE)
+                       ibuf->flags |= IB_cm_predivide;
+               else
+                       ibuf->flags &= ~IB_cm_predivide;
 
                /* this function accepts link==NULL */
                BLI_insertlinkbefore(&ima->ibufs, link, ibuf);
@@ -2304,9 +2308,17 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_
 
        /* since its possible to access the buffer from the image directly, set the profile [#25073] */
        ibuf->profile= (iuser->scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) ? IB_PROFILE_LINEAR_RGB : IB_PROFILE_NONE;
-
        ibuf->dither= dither;
 
+       if(iuser->scene->r.color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE) {
+               ibuf->flags |= IB_cm_predivide;
+               ima->flag |= IMA_CM_PREDIVIDE;
+       }
+       else {
+               ibuf->flags &= ~IB_cm_predivide;
+               ima->flag &= ~IMA_CM_PREDIVIDE;
+       }
+
        ima->ok= IMA_OK_LOADED;
 
        return ibuf;
index cd3d87a89790e66765e3d68eb0d88ecbd1c5060a..33e9192a23e15f1db7ecfeecfb9fd0011533a03a 100644 (file)
@@ -136,13 +136,8 @@ 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, int gamma_correct);
-
-
 void glaDrawPixelsTex          (float x, float y, int img_w, int img_h, int format, void *rect);
 
 void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, int format, void *rect, float scaleX, float scaleY);
index bff93fea0677c64b44174dd19fcb4234c8f88b44..e4597d6afc38e2384b22ae026607badca6549be4 100644 (file)
@@ -138,11 +138,14 @@ void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volat
        rectf+= 4*(rr->rectx*ymin + xmin);
        rectc= (unsigned char*)(ibuf->rect + ibuf->x*rymin + rxmin);
 
-       if(scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT))
+       if(scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
                profile_from= IB_PROFILE_LINEAR_RGB;
-       else
+               predivide= (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE);
+       }
+       else {
                profile_from= IB_PROFILE_SRGB;
-       predivide= 0;
+               predivide= 0;
+       }
 
        IMB_buffer_byte_from_float(rectc, rectf,
                4, ibuf->dither, IB_PROFILE_SRGB, profile_from, predivide,
index feff1e05d6074e77c6205dcf60b58cee2ad7132b..86328ca2a6406d54ba7225da3b0e533fd10701b4 100644 (file)
@@ -460,12 +460,15 @@ static int ed_preview_draw_rect(ScrArea *sa, Scene *sce, ID *id, int split, int
        Render *re;
        RenderResult rres;
        char name[32];
-       int do_gamma_correct=0;
+       int do_gamma_correct=0, do_predivide=0;
        int offx=0, newx= rect->xmax-rect->xmin, newy= rect->ymax-rect->ymin;
 
        if (id && GS(id->name) != ID_TE) {
                /* exception: don't color manage texture previews - show the raw values */
-               if (sce) do_gamma_correct = sce->r.color_mgt_flag & R_COLOR_MANAGEMENT;
+               if (sce) {
+                       do_gamma_correct = sce->r.color_mgt_flag & R_COLOR_MANAGEMENT;
+                       do_predivide = sce->r.color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE;
+               }
        }
 
        if(!split || first) sprintf(name, "Preview %p", (void *)sa);
@@ -488,10 +491,28 @@ static int ed_preview_draw_rect(ScrArea *sa, Scene *sce, ID *id, int split, int
        if(rres.rectf) {
                
                if(ABS(rres.rectx-newx)<2 && ABS(rres.recty-newy)<2) {
+
                        newrect->xmax= MAX2(newrect->xmax, rect->xmin + rres.rectx + offx);
                        newrect->ymax= MAX2(newrect->ymax, rect->ymin + rres.recty);
 
-                       glaDrawPixelsSafe_to32(rect->xmin+offx, rect->ymin, rres.rectx, rres.recty, rres.rectx, rres.rectf, do_gamma_correct);
+                       if(rres.rectx && rres.recty) {
+                               /* temporary conversion to byte for drawing */
+                               float fx= rect->xmin + offx;
+                               float fy= rect->ymin;
+                               int profile_from= (do_gamma_correct)? IB_PROFILE_LINEAR_RGB: IB_PROFILE_SRGB;
+                               int dither= 0;
+                               unsigned char *rect_byte;
+
+                               rect_byte= MEM_mallocN(rres.rectx*rres.recty*sizeof(int), "ed_preview_draw_rect");
+
+                               IMB_buffer_byte_from_float(rect_byte, rres.rectf,
+                                       4, dither, IB_PROFILE_SRGB, profile_from, do_predivide, 
+                                       rres.rectx, rres.recty, rres.rectx, rres.rectx);
+
+                               glaDrawPixelsSafe(fx, fy, rres.rectx, rres.recty, rres.rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect_byte);
+
+                               MEM_freeN(rect_byte);
+                       }
 
                        RE_ReleaseResultImage(re);
                        return 1;
index 8f04940efd6541af0c7172a8d6df983bb276b587..0b231ee7b96f5d9ae63ee38597a377b9868be9b1 100644 (file)
@@ -45,9 +45,6 @@
 #include "BIF_gl.h"
 #include "BIF_glutil.h"
 
-#include "IMB_imbuf.h"
-#include "IMB_imbuf_types.h"
-
 #ifndef GL_CLAMP_TO_EDGE
 #define GL_CLAMP_TO_EDGE                        0x812F
 #endif
@@ -562,27 +559,6 @@ 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);
 }
 
-/* row_w is unused but kept for completeness */
-void glaDrawPixelsSafe_to32(float fx, float fy, int img_w, int img_h, int UNUSED(row_w), float *rectf, int do_gamma_correct)
-{
-       unsigned char *rect32;
-       int profile_from= (do_gamma_correct)? IB_PROFILE_LINEAR_RGB: IB_PROFILE_SRGB;
-       int predivide= 0;
-       
-       /* copy imgw-imgh to a temporal 32 bits rect */
-       if(img_w<1 || img_h<1) return;
-       
-       rect32= MEM_mallocN(img_w*img_h*sizeof(int), "temp 32 bits");
-       
-       IMB_buffer_byte_from_float(rect32, rectf,
-               4, 0, IB_PROFILE_SRGB, profile_from, predivide, 
-               img_w, img_h, img_w, img_w);
-       
-       glaDrawPixelsSafe(fx, fy, img_w, img_h, img_w, GL_RGBA, GL_UNSIGNED_BYTE, rect32);
-
-       MEM_freeN(rect32);
-}
-
 void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int format, int type, void *rect)
 {
        float xzoom= glaGetOneFloat(GL_ZOOM_X);
index c647ff3df53ea60b775ceafdf57e45b4ba39bafd..8d8c79386c543cdd939e3f7872f2084260251162 100644 (file)
@@ -750,7 +750,9 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char
                                        uiLayoutSetActive(row, RNA_boolean_get(&imaptr, "use_fields"));
                                        uiItemR(row, &imaptr, "field_order", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
                                        
-                                       uiItemR(split, &imaptr, "use_premultiply", 0, NULL, ICON_NONE);
+                                       row= uiLayoutRow(layout, 0);
+                                       uiItemR(row, &imaptr, "use_premultiply", 0, NULL, ICON_NONE);
+                                       uiItemR(row, &imaptr, "use_color_unpremultiply", 0, NULL, ICON_NONE);
                                }
                        }
 
index 1a808e8ee5f2aed734a06f1ee6d0a7f9650ffc0f..9d4c5705bd17821b2b60f39edbcd45578b010b82 100644 (file)
@@ -248,12 +248,10 @@ static void node_area_listener(ScrArea *sa, wmNotifier *wmn)
                case NC_IMAGE:
                        if (wmn->action == NA_EDITED) {
                                if(type==NTREE_COMPOSIT) {
-                                       Scene *scene= wmn->window->screen->scene;
-                                       
                                        /* note that nodeUpdateID is already called by BKE_image_signal() on all
                                         * scenes so really this is just to know if the images is used in the compo else
                                         * painting on images could become very slow when the compositor is open. */
-                                       if(nodeUpdateID(scene->nodetree, wmn->reference))
+                                       if(nodeUpdateID(snode->nodetree, wmn->reference))
                                                ED_area_tag_refresh(sa);
                                }
                        }
index 5ce3d63fe86079d32087da9106ef42eee4c470e8..adf6f4a143bbae5c445d12e387b14f1fb7a5d743 100644 (file)
@@ -158,6 +158,7 @@ typedef struct ImBuf {
 #define IB_tiles                       (1 << 10)
 #define IB_tilecache           (1 << 11)
 #define IB_premul                      (1 << 12)
+#define IB_cm_predivide                (1 << 13)
 
 /*
  * The bit flag is stored in the ImBuf.ftype variable.
index 8a3f6358c5b3f68f6b1158b9131d3d7ca8602219..0dfc1852e290ce7bf997ce112cf0d0dfc5fc3a16 100644 (file)
@@ -453,7 +453,8 @@ void IMB_buffer_byte_from_byte(uchar *rect_to, const uchar *rect_from,
 
 void IMB_rect_from_float(struct ImBuf *ibuf)
 {
-       int predivide= 0, profile_from;
+       int predivide= (ibuf->flags & IB_cm_predivide);
+       int profile_from;
 
        /* verify we have a float buffer */
        if(ibuf->rect_float==NULL)
@@ -485,7 +486,8 @@ void IMB_partial_rect_from_float(struct ImBuf *ibuf, float *buffer, int x, int y
 {
        float *rect_float;
        uchar *rect_byte;
-       int predivide= 0, profile_from;
+       int predivide= (ibuf->flags & IB_cm_predivide);
+       int profile_from;
 
        /* verify we have a float buffer */
        if(ibuf->rect_float==NULL || buffer==NULL)
@@ -521,7 +523,8 @@ void IMB_partial_rect_from_float(struct ImBuf *ibuf, float *buffer, int x, int y
 
 void IMB_float_from_rect(struct ImBuf *ibuf)
 {
-       int predivide= 0, profile_from;
+       int predivide= (ibuf->flags & IB_cm_predivide);
+       int profile_from;
 
        /* verify if we byte and float buffers */
        if(ibuf->rect==NULL)
@@ -546,7 +549,7 @@ void IMB_float_from_rect(struct ImBuf *ibuf)
 /* no profile conversion */
 void IMB_float_from_rect_simple(struct ImBuf *ibuf)
 {
-       int predivide= 0;
+       int predivide= (ibuf->flags & IB_cm_predivide);
 
        if(ibuf->rect_float==NULL)
                imb_addrectfloatImBuf(ibuf);
@@ -558,7 +561,8 @@ void IMB_float_from_rect_simple(struct ImBuf *ibuf)
 
 void IMB_convert_profile(struct ImBuf *ibuf, int profile)
 {
-       int predivide= 0, profile_from, profile_to;
+       int predivide= (ibuf->flags & IB_cm_predivide);
+       int profile_from, profile_to;
 
        if(ibuf->profile == profile)
                return;
@@ -599,7 +603,8 @@ void IMB_convert_profile(struct ImBuf *ibuf, int profile)
  * if the return  */
 float *IMB_float_profile_ensure(struct ImBuf *ibuf, int profile, int *alloc)
 {
-       int predivide= 0, profile_from, profile_to;
+       int predivide= (ibuf->flags & IB_cm_predivide);
+       int profile_from, profile_to;
 
        /* determine profiles */
        if(ibuf->profile == IB_PROFILE_NONE)
index fc8807d0f7cfc831e129401acbb5132be0280368..a1b0ab06ecd7b7dca42dc52d5cc8ca5b90251d4e 100644 (file)
@@ -112,14 +112,14 @@ typedef struct Image {
 /* **************** IMAGE ********************* */
 
 /* Image.flag */
-#define IMA_FIELDS             1
-#define IMA_STD_FIELD  2
-#define IMA_DO_PREMUL  4
-
-#define        IMA_REFLECT             16
-#define IMA_NOCOLLECT   32
-#define IMA_DEPRECATED 64
-#define IMA_OLD_PREMUL 128
+#define IMA_FIELDS                     1
+#define IMA_STD_FIELD          2
+#define IMA_DO_PREMUL          4
+#define IMA_REFLECT                    16
+#define IMA_NOCOLLECT          32
+#define IMA_DEPRECATED         64
+#define IMA_OLD_PREMUL         128
+#define IMA_CM_PREDIVIDE       256
 
 /* Image.tpageflag */
 #define IMA_TILES                      1
index d1ab12433fb6b7371ecc2f3f2d2fe263d37dd981..9b49bc53ef3684eed33676eac138eb9819423390 100644 (file)
@@ -1100,7 +1100,8 @@ typedef struct Scene {
 #define R_ALPHAKEY             2
 
 /* color_mgt_flag */
-#define R_COLOR_MANAGEMENT     1
+#define R_COLOR_MANAGEMENT              (1 << 0)
+#define R_COLOR_MANAGEMENT_PREDIVIDE    (1 << 1)
 
 /* subimtype, flag options for imtype */
 #define R_OPENEXR_HALF    1                                      /*deprecated*/
index 6f6a27abe7c911b2ad0a78cc8eb91d08d5c57ed1..9e7f4a349e7c534ef4fcf3464aae2ba94a2c073b 100644 (file)
@@ -40,6 +40,7 @@
 #include "BKE_image.h"
 
 #include "WM_types.h"
+#include "WM_api.h"
 
 static EnumPropertyItem image_source_items[]= {
        {IMA_SRC_FILE, "FILE", 0, "Single Image", "Single image file"},
@@ -110,6 +111,7 @@ static void rna_Image_reload_update(Main *UNUSED(bmain), Scene *UNUSED(scene), P
 {
        Image *ima= ptr->id.data;
        BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD);
+       WM_main_add_notifier(NC_IMAGE|NA_EDITED, &ima->id);
        DAG_id_tag_update(&ima->id, 0);
 }
 
@@ -475,6 +477,11 @@ static void rna_def_image(BlenderRNA *brna)
        RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_DO_PREMUL);
        RNA_def_property_ui_text(prop, "Premultiply", "Convert RGB from key alpha to premultiplied alpha");
        RNA_def_property_update(prop, NC_IMAGE|ND_DISPLAY, "rna_Image_reload_update");
+       
+       prop= RNA_def_property(srna, "use_color_unpremultiply", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_CM_PREDIVIDE);
+       RNA_def_property_ui_text(prop, "Color Unpremultiply", "For premultiplied alpha images, do color space conversion on colors without alpha, to avoid fringing for images with light backgrounds");
+       RNA_def_property_update(prop, NC_IMAGE|ND_DISPLAY, "rna_Image_reload_update");
 
        prop= RNA_def_property(srna, "is_dirty", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_funcs(prop, "rna_Image_dirty_get", NULL);
index 54267725fe21c3299c5cc301521ddf0d96e144c2..b69ca64773c47b33cb4e59050aea1a4c6b2a23ed 100644 (file)
@@ -3228,7 +3228,11 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
        RNA_def_property_boolean_sdna(prop, NULL, "color_mgt_flag", R_COLOR_MANAGEMENT);
        RNA_def_property_ui_text(prop, "Color Management", "Use linear workflow - gamma corrected imaging pipeline");
        RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, "rna_RenderSettings_color_management_update");
-
+       
+       prop= RNA_def_property(srna, "use_color_unpremultiply", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "color_mgt_flag", R_COLOR_MANAGEMENT_PREDIVIDE);
+       RNA_def_property_ui_text(prop, "Color Unpremultipy", "For premultiplied alpha render output, do color space conversion on colors without alpha, to avoid fringing on light backgrounds");
+       RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
        
        prop= RNA_def_property(srna, "use_file_extension", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "scemode", R_EXTENSION);
index 8aaf1771b68eaa1da3dfbb3e895d55ca265606cf..72037f232add2e6d3ff836afe52fe91217dd1743 100644 (file)
@@ -607,7 +607,7 @@ void generate_preview(void *data, bNode *node, CompBuf *stackbuf)
        bNodePreview *preview= node->preview;
        int xsize, ysize;
        int profile_from= (rd->color_mgt_flag & R_COLOR_MANAGEMENT)? IB_PROFILE_LINEAR_RGB: IB_PROFILE_SRGB;
-       int predivide= 0;
+       int predivide= (rd->color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE);
        int dither= 0;
        unsigned char *rect;
        
index fd7c87adea20bbf3d129436961dbd2a6fda4d1b5..57b5cec4256d1b76f645ccbc9285f04518a670be 100644 (file)
@@ -62,7 +62,7 @@ static bNodeSocketTemplate cmp_node_rlayers_out[]= {
 float *node_composit_get_float_buffer(RenderData *rd, ImBuf *ibuf, int *alloc)
 {
        float *rect;
-       int predivide= 0;
+       int predivide= (ibuf->flags & IB_cm_predivide);
 
        *alloc= FALSE;
 
index 7713dcffc5c8674c27809085d4020468cc95569c..555d5c12a8649ed46b74d6841bef8c518903d072 100644 (file)
@@ -1154,7 +1154,7 @@ void RE_ResultGet32(Render *re, unsigned int *rect)
        }
        else if(rres.rectf) {
                int profile_from= (re->r.color_mgt_flag & R_COLOR_MANAGEMENT)? IB_PROFILE_LINEAR_RGB: IB_PROFILE_SRGB;
-               int predivide= 0;
+               int predivide= (re->r.color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE);
                int dither= 0;
 
                IMB_buffer_byte_from_float((unsigned char*)rect, rres.rectf,
@@ -2556,7 +2556,7 @@ static void do_render_seq(Render * re)
                         * render engine delivers */
                        int profile_to= (re->r.color_mgt_flag & R_COLOR_MANAGEMENT)? IB_PROFILE_LINEAR_RGB: IB_PROFILE_SRGB;
                        int profile_from= (ibuf->profile == IB_PROFILE_LINEAR_RGB)? IB_PROFILE_LINEAR_RGB: IB_PROFILE_SRGB;
-                       int predivide= 0;
+                       int predivide= (re->r.color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE);
 
                        if (!rr->rectf)
                                rr->rectf= MEM_mallocN(4*sizeof(float)*rr->rectx*rr->recty, "render_seq rectf");
@@ -2995,7 +2995,8 @@ static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovie
                        }
                }
                else {
-                       ImBuf *ibuf= IMB_allocImBuf(rres.rectx, rres.recty, scene->r.im_format.planes, 0);
+                       int flags = (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE)? IB_cm_predivide: 0;
+                       ImBuf *ibuf= IMB_allocImBuf(rres.rectx, rres.recty, scene->r.im_format.planes, flags);
                        
                        /* if not exists, BKE_write_ibuf makes one */
                        ibuf->rect= (unsigned int *)rres.rect32;