diff options
Diffstat (limited to 'source')
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); + } + } } } |