diff options
21 files changed, 200 insertions, 100 deletions
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index 1b47c4123e3..2a5c163aaeb 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -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); } diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index f42a15706e7..13097f6bf8e 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -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; diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 6301b416724..160e7d9ff1e 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -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; } diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h index 70d7fd3632d..ed2780f8471 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/render/image.h @@ -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; diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 3905189c8b0..c5b0f6a1e79 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -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); } diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 3dd84ad8dca..6b21be88663 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -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; diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/util_texture.h index 5ce16e0095a..d43852480d1 100644 --- a/intern/cycles/util/util_texture.h +++ b/intern/cycles/util/util_texture.h @@ -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 diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 5d659d63e27..077bbce2264 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -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 diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index d59ead25396..9960994400f 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -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 */ diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 77ef27182f2..7b0aab99aea 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -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; } } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 7ba0eb7b791..13ccc374073 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -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. */ } diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 68af854e367..9f429329597 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -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) { diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 674dc61cd9e..8574f33bce6 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -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); diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index 0f2529e261a..61aa1f401a0 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -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, }; /** \} */ diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index ba0f10446a1..8a10af7e184 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -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]; + } + } } } diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index 1e16f0975e2..aa49453d68c 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -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; diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c index 9297227bc87..d9f3c7ec6bb 100644 --- a/source/blender/imbuf/intern/readimage.c +++ b/source/blender/imbuf/intern/readimage.c @@ -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) { diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 3e42aa17492..6141472125f 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -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 diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 40c8c7e1582..c033e393625 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -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); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.c index 8086bb2e15d..786386bb63e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c @@ -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 { diff --git a/source/blender/render/intern/source/imagetexture.c b/source/blender/render/intern/source/imagetexture.c index c147794dae7..1aa16dc1019 100644 --- a/source/blender/render/intern/source/imagetexture.c +++ b/source/blender/render/intern/source/imagetexture.c @@ -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; } |