Images: don't (un)premultipy non-color data
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Sun, 19 May 2019 00:56:12 +0000 (02:56 +0200)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Sun, 19 May 2019 12:36:42 +0000 (14:36 +0200)
The previous behavior here was wrong for some specific combinations of
settings, non-color RGB channels should never be affected by the alpha
channel.

intern/cycles/render/colorspace.cpp
intern/cycles/render/colorspace.h
intern/cycles/render/image.cpp
intern/cycles/render/nodes.cpp
source/blender/imbuf/intern/readimage.c
source/blender/nodes/shader/nodes/node_shader_tex_image.c

index 7b57478..f4c217b 100644 (file)
@@ -82,6 +82,31 @@ ColorSpaceProcessor *ColorSpaceManager::get_processor(ustring colorspace)
 #endif
 }
 
+bool ColorSpaceManager::colorspace_is_data(ustring colorspace)
+{
+  if (colorspace == u_colorspace_auto || colorspace == u_colorspace_raw ||
+      colorspace == u_colorspace_srgb) {
+    return false;
+  }
+
+#ifdef WITH_OCIO
+  OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
+  if (!config) {
+    return false;
+  }
+
+  try {
+    OCIO::ConstColorSpaceRcPtr space = config->getColorSpace(colorspace.c_str());
+    return space && space->isData();
+  }
+  catch (OCIO::Exception &exception) {
+    return false;
+  }
+#else
+  return false;
+#endif
+}
+
 ustring ColorSpaceManager::detect_known_colorspace(ustring colorspace,
                                                    const char *file_format,
                                                    bool is_float)
index e7c7f94..9fea2d6 100644 (file)
@@ -37,6 +37,9 @@ class ColorSpaceManager {
                                          const char *file_format,
                                          bool is_float);
 
+  /* Test if colorspace is for non-color data. */
+  static bool colorspace_is_data(ustring colorspace);
+
   /* Convert pixels in the specified colorspace to scene linear color for
    * rendering. Must be a colorspace returned from detect_known_colorspace. */
   template<typename T>
index 431e923..6301b41 100644 (file)
@@ -513,8 +513,14 @@ bool ImageManager::file_load_image_generic(Image *img, unique_ptr<ImageInput> *i
     ImageSpec spec = ImageSpec();
     ImageSpec config = ImageSpec();
 
-    if (img->use_alpha == false)
+    /* For typical RGBA images we let OIIO convert to associated alpha,
+     * but some types we want to leave the RGB channels untouched. */
+    const bool associate_alpha = !(ColorSpaceManager::colorspace_is_data(img->colorspace) ||
+                                   img->use_alpha == false);
+
+    if (!associate_alpha) {
       config.attribute("oiio:UnassociatedAlpha", 1);
+    }
 
     if (!(*in)->open(img->filename, spec, config)) {
       return false;
index 27e6309..3905189 100644 (file)
@@ -316,7 +316,12 @@ void ImageTextureNode::compile(SVMCompiler &compiler)
       flags |= NODE_IMAGE_COMPRESS_AS_SRGB;
     }
     if (!alpha_out->links.empty()) {
-      flags |= NODE_IMAGE_ALPHA_UNASSOCIATE;
+      const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) ||
+                                       use_alpha == false);
+
+      if (unassociate_alpha) {
+        flags |= NODE_IMAGE_ALPHA_UNASSOCIATE;
+      }
     }
 
     if (projection != NODE_IMAGE_PROJ_BOX) {
@@ -389,11 +394,14 @@ void ImageTextureNode::compile(OSLCompiler &compiler)
     compiler.parameter_texture("filename", slot);
   }
 
+  const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) ||
+                                   use_alpha == false);
+
   compiler.parameter(this, "projection");
   compiler.parameter(this, "projection_blend");
   compiler.parameter("convert_from_srgb", compress_as_srgb);
   compiler.parameter("ignore_alpha", !use_alpha);
-  compiler.parameter("unassociate_alpha", !alpha_out->links.empty());
+  compiler.parameter("unassociate_alpha", !alpha_out->links.empty() && unassociate_alpha);
   compiler.parameter("is_float", is_float);
   compiler.parameter(this, "interpolation");
   compiler.parameter(this, "extension");
index e1df5c6..9297227 100644 (file)
@@ -49,8 +49,6 @@ static void imb_handle_alpha(ImBuf *ibuf,
                              char colorspace[IM_MAX_SPACE],
                              char effective_colorspace[IM_MAX_SPACE])
 {
-  int alpha_flags;
-
   if (colorspace) {
     if (ibuf->rect != NULL && ibuf->rect_float == NULL) {
       /* byte buffer is never internally converted to some standard space,
@@ -62,6 +60,9 @@ static void imb_handle_alpha(ImBuf *ibuf,
     BLI_strncpy(colorspace, effective_colorspace, IM_MAX_SPACE);
   }
 
+  bool is_data = (colorspace && IMB_colormanagement_space_name_is_data(colorspace));
+  int alpha_flags;
+
   if (flags & IB_alphamode_detect) {
     alpha_flags = ibuf->flags & IB_alphamode_premul;
   }
@@ -69,7 +70,10 @@ static void imb_handle_alpha(ImBuf *ibuf,
     alpha_flags = flags & IB_alphamode_premul;
   }
 
-  if (flags & IB_ignore_alpha) {
+  if (is_data) {
+    /* Don't touch alpha. */
+  }
+  else if (flags & IB_ignore_alpha) {
     IMB_rectfill_alpha(ibuf, 1.0f);
   }
   else {
index a9183cd..8086bb2 100644 (file)
@@ -180,6 +180,9 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
   }
 
   if (out[0].hasoutput) {
+    /* When the alpha socket is used, unpremultiply alpha. This makes it so
+     * that if we blend the color with a transparent shader using alpha as
+     * a factor, we don't multiply alpha into the color twice. */
     if (out[1].hasoutput &&
         !IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) {
       GPU_link(mat, "tex_color_alpha_unpremultiply", out[0].link, &out[0].link);