Images: change alpha settings to support channel packing
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Sat, 18 May 2019 18:52:20 +0000 (20:52 +0200)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Sun, 19 May 2019 12:36:42 +0000 (14:36 +0200)
This also replaces the Use Alpha setting. We now have these alpha modes:

* Straight: store RGB and alpha channels separately with alpha acting as a
  mask, also known as unassociated alpha.
* Premultiplied: transparent RGB pixels are multiplied by the alpha channel.
  The natural format for renders.
* Channel Packed: different images are packed in the RGB and alpha channels,
  and they should not influence each other. Channel packing is commonly used
  by game engines to save memory.
* None: ignore alpha channel from the file and make image fully opaque.

Cycles OSL does not correctly support Channel Packed and None yet, we are
missing fine control over the OpenImageIO texture cache to do that.

Fixes T53672

21 files changed:
intern/cycles/blender/blender_mesh.cpp
intern/cycles/blender/blender_shader.cpp
intern/cycles/render/image.cpp
intern/cycles/render/image.h
intern/cycles/render/nodes.cpp
intern/cycles/render/nodes.h
intern/cycles/util/util_texture.h
source/blender/blenkernel/BKE_blender_version.h
source/blender/blenkernel/intern/image.c
source/blender/blenloader/intern/versioning_260.c
source/blender/blenloader/intern/versioning_280.c
source/blender/editors/space_image/image_buttons.c
source/blender/imbuf/IMB_imbuf.h
source/blender/imbuf/IMB_imbuf_types.h
source/blender/imbuf/intern/colormanagement.c
source/blender/imbuf/intern/divers.c
source/blender/imbuf/intern/readimage.c
source/blender/makesdna/DNA_image_types.h
source/blender/makesrna/intern/rna_image.c
source/blender/nodes/shader/nodes/node_shader_tex_image.c
source/blender/render/intern/source/imagetexture.c

index 1b47c4123e3d18034defcc7a00a53f50a734d78d..2a5c163aaeb5c0b12f5fe7161777078512d6701c 100644 (file)
@@ -292,7 +292,6 @@ static void create_mesh_volume_attribute(
   VoxelAttribute *volume_data = attr->data_voxel();
   ImageMetaData metadata;
   bool animated = false;
-  bool use_alpha = true;
 
   volume_data->manager = image_manager;
   volume_data->slot = image_manager->add_image(Attribute::standard_name(std),
@@ -301,7 +300,7 @@ static void create_mesh_volume_attribute(
                                                frame,
                                                INTERPOLATION_LINEAR,
                                                EXTENSION_CLIP,
-                                               use_alpha,
+                                               IMAGE_ALPHA_AUTO,
                                                u_colorspace_raw,
                                                metadata);
 }
index f42a15706e71a31c93baf8ad2a4d9df7fb6064f6..13097f6bf8ef7c0a289872bd0c0e2deee41e8a11 100644 (file)
@@ -90,6 +90,12 @@ template<typename NodeType> static ExtensionType get_image_extension(NodeType &b
   return (ExtensionType)validate_enum_value(value, EXTENSION_NUM_TYPES, EXTENSION_REPEAT);
 }
 
+static ImageAlphaType get_image_alpha_type(BL::Image &b_image)
+{
+  int value = b_image.alpha_mode();
+  return (ImageAlphaType)validate_enum_value(value, IMAGE_ALPHA_NUM_TYPES, IMAGE_ALPHA_AUTO);
+}
+
 /* Graph */
 
 static BL::NodeSocket get_node_output(BL::Node &b_node, const string &name)
@@ -655,7 +661,7 @@ static ShaderNode *add_node(Scene *scene,
       image->colorspace = get_enum_identifier(colorspace_ptr, "name");
 
       image->animated = b_image_node.image_user().use_auto_refresh();
-      image->use_alpha = b_image.use_alpha();
+      image->alpha_type = get_image_alpha_type(b_image);
 
       /* TODO: restore */
       /* TODO(sergey): Does not work properly when we change builtin type. */
@@ -703,7 +709,7 @@ static ShaderNode *add_node(Scene *scene,
       env->colorspace = get_enum_identifier(colorspace_ptr, "name");
 
       env->animated = b_env_node.image_user().use_auto_refresh();
-      env->use_alpha = b_image.use_alpha();
+      env->alpha_type = get_image_alpha_type(b_image);
 
       /* TODO: restore */
       /* TODO(sergey): Does not work properly when we change builtin type. */
@@ -868,7 +874,7 @@ static ShaderNode *add_node(Scene *scene,
                                              point_density->builtin_data,
                                              point_density->interpolation,
                                              EXTENSION_CLIP,
-                                             true,
+                                             IMAGE_ALPHA_AUTO,
                                              u_colorspace_raw);
     }
     node = point_density;
index 6301b41672417effd5cb5bbf9c0084abab9e55f8..160e7d9ff1eb1008020a41069e717814009b1d13 100644 (file)
@@ -299,12 +299,12 @@ static bool image_equals(ImageManager::Image *image,
                          void *builtin_data,
                          InterpolationType interpolation,
                          ExtensionType extension,
-                         bool use_alpha,
+                         ImageAlphaType alpha_type,
                          ustring colorspace)
 {
   return image->filename == filename && image->builtin_data == builtin_data &&
          image->interpolation == interpolation && image->extension == extension &&
-         image->use_alpha == use_alpha && image->colorspace == colorspace;
+         image->alpha_type == alpha_type && image->colorspace == colorspace;
 }
 
 int ImageManager::add_image(const string &filename,
@@ -313,7 +313,7 @@ int ImageManager::add_image(const string &filename,
                             float frame,
                             InterpolationType interpolation,
                             ExtensionType extension,
-                            bool use_alpha,
+                            ImageAlphaType alpha_type,
                             ustring colorspace,
                             ImageMetaData &metadata)
 {
@@ -338,14 +338,15 @@ int ImageManager::add_image(const string &filename,
   /* Fnd existing image. */
   for (slot = 0; slot < images[type].size(); slot++) {
     img = images[type][slot];
-    if (img && image_equals(
-                   img, filename, builtin_data, interpolation, extension, use_alpha, colorspace)) {
+    if (img &&
+        image_equals(
+            img, filename, builtin_data, interpolation, extension, alpha_type, colorspace)) {
       if (img->frame != frame) {
         img->frame = frame;
         img->need_load = true;
       }
-      if (img->use_alpha != use_alpha) {
-        img->use_alpha = use_alpha;
+      if (img->alpha_type != alpha_type) {
+        img->alpha_type = alpha_type;
         img->need_load = true;
       }
       if (img->colorspace != colorspace) {
@@ -399,7 +400,7 @@ int ImageManager::add_image(const string &filename,
   img->interpolation = interpolation;
   img->extension = extension;
   img->users = 1;
-  img->use_alpha = use_alpha;
+  img->alpha_type = alpha_type;
   img->colorspace = colorspace;
   img->mem = NULL;
 
@@ -445,7 +446,7 @@ void ImageManager::remove_image(const string &filename,
                                 void *builtin_data,
                                 InterpolationType interpolation,
                                 ExtensionType extension,
-                                bool use_alpha,
+                                ImageAlphaType alpha_type,
                                 ustring colorspace)
 {
   size_t slot;
@@ -457,7 +458,7 @@ void ImageManager::remove_image(const string &filename,
                                              builtin_data,
                                              interpolation,
                                              extension,
-                                             use_alpha,
+                                             alpha_type,
                                              colorspace)) {
         remove_image(type_index_to_flattened_slot(slot, (ImageDataType)type));
         return;
@@ -474,7 +475,7 @@ void ImageManager::tag_reload_image(const string &filename,
                                     void *builtin_data,
                                     InterpolationType interpolation,
                                     ExtensionType extension,
-                                    bool use_alpha,
+                                    ImageAlphaType alpha_type,
                                     ustring colorspace)
 {
   for (size_t type = 0; type < IMAGE_DATA_NUM_TYPES; type++) {
@@ -484,7 +485,7 @@ void ImageManager::tag_reload_image(const string &filename,
                                              builtin_data,
                                              interpolation,
                                              extension,
-                                             use_alpha,
+                                             alpha_type,
                                              colorspace)) {
         images[type][slot]->need_load = true;
         break;
@@ -516,7 +517,8 @@ bool ImageManager::file_load_image_generic(Image *img, unique_ptr<ImageInput> *i
     /* 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);
+                                   img->alpha_type == IMAGE_ALPHA_IGNORE ||
+                                   img->alpha_type == IMAGE_ALPHA_CHANNEL_PACKED);
 
     if (!associate_alpha) {
       config.attribute("oiio:UnassociatedAlpha", 1);
@@ -692,7 +694,7 @@ bool ImageManager::file_load_image(Image *img,
     }
 
     /* Disable alpha if requested by the user. */
-    if (img->use_alpha == false) {
+    if (img->alpha_type == IMAGE_ALPHA_IGNORE) {
       for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
         pixels[i * 4 + 3] = one;
       }
index 70d7fd3632d94813f424ded144b34c89582c89a4..ed2780f847143048a8f610e9e517a1d72e1c83de 100644 (file)
@@ -83,7 +83,7 @@ class ImageManager {
                 float frame,
                 InterpolationType interpolation,
                 ExtensionType extension,
-                bool use_alpha,
+                ImageAlphaType alpha_type,
                 ustring colorspace,
                 ImageMetaData &metadata);
   void add_image_user(int flat_slot);
@@ -92,13 +92,13 @@ class ImageManager {
                     void *builtin_data,
                     InterpolationType interpolation,
                     ExtensionType extension,
-                    bool use_alpha,
+                    ImageAlphaType alpha_type,
                     ustring colorspace);
   void tag_reload_image(const string &filename,
                         void *builtin_data,
                         InterpolationType interpolation,
                         ExtensionType extension,
-                        bool use_alpha,
+                        ImageAlphaType alpha_type,
                         ustring colorspace);
   bool get_image_metadata(const string &filename,
                           void *builtin_data,
@@ -147,7 +147,7 @@ class ImageManager {
     ImageMetaData metadata;
 
     ustring colorspace;
-    bool use_alpha;
+    ImageAlphaType alpha_type;
     bool need_load;
     bool animated;
     float frame;
index 3905189c8b0ff7217467abe999cd32d80a13fc6c..c5b0f6a1e79ba853a40abbd4c1fd428823473d1e 100644 (file)
@@ -210,7 +210,13 @@ NODE_DEFINE(ImageTextureNode)
   SOCKET_STRING(filename, "Filename", ustring());
   SOCKET_STRING(colorspace, "Colorspace", u_colorspace_auto);
 
-  SOCKET_BOOLEAN(use_alpha, "Use Alpha", true);
+  static NodeEnum alpha_type_enum;
+  alpha_type_enum.insert("auto", IMAGE_ALPHA_AUTO);
+  alpha_type_enum.insert("unassociated", IMAGE_ALPHA_UNASSOCIATED);
+  alpha_type_enum.insert("associated", IMAGE_ALPHA_ASSOCIATED);
+  alpha_type_enum.insert("channel_packed", IMAGE_ALPHA_CHANNEL_PACKED);
+  alpha_type_enum.insert("ignore", IMAGE_ALPHA_IGNORE);
+  SOCKET_ENUM(alpha_type, "Alpha Type", alpha_type_enum, IMAGE_ALPHA_AUTO);
 
   static NodeEnum interpolation_enum;
   interpolation_enum.insert("closest", INTERPOLATION_CLOSEST);
@@ -257,7 +263,7 @@ ImageTextureNode::~ImageTextureNode()
 {
   if (image_manager) {
     image_manager->remove_image(
-        filename.string(), builtin_data, interpolation, extension, use_alpha, colorspace);
+        filename.string(), builtin_data, interpolation, extension, alpha_type, colorspace);
   }
 }
 
@@ -300,12 +306,12 @@ void ImageTextureNode::compile(SVMCompiler &compiler)
                                     0,
                                     interpolation,
                                     extension,
-                                    use_alpha,
+                                    alpha_type,
                                     colorspace,
                                     metadata);
     is_float = metadata.is_float;
     compress_as_srgb = metadata.compress_as_srgb;
-    colorspace = metadata.colorspace;
+    known_colorspace = metadata.colorspace;
   }
 
   if (slot != -1) {
@@ -317,7 +323,8 @@ void ImageTextureNode::compile(SVMCompiler &compiler)
     }
     if (!alpha_out->links.empty()) {
       const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) ||
-                                       use_alpha == false);
+                                       alpha_type == IMAGE_ALPHA_CHANNEL_PACKED ||
+                                       alpha_type == IMAGE_ALPHA_IGNORE);
 
       if (unassociate_alpha) {
         flags |= NODE_IMAGE_ALPHA_UNASSOCIATE;
@@ -378,29 +385,30 @@ void ImageTextureNode::compile(OSLCompiler &compiler)
                                       0,
                                       interpolation,
                                       extension,
-                                      use_alpha,
+                                      alpha_type,
                                       colorspace,
                                       metadata);
     }
     is_float = metadata.is_float;
     compress_as_srgb = metadata.compress_as_srgb;
-    colorspace = metadata.colorspace;
+    known_colorspace = metadata.colorspace;
   }
 
   if (slot == -1) {
-    compiler.parameter_texture("filename", filename, colorspace);
+    compiler.parameter_texture("filename", filename, known_colorspace);
   }
   else {
     compiler.parameter_texture("filename", slot);
   }
 
   const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) ||
-                                   use_alpha == false);
+                                   alpha_type == IMAGE_ALPHA_CHANNEL_PACKED ||
+                                   alpha_type == IMAGE_ALPHA_IGNORE);
 
   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("compress_as_srgb", compress_as_srgb);
+  compiler.parameter("ignore_alpha", alpha_type == IMAGE_ALPHA_IGNORE);
   compiler.parameter("unassociate_alpha", !alpha_out->links.empty() && unassociate_alpha);
   compiler.parameter("is_float", is_float);
   compiler.parameter(this, "interpolation");
@@ -420,7 +428,13 @@ NODE_DEFINE(EnvironmentTextureNode)
   SOCKET_STRING(filename, "Filename", ustring());
   SOCKET_STRING(colorspace, "Colorspace", u_colorspace_auto);
 
-  SOCKET_BOOLEAN(use_alpha, "Use Alpha", true);
+  static NodeEnum alpha_type_enum;
+  alpha_type_enum.insert("auto", IMAGE_ALPHA_AUTO);
+  alpha_type_enum.insert("unassociated", IMAGE_ALPHA_UNASSOCIATED);
+  alpha_type_enum.insert("associated", IMAGE_ALPHA_ASSOCIATED);
+  alpha_type_enum.insert("channel_packed", IMAGE_ALPHA_CHANNEL_PACKED);
+  alpha_type_enum.insert("ignore", IMAGE_ALPHA_IGNORE);
+  SOCKET_ENUM(alpha_type, "Alpha Type", alpha_type_enum, IMAGE_ALPHA_AUTO);
 
   static NodeEnum interpolation_enum;
   interpolation_enum.insert("closest", INTERPOLATION_CLOSEST);
@@ -457,7 +471,7 @@ EnvironmentTextureNode::~EnvironmentTextureNode()
 {
   if (image_manager) {
     image_manager->remove_image(
-        filename.string(), builtin_data, interpolation, EXTENSION_REPEAT, use_alpha, colorspace);
+        filename.string(), builtin_data, interpolation, EXTENSION_REPEAT, alpha_type, colorspace);
   }
 }
 
@@ -498,12 +512,12 @@ void EnvironmentTextureNode::compile(SVMCompiler &compiler)
                                     0,
                                     interpolation,
                                     EXTENSION_REPEAT,
-                                    use_alpha,
+                                    alpha_type,
                                     colorspace,
                                     metadata);
     is_float = metadata.is_float;
     compress_as_srgb = metadata.compress_as_srgb;
-    colorspace = metadata.colorspace;
+    known_colorspace = metadata.colorspace;
   }
 
   if (slot != -1) {
@@ -558,17 +572,17 @@ void EnvironmentTextureNode::compile(OSLCompiler &compiler)
                                       0,
                                       interpolation,
                                       EXTENSION_REPEAT,
-                                      use_alpha,
+                                      alpha_type,
                                       colorspace,
                                       metadata);
     }
     is_float = metadata.is_float;
     compress_as_srgb = metadata.compress_as_srgb;
-    colorspace = metadata.colorspace;
+    known_colorspace = metadata.colorspace;
   }
 
   if (slot == -1) {
-    compiler.parameter_texture("filename", filename, colorspace);
+    compiler.parameter_texture("filename", filename, known_colorspace);
   }
   else {
     compiler.parameter_texture("filename", slot);
@@ -576,8 +590,8 @@ void EnvironmentTextureNode::compile(OSLCompiler &compiler)
 
   compiler.parameter(this, "projection");
   compiler.parameter(this, "interpolation");
-  compiler.parameter("convert_from_srgb", compress_as_srgb);
-  compiler.parameter("ignore_alpha", !use_alpha);
+  compiler.parameter("compress_as_srgb", compress_as_srgb);
+  compiler.parameter("ignore_alpha", alpha_type == IMAGE_ALPHA_IGNORE);
   compiler.parameter("is_float", is_float);
   compiler.add(this, "node_environment_texture");
 }
@@ -1490,8 +1504,12 @@ PointDensityTextureNode::PointDensityTextureNode() : ShaderNode(node_type)
 PointDensityTextureNode::~PointDensityTextureNode()
 {
   if (image_manager) {
-    image_manager->remove_image(
-        filename.string(), builtin_data, interpolation, EXTENSION_CLIP, true, ustring());
+    image_manager->remove_image(filename.string(),
+                                builtin_data,
+                                interpolation,
+                                EXTENSION_CLIP,
+                                IMAGE_ALPHA_AUTO,
+                                ustring());
   }
 }
 
@@ -1524,7 +1542,7 @@ void PointDensityTextureNode::add_image()
                                     0,
                                     interpolation,
                                     EXTENSION_CLIP,
-                                    true,
+                                    IMAGE_ALPHA_AUTO,
                                     u_colorspace_raw,
                                     metadata);
   }
index 3dd84ad8dcabcf9bc36d170756ebb6a6bf5d0436..6b21be88663c6526ba4e1e4b95d93e92cdd42cbb 100644 (file)
@@ -100,10 +100,10 @@ class ImageTextureNode : public ImageSlotTextureNode {
   }
 
   /* Parameters. */
-  bool use_alpha;
   ustring filename;
   void *builtin_data;
   ustring colorspace;
+  ImageAlphaType alpha_type;
   NodeImageProjection projection;
   InterpolationType interpolation;
   ExtensionType extension;
@@ -141,10 +141,10 @@ class EnvironmentTextureNode : public ImageSlotTextureNode {
   }
 
   /* Parameters. */
-  bool use_alpha;
   ustring filename;
   void *builtin_data;
   ustring colorspace;
+  ImageAlphaType alpha_type;
   NodeEnvironmentProjection projection;
   InterpolationType interpolation;
   bool animated;
index 5ce16e0095a2338621ba75659ffbdcdbd4a665c0..d43852480d15596c06e7dad18db8b6c48da712ee 100644 (file)
@@ -59,6 +59,18 @@ typedef enum ImageDataType {
   IMAGE_DATA_NUM_TYPES
 } ImageDataType;
 
+/* Alpha types
+ * How to treat alpha in images. */
+typedef enum ImageAlphaType {
+  IMAGE_ALPHA_UNASSOCIATED = 0,
+  IMAGE_ALPHA_ASSOCIATED = 1,
+  IMAGE_ALPHA_CHANNEL_PACKED = 2,
+  IMAGE_ALPHA_IGNORE = 3,
+  IMAGE_ALPHA_AUTO = 4,
+
+  IMAGE_ALPHA_NUM_TYPES,
+} ImageAlphaType;
+
 #define IMAGE_DATA_TYPE_SHIFT 3
 #define IMAGE_DATA_TYPE_MASK 0x7
 
index 5d659d63e277c0d64bfa360de51590e219eb7bb1..077bbce2264814a8a23d4bb0f4e66eaf8dd9f510 100644 (file)
@@ -27,7 +27,7 @@
  * \note Use #STRINGIFY() rather than defining with quotes.
  */
 #define BLENDER_VERSION 280
-#define BLENDER_SUBVERSION 69
+#define BLENDER_SUBVERSION 70
 /** Several breakages with 280, e.g. collections vs layers. */
 #define BLENDER_MINVERSION 280
 #define BLENDER_MINSUBVERSION 0
index d59ead25396083384109ada02c1f8040987fdd96..9960994400faa3aed41f5afda31f0aef06c78c01 100644 (file)
@@ -502,6 +502,12 @@ static void image_init_color_management(Image *ima)
     if (ibuf->flags & IB_alphamode_premul) {
       ima->alpha_mode = IMA_ALPHA_PREMUL;
     }
+    else if (ibuf->flags & IB_alphamode_channel_packed) {
+      ima->alpha_mode = IMA_ALPHA_CHANNEL_PACKED;
+    }
+    else if (ibuf->flags & IB_alphamode_ignore) {
+      ima->alpha_mode = IMA_ALPHA_IGNORE;
+    }
     else {
       ima->alpha_mode = IMA_ALPHA_STRAIGHT;
     }
@@ -3592,16 +3598,18 @@ static void image_initialize_after_load(Image *ima, ImBuf *UNUSED(ibuf))
 
 static int imbuf_alpha_flags_for_image(Image *ima)
 {
-  int flag = 0;
-
-  if (ima->flag & IMA_IGNORE_ALPHA) {
-    flag |= IB_ignore_alpha;
-  }
-  else if (ima->alpha_mode == IMA_ALPHA_PREMUL) {
-    flag |= IB_alphamode_premul;
+  switch (ima->alpha_mode) {
+    case IMA_ALPHA_STRAIGHT:
+      return 0;
+    case IMA_ALPHA_PREMUL:
+      return IB_alphamode_premul;
+    case IMA_ALPHA_CHANNEL_PACKED:
+      return IB_alphamode_channel_packed;
+    case IMA_ALPHA_IGNORE:
+      return IB_alphamode_ignore;
   }
 
-  return flag;
+  return 0;
 }
 
 /* the number of files will vary according to the stereo format */
index 77ef27182f2090fa563b50f57383dc5571a5e502..7b0aab99aea6e8527133f80841d619eccffdc96a 100644 (file)
@@ -1808,6 +1808,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
         Image *image = blo_do_versions_newlibadr(fd, tex->id.lib, tex->ima);
 
         if (image && (image->flag & IMA_DO_PREMUL) == 0) {
+          const int IMA_IGNORE_ALPHA = (1 << 12);
           image->flag |= IMA_IGNORE_ALPHA;
         }
       }
index 7ba0eb7b791e5ee558fab7a18b76b0a907783e76..13ccc374073a752d85be35e3414377d6588546c7 100644 (file)
@@ -3450,6 +3450,17 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
     }
   }
 
+  if (!MAIN_VERSION_ATLEAST(bmain, 280, 70)) {
+    /* New image alpha modes. */
+    LISTBASE_FOREACH (Image *, image, &bmain->images) {
+      const int IMA_IGNORE_ALPHA = (1 << 12);
+      if (image->flag & IMA_IGNORE_ALPHA) {
+        image->alpha_mode = IMA_ALPHA_IGNORE;
+        image->flag &= ~IMA_IGNORE_ALPHA;
+      }
+    }
+  }
+
   {
     /* Versioning code until next subversion bump goes here. */
   }
index 68af854e367b310486a6d3f316cb49b6cc7fbc60..9f4293295972ca67e4905774f9a90d154cef2749 100644 (file)
@@ -42,6 +42,7 @@
 
 #include "RE_pipeline.h"
 
+#include "IMB_colormanagement.h"
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
 
@@ -995,10 +996,11 @@ void uiTemplateImage(uiLayout *layout,
 
           if (has_alpha) {
             col = uiLayoutColumn(layout, false);
-            uiItemR(col, &imaptr, "use_alpha", 0, NULL, ICON_NONE);
-            row = uiLayoutRow(col, false);
-            uiLayoutSetActive(row, RNA_boolean_get(&imaptr, "use_alpha"));
-            uiItemR(row, &imaptr, "alpha_mode", 0, IFACE_("Alpha"), ICON_NONE);
+            uiItemR(col, &imaptr, "alpha_mode", 0, IFACE_("Alpha"), ICON_NONE);
+
+            /* Alpha mode has no effect for non-color data. */
+            bool is_data = IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name);
+            uiLayoutSetActive(col, !is_data);
           }
 
           if (ima->source == IMA_SRC_MOVIE) {
index 674dc61cd9e22f8e5f6e0e720dbeb7f28443b83a..8574f33bce69b444b8ad25bb41bca858e18426c7 100644 (file)
@@ -476,6 +476,9 @@ int imb_get_anim_type(const char *name);
  */
 bool IMB_isfloat(struct ImBuf *ibuf);
 
+/* Do byte/float and colorspace conversions need to take alpha into account? */
+bool IMB_alpha_affects_rgb(const struct ImBuf *ibuf);
+
 /* create char buffer, color corrected if necessary, for ImBufs that lack one */
 void IMB_rect_from_float(struct ImBuf *ibuf);
 void IMB_float_from_rect(struct ImBuf *ibuf);
index 0f2529e261ab98176b928f60a4706c6cd31e323f..61aa1f401a0be2e9d98d036ee166a11bfc36f9c9 100644 (file)
@@ -285,10 +285,12 @@ enum {
   IB_alphamode_premul = 1 << 12,
   /** if this flag is set, alpha mode would be guessed from file */
   IB_alphamode_detect = 1 << 13,
+  /* alpha channel is unrelated to RGB and should not affect it */
+  IB_alphamode_channel_packed = 1 << 14,
   /** ignore alpha on load and substitute it with 1.0f */
-  IB_ignore_alpha = 1 << 14,
-  IB_thumbnail = 1 << 15,
-  IB_multiview = 1 << 16,
+  IB_alphamode_ignore = 1 << 15,
+  IB_thumbnail = 1 << 16,
+  IB_multiview = 1 << 17,
 };
 
 /** \} */
index ba0f10446a1546e6991cffc9f85fd2ae4800c3e7..8a10af7e184ab140d11a3f9e22eac84a908093df 100644 (file)
@@ -1057,13 +1057,19 @@ void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace)
 
   if (ibuf->rect_float) {
     const char *to_colorspace = global_role_scene_linear;
+    const bool predivide = IMB_alpha_affects_rgb(ibuf);
 
     if (ibuf->rect) {
       imb_freerectImBuf(ibuf);
     }
 
-    IMB_colormanagement_transform(
-        ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, true);
+    IMB_colormanagement_transform(ibuf->rect_float,
+                                  ibuf->x,
+                                  ibuf->y,
+                                  ibuf->channels,
+                                  from_colorspace,
+                                  to_colorspace,
+                                  predivide);
   }
 }
 
@@ -1405,6 +1411,7 @@ typedef struct DisplayBufferThread {
   int channels;
   float dither;
   bool is_data;
+  bool predivide;
 
   const char *byte_colorspace;
   const char *float_colorspace;
@@ -1469,6 +1476,7 @@ static void display_buffer_init_handle(void *handle_v,
   handle->channels = channels;
   handle->dither = dither;
   handle->is_data = is_data;
+  handle->predivide = IMB_alpha_affects_rgb(ibuf);
 
   handle->byte_colorspace = init_data->byte_colorspace;
   handle->float_colorspace = init_data->float_colorspace;
@@ -1486,6 +1494,7 @@ static void display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle,
 
   bool is_data = handle->is_data;
   bool is_data_display = handle->cm_processor->is_data_result;
+  bool predivide = handle->predivide;
 
   if (!handle->buffer) {
     unsigned char *byte_buffer = handle->byte_buffer;
@@ -1534,7 +1543,7 @@ static void display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle,
 
     if (!is_data && !is_data_display) {
       IMB_colormanagement_transform(
-          linear_buffer, width, height, channels, from_colorspace, to_colorspace, true);
+          linear_buffer, width, height, channels, from_colorspace, to_colorspace, predivide);
     }
 
     *is_straight_alpha = false;
@@ -1590,13 +1599,13 @@ static void *do_display_buffer_apply_thread(void *handle_v)
     }
   }
   else {
-    bool is_straight_alpha, predivide;
+    bool is_straight_alpha;
     float *linear_buffer = MEM_mallocN(((size_t)channels) * width * height * sizeof(float),
                                        "color conversion linear buffer");
 
     display_buffer_apply_get_linear_buffer(handle, height, linear_buffer, &is_straight_alpha);
 
-    predivide = is_straight_alpha == false;
+    bool predivide = handle->predivide && (is_straight_alpha == false);
 
     if (is_data) {
       /* special case for data buffers - no color space conversions,
@@ -2178,6 +2187,7 @@ void IMB_colormanagement_imbuf_to_srgb_texture(unsigned char *out_buffer,
 
   /* TODO(brecht): make this multithreaded, or at least process in batches. */
   const unsigned char *in_buffer = (unsigned char *)ibuf->rect;
+  const bool use_premultiply = IMB_alpha_affects_rgb(ibuf);
 
   for (int y = 0; y < height; y++) {
     const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
@@ -2192,11 +2202,13 @@ void IMB_colormanagement_imbuf_to_srgb_texture(unsigned char *out_buffer,
         rgba_uchar_to_float(pixel, in);
         OCIO_processorApplyRGB(processor, pixel);
         linearrgb_to_srgb_v3_v3(pixel, pixel);
-        mul_v3_fl(pixel, pixel[3]);
+        if (use_premultiply) {
+          mul_v3_fl(pixel, pixel[3]);
+        }
         rgba_float_to_uchar(out, pixel);
       }
     }
-    else {
+    else if (use_premultiply) {
       /* Premultiply only. */
       for (int x = 0; x < width; x++, in += 4, out += 4) {
         out[0] = (in[0] * in[3]) >> 8;
@@ -2205,6 +2217,15 @@ void IMB_colormanagement_imbuf_to_srgb_texture(unsigned char *out_buffer,
         out[3] = in[3];
       }
     }
+    else {
+      /* Copy only. */
+      for (int x = 0; x < width; x++, in += 4, out += 4) {
+        out[0] = in[0];
+        out[1] = in[1];
+        out[2] = in[2];
+        out[3] = in[3];
+      }
+    }
   }
 }
 
index 1e16f0975e26d3870f025f8291216bd5585d13b8..aa49453d68c9e56f3f83bc6c415dac01ee0580f9 100644 (file)
@@ -96,6 +96,12 @@ MINLINE void float_to_byte_dither_v4(
   b[3] = unit_float_to_uchar_clamp(f[3]);
 }
 
+/* Test if colorspace conversions of pixels in buffer need to take into account alpha. */
+bool IMB_alpha_affects_rgb(const ImBuf *ibuf)
+{
+  return (ibuf->flags & IB_alphamode_channel_packed) == 0;
+}
+
 /* float to byte pixels, output 4-channel RGBA */
 void IMB_buffer_byte_from_float(uchar *rect_to,
                                 const float *rect_from,
@@ -728,16 +734,19 @@ void IMB_rect_from_float(ImBuf *ibuf)
   buffer = MEM_dupallocN(ibuf->rect_float);
 
   /* first make float buffer in byte space */
+  const bool predivide = IMB_alpha_affects_rgb(ibuf);
   IMB_colormanagement_transform(buffer,
                                 ibuf->x,
                                 ibuf->y,
                                 ibuf->channels,
                                 from_colorspace,
                                 ibuf->rect_colorspace->name,
-                                true);
+                                predivide);
 
   /* convert from float's premul alpha to byte's straight alpha */
-  IMB_unpremultiply_rect_float(buffer, ibuf->channels, ibuf->x, ibuf->y);
+  if (IMB_alpha_affects_rgb(ibuf)) {
+    IMB_unpremultiply_rect_float(buffer, ibuf->channels, ibuf->x, ibuf->y);
+  }
 
   /* convert float to byte */
   IMB_buffer_byte_from_float((unsigned char *)ibuf->rect,
@@ -802,7 +811,9 @@ void IMB_float_from_rect(ImBuf *ibuf)
       rect_float, ibuf->x, ibuf->y, ibuf->channels, ibuf->rect_colorspace, false);
 
   /* byte buffer is straight alpha, float should always be premul */
-  IMB_premultiply_rect_float(rect_float, ibuf->channels, ibuf->x, ibuf->y);
+  if (IMB_alpha_affects_rgb(ibuf)) {
+    IMB_premultiply_rect_float(rect_float, ibuf->channels, ibuf->x, ibuf->y);
+  }
 
   if (ibuf->rect_float == NULL) {
     ibuf->rect_float = rect_float;
index 9297227bc8781b076732ca6a80460d47d756ff34..d9f3c7ec6bb9e403f17ff225af62655d5eaa2325 100644 (file)
@@ -61,20 +61,16 @@ static void imb_handle_alpha(ImBuf *ibuf,
   }
 
   bool is_data = (colorspace && IMB_colormanagement_space_name_is_data(colorspace));
-  int alpha_flags;
+  int alpha_flags = (flags & IB_alphamode_detect) ? ibuf->flags : flags;
 
-  if (flags & IB_alphamode_detect) {
-    alpha_flags = ibuf->flags & IB_alphamode_premul;
-  }
-  else {
-    alpha_flags = flags & IB_alphamode_premul;
-  }
-
-  if (is_data) {
+  if (is_data || (flags & IB_alphamode_channel_packed)) {
     /* Don't touch alpha. */
+    ibuf->flags |= IB_alphamode_channel_packed;
   }
-  else if (flags & IB_ignore_alpha) {
+  else if (flags & IB_alphamode_ignore) {
+    /* Make opaque. */
     IMB_rectfill_alpha(ibuf, 1.0f);
+    ibuf->flags |= IB_alphamode_ignore;
   }
   else {
     if (alpha_flags & IB_alphamode_premul) {
index 3e42aa17492c1f44430973dc52aab0277eca69d2..6141472125f2102f65e054dd11e91c3232c5ec11 100644 (file)
@@ -178,7 +178,7 @@ enum {
   /** For image user, but these flags are mixed. */
   IMA_USER_FRAME_IN_RANGE = (1 << 10),
   IMA_VIEW_AS_RENDER = (1 << 11),
-  IMA_IGNORE_ALPHA = (1 << 12),
+  IMA_FLAG_UNUSED_12 = (1 << 12), /* cleared */
   IMA_DEINTERLACE = (1 << 13),
   IMA_USE_VIEWS = (1 << 14),
   IMA_FLAG_UNUSED_15 = (1 << 15), /* cleared */
@@ -233,6 +233,8 @@ enum {
 enum {
   IMA_ALPHA_STRAIGHT = 0,
   IMA_ALPHA_PREMUL = 1,
+  IMA_ALPHA_CHANNEL_PACKED = 2,
+  IMA_ALPHA_IGNORE = 3,
 };
 
 #endif
index 40c8c7e1582b9a9c16673e5b08b787aeaa010af9..c033e393625e451baf48fe279b5d179a4e72d1a8 100644 (file)
@@ -680,12 +680,26 @@ static void rna_def_image(BlenderRNA *brna)
        "STRAIGHT",
        0,
        "Straight",
-       "Transparent RGB and alpha pixels are unmodified"},
+       "Store RGB and alpha channels separately with alpha acting as a mask, also known as "
+       "unassociated alpha. Commonly used by image editing applications and file formats like "
+       "PNG"},
       {IMA_ALPHA_PREMUL,
        "PREMUL",
        0,
        "Premultiplied",
-       "Transparent RGB pixels are multiplied by the alpha channel"},
+       "Store RGB channels with alpha multipled in, also known as associated alpha. The natural "
+       "format for renders and used by file formats like OpenEXR"},
+      {IMA_ALPHA_CHANNEL_PACKED,
+       "CHANNEL_PACKED",
+       0,
+       "Channel Packed",
+       "Different images are packed in the RGB and alpha channels, and they should not "
+       "affect each other. Channel packing is commonly used by game engines to save memory"},
+      {IMA_ALPHA_IGNORE,
+       "NONE",
+       0,
+       "None",
+       "Ignore alpha channel from the file and make image fully opaque"},
       {0, NULL, 0, NULL, NULL},
   };
 
@@ -744,15 +758,6 @@ static void rna_def_image(BlenderRNA *brna)
       "Apply render part of display transformation when displaying this image on the screen");
   RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL);
 
-  prop = RNA_def_property(srna, "use_alpha", PROP_BOOLEAN, PROP_NONE);
-  RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC);
-  RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", IMA_IGNORE_ALPHA);
-  RNA_def_property_ui_text(
-      prop,
-      "Use Alpha",
-      "Use the alpha channel information from the image or make image fully opaque");
-  RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_colormanage_update");
-
   prop = RNA_def_property(srna, "use_deinterlace", PROP_BOOLEAN, PROP_NONE);
   RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC);
   RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_DEINTERLACE);
index 8086bb2e15d59bc37aa9a4d3ac7cd153c7c66493..786386bb63e2f8d6eeab616264ac0c0f77688740 100644 (file)
@@ -184,7 +184,8 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
      * 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)) {
+        !(ELEM(ima->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED) ||
+          IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name))) {
       GPU_link(mat, "tex_color_alpha_unpremultiply", out[0].link, &out[0].link);
     }
     else {
index c147794dae77fad134257e89fcc1460b4cb93e49..1aa16dc1019688292fce8acf948b02a9faf9d31d 100644 (file)
@@ -233,7 +233,7 @@ int imagewrap(Tex *tex,
 
   /* keep this before interpolation [#29761] */
   if (ima) {
-    if ((tex->imaflag & TEX_USEALPHA) && (ima->flag & IMA_IGNORE_ALPHA) == 0) {
+    if ((tex->imaflag & TEX_USEALPHA) && (ima->alpha_mode != IMA_ALPHA_IGNORE)) {
       if ((tex->imaflag & TEX_CALCALPHA) == 0) {
         texres->talpha = true;
       }
@@ -1056,7 +1056,7 @@ static int imagewraposa_aniso(Tex *tex,
   image_mipmap_test(tex, ibuf);
 
   if (ima) {
-    if ((tex->imaflag & TEX_USEALPHA) && (ima->flag & IMA_IGNORE_ALPHA) == 0) {
+    if ((tex->imaflag & TEX_USEALPHA) && (ima->alpha_mode != IMA_ALPHA_IGNORE)) {
       if ((tex->imaflag & TEX_CALCALPHA) == 0) {
         texres->talpha = 1;
       }
@@ -1512,7 +1512,7 @@ int imagewraposa(Tex *tex,
   image_mipmap_test(tex, ibuf);
 
   if (ima) {
-    if ((tex->imaflag & TEX_USEALPHA) && (ima->flag & IMA_IGNORE_ALPHA) == 0) {
+    if ((tex->imaflag & TEX_USEALPHA) && (ima->alpha_mode != IMA_ALPHA_IGNORE)) {
       if ((tex->imaflag & TEX_CALCALPHA) == 0) {
         texres->talpha = true;
       }