From fb03f50e069d66c99391e4796e1b9eaa2b4cc133 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 7 Jun 2019 17:49:58 +0200 Subject: Fix T64625: Eevee image textures with alpha have dark edges Now 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 where pixels with zero alpha bleed in. It also adds more code complexity since image textures are no longer all stored the same way. This changes allows us to keep using sRGB texture formats, which have edge darkening when stored with premultiplied alpha. Game engines seems to generally do the same thing, and we want to be compatible with them. --- source/blender/gpu/intern/gpu_draw.c | 75 ++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 38 deletions(-) (limited to 'source/blender/gpu/intern/gpu_draw.c') 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); -- cgit v1.2.3