Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_utils.c6
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl13
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl13
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl12
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl9
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl3
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl3
-rw-r--r--source/blender/draw/engines/workbench/workbench_materials.c2
-rw-r--r--source/blender/draw/modes/object_mode.c7
-rw-r--r--source/blender/draw/modes/paint_texture_mode.c9
-rw-r--r--source/blender/draw/modes/shaders/object_empty_image_frag.glsl9
-rw-r--r--source/blender/draw/modes/shaders/paint_texture_frag.glsl13
-rw-r--r--source/blender/gpu/intern/gpu_draw.c75
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material.glsl5
-rw-r--r--source/blender/imbuf/IMB_colormanagement.h12
-rw-r--r--source/blender/imbuf/intern/colormanagement.c59
-rw-r--r--source/blender/makesrna/intern/rna_image_api.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_environment.c15
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_image.c32
19 files changed, 221 insertions, 78 deletions
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c
index cd35395064b..9babd8cd058 100644
--- a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c
@@ -467,6 +467,8 @@ static DRWShadingGroup *DRW_gpencil_shgroup_fill_create(GPENCIL_e_data *e_data,
else {
GPUTexture *texture = GPU_texture_from_blender(gp_style->ima, &iuser, GL_TEXTURE_2D);
DRW_shgroup_uniform_texture(grp, "myTexture", texture);
+ DRW_shgroup_uniform_bool_copy(
+ grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL));
stl->shgroups[id].texture_clamp = gp_style->flag & GP_STYLE_COLOR_TEX_CLAMP ? 1 : 0;
DRW_shgroup_uniform_int(grp, "texture_clamp", &stl->shgroups[id].texture_clamp, 1);
@@ -633,6 +635,8 @@ DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(GPENCIL_e_data *e_data,
else {
GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D);
DRW_shgroup_uniform_texture(grp, "myTexture", texture);
+ DRW_shgroup_uniform_bool_copy(
+ grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL));
BKE_image_release_ibuf(image, ibuf, NULL);
}
@@ -789,6 +793,8 @@ static DRWShadingGroup *DRW_gpencil_shgroup_point_create(GPENCIL_e_data *e_data,
else {
GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D);
DRW_shgroup_uniform_texture(grp, "myTexture", texture);
+ DRW_shgroup_uniform_bool_copy(
+ grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL));
BKE_image_release_ibuf(image, ibuf, NULL);
}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl
index 192720a4e98..1fdfd05332e 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl
@@ -19,6 +19,7 @@ uniform int drawmode;
uniform float layer_opacity;
uniform sampler2D myTexture;
+uniform bool myTexturePremultiplied;
uniform int texture_clamp;
uniform int viewport_xray;
@@ -99,11 +100,15 @@ float linearrgb_to_srgb(float c)
}
}
-vec4 texture_read_as_srgb(sampler2D tex, vec2 co)
+vec4 texture_read_as_srgb(sampler2D tex, bool premultiplied, vec2 co)
{
/* By convention image textures return scene linear colors, but
* grease pencil still works in srgb. */
vec4 color = texture(tex, co);
+ /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */
+ if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) {
+ color.rgb = color.rgb / color.a;
+ }
color.r = linearrgb_to_srgb(color.r);
color.g = linearrgb_to_srgb(color.g);
color.b = linearrgb_to_srgb(color.b);
@@ -118,8 +123,10 @@ void main()
vec2 rot_tex = (matrot_tex * (texCoord_interp - t_center)) + t_center + texture_offset;
vec4 tmp_color;
tmp_color = (texture_clamp == 0) ?
- texture_read_as_srgb(myTexture, rot_tex * texture_scale) :
- texture_read_as_srgb(myTexture, clamp(rot_tex * texture_scale, 0.0, 1.0));
+ texture_read_as_srgb(
+ myTexture, myTexturePremultiplied, rot_tex * texture_scale) :
+ texture_read_as_srgb(
+ myTexture, myTexturePremultiplied, clamp(rot_tex * texture_scale, 0.0, 1.0));
vec4 text_color = vec4(tmp_color[0], tmp_color[1], tmp_color[2], tmp_color[3] * texture_opacity);
vec4 chesscolor;
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl
index 569a68679b4..7fed42aca0d 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl
@@ -1,6 +1,7 @@
uniform int color_type;
uniform int mode;
uniform sampler2D myTexture;
+uniform bool myTexturePremultiplied;
uniform float gradient_f;
uniform vec2 gradient_s;
@@ -57,11 +58,15 @@ float linearrgb_to_srgb(float c)
}
}
-vec4 texture_read_as_srgb(sampler2D tex, vec2 co)
+vec4 texture_read_as_srgb(sampler2D tex, bool premultiplied, vec2 co)
{
/* By convention image textures return scene linear colors, but
* grease pencil still works in srgb. */
vec4 color = texture(tex, co);
+ /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */
+ if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) {
+ color.rgb = color.rgb / color.a;
+ }
color.r = linearrgb_to_srgb(color.r);
color.g = linearrgb_to_srgb(color.g);
color.b = linearrgb_to_srgb(color.b);
@@ -86,15 +91,13 @@ void main()
}
}
- vec4 tmp_color = texture_read_as_srgb(myTexture, mTexCoord);
-
/* Solid */
if ((color_type == GPENCIL_COLOR_SOLID) || (no_texture)) {
fragColor = mColor;
}
/* texture */
if ((color_type == GPENCIL_COLOR_TEXTURE) && (!no_texture)) {
- vec4 text_color = texture_read_as_srgb(myTexture, mTexCoord);
+ vec4 text_color = texture_read_as_srgb(myTexture, myTexturePremultiplied, mTexCoord);
if (mix_stroke_factor > 0.0) {
fragColor.rgb = mix(text_color.rgb, colormix.rgb, mix_stroke_factor);
fragColor.a = text_color.a;
@@ -108,7 +111,7 @@ void main()
}
/* pattern */
if ((color_type == GPENCIL_COLOR_PATTERN) && (!no_texture)) {
- vec4 text_color = texture_read_as_srgb(myTexture, mTexCoord);
+ vec4 text_color = texture_read_as_srgb(myTexture, myTexturePremultiplied, mTexCoord);
fragColor = mColor;
/* mult both alpha factor to use strength factor with color alpha limit */
fragColor.a = min(text_color.a * mColor.a, mColor.a);
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl
index 7d0ebe88aa4..bc703d2a078 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl
@@ -1,5 +1,6 @@
uniform int color_type;
uniform sampler2D myTexture;
+uniform bool myTexturePremultiplied;
uniform float gradient_f;
@@ -37,11 +38,15 @@ float linearrgb_to_srgb(float c)
}
}
-vec4 texture_read_as_srgb(sampler2D tex, vec2 co)
+vec4 texture_read_as_srgb(sampler2D tex, bool premultiplied, vec2 co)
{
/* By convention image textures return scene linear colors, but
* grease pencil still works in srgb. */
vec4 color = texture(tex, co);
+ /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */
+ if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) {
+ color.rgb = color.rgb / color.a;
+ }
color.r = linearrgb_to_srgb(color.r);
color.g = linearrgb_to_srgb(color.g);
color.b = linearrgb_to_srgb(color.b);
@@ -68,10 +73,11 @@ void main()
/* texture for endcaps */
vec4 text_color;
if (uvfac[1] == ENDCAP) {
- text_color = texture_read_as_srgb(myTexture, vec2(mTexCoord.x, mTexCoord.y));
+ text_color = texture_read_as_srgb(
+ myTexture, myTexturePremultiplied, vec2(mTexCoord.x, mTexCoord.y));
}
else {
- text_color = texture_read_as_srgb(myTexture, mTexCoord);
+ text_color = texture_read_as_srgb(myTexture, myTexturePremultiplied, mTexCoord);
}
/* texture */
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl
index 96f8f6e4c7a..59a463f49c3 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl
@@ -140,7 +140,10 @@ vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped)
return matcap_uv * 0.496 + 0.5;
}
-vec4 workbench_sample_texture(sampler2D image, vec2 coord, bool nearest_sampling)
+vec4 workbench_sample_texture(sampler2D image,
+ vec2 coord,
+ bool nearest_sampling,
+ bool premultiplied)
{
vec2 tex_size = vec2(textureSize(image, 0).xy);
/* TODO(fclem) We could do the same with sampler objects.
@@ -148,8 +151,8 @@ vec4 workbench_sample_texture(sampler2D image, vec2 coord, bool nearest_sampling
vec2 uv = nearest_sampling ? (floor(coord * tex_size) + 0.5) / tex_size : coord;
vec4 color = texture(image, uv);
- /* Unpremultiply, ideally shaders would be added so this is not needed. */
- if (!(color.a == 0.0 || color.a == 1.0)) {
+ /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */
+ if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) {
color.rgb = color.rgb / color.a;
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl
index 51bce639b63..c78b2182d04 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl
@@ -2,6 +2,7 @@
uniform float ImageTransparencyCutoff = 0.1;
uniform sampler2D image;
uniform bool imageNearest;
+uniform bool imagePremultiplied;
uniform float alpha = 0.5;
uniform vec2 invertedViewportSize;
@@ -43,7 +44,7 @@ void main()
vec4 diffuse_color;
#if defined(V3D_SHADING_TEXTURE_COLOR)
- diffuse_color = workbench_sample_texture(image, uv_interp, imageNearest);
+ diffuse_color = workbench_sample_texture(image, uv_interp, imageNearest, imagePremultiplied);
if (diffuse_color.a < ImageTransparencyCutoff) {
discard;
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl
index af9f1d14f4a..c673b2484de 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl
@@ -7,6 +7,7 @@ uniform float materialRoughness;
uniform sampler2D image;
uniform float ImageTransparencyCutoff = 0.1;
uniform bool imageNearest;
+uniform bool imagePremultiplied;
#ifdef NORMAL_VIEWPORT_PASS_ENABLED
in vec3 normal_viewport;
@@ -40,7 +41,7 @@ void main()
vec4 color;
# if defined(V3D_SHADING_TEXTURE_COLOR)
- color = workbench_sample_texture(image, uv_interp, imageNearest);
+ color = workbench_sample_texture(image, uv_interp, imageNearest, imagePremultiplied);
if (color.a < ImageTransparencyCutoff) {
discard;
}
diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c
index 6ca55d48681..d570fda9dac 100644
--- a/source/blender/draw/engines/workbench/workbench_materials.c
+++ b/source/blender/draw/engines/workbench/workbench_materials.c
@@ -296,6 +296,8 @@ void workbench_material_shgroup_uniform(WORKBENCH_PrivateData *wpd,
V3D_SHADING_TEXTURE_COLOR) {
GPUTexture *tex = GPU_texture_from_blender(material->ima, material->iuser, GL_TEXTURE_2D);
DRW_shgroup_uniform_texture(grp, "image", tex);
+ DRW_shgroup_uniform_bool_copy(
+ grp, "imagePremultiplied", (material->ima->alpha_mode == IMA_ALPHA_PREMUL));
DRW_shgroup_uniform_bool_copy(grp, "imageNearest", (interp == SHD_INTERP_CLOSEST));
}
else {
diff --git a/source/blender/draw/modes/object_mode.c b/source/blender/draw/modes/object_mode.c
index 8dae160682a..bbf364960c9 100644
--- a/source/blender/draw/modes/object_mode.c
+++ b/source/blender/draw/modes/object_mode.c
@@ -974,9 +974,10 @@ static void DRW_shgroup_empty_image(OBJECT_Shaders *sh_data,
const bool use_alpha_blend = (ob->empty_image_flag & OB_EMPTY_IMAGE_USE_ALPHA_BLEND) != 0;
GPUTexture *tex = NULL;
+ Image *ima = ob->data;
- if (ob->data != NULL) {
- tex = GPU_texture_from_blender(ob->data, ob->iuser, GL_TEXTURE_2D);
+ if (ima != NULL) {
+ tex = GPU_texture_from_blender(ima, ob->iuser, GL_TEXTURE_2D);
if (tex) {
size[0] = GPU_texture_width(tex);
size[1] = GPU_texture_height(tex);
@@ -1023,6 +1024,8 @@ static void DRW_shgroup_empty_image(OBJECT_Shaders *sh_data,
DRW_shgroup_uniform_float(grp, "size", &ob->empty_drawsize, 1);
DRW_shgroup_uniform_vec2(grp, "offset", ob->ima_ofs, 1);
DRW_shgroup_uniform_texture(grp, "image", tex);
+ DRW_shgroup_uniform_bool_copy(
+ grp, "imagePremultiplied", (ima->alpha_mode == IMA_ALPHA_PREMUL));
DRW_shgroup_uniform_vec4(grp, "objectColor", ob->color, 1);
DRW_shgroup_uniform_bool_copy(grp, "useAlphaTest", !use_alpha_blend);
if (sh_cfg == GPU_SHADER_CFG_CLIPPED) {
diff --git a/source/blender/draw/modes/paint_texture_mode.c b/source/blender/draw/modes/paint_texture_mode.c
index a31efccda88..5f833e4c6a1 100644
--- a/source/blender/draw/modes/paint_texture_mode.c
+++ b/source/blender/draw/modes/paint_texture_mode.c
@@ -206,6 +206,7 @@ static void PAINT_TEXTURE_engine_init(void *vedata)
}
static DRWShadingGroup *create_texture_paint_shading_group(PAINT_TEXTURE_PassList *psl,
+ const Image *image,
const struct GPUTexture *texture,
const DRWContextState *draw_ctx,
const bool nearest_interp)
@@ -219,6 +220,8 @@ static DRWShadingGroup *create_texture_paint_shading_group(PAINT_TEXTURE_PassLis
DRWShadingGroup *grp = DRW_shgroup_create(masking_enabled ? sh_data->image_mask : sh_data->image,
psl->image_faces);
DRW_shgroup_uniform_texture(grp, "image", texture);
+ DRW_shgroup_uniform_bool_copy(
+ grp, "imagePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL));
DRW_shgroup_uniform_float(grp, "alpha", &draw_ctx->v3d->overlay.texture_paint_mode_opacity, 1);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_bool_copy(grp, "nearestInterp", nearest_interp);
@@ -227,6 +230,8 @@ static DRWShadingGroup *create_texture_paint_shading_group(PAINT_TEXTURE_PassLis
const bool masking_inverted = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) > 0;
GPUTexture *stencil = GPU_texture_from_blender(imapaint->stencil, NULL, GL_TEXTURE_2D);
DRW_shgroup_uniform_texture(grp, "maskingImage", stencil);
+ DRW_shgroup_uniform_bool_copy(
+ grp, "maskingImagePremultiplied", (imapaint->stencil->alpha_mode == IMA_ALPHA_PREMUL));
DRW_shgroup_uniform_vec3(grp, "maskingColor", imapaint->stencil_col, 1);
DRW_shgroup_uniform_bool_copy(grp, "maskingInvertStencil", masking_inverted);
}
@@ -283,7 +288,7 @@ static void PAINT_TEXTURE_cache_init(void *vedata)
if (tex) {
DRWShadingGroup *grp = create_texture_paint_shading_group(
- psl, tex, draw_ctx, interp == SHD_INTERP_CLOSEST);
+ psl, ima, tex, draw_ctx, interp == SHD_INTERP_CLOSEST);
stl->g_data->shgroup_image_array[i] = grp;
}
else {
@@ -297,7 +302,7 @@ static void PAINT_TEXTURE_cache_init(void *vedata)
if (tex) {
DRWShadingGroup *grp = create_texture_paint_shading_group(
- psl, tex, draw_ctx, imapaint->interp == IMAGEPAINT_INTERP_CLOSEST);
+ psl, ima, tex, draw_ctx, imapaint->interp == IMAGEPAINT_INTERP_CLOSEST);
stl->g_data->shgroup_image_array[0] = grp;
}
else {
diff --git a/source/blender/draw/modes/shaders/object_empty_image_frag.glsl b/source/blender/draw/modes/shaders/object_empty_image_frag.glsl
index deb82a8904e..88220140aec 100644
--- a/source/blender/draw/modes/shaders/object_empty_image_frag.glsl
+++ b/source/blender/draw/modes/shaders/object_empty_image_frag.glsl
@@ -9,6 +9,7 @@ out vec4 fragColor;
#ifndef USE_WIRE
uniform sampler2D image;
+uniform bool imagePremultiplied;
#endif
uniform int depthMode;
@@ -24,11 +25,15 @@ float linearrgb_to_srgb(float c)
}
}
-vec4 texture_read_as_srgb(sampler2D tex, vec2 co)
+vec4 texture_read_as_srgb(sampler2D tex, bool premultiplied, vec2 co)
{
/* By convention image textures return scene linear colors, but
* overlays still assume srgb. */
vec4 color = texture(tex, co);
+ /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */
+ if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) {
+ color.rgb = color.rgb / color.a;
+ }
color.r = linearrgb_to_srgb(color.r);
color.g = linearrgb_to_srgb(color.g);
color.b = linearrgb_to_srgb(color.b);
@@ -40,7 +45,7 @@ void main()
#ifdef USE_WIRE
fragColor = finalColor;
#else
- vec4 tex_col = texture_read_as_srgb(image, texCoord_interp);
+ vec4 tex_col = texture_read_as_srgb(image, imagePremultiplied, texCoord_interp);
fragColor = finalColor * tex_col;
if (useAlphaTest) {
diff --git a/source/blender/draw/modes/shaders/paint_texture_frag.glsl b/source/blender/draw/modes/shaders/paint_texture_frag.glsl
index 4a3c5cb430c..af7ea99e6a1 100644
--- a/source/blender/draw/modes/shaders/paint_texture_frag.glsl
+++ b/source/blender/draw/modes/shaders/paint_texture_frag.glsl
@@ -7,11 +7,13 @@ in vec2 masking_uv_interp;
out vec4 fragColor;
uniform sampler2D image;
+uniform bool imagePremultiplied;
uniform float alpha = 1.0;
uniform bool nearestInterp;
#ifdef TEXTURE_PAINT_MASK
uniform sampler2D maskingImage;
+uniform bool maskingImagePremultiplied;
uniform vec3 maskingColor;
uniform bool maskingInvertStencil;
#endif
@@ -26,11 +28,15 @@ float linearrgb_to_srgb(float c)
}
}
-vec4 texture_read_as_srgb(sampler2D tex, vec2 co)
+vec4 texture_read_as_srgb(sampler2D tex, bool premultiplied, vec2 co)
{
/* By convention image textures return scene linear colors, but
* overlays still assume srgb. */
vec4 color = texture(tex, co);
+ /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */
+ if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) {
+ color.rgb = color.rgb / color.a;
+ }
color.r = linearrgb_to_srgb(color.r);
color.g = linearrgb_to_srgb(color.g);
color.b = linearrgb_to_srgb(color.b);
@@ -45,11 +51,12 @@ void main()
uv = (floor(uv_interp * tex_size) + 0.5) / tex_size;
}
- vec4 color = texture_read_as_srgb(image, uv);
+ vec4 color = texture_read_as_srgb(image, imagePremultiplied, uv);
color.a *= alpha;
#ifdef TEXTURE_PAINT_MASK
- vec4 mask = vec4(texture(maskingImage, masking_uv_interp).rgb, 1.0);
+ vec4 mask = vec4(
+ texture_read_as_srgb(maskingImage, maskingImagePremultiplied, masking_uv_interp).rgb, 1.0);
if (maskingInvertStencil) {
mask.rgb = 1.0 - mask.rgb;
}
diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c
index 4830b994415..d3d7b3b7c1d 100644
--- a/source/blender/gpu/intern/gpu_draw.c
+++ b/source/blender/gpu/intern/gpu_draw.c
@@ -242,29 +242,31 @@ static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget)
return bindcode;
}
- IMB_colormanagement_imbuf_to_srgb_texture(
- rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb);
+ /* Texture storage of images is defined by the alpha mode of the image. The
+ * downside of this is that there can be artifacts near alpha edges. However,
+ * this allows us to use sRGB texture formats and preserves color values in
+ * zero alpha areas, and appears generally closer to what game engines that we
+ * want to be compatible with do. */
+ const bool store_premultiplied = (ima->alpha_mode == IMA_ALPHA_PREMUL);
+ IMB_colormanagement_imbuf_to_byte_texture(
+ rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied);
}
}
- else if (ibuf->channels != 4) {
+ else {
/* Float image is already in scene linear colorspace or non-color data by
* convention, no colorspace conversion needed. But we do require 4 channels
* currently. */
- rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__);
- if (rect_float == NULL) {
- return bindcode;
- }
+ const bool store_premultiplied = (ima->alpha_mode != IMA_ALPHA_STRAIGHT);
+
+ if (ibuf->channels != 4 || !store_premultiplied) {
+ rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__);
+ if (rect_float == NULL) {
+ return bindcode;
+ }
- IMB_buffer_float_from_float(rect_float,
- ibuf->rect_float,
- ibuf->channels,
- IB_PROFILE_LINEAR_RGB,
- IB_PROFILE_LINEAR_RGB,
- false,
- ibuf->x,
- ibuf->y,
- ibuf->x,
- ibuf->x);
+ IMB_colormanagement_imbuf_to_float_texture(
+ rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
+ }
}
/* Create OpenGL texture. */
@@ -348,7 +350,7 @@ static void gpu_texture_update_unscaled(
glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
}
-static void gpu_texture_update_from_ibuf(ImBuf *ibuf, int x, int y, int w, int h)
+static void gpu_texture_update_from_ibuf(Image *ima, ImBuf *ibuf, int x, int y, int w, int h)
{
/* Partial update of texture for texture painting. This is often much
* quicker than fully updating the texture for high resolution images.
@@ -388,30 +390,27 @@ static void gpu_texture_update_from_ibuf(ImBuf *ibuf, int x, int y, int w, int h
/* Convert to scene linear with sRGB compression, and premultiplied for
* correct texture interpolation. */
- IMB_colormanagement_imbuf_to_srgb_texture(rect, x, y, w, h, ibuf, compress_as_srgb);
+ const bool store_premultiplied = (ima->alpha_mode == IMA_ALPHA_PREMUL);
+ IMB_colormanagement_imbuf_to_byte_texture(
+ rect, x, y, w, h, ibuf, compress_as_srgb, store_premultiplied);
}
}
- else if (ibuf->channels != 4 || scaled) {
+ else {
/* Float pixels. */
- rect_float = MEM_mallocN(sizeof(float) * 4 * x * y, __func__);
- if (rect_float == NULL) {
- return;
- }
+ const bool store_premultiplied = (ima->alpha_mode != IMA_ALPHA_STRAIGHT);
- tex_stride = w;
- tex_offset = 0;
+ if (ibuf->channels != 4 || scaled || !store_premultiplied) {
+ rect_float = MEM_mallocN(sizeof(float) * 4 * x * y, __func__);
+ if (rect_float == NULL) {
+ return;
+ }
- size_t ibuf_offset = (y * ibuf->x + x) * ibuf->channels;
- IMB_buffer_float_from_float(rect_float,
- ibuf->rect_float + ibuf_offset,
- ibuf->channels,
- IB_PROFILE_LINEAR_RGB,
- IB_PROFILE_LINEAR_RGB,
- false,
- w,
- h,
- x,
- ibuf->x);
+ tex_stride = w;
+ tex_offset = 0;
+
+ IMB_colormanagement_imbuf_to_float_texture(
+ rect_float, x, y, w, h, ibuf, store_premultiplied);
+ }
}
if (scaled) {
@@ -825,7 +824,7 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i
/* Partial update of texture. */
GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0);
- gpu_texture_update_from_ibuf(ibuf, x, y, w, h);
+ gpu_texture_update_from_ibuf(ima, ibuf, x, y, w, h);
if (GPU_get_mipmap()) {
glGenerateMipmap(GL_TEXTURE_2D);
diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl
index 4135151050e..1750e124c29 100644
--- a/source/blender/gpu/shaders/gpu_shader_material.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_material.glsl
@@ -2425,6 +2425,11 @@ void tex_color_alpha_clear(vec4 color, out vec4 result)
result = vec4(color.rgb, 1.0);
}
+void tex_color_alpha_premultiply(vec4 color, out vec4 result)
+{
+ result = vec4(color.rgb * color.a, 1.0);
+}
+
void tex_color_alpha_unpremultiply(vec4 color, out vec4 result)
{
if (color.a == 0.0 || color.a == 1.0) {
diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h
index a27945cc369..b12339aef6d 100644
--- a/source/blender/imbuf/IMB_colormanagement.h
+++ b/source/blender/imbuf/IMB_colormanagement.h
@@ -129,13 +129,21 @@ void IMB_colormanagement_colorspace_to_scene_linear(float *buffer,
struct ColorSpace *colorspace,
bool predivide);
-void IMB_colormanagement_imbuf_to_srgb_texture(unsigned char *rect,
+void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer,
const int x,
const int y,
const int width,
const int height,
const struct ImBuf *ibuf,
- const bool compress_as_srgb);
+ const bool compress_as_srgb,
+ const bool store_premultiplied);
+void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer,
+ const int offset_x,
+ const int offset_y,
+ const int width,
+ const int height,
+ const struct ImBuf *ibuf,
+ const bool store_premultiplied);
void IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3]);
void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3]);
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index 583a3be8168..bec29252b6f 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -2176,13 +2176,14 @@ void IMB_colormanagement_colorspace_to_scene_linear(float *buffer,
}
}
-void IMB_colormanagement_imbuf_to_srgb_texture(unsigned char *out_buffer,
+void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer,
const int offset_x,
const int offset_y,
const int width,
const int height,
const struct ImBuf *ibuf,
- const bool compress_as_srgb)
+ const bool compress_as_srgb,
+ const bool store_premultiplied)
{
/* Convert byte buffer for texture storage on the GPU. These have builtin
* support for converting sRGB to linear, which allows us to store textures
@@ -2197,7 +2198,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);
+ const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied;
for (int y = 0; y < height; y++) {
const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
@@ -2239,6 +2240,58 @@ void IMB_colormanagement_imbuf_to_srgb_texture(unsigned char *out_buffer,
}
}
+void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer,
+ const int offset_x,
+ const int offset_y,
+ const int width,
+ const int height,
+ const struct ImBuf *ibuf,
+ const bool store_premultiplied)
+{
+ /* Float texture are stored in scene linear color space, with premultiplied
+ * alpha depending on the image alpha mode. */
+ const float *in_buffer = ibuf->rect_float;
+ const int in_channels = ibuf->channels;
+ const bool use_unpremultiply = IMB_alpha_affects_rgb(ibuf) && !store_premultiplied;
+
+ for (int y = 0; y < height; y++) {
+ const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
+ const size_t out_offset = y * width;
+ const float *in = in_buffer + in_offset * 4;
+ float *out = out_buffer + out_offset * 4;
+
+ if (in_channels == 1) {
+ /* Copy single channel. */
+ for (int x = 0; x < width; x++, in += 1, out += 4) {
+ out[0] = in[0];
+ out[1] = in[0];
+ out[2] = in[0];
+ out[3] = in[0];
+ }
+ }
+ else if (in_channels == 3) {
+ /* Copy RGB. */
+ for (int x = 0; x < width; x++, in += 3, out += 4) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = 1.0f;
+ }
+ }
+ else if (in_channels == 4) {
+ /* Copy or convert RGBA. */
+ if (use_unpremultiply) {
+ for (int x = 0; x < width; x++, in += 4, out += 4) {
+ premul_to_straight_v4_v4(out, in);
+ }
+ }
+ else {
+ memcpy(out, in, sizeof(float) * 4 * width);
+ }
+ }
+ }
+}
+
/* Conversion between color picking role. Typically we would expect such a
* requirements:
* - It is approximately perceptually linear, so that the HSV numbers and
diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c
index a26b4c6b6e4..933dae70457 100644
--- a/source/blender/makesrna/intern/rna_image_api.c
+++ b/source/blender/makesrna/intern/rna_image_api.c
@@ -342,7 +342,7 @@ void RNA_api_image(StructRNA *srna)
func,
"Load the image into an OpenGL texture. On success, image.bindcode will contain the "
"OpenGL texture bindcode. Colors read from the texture will be in scene linear color space "
- "and have premultiplied alpha.");
+ "and have premultiplied or straight alpha matching the image alpha mode");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_int(
func, "frame", 0, 0, INT_MAX, "Frame", "Frame of image sequence or movie", 0, INT_MAX);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c
index 615f55e4350..bd8355ec885 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c
@@ -121,7 +121,20 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
}
if (out[0].hasoutput) {
- GPU_link(mat, "tex_color_alpha_clear", out[0].link, &out[0].link);
+ if (ELEM(ima->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED) ||
+ IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) {
+ /* Don't let alpha affect color output in these cases. */
+ GPU_link(mat, "tex_color_alpha_clear", out[0].link, &out[0].link);
+ }
+ else {
+ /* Always output with premultiplied alpha. */
+ if (ima->alpha_mode == IMA_ALPHA_PREMUL) {
+ GPU_link(mat, "tex_color_alpha_clear", out[0].link, &out[0].link);
+ }
+ else {
+ GPU_link(mat, "tex_color_alpha_premultiply", out[0].link, &out[0].link);
+ }
+ }
}
return true;
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 786386bb63e..6f3614e357d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c
@@ -180,16 +180,32 @@ 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 &&
- !(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);
+ if (ELEM(ima->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED) ||
+ IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) {
+ /* Don't let alpha affect color output in these cases. */
+ GPU_link(mat, "tex_color_alpha_clear", out[0].link, &out[0].link);
}
else {
- GPU_link(mat, "tex_color_alpha_clear", out[0].link, &out[0].link);
+ /* Output premultiplied alpha depending on alpha socket usage. 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. And if we do
+ * not, then there will be no artifacts from zero alpha areas. */
+ if (ima->alpha_mode == IMA_ALPHA_PREMUL) {
+ if (out[1].hasoutput) {
+ GPU_link(mat, "tex_color_alpha_unpremultiply", out[0].link, &out[0].link);
+ }
+ else {
+ GPU_link(mat, "tex_color_alpha_clear", out[0].link, &out[0].link);
+ }
+ }
+ else {
+ if (out[1].hasoutput) {
+ GPU_link(mat, "tex_color_alpha_clear", out[0].link, &out[0].link);
+ }
+ else {
+ GPU_link(mat, "tex_color_alpha_premultiply", out[0].link, &out[0].link);
+ }
+ }
}
}