Cycles/Eevee: unified and improved texture image color space handling
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Tue, 23 Apr 2019 11:56:30 +0000 (13:56 +0200)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Mon, 13 May 2019 13:56:10 +0000 (15:56 +0200)
Cycles now uses the color space on the image datablock, and uses OpenColorIO
to convert to scene linear as needed. Byte images do not take extra memory,
they are compressed in scene linear + sRGB transfer function which in common
cases is a no-op.

Eevee and workbench were changed to work similar. Float images are stored as
scene linear. Byte images are compressed as scene linear + sRGB and stored in
a GL_SRGB8_ALPHA8 texture. From the GLSL shader side this means they are read
as scene linear, simplifying the code and taking advantage of hardware support.

Further, OpenGL image textures are now all stored with premultiplied alpha.
Eevee texture sampling looks a little different now because interpolation
happens premultiplied and in scene linear space.

Overlays and grease pencil work in sRGB space so those now have an extra
conversion to sRGB after reading from image textures. This is not particularly
elegant but as long as engines use different conventions, one or the other
needs to do conversion.

This change breaks compatibility for cases where multiple image texture nodes
were using the same image with different color space node settings. However it
gives more predictable behavior for baking and texture painting if save, load
and image editing operations have a single color space to handle.

Differential Revision: https://developer.blender.org/D4807

42 files changed:
intern/cycles/blender/blender_shader.cpp
intern/cycles/render/colorspace.cpp
intern/opencolorio/ocio_capi.h
intern/opencolorio/ocio_impl.cc
source/blender/blenkernel/BKE_blender_version.h
source/blender/blenloader/intern/versioning_cycles.c
source/blender/draw/engines/gpencil/gpencil_draw_utils.c
source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl
source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl
source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl
source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl
source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl
source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl
source/blender/draw/engines/workbench/workbench_forward.c
source/blender/draw/engines/workbench/workbench_materials.c
source/blender/draw/intern/draw_manager_data.c
source/blender/draw/modes/object_mode.c
source/blender/draw/modes/paint_texture_mode.c
source/blender/draw/modes/shaders/object_empty_image_frag.glsl
source/blender/draw/modes/shaders/paint_texture_frag.glsl
source/blender/editors/sculpt_paint/paint_image_proj.c
source/blender/editors/space_node/drawnode.c
source/blender/gpu/GPU_draw.h
source/blender/gpu/GPU_material.h
source/blender/gpu/GPU_texture.h
source/blender/gpu/intern/gpu_codegen.c
source/blender/gpu/intern/gpu_codegen.h
source/blender/gpu/intern/gpu_draw.c
source/blender/gpu/intern/gpu_material.c
source/blender/gpu/intern/gpu_texture.c
source/blender/gpu/shaders/gpu_shader_material.glsl
source/blender/imbuf/IMB_colormanagement.h
source/blender/imbuf/intern/IMB_colormanagement_intern.h
source/blender/imbuf/intern/colormanagement.c
source/blender/makesdna/DNA_image_types.h
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/intern/rna_color.c
source/blender/makesrna/intern/rna_image_api.c
source/blender/makesrna/intern/rna_nodetree.c
source/blender/nodes/shader/node_shader_util.h
source/blender/nodes/shader/nodes/node_shader_tex_environment.c
source/blender/nodes/shader/nodes/node_shader_tex_image.c

index e6ec8b22d7a11965ed70a3a6c7b3fa305c91629a..a08f767f96463042610f3f70828b8330900782ef 100644 (file)
@@ -651,6 +651,9 @@ static ShaderNode *add_node(Scene *scene,
         image->builtin_data = NULL;
       }
 
+      PointerRNA colorspace_ptr = b_image.colorspace_settings().ptr;
+      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();
 
@@ -662,18 +665,11 @@ static ShaderNode *add_node(Scene *scene,
                                                image->builtin_data,
                                                get_image_interpolation(b_image_node),
                                                get_image_extension(b_image_node),
-                                               image->use_alpha);
+                                               image->use_alpha,
+                                               image->colorspace);
       }
 #endif
     }
-    switch (b_image_node.color_space()) {
-      case BL::ShaderNodeTexImage::color_space_NONE:
-        image->colorspace = u_colorspace_raw;
-        break;
-      case BL::ShaderNodeTexImage::color_space_COLOR:
-        image->colorspace = u_colorspace_auto;
-        break;
-    }
     image->projection = (NodeImageProjection)b_image_node.projection();
     image->interpolation = get_image_interpolation(b_image_node);
     image->extension = get_image_extension(b_image_node);
@@ -703,6 +699,9 @@ static ShaderNode *add_node(Scene *scene,
         env->builtin_data = NULL;
       }
 
+      PointerRNA colorspace_ptr = b_image.colorspace_settings().ptr;
+      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();
 
@@ -714,18 +713,11 @@ static ShaderNode *add_node(Scene *scene,
                                                env->builtin_data,
                                                get_image_interpolation(b_env_node),
                                                EXTENSION_REPEAT,
-                                               env->use_alpha);
+                                               env->use_alpha,
+                                               env->colorspace);
       }
 #endif
     }
-    switch (b_env_node.color_space()) {
-      case BL::ShaderNodeTexEnvironment::color_space_NONE:
-        env->colorspace = u_colorspace_raw;
-        break;
-      case BL::ShaderNodeTexEnvironment::color_space_COLOR:
-        env->colorspace = u_colorspace_auto;
-        break;
-    }
     env->interpolation = get_image_interpolation(b_env_node);
     env->projection = (NodeEnvironmentProjection)b_env_node.projection();
     BL::TexMapping b_texture_mapping(b_env_node.texture_mapping());
index cdce1f70188bff76fa3d61df5ad4fc89f0a16257..7b57478ff51ab2baa2fe3a0393fa711aeabdd3eb 100644 (file)
@@ -242,6 +242,10 @@ inline void processor_apply_pixels(const OCIO::Processor *processor,
                                    size_t width,
                                    size_t height)
 {
+  /* TODO: implement faster version for when we know the conversion
+   * is a simple matrix transform between linear spaces. In that case
+   * unpremultiply is not needed. */
+
   /* Process large images in chunks to keep temporary memory requirement down. */
   size_t y_chunk_size = max(1, 16 * 1024 * 1024 / (sizeof(float4) * width));
   vector<float4> float_pixels(y_chunk_size * width);
@@ -252,7 +256,16 @@ inline void processor_apply_pixels(const OCIO::Processor *processor,
 
     for (size_t y = y0; y < y1; y++) {
       for (size_t x = 0; x < width; x++, i++) {
-        float_pixels[i] = cast_to_float4(pixels + 4 * (y * width + x));
+        float4 value = cast_to_float4(pixels + 4 * (y * width + x));
+
+        if (!(value.w == 0.0f || value.w == 1.0f)) {
+          float inv_alpha = 1.0f / value.w;
+          value.x *= inv_alpha;
+          value.y *= inv_alpha;
+          value.z *= inv_alpha;
+        }
+
+        float_pixels[i] = value;
       }
     }
 
@@ -263,25 +276,20 @@ inline void processor_apply_pixels(const OCIO::Processor *processor,
     for (size_t y = y0; y < y1; y++) {
       for (size_t x = 0; x < width; x++, i++) {
         float4 value = float_pixels[i];
+
+        value.x *= value.w;
+        value.y *= value.w;
+        value.z *= value.w;
+
         if (compress_as_srgb) {
           value = color_linear_to_srgb_v4(value);
         }
+
         cast_from_float4(pixels + 4 * (y * width + x), value);
       }
     }
   }
 }
-
-/* Fast version for float images, which OpenColorIO can handle natively. */
-template<>
-inline void processor_apply_pixels(const OCIO::Processor *processor,
-                                   float *pixels,
-                                   size_t width,
-                                   size_t height)
-{
-  OCIO::PackedImageDesc desc(pixels, width, height, 4);
-  processor->apply(desc);
-}
 #endif
 
 template<typename T>
index f4d7717ba4679a4cf3b7b2532b8c8efa17865302..9ba2d8fb8f9652123a64838891a5f7fa1b0e1760 100644 (file)
@@ -31,6 +31,7 @@ struct OCIO_GLSLDrawState;
     int unused; \
   } * name
 
+#define OCIO_ROLE_DATA "data"
 #define OCIO_ROLE_SCENE_LINEAR "scene_linear"
 #define OCIO_ROLE_COLOR_PICKING "color_picking"
 #define OCIO_ROLE_TEXTURE_PAINT "texture_paint"
index 644117e00004164f4a253a25ca423faf39db4f94..25c1df70045b24f8e47d367191e9750c6aaa1de1 100644 (file)
@@ -552,7 +552,7 @@ void OCIOImpl::colorSpaceIsBuiltin(OCIO_ConstConfigRcPtr *config_,
     processor = (*config)->getProcessor((*cs)->getName(), "scene_linear");
   }
   catch (Exception &exception) {
-    OCIO_reportException(exception);
+    /* Silently ignore if no conversion possible, then it's not scene linear or sRGB. */
     is_scene_linear = false;
     is_srgb = false;
     return;
index 16275c96eec669893cd6b989a60ed2281cb6df23..74d33a9c2752dc6dcda46f4cde96d9d1c3000968 100644 (file)
@@ -27,7 +27,7 @@
  * \note Use #STRINGIFY() rather than defining with quotes.
  */
 #define BLENDER_VERSION 280
-#define BLENDER_SUBVERSION 62
+#define BLENDER_SUBVERSION 63
 /** Several breakages with 280, e.g. collections vs layers. */
 #define BLENDER_MINVERSION 280
 #define BLENDER_MINSUBVERSION 0
index 023bd6853524246287e55773218980bc784dd6ac..2bd379c6f198e23561ee939c03259864578eaafe 100644 (file)
@@ -38,6 +38,8 @@
 #include "BKE_main.h"
 #include "BKE_node.h"
 
+#include "IMB_colormanagement.h"
+
 #include "BLO_readfile.h"
 #include "readfile.h"
 
@@ -262,6 +264,33 @@ static void ambient_occlusion_node_relink(bNodeTree *ntree)
   }
 }
 
+static void image_node_colorspace(bNode *node)
+{
+  if (node->id == NULL) {
+    return;
+  }
+
+  int color_space;
+  if (node->type == SH_NODE_TEX_IMAGE) {
+    NodeTexImage *tex = node->storage;
+    color_space = tex->color_space;
+  }
+  else if (node->type == SH_NODE_TEX_ENVIRONMENT) {
+    NodeTexEnvironment *tex = node->storage;
+    color_space = tex->color_space;
+  }
+  else {
+    return;
+  }
+
+  const int SHD_COLORSPACE_NONE = 0;
+  Image *image = (Image *)node->id;
+  if (color_space == SHD_COLORSPACE_NONE) {
+    STRNCPY(image->colorspace_settings.name,
+            IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DATA));
+  }
+}
+
 void blo_do_versions_cycles(FileData *UNUSED(fd), Library *UNUSED(lib), Main *bmain)
 {
   /* Particle shape shared with Eevee. */
@@ -326,6 +355,12 @@ void do_versions_after_linking_cycles(Main *bmain)
       if (!MAIN_VERSION_ATLEAST(bmain, 279, 5)) {
         ambient_occlusion_node_relink(ntree);
       }
+
+      if (!MAIN_VERSION_ATLEAST(bmain, 280, 63)) {
+        for (bNode *node = ntree->nodes.first; node; node = node->next) {
+          image_node_colorspace(node);
+        }
+      }
     }
     FOREACH_NODETREE_END;
   }
index c23b4c254e4b19e7c0d2a0422ffb2ab8d821c676..d57c43ab3754eace3d55ccd1b7fdb0080cb23f66 100644 (file)
@@ -465,7 +465,7 @@ static DRWShadingGroup *DRW_gpencil_shgroup_fill_create(GPENCIL_e_data *e_data,
       BKE_image_release_ibuf(image, ibuf, NULL);
     }
     else {
-      GPUTexture *texture = GPU_texture_from_blender(gp_style->ima, &iuser, GL_TEXTURE_2D, true);
+      GPUTexture *texture = GPU_texture_from_blender(gp_style->ima, &iuser, GL_TEXTURE_2D);
       DRW_shgroup_uniform_texture(grp, "myTexture", texture);
 
       stl->shgroups[id].texture_clamp = gp_style->flag & GP_STYLE_COLOR_TEX_CLAMP ? 1 : 0;
@@ -631,7 +631,7 @@ DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(GPENCIL_e_data *e_data,
       BKE_image_release_ibuf(image, ibuf, NULL);
     }
     else {
-      GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D, true);
+      GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D);
       DRW_shgroup_uniform_texture(grp, "myTexture", texture);
 
       BKE_image_release_ibuf(image, ibuf, NULL);
@@ -787,7 +787,7 @@ static DRWShadingGroup *DRW_gpencil_shgroup_point_create(GPENCIL_e_data *e_data,
       BKE_image_release_ibuf(image, ibuf, NULL);
     }
     else {
-      GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D, true);
+      GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D);
       DRW_shgroup_uniform_texture(grp, "myTexture", texture);
 
       BKE_image_release_ibuf(image, ibuf, NULL);
index acf60fc2d59e146c8a7d6ff57d931ad0f27d0a69..80fae9ab5189695728e105e4bbdc78407f4b380f 100644 (file)
@@ -89,6 +89,27 @@ void set_color(in vec4 color,
   ocolor.a *= layer_opacity;
 }
 
+float linearrgb_to_srgb(float c)
+{
+  if (c < 0.0031308) {
+    return (c < 0.0) ? 0.0 : c * 12.92;
+  }
+  else {
+    return 1.055 * pow(c, 1.0 / 2.4) - 0.055;
+  }
+}
+
+vec4 texture_read_as_srgb(sampler2D tex, vec2 co)
+{
+  /* By convention image textures return scene linear colors, but
+   * grease pencil still works in srgb. */
+  vec4 color = texture2D(tex, co);
+  color.r = linearrgb_to_srgb(color.r);
+  color.g = linearrgb_to_srgb(color.g);
+  color.b = linearrgb_to_srgb(color.b);
+  return color;
+}
+
 void main()
 {
   vec2 t_center = vec2(0.5, 0.5);
@@ -97,8 +118,8 @@ void main()
   vec2 rot_tex = (matrot_tex * (texCoord_interp - t_center)) + t_center + texture_offset;
   vec4 tmp_color;
   tmp_color = (texture_clamp == 0) ?
-                  texture2D(myTexture, rot_tex * texture_scale) :
-                  texture2D(myTexture, clamp(rot_tex * texture_scale, 0.0, 1.0));
+                  texture_read_as_srgb(myTexture, rot_tex * texture_scale) :
+                  texture_read_as_srgb(myTexture, 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;
 
index cc47e12b3032a429aecb20ea1e518025991c79a8..98c47b1f1f00b5d0306b83ab0e9895431470aa55 100644 (file)
@@ -47,6 +47,27 @@ vec2 check_box_point(vec2 pt, vec2 radius)
   return rtn;
 }
 
+float linearrgb_to_srgb(float c)
+{
+  if (c < 0.0031308) {
+    return (c < 0.0) ? 0.0 : c * 12.92;
+  }
+  else {
+    return 1.055 * pow(c, 1.0 / 2.4) - 0.055;
+  }
+}
+
+vec4 texture_read_as_srgb(sampler2D tex, vec2 co)
+{
+  /* By convention image textures return scene linear colors, but
+   * grease pencil still works in srgb. */
+  vec4 color = texture2D(tex, co);
+  color.r = linearrgb_to_srgb(color.r);
+  color.g = linearrgb_to_srgb(color.g);
+  color.b = linearrgb_to_srgb(color.b);
+  return color;
+}
+
 void main()
 {
   vec2 centered = mTexCoord - vec2(0.5);
@@ -65,7 +86,7 @@ void main()
     }
   }
 
-  vec4 tmp_color = texture2D(myTexture, mTexCoord);
+  vec4 tmp_color = texture_read_as_srgb(myTexture, mTexCoord);
 
   /* Solid */
   if ((color_type == GPENCIL_COLOR_SOLID) || (no_texture)) {
@@ -73,7 +94,7 @@ void main()
   }
   /* texture */
   if ((color_type == GPENCIL_COLOR_TEXTURE) && (!no_texture)) {
-    vec4 text_color = texture2D(myTexture, mTexCoord);
+    vec4 text_color = texture_read_as_srgb(myTexture, mTexCoord);
     if (mix_stroke_factor > 0.0) {
       fragColor.rgb = mix(text_color.rgb, colormix.rgb, mix_stroke_factor);
       fragColor.a = text_color.a;
@@ -87,7 +108,7 @@ void main()
   }
   /* pattern */
   if ((color_type == GPENCIL_COLOR_PATTERN) && (!no_texture)) {
-    vec4 text_color = texture2D(myTexture, mTexCoord);
+    vec4 text_color = texture_read_as_srgb(myTexture, 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);
index 6b7cee888ea54ad7aa124d412e9ebc58fc42d75c..6b3fcad12409f0fd34299f624f54d0c80ae38675 100644 (file)
@@ -27,6 +27,27 @@ out vec4 fragColor;
 
 bool no_texture = (shading_type[0] == OB_SOLID) && (shading_type[1] != V3D_SHADING_TEXTURE_COLOR);
 
+float linearrgb_to_srgb(float c)
+{
+  if (c < 0.0031308) {
+    return (c < 0.0) ? 0.0 : c * 12.92;
+  }
+  else {
+    return 1.055 * pow(c, 1.0 / 2.4) - 0.055;
+  }
+}
+
+vec4 texture_read_as_srgb(sampler2D tex, vec2 co)
+{
+  /* By convention image textures return scene linear colors, but
+   * grease pencil still works in srgb. */
+  vec4 color = texture2D(tex, co);
+  color.r = linearrgb_to_srgb(color.r);
+  color.g = linearrgb_to_srgb(color.g);
+  color.b = linearrgb_to_srgb(color.b);
+  return color;
+}
+
 void main()
 {
 
@@ -47,10 +68,10 @@ void main()
   /* texture for endcaps */
   vec4 text_color;
   if (uvfac[1] == ENDCAP) {
-    text_color = texture2D(myTexture, vec2(mTexCoord.x, mTexCoord.y));
+    text_color = texture_read_as_srgb(myTexture, vec2(mTexCoord.x, mTexCoord.y));
   }
   else {
-    text_color = texture2D(myTexture, mTexCoord);
+    text_color = texture_read_as_srgb(myTexture, mTexCoord);
   }
 
   /* texture */
index c76ad8c1d7b344ccd0ce3e1810074f9d83fa755b..96f8f6e4c7a1ba0dce43ab36487cd92dd768d911 100644 (file)
@@ -140,32 +140,18 @@ vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped)
   return matcap_uv * 0.496 + 0.5;
 }
 
-float srgb_to_linearrgb(float c)
-{
-  if (c < 0.04045) {
-    return (c < 0.0) ? 0.0 : c * (1.0 / 12.92);
-  }
-  else {
-    return pow((c + 0.055) * (1.0 / 1.055), 2.4);
-  }
-}
-
-vec4 srgb_to_linearrgb(vec4 col_from)
-{
-  vec4 col_to;
-  col_to.r = srgb_to_linearrgb(col_from.r);
-  col_to.g = srgb_to_linearrgb(col_from.g);
-  col_to.b = srgb_to_linearrgb(col_from.b);
-  col_to.a = col_from.a;
-  return col_to;
-}
-
-vec4 workbench_sample_texture(sampler2D image, vec2 coord, bool srgb, bool nearest_sampling)
+vec4 workbench_sample_texture(sampler2D image, vec2 coord, bool nearest_sampling)
 {
   vec2 tex_size = vec2(textureSize(image, 0).xy);
   /* TODO(fclem) We could do the same with sampler objects.
    * But this is a quick workaround instead of messing with the GPUTexture itself. */
   vec2 uv = nearest_sampling ? (floor(coord * tex_size) + 0.5) / tex_size : coord;
   vec4 color = texture(image, uv);
-  return (srgb) ? srgb_to_linearrgb(color) : color;
+
+  /* Unpremultiply, ideally shaders would be added so this is not needed. */
+  if (!(color.a == 0.0 || color.a == 1.0)) {
+    color.rgb = color.rgb / color.a;
+  }
+
+  return color;
 }
index 322437874018b48d0723e49341a2ba192ba0d500..51bce639b6374300b3aa4569f7df757dd2de7ad7 100644 (file)
@@ -1,7 +1,6 @@
 
 uniform float ImageTransparencyCutoff = 0.1;
 uniform sampler2D image;
-uniform bool imageSrgb;
 uniform bool imageNearest;
 
 uniform float alpha = 0.5;
@@ -44,7 +43,7 @@ void main()
   vec4 diffuse_color;
 
 #if defined(V3D_SHADING_TEXTURE_COLOR)
-  diffuse_color = workbench_sample_texture(image, uv_interp, imageSrgb, imageNearest);
+  diffuse_color = workbench_sample_texture(image, uv_interp, imageNearest);
   if (diffuse_color.a < ImageTransparencyCutoff) {
     discard;
   }
index 2596fc4cf8861e304561538f28c48b1d1dabc0b7..af9f1d14f4a64dd4463400465eae7056466c520c 100644 (file)
@@ -6,7 +6,6 @@ uniform float materialRoughness;
 
 uniform sampler2D image;
 uniform float ImageTransparencyCutoff = 0.1;
-uniform bool imageSrgb;
 uniform bool imageNearest;
 
 #ifdef NORMAL_VIEWPORT_PASS_ENABLED
@@ -41,7 +40,7 @@ void main()
   vec4 color;
 
 #  if defined(V3D_SHADING_TEXTURE_COLOR)
-  color = workbench_sample_texture(image, uv_interp, imageSrgb, imageNearest);
+  color = workbench_sample_texture(image, uv_interp, imageNearest);
   if (color.a < ImageTransparencyCutoff) {
     discard;
   }
index 25f95f0d4bc461cf416c3d424527d9e376aec56d..13bd6fe9e4d7fcf93c8f63446f7847393a17a70f 100644 (file)
@@ -202,8 +202,7 @@ WORKBENCH_MaterialData *workbench_forward_get_or_create_material_data(WORKBENCH_
         V3D_SHADING_TEXTURE_COLOR) {
       material->shgrp_object_outline = DRW_shgroup_create(sh_data->object_outline_texture_sh,
                                                           psl->object_outline_pass);
-      GPUTexture *tex = GPU_texture_from_blender(
-          material->ima, material->iuser, GL_TEXTURE_2D, false);
+      GPUTexture *tex = GPU_texture_from_blender(material->ima, material->iuser, GL_TEXTURE_2D);
       DRW_shgroup_uniform_texture(material->shgrp_object_outline, "image", tex);
     }
     else {
index c403e358d6ac42587416a0939617e96de0262478..b280b6fd01a08529f92b0be89db6eca1696ec634 100644 (file)
@@ -294,15 +294,8 @@ void workbench_material_shgroup_uniform(WORKBENCH_PrivateData *wpd,
 
   if (workbench_material_determine_color_type(wpd, material->ima, ob, false) ==
       V3D_SHADING_TEXTURE_COLOR) {
-    ImBuf *ibuf = BKE_image_acquire_ibuf(material->ima, material->iuser, NULL);
-    const bool do_color_correction = wpd->use_color_management &&
-                                     (ibuf &&
-                                      (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0);
-    BKE_image_release_ibuf(material->ima, ibuf, NULL);
-    GPUTexture *tex = GPU_texture_from_blender(
-        material->ima, material->iuser, GL_TEXTURE_2D, false);
+    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, "imageSrgb", do_color_correction);
     DRW_shgroup_uniform_bool_copy(grp, "imageNearest", (interp == SHD_INTERP_CLOSEST));
   }
   else {
index 1f48129116d43cf3e9e1da6196c67f0fa3d9457b..0375db1225545a453435fa24bab0eeddd539a7a1 100644 (file)
@@ -1009,8 +1009,7 @@ static DRWShadingGroup *drw_shgroup_material_inputs(DRWShadingGroup *grp,
       if (input->ima) {
         GPUTexture **tex_ref = BLI_memblock_alloc(DST.vmempool->images);
 
-        *tex_ref = tex = GPU_texture_from_blender(
-            input->ima, input->iuser, GL_TEXTURE_2D, input->image_isdata);
+        *tex_ref = tex = GPU_texture_from_blender(input->ima, input->iuser, GL_TEXTURE_2D);
 
         GPU_texture_ref(tex);
       }
index cc313620a11f0cba208e3ada351e8e742fc06705..9d6732fbcab3acfde0cc2bbd8c262e8db1a95fd4 100644 (file)
@@ -988,7 +988,7 @@ static void DRW_shgroup_empty_image(OBJECT_Shaders *sh_data,
   GPUTexture *tex = NULL;
 
   if (ob->data != NULL) {
-    tex = GPU_texture_from_blender(ob->data, ob->iuser, GL_TEXTURE_2D, false);
+    tex = GPU_texture_from_blender(ob->data, ob->iuser, GL_TEXTURE_2D);
     if (tex) {
       size[0] = GPU_texture_width(tex);
       size[1] = GPU_texture_height(tex);
index 123dc7fca881ca872dcb2cc9cad69a01f349320b..da32744b9ea23034e0544941d8b52f49d12b3249 100644 (file)
@@ -182,7 +182,7 @@ static DRWShadingGroup *create_texture_paint_shading_group(PAINT_TEXTURE_PassLis
 
   if (masking_enabled) {
     const bool masking_inverted = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) > 0;
-    GPUTexture *stencil = GPU_texture_from_blender(imapaint->stencil, NULL, GL_TEXTURE_2D, false);
+    GPUTexture *stencil = GPU_texture_from_blender(imapaint->stencil, NULL, GL_TEXTURE_2D);
     DRW_shgroup_uniform_texture(grp, "maskingImage", stencil);
     DRW_shgroup_uniform_vec3(grp, "maskingColor", imapaint->stencil_col, 1);
     DRW_shgroup_uniform_bool_copy(grp, "maskingInvertStencil", masking_inverted);
@@ -236,7 +236,7 @@ static void PAINT_TEXTURE_cache_init(void *vedata)
                                                   NULL;
           int interp = (ma && ma->texpaintslot) ? ma->texpaintslot[ma->paint_active_slot].interp :
                                                   0;
-          GPUTexture *tex = GPU_texture_from_blender(ima, NULL, GL_TEXTURE_2D, false);
+          GPUTexture *tex = GPU_texture_from_blender(ima, NULL, GL_TEXTURE_2D);
 
           if (tex) {
             DRWShadingGroup *grp = create_texture_paint_shading_group(
@@ -250,7 +250,7 @@ static void PAINT_TEXTURE_cache_init(void *vedata)
       }
       else {
         Image *ima = imapaint->canvas;
-        GPUTexture *tex = GPU_texture_from_blender(ima, NULL, GL_TEXTURE_2D, false);
+        GPUTexture *tex = GPU_texture_from_blender(ima, NULL, GL_TEXTURE_2D);
 
         if (tex) {
           DRWShadingGroup *grp = create_texture_paint_shading_group(
index 386d05636f98415fcb458c979926048bfe6ee901..dbc403dc39b098839585cd7c60e3e8444d71622b 100644 (file)
@@ -14,12 +14,33 @@ uniform sampler2D image;
 uniform int depthMode;
 uniform bool useAlphaTest;
 
+float linearrgb_to_srgb(float c)
+{
+  if (c < 0.0031308) {
+    return (c < 0.0) ? 0.0 : c * 12.92;
+  }
+  else {
+    return 1.055 * pow(c, 1.0 / 2.4) - 0.055;
+  }
+}
+
+vec4 texture_read_as_srgb(sampler2D tex, vec2 co)
+{
+  /* By convention image textures return scene linear colors, but
+   * overlays still assume srgb. */
+  vec4 color = texture2D(tex, co);
+  color.r = linearrgb_to_srgb(color.r);
+  color.g = linearrgb_to_srgb(color.g);
+  color.b = linearrgb_to_srgb(color.b);
+  return color;
+}
+
 void main()
 {
 #ifdef USE_WIRE
   fragColor = finalColor;
 #else
-  vec4 tex_col = texture(image, texCoord_interp);
+  vec4 tex_col = texture_read_as_srgb(image, texCoord_interp);
   fragColor = finalColor * tex_col;
 
   if (useAlphaTest) {
index c7e110122c5ba0e2b730d2c82be38f87c7eecb8c..5d74213a44563c284e9c86cde2185adf17826105 100644 (file)
@@ -16,6 +16,27 @@ uniform vec3 maskingColor;
 uniform bool maskingInvertStencil;
 #endif
 
+float linearrgb_to_srgb(float c)
+{
+  if (c < 0.0031308) {
+    return (c < 0.0) ? 0.0 : c * 12.92;
+  }
+  else {
+    return 1.055 * pow(c, 1.0 / 2.4) - 0.055;
+  }
+}
+
+vec4 texture_read_as_srgb(sampler2D tex, vec2 co)
+{
+  /* By convention image textures return scene linear colors, but
+   * overlays still assume srgb. */
+  vec4 color = texture2D(tex, co);
+  color.r = linearrgb_to_srgb(color.r);
+  color.g = linearrgb_to_srgb(color.g);
+  color.b = linearrgb_to_srgb(color.b);
+  return color;
+}
+
 void main()
 {
   vec2 uv = uv_interp;
@@ -24,7 +45,7 @@ void main()
     uv = (floor(uv_interp * tex_size) + 0.5) / tex_size;
   }
 
-  vec4 color = texture(image, uv);
+  vec4 color = texture_read_as_srgb(image, uv);
   color.a *= alpha;
 
 #ifdef TEXTURE_PAINT_MASK
index 0340a4989e1e0aba0fa17da55e377dba221ecf7c..9cc12b41a63b0432fd92232005d8ad6142b5567e 100644 (file)
@@ -6394,7 +6394,7 @@ static const EnumPropertyItem layer_type_items[] = {
     {0, NULL, 0, NULL, NULL},
 };
 
-static Image *proj_paint_image_create(wmOperator *op, Main *bmain)
+static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data)
 {
   Image *ima;
   float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
@@ -6417,6 +6417,11 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain)
   ima = BKE_image_add_generated(
       bmain, width, height, imagename, alpha ? 32 : 24, use_float, gen_type, color, false);
 
+  if (is_data) {
+    STRNCPY(ima->colorspace_settings.name,
+            IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DATA));
+  }
+
   return ima;
 }
 
@@ -6487,6 +6492,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op)
   if (ma) {
     Main *bmain = CTX_data_main(C);
     int type = RNA_enum_get(op->ptr, "type");
+    bool is_data = (type > LAYER_BASE_COLOR);
 
     bNode *imanode;
     bNodeTree *ntree = ma->nodetree;
@@ -6501,7 +6507,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op)
     /* try to add an image node */
     imanode = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE);
 
-    ima = proj_paint_image_create(op, bmain);
+    ima = proj_paint_image_create(op, bmain, is_data);
     imanode->id = &ima->id;
 
     nodeSetActive(ntree, imanode);
@@ -6553,12 +6559,6 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op)
         }
       }
 
-      if (type > LAYER_BASE_COLOR) {
-        /* This is a "non color data" image */
-        NodeTexImage *tex = imanode->storage;
-        tex->color_space = SHD_COLORSPACE_NONE;
-      }
-
       /* Check if the socket in already connected to something */
       bNodeLink *link = in_sock ? in_sock->link : NULL;
       if (in_sock != NULL && link == NULL) {
index 447fea8098c67dac105b63f5edfac05a79cb3c4c..bf6ec961a5d9a1d6207dd4d9dd7399c1661f84cd 100644 (file)
@@ -708,6 +708,11 @@ static void node_buts_image_user(uiLayout *layout,
     col = uiLayoutColumn(layout, false);
     uiItemR(col, ptr, "layer", 0, NULL, ICON_NONE);
   }
+
+  uiLayout *split = uiLayoutSplit(layout, 0.5f, true);
+  PointerRNA colorspace_settings_ptr = RNA_pointer_get(imaptr, "colorspace_settings");
+  uiItemL(split, IFACE_("Color Space"), ICON_NONE);
+  uiItemR(split, &colorspace_settings_ptr, "name", 0, "", ICON_NONE);
 }
 
 static void node_shader_buts_mapping(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -782,7 +787,6 @@ static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA
                NULL,
                UI_TEMPLATE_ID_FILTER_ALL,
                false);
-  uiItemR(layout, ptr, "color_space", 0, "", ICON_NONE);
   uiItemR(layout, ptr, "interpolation", 0, "", ICON_NONE);
   uiItemR(layout, ptr, "projection", 0, "", ICON_NONE);
 
@@ -820,11 +824,10 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin
                UI_TEMPLATE_ID_FILTER_ALL,
                false);
 
-  node_buts_image_user(layout, C, &iuserptr, &imaptr, &iuserptr, false);
-
-  uiItemR(layout, ptr, "color_space", 0, "", ICON_NONE);
   uiItemR(layout, ptr, "interpolation", 0, "", ICON_NONE);
   uiItemR(layout, ptr, "projection", 0, "", ICON_NONE);
+
+  node_buts_image_user(layout, C, &iuserptr, &imaptr, &iuserptr, false);
 }
 
 static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -875,7 +878,6 @@ static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, P
     uiTemplateImageInfo(layout, C, ima, iuserptr.data);
   }
 
-  uiItemR(layout, ptr, "color_space", 0, IFACE_("Color Space"), ICON_NONE);
   uiItemR(layout, ptr, "interpolation", 0, IFACE_("Interpolation"), ICON_NONE);
   uiItemR(layout, ptr, "projection", 0, IFACE_("Projection"), ICON_NONE);
 }
index 300fc7c65a2372e51734ec35ae46a3dc499052a1..eb54ff127d8a4d771a0c2a906b2625cb37e79517 100644 (file)
@@ -84,7 +84,7 @@ void GPU_create_gl_tex(unsigned int *bind,
                        int recth,
                        int textarget,
                        bool mipmap,
-                       bool use_hight_bit_depth,
+                       bool use_srgb,
                        struct Image *ima);
 void GPU_create_gl_tex_compressed(unsigned int *bind,
                                   unsigned int *pix,
index 1640209c717f6dc726625fefdc9f7d29b2076edb..86c9764a68f3222d4f1c58f3943d5a4d62b7f22f 100644 (file)
@@ -145,7 +145,7 @@ typedef enum eGPUMaterialStatus {
 GPUNodeLink *GPU_attribute(CustomDataType type, const char *name);
 GPUNodeLink *GPU_constant(float *num);
 GPUNodeLink *GPU_uniform(float *num);
-GPUNodeLink *GPU_image(struct Image *ima, struct ImageUser *iuser, bool is_data);
+GPUNodeLink *GPU_image(struct Image *ima, struct ImageUser *iuser);
 GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *layer);
 GPUNodeLink *GPU_builtin(eGPUBuiltin builtin);
 
@@ -202,7 +202,6 @@ struct GPUUniformBuffer *GPU_material_create_sss_profile_ubo(void);
 
 void GPU_material_vertex_attrs(GPUMaterial *material, struct GPUVertAttrLayers *attrs);
 
-bool GPU_material_do_color_management(GPUMaterial *mat);
 bool GPU_material_use_domain_surface(GPUMaterial *mat);
 bool GPU_material_use_domain_volume(GPUMaterial *mat);
 
index 3527398a3968595d2f3bdf727ca75f068b436c0c..3fb7dfc6331ccee69c4604a5695ad677a147c908 100644 (file)
@@ -186,10 +186,7 @@ GPUTexture *GPU_texture_create_from_vertbuf(struct GPUVertBuf *vert);
 GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat data_type, const uint buffer);
 
 GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode);
-GPUTexture *GPU_texture_from_blender(struct Image *ima,
-                                     struct ImageUser *iuser,
-                                     int textarget,
-                                     bool is_data);
+GPUTexture *GPU_texture_from_blender(struct Image *ima, struct ImageUser *iuser, int textarget);
 GPUTexture *GPU_texture_from_preview(struct PreviewImage *prv, int mipmap);
 
 void GPU_texture_add_mipmap(GPUTexture *tex,
index a2c1a01a82c22810a6062a2dfb9447978fbbb11b..6259780c26181a251a90ca53a731e135ad6b65d7 100644 (file)
@@ -1503,7 +1503,6 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType
       input->source = GPU_SOURCE_TEX;
       input->ima = link->ima;
       input->iuser = link->iuser;
-      input->image_isdata = link->image_isdata;
       break;
     case GPU_NODE_LINK_ATTR:
       input->source = GPU_SOURCE_ATTR;
@@ -1748,13 +1747,12 @@ GPUNodeLink *GPU_uniform(float *num)
   return link;
 }
 
-GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, bool is_data)
+GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser)
 {
   GPUNodeLink *link = GPU_node_link_create();
   link->link_type = GPU_NODE_LINK_IMAGE_BLENDER;
   link->ima = ima;
   link->iuser = iuser;
-  link->image_isdata = is_data;
   return link;
 }
 
index 73155c3aafa0d54e70517efab1e748d3abe6013e..d1bb3f26920d42495c22666c67183dd8176f4e66 100644 (file)
@@ -99,7 +99,6 @@ struct GPUNodeLink {
     struct {
       struct Image *ima;
       struct ImageUser *iuser;
-      bool image_isdata;
     };
   };
 };
@@ -137,7 +136,6 @@ typedef struct GPUInput {
       struct GPUTexture **coba; /* input texture, only set at runtime */
       struct Image *ima;        /* image */
       struct ImageUser *iuser;  /* image user */
-      bool image_isdata;        /* image does not contain color data */
       bool bindtex;             /* input is responsible for binding the texture? */
       int texid;                /* number for multitexture, starting from zero */
       eGPUType textype;         /* texture type (2D, 1D Array ...) */
index edc2f2171a5e86333984fd0e86f2f0266588cda4..f1c82dc53a78eac99f83e17186dbcc590116b8e8 100644 (file)
@@ -50,6 +50,7 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "IMB_colormanagement.h"
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
 
@@ -207,53 +208,232 @@ static GPUTexture **gpu_get_image_gputexture(Image *ima, GLenum textarget)
   return NULL;
 }
 
-typedef struct VerifyThreadData {
-  ImBuf *ibuf;
-  float *srgb_frect;
-} VerifyThreadData;
+static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget)
+{
+  uint bindcode = 0;
+  const bool mipmap = GPU_get_mipmap();
+
+#ifdef WITH_DDS
+  if (ibuf->ftype == IMB_FTYPE_DDS) {
+    /* DDS is loaded directly in compressed form. */
+    GPU_create_gl_tex_compressed(
+        &bindcode, ibuf->rect, ibuf->x, ibuf->y, textarget, mipmap, ima, ibuf);
+    return bindcode;
+  }
+#endif
+
+  /* Regular uncompressed texture. */
+  float *rect_float = ibuf->rect_float;
+  uchar *rect = (uchar *)ibuf->rect;
+  bool compress_as_srgb = false;
+
+  if (rect_float == NULL) {
+    /* Byte image is in original colorspace from the file. If the file is sRGB
+     * scene linear, or non-color data no conversion is needed. Otherwise we
+     * compress as scene linear + sRGB transfer function to avoid precision loss
+     * in common cases.
+     *
+     * We must also convert to premultiplied for correct texture interpolation
+     * and consistency with float images. */
+    if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) {
+      compress_as_srgb = !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace);
+
+      rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__);
+      if (rect == NULL) {
+        return bindcode;
+      }
+
+      IMB_colormanagement_imbuf_to_srgb_texture(
+          rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb);
+    }
+  }
+  else if (ibuf->channels != 4) {
+    /* 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;
+    }
 
-static void gpu_verify_high_bit_srgb_buffer_slice(float *srgb_frect,
-                                                  ImBuf *ibuf,
-                                                  const int start_line,
-                                                  const int height)
+    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);
+  }
+
+  /* Create OpenGL texture. */
+  GPU_create_gl_tex(&bindcode,
+                    (uint *)rect,
+                    rect_float,
+                    ibuf->x,
+                    ibuf->y,
+                    textarget,
+                    mipmap,
+                    compress_as_srgb,
+                    ima);
+
+  /* Free buffers if needed. */
+  if (rect && rect != (uchar *)ibuf->rect) {
+    MEM_freeN(rect);
+  }
+  if (rect_float && rect_float != ibuf->rect_float) {
+    MEM_freeN(rect_float);
+  }
+
+  return bindcode;
+}
+
+static void gpu_texture_update_scaled(
+    uchar *rect, float *rect_float, int full_w, int full_h, int x, int y, int w, int h)
 {
-  size_t offset = ibuf->channels * start_line * ibuf->x;
-  float *current_srgb_frect = srgb_frect + offset;
-  float *current_rect_float = ibuf->rect_float + offset;
-  IMB_buffer_float_from_float(current_srgb_frect,
-                              current_rect_float,
-                              ibuf->channels,
-                              IB_PROFILE_SRGB,
-                              IB_PROFILE_LINEAR_RGB,
-                              true,
-                              ibuf->x,
-                              height,
-                              ibuf->x,
-                              ibuf->x);
-  IMB_buffer_float_unpremultiply(current_srgb_frect, ibuf->x, height);
+  /* Partial update with scaling. */
+  int limit_w = smaller_power_of_2_limit(full_w);
+  int limit_h = smaller_power_of_2_limit(full_h);
+  float xratio = limit_w / (float)full_w;
+  float yratio = limit_h / (float)full_h;
+
+  /* Find sub coordinates in scaled image. Take ceiling because we will be
+   * losing 1 pixel due to rounding errors in x,y. */
+  int sub_x = x * xratio;
+  int sub_y = y * yratio;
+  int sub_w = (int)ceil(xratio * w);
+  int sub_h = (int)ceil(yratio * h);
+
+  /* ...but take back if we are over the limit! */
+  if (sub_w + sub_x > limit_w) {
+    sub_w--;
+  }
+  if (sub_h + sub_y > limit_h) {
+    sub_h--;
+  }
+
+  /* Scale pixels. */
+  ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, w, h);
+  IMB_scaleImBuf(ibuf, sub_w, sub_h);
+
+  if (ibuf->rect_float) {
+    glTexSubImage2D(
+        GL_TEXTURE_2D, 0, sub_x, sub_y, sub_w, sub_h, GL_RGBA, GL_FLOAT, ibuf->rect_float);
+  }
+  else {
+    glTexSubImage2D(
+        GL_TEXTURE_2D, 0, sub_x, sub_y, sub_w, sub_h, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
+  }
+
+  IMB_freeImBuf(ibuf);
 }
 
-static void verify_thread_do(void *data_v, int start_scanline, int num_scanlines)
+static void gpu_texture_update_unscaled(
+    uchar *rect, float *rect_float, int x, int y, int w, int h, GLint tex_stride, GLint tex_offset)
 {
-  VerifyThreadData *data = (VerifyThreadData *)data_v;
-  gpu_verify_high_bit_srgb_buffer_slice(
-      data->srgb_frect, data->ibuf, start_scanline, num_scanlines);
+  /* Partial update without scaling. Stride and offset are used to copy only a
+   * subset of a possible larger buffer than what we are updating. */
+  GLint row_length;
+  glGetIntegerv(GL_UNPACK_ROW_LENGTH, &row_length);
+  glPixelStorei(GL_UNPACK_ROW_LENGTH, tex_stride);
+
+  if (rect_float == NULL) {
+    glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect + tex_offset);
+  }
+  else {
+    glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, rect_float + tex_offset);
+  }
+
+  glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
 }
 
-static void gpu_verify_high_bit_srgb_buffer(float *srgb_frect, ImBuf *ibuf)
+static void gpu_texture_update_from_ibuf(ImBuf *ibuf, int x, int y, int w, int h)
 {
-  if (ibuf->y < 64) {
-    gpu_verify_high_bit_srgb_buffer_slice(srgb_frect, ibuf, 0, ibuf->y);
+  /* Partial update of texture for texture painting. This is often much
+   * quicker than fully updating the texture for high resolution images.
+   * Assumes the OpenGL texture is bound to 0. */
+  const bool scaled = is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y);
+
+  if (scaled) {
+    /* Extra padding to account for bleed from neighboring pixels. */
+    const int padding = 4;
+    const int xmax = min_ii(x + w + padding, ibuf->x);
+    const int ymax = min_ii(y + h + padding, ibuf->y);
+    x = max_ii(x - padding, 0);
+    y = max_ii(y - padding, 0);
+    w = xmax - x;
+    h = ymax - y;
+  }
+
+  /* Get texture data pointers. */
+  float *rect_float = ibuf->rect_float;
+  uchar *rect = (uchar *)ibuf->rect;
+  GLint tex_stride = ibuf->x;
+  GLint tex_offset = ibuf->channels * (y * ibuf->x + x);
+
+  if (rect_float == NULL) {
+    /* Byte pixels. */
+    if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) {
+      const bool compress_as_srgb = !IMB_colormanagement_space_is_scene_linear(
+          ibuf->rect_colorspace);
+
+      rect = MEM_mallocN(sizeof(uchar) * 4 * w * h, __func__);
+      if (rect == NULL) {
+        return;
+      }
+
+      tex_stride = w;
+      tex_offset = 0;
+
+      /* 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);
+    }
+  }
+  else if (ibuf->channels != 4 || scaled) {
+    /* Float pixels. */
+    rect_float = MEM_mallocN(sizeof(float) * 4 * x * y, __func__);
+    if (rect_float == NULL) {
+      return;
+    }
+
+    tex_stride = w;
+    tex_offset = 0;
+
+    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);
+  }
+
+  if (scaled) {
+    /* Slower update where we first have to scale the input pixels. */
+    gpu_texture_update_scaled(rect, rect_float, ibuf->x, ibuf->y, x, y, w, h);
   }
   else {
-    VerifyThreadData data;
-    data.ibuf = ibuf;
-    data.srgb_frect = srgb_frect;
-    IMB_processor_apply_threaded_scanlines(ibuf->y, verify_thread_do, &data);
+    /* Fast update at same resolution. */
+    gpu_texture_update_unscaled(rect, rect_float, x, y, w, h, tex_stride, tex_offset);
+  }
+
+  /* Free buffers if needed. */
+  if (rect && rect != (uchar *)ibuf->rect) {
+    MEM_freeN(rect);
+  }
+  if (rect_float && rect_float != ibuf->rect_float) {
+    MEM_freeN(rect_float);
   }
 }
 
-GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget, bool is_data)
+GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget)
 {
   if (ima == NULL) {
     return NULL;
@@ -286,62 +466,7 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget
     return *tex;
   }
 
-  /* flag to determine whether deep format is used */
-  bool use_high_bit_depth = false, do_color_management = false;
-
-  if (ibuf->rect_float) {
-    use_high_bit_depth = true;
-
-    /* TODO unneeded when float images are correctly treated as linear always */
-    if (!is_data) {
-      do_color_management = true;
-    }
-  }
-
-  const int rectw = ibuf->x;
-  const int recth = ibuf->y;
-  uint *rect = ibuf->rect;
-  float *frect = NULL;
-  float *srgb_frect = NULL;
-
-  if (use_high_bit_depth) {
-    if (do_color_management) {
-      frect = srgb_frect = MEM_mallocN(ibuf->x * ibuf->y * sizeof(*srgb_frect) * 4,
-                                       "floar_buf_col_cor");
-      gpu_verify_high_bit_srgb_buffer(srgb_frect, ibuf);
-    }
-    else {
-      frect = ibuf->rect_float;
-    }
-  }
-
-  const bool mipmap = GPU_get_mipmap();
-
-#ifdef WITH_DDS
-  if (ibuf->ftype == IMB_FTYPE_DDS) {
-    GPU_create_gl_tex_compressed(&bindcode, rect, rectw, recth, textarget, mipmap, ima, ibuf);
-  }
-  else
-#endif
-  {
-    GPU_create_gl_tex(
-        &bindcode, rect, frect, rectw, recth, textarget, mipmap, use_high_bit_depth, ima);
-  }
-
-  /* mark as non-color data texture */
-  if (bindcode) {
-    if (is_data) {
-      ima->gpuflag |= IMA_GPU_IS_DATA;
-    }
-    else {
-      ima->gpuflag &= ~IMA_GPU_IS_DATA;
-    }
-  }
-
-  /* clean up */
-  if (srgb_frect) {
-    MEM_freeN(srgb_frect);
-  }
+  bindcode = gpu_texture_create_from_ibuf(ima, ibuf, textarget);
 
   BKE_image_release_ibuf(ima, ibuf, NULL);
 
@@ -349,15 +474,14 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget
   return *tex;
 }
 
-static void **gpu_gen_cube_map(
-    uint *rect, float *frect, int rectw, int recth, bool use_high_bit_depth)
+static void **gpu_gen_cube_map(uint *rect, float *frect, int rectw, int recth)
 {
-  size_t block_size = use_high_bit_depth ? sizeof(float[4]) : sizeof(uchar[4]);
+  size_t block_size = frect ? sizeof(float[4]) : sizeof(uchar[4]);
   void **sides = NULL;
   int h = recth / 2;
   int w = rectw / 3;
 
-  if ((use_high_bit_depth && frect == NULL) || (!use_high_bit_depth && rect == NULL) || w != h) {
+  if (w != h) {
     return sides;
   }
 
@@ -376,7 +500,7 @@ static void **gpu_gen_cube_map(
    * | NegZ | PosZ | PosY |
    * |______|______|______|
    */
-  if (use_high_bit_depth) {
+  if (frect) {
     float(*frectb)[4] = (float(*)[4])frect;
     float(**fsides)[4] = (float(**)[4])sides;
 
@@ -430,7 +554,7 @@ void GPU_create_gl_tex(uint *bind,
                        int recth,
                        int textarget,
                        bool mipmap,
-                       bool use_high_bit_depth,
+                       bool use_srgb,
                        Image *ima)
 {
   ImBuf *ibuf = NULL;
@@ -441,7 +565,7 @@ void GPU_create_gl_tex(uint *bind,
     rectw = smaller_power_of_2_limit(rectw);
     recth = smaller_power_of_2_limit(recth);
 
-    if (use_high_bit_depth) {
+    if (frect) {
       ibuf = IMB_allocFromBuffer(NULL, frect, tpx, tpy);
       IMB_scaleImBuf(ibuf, rectw, recth);
 
@@ -459,12 +583,15 @@ void GPU_create_gl_tex(uint *bind,
   glGenTextures(1, (GLuint *)bind);
   glBindTexture(textarget, *bind);
 
+  GLenum internal_format = (frect) ? GL_RGBA16F : (use_srgb) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
+
   if (textarget == GL_TEXTURE_2D) {
-    if (use_high_bit_depth) {
-      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, rectw, recth, 0, GL_RGBA, GL_FLOAT, frect);
+    if (frect) {
+      glTexImage2D(GL_TEXTURE_2D, 0, internal_format, rectw, recth, 0, GL_RGBA, GL_FLOAT, frect);
     }
     else {
-      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, rectw, recth, 0, GL_RGBA, GL_UNSIGNED_BYTE, rect);
+      glTexImage2D(
+          GL_TEXTURE_2D, 0, internal_format, rectw, recth, 0, GL_RGBA, GL_UNSIGNED_BYTE, rect);
     }
 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1));
@@ -484,15 +611,14 @@ void GPU_create_gl_tex(uint *bind,
     int w = rectw / 3, h = recth / 2;
 
     if (h == w && is_power_of_2_i(h) && !is_over_resolution_limit(textarget, h, w)) {
-      void **cube_map = gpu_gen_cube_map(rect, frect, rectw, recth, use_high_bit_depth);
-      GLenum informat = use_high_bit_depth ? GL_RGBA16F : GL_RGBA8;
-      GLenum type = use_high_bit_depth ? GL_FLOAT : GL_UNSIGNED_BYTE;
+      void **cube_map = gpu_gen_cube_map(rect, frect, rectw, recth);
+      GLenum type = frect ? GL_FLOAT : GL_UNSIGNED_BYTE;
 
       if (cube_map) {
         for (int i = 0; i < 6; i++) {
           glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
                        0,
-                       informat,
+                       internal_format,
                        w,
                        h,
                        0,
@@ -617,14 +743,14 @@ void GPU_create_gl_tex_compressed(
 #ifndef WITH_DDS
   (void)ibuf;
   /* Fall back to uncompressed if DDS isn't enabled */
-  GPU_create_gl_tex(bind, pix, NULL, x, y, textarget, mipmap, 0, ima);
+  GPU_create_gl_tex(bind, pix, NULL, x, y, textarget, mipmap, true, ima);
 #else
   glGenTextures(1, (GLuint *)bind);
   glBindTexture(textarget, *bind);
 
   if (textarget == GL_TEXTURE_2D && GPU_upload_dxt_texture(ibuf) == 0) {
     glDeleteTextures(1, (GLuint *)bind);
-    GPU_create_gl_tex(bind, pix, NULL, x, y, textarget, mipmap, 0, ima);
+    GPU_create_gl_tex(bind, pix, NULL, x, y, textarget, mipmap, true, ima);
   }
 
   glBindTexture(textarget, 0);
@@ -680,146 +806,20 @@ void GPU_paint_set_mipmap(Main *bmain, bool mipmap)
   }
 }
 
-/* check if image has been downscaled and do scaled partial update */
-static bool gpu_check_scaled_image(
-    ImBuf *ibuf, Image *ima, float *frect, int x, int y, int w, int h)
-{
-  if (is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y)) {
-    int x_limit = smaller_power_of_2_limit(ibuf->x);
-    int y_limit = smaller_power_of_2_limit(ibuf->y);
-
-    float xratio = x_limit / (float)ibuf->x;
-    float yratio = y_limit / (float)ibuf->y;
-
-    /* find new width, height and x,y gpu texture coordinates */
-
-    /* take ceiling because we will be losing 1 pixel due to rounding errors in x,y... */
-    int rectw = (int)ceil(xratio * w);
-    int recth = (int)ceil(yratio * h);
-
-    x *= xratio;
-    y *= yratio;
-
-    /* ...but take back if we are over the limit! */
-    if (rectw + x > x_limit) {
-      rectw--;
-    }
-    if (recth + y > y_limit) {
-      recth--;
-    }
-
-    GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0);
-
-    /* float rectangles are already continuous in memory so we can use IMB_scaleImBuf */
-    if (frect) {
-      ImBuf *ibuf_scale = IMB_allocFromBuffer(NULL, frect, w, h);
-      IMB_scaleImBuf(ibuf_scale, rectw, recth);
-
-      glTexSubImage2D(
-          GL_TEXTURE_2D, 0, x, y, rectw, recth, GL_RGBA, GL_FLOAT, ibuf_scale->rect_float);
-
-      IMB_freeImBuf(ibuf_scale);
-    }
-    /* byte images are not continuous in memory so do manual interpolation */
-    else {
-      uchar *scalerect = MEM_mallocN(rectw * recth * sizeof(*scalerect) * 4, "scalerect");
-      uint *p = (uint *)scalerect;
-      int i, j;
-      float inv_xratio = 1.0f / xratio;
-      float inv_yratio = 1.0f / yratio;
-      for (i = 0; i < rectw; i++) {
-        float u = (x + i) * inv_xratio;
-        for (j = 0; j < recth; j++) {
-          float v = (y + j) * inv_yratio;
-          bilinear_interpolation_color_wrap(ibuf, (uchar *)(p + i + j * (rectw)), NULL, u, v);
-        }
-      }
-
-      glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, rectw, recth, GL_RGBA, GL_UNSIGNED_BYTE, scalerect);
-
-      MEM_freeN(scalerect);
-    }
-
-    if (GPU_get_mipmap()) {
-      glGenerateMipmap(GL_TEXTURE_2D);
-    }
-    else {
-      ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE;
-    }
-
-    GPU_texture_unbind(ima->gputexture[TEXTARGET_TEXTURE_2D]);
-
-    return true;
-  }
-
-  return false;
-}
-
 void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h)
 {
   ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
 
   if ((ima->gputexture[TEXTARGET_TEXTURE_2D] == NULL) || (ibuf == NULL) || (w == 0) || (h == 0)) {
-    /* these cases require full reload still */
+    /* Full reload of texture. */
     GPU_free_image(ima);
   }
   else {
-    /* for the special case, we can do a partial update
-     * which is much quicker for painting */
-    GLint row_length, skip_pixels, skip_rows;
-
-    /* if color correction is needed, we must update the part that needs updating. */
-    if (ibuf->rect_float) {
-      float *buffer = MEM_mallocN(w * h * sizeof(float) * 4, "temp_texpaint_float_buf");
-      bool is_data = (ima->gpuflag & IMA_GPU_IS_DATA) != 0;
-      IMB_partial_rect_from_float(ibuf, buffer, x, y, w, h, is_data);
-
-      if (gpu_check_scaled_image(ibuf, ima, buffer, x, y, w, h)) {
-        MEM_freeN(buffer);
-        BKE_image_release_ibuf(ima, ibuf, NULL);
-        return;
-      }
-
-      GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0);
-      glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, buffer);
-
-      MEM_freeN(buffer);
-
-      if (GPU_get_mipmap()) {
-        glGenerateMipmap(GL_TEXTURE_2D);
-      }
-      else {
-        ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE;
-      }
-
-      GPU_texture_unbind(ima->gputexture[TEXTARGET_TEXTURE_2D]);
-
-      BKE_image_release_ibuf(ima, ibuf, NULL);
-      return;
-    }
-
-    if (gpu_check_scaled_image(ibuf, ima, NULL, x, y, w, h)) {
-      BKE_image_release_ibuf(ima, ibuf, NULL);
-      return;
-    }
-
+    /* Partial update of texture. */
     GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0);
 
-    glGetIntegerv(GL_UNPACK_ROW_LENGTH, &row_length);
-    glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &skip_pixels);
-    glGetIntegerv(GL_UNPACK_SKIP_ROWS, &skip_rows);
-
-    glPixelStorei(GL_UNPACK_ROW_LENGTH, ibuf->x);
-    glPixelStorei(GL_UNPACK_SKIP_PIXELS, x);
-    glPixelStorei(GL_UNPACK_SKIP_ROWS, y);
-
-    glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
-
-    glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
-    glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels);
-    glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_rows);
+    gpu_texture_update_from_ibuf(ibuf, x, y, w, h);
 
-    /* see comment above as to why we are using gpu mipmap generation here */
     if (GPU_get_mipmap()) {
       glGenerateMipmap(GL_TEXTURE_2D);
     }
@@ -1242,7 +1242,7 @@ static void gpu_free_image_immediate(Image *ima)
     }
   }
 
-  ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE | IMA_GPU_IS_DATA);
+  ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE);
 }
 
 void GPU_free_image(Image *ima)
index 1a668a48eed76fe7c59f7189b2a1d7008124ca7d..6aa13b0d8ca445ae10b61304b228d783c65b3610 100644 (file)
@@ -597,15 +597,6 @@ eGPUMaterialStatus GPU_material_status(GPUMaterial *mat)
 
 /* Code generation */
 
-bool GPU_material_do_color_management(GPUMaterial *mat)
-{
-  if (!BKE_scene_check_color_management_enabled(mat->scene)) {
-    return false;
-  }
-
-  return true;
-}
-
 bool GPU_material_use_domain_surface(GPUMaterial *mat)
 {
   return (mat->domain & GPU_DOMAIN_SURFACE);
index ad4831ed903f148057816a6a8a82330ba07d6fc0..58d0dd5576f4bce7ad60fb0e96e3e39211dcd4ad 100644 (file)
@@ -1081,7 +1081,7 @@ GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap)
   /* this binds a texture, so that's why we restore it to 0 */
   if (bindcode == 0) {
     GPU_create_gl_tex(
-        &bindcode, prv->rect[0], NULL, prv->w[0], prv->h[0], GL_TEXTURE_2D, mipmap, 0, NULL);
+        &bindcode, prv->rect[0], NULL, prv->w[0], prv->h[0], GL_TEXTURE_2D, mipmap, false, NULL);
   }
   if (tex) {
     tex->bindcode = bindcode;
index 2f80bc95a0c2c6a61913fd43b6b9efb5c792e026..5596940cbf6482f8c2bd52d9b5bafae3829421aa 100644 (file)
@@ -117,38 +117,6 @@ void hsv_to_rgb(vec4 hsv, out vec4 outcol)
   outcol = vec4(rgb, hsv.w);
 }
 
-float srgb_to_linearrgb(float c)
-{
-  if (c < 0.04045)
-    return (c < 0.0) ? 0.0 : c * (1.0 / 12.92);
-  else
-    return pow((c + 0.055) * (1.0 / 1.055), 2.4);
-}
-
-float linearrgb_to_srgb(float c)
-{
-  if (c < 0.0031308)
-    return (c < 0.0) ? 0.0 : c * 12.92;
-  else
-    return 1.055 * pow(c, 1.0 / 2.4) - 0.055;
-}
-
-void srgb_to_linearrgb(vec4 col_from, out vec4 col_to)
-{
-  col_to.r = srgb_to_linearrgb(col_from.r);
-  col_to.g = srgb_to_linearrgb(col_from.g);
-  col_to.b = srgb_to_linearrgb(col_from.b);
-  col_to.a = col_from.a;
-}
-
-void linearrgb_to_srgb(vec4 col_from, out vec4 col_to)
-{
-  col_to.r = linearrgb_to_srgb(col_from.r);
-  col_to.g = linearrgb_to_srgb(col_from.g);
-  col_to.b = linearrgb_to_srgb(col_from.b);
-  col_to.a = col_from.a;
-}
-
 void color_to_normal_new_shading(vec3 color, out vec3 normal)
 {
   normal = vec3(2.0) * color - vec3(1.0);
@@ -2330,6 +2298,21 @@ void node_tex_environment_empty(vec3 co, out vec4 color)
 /* 16bits floats limits. Higher/Lower values produce +/-inf. */
 #define safe_color(a) (clamp(a, -65520.0, 65520.0))
 
+void tex_color_alpha_clear(vec4 color, out vec4 result)
+{
+  result = vec4(color.rgb, 1.0);
+}
+
+void tex_color_alpha_unpremultiply(vec4 color, out vec4 result)
+{
+  if (color.a == 0.0 || color.a == 1.0) {
+    result = vec4(color.rgb, 1.0);
+  }
+  else {
+    result = vec4(color.rgb / color.a, 1.0);
+  }
+}
+
 void node_tex_image_linear(vec3 co, sampler2D ima, out vec4 color, out float alpha)
 {
   color = safe_color(texture(ima, co.xy));
index 620f8984d9f17c03111f7b807012a4972a6cf95f..e683d38a0aac026c5043d87e371815f5ebc781b8 100644 (file)
@@ -58,6 +58,10 @@ void IMB_colormanagement_assign_rect_colorspace(struct ImBuf *ibuf, const char *
 const char *IMB_colormanagement_get_float_colorspace(struct ImBuf *ibuf);
 const char *IMB_colormanagement_get_rect_colorspace(struct ImBuf *ibuf);
 
+bool IMB_colormanagement_space_is_data(struct ColorSpace *colorspace);
+bool IMB_colormanagement_space_is_scene_linear(struct ColorSpace *colorspace);
+bool IMB_colormanagement_space_is_srgb(struct ColorSpace *colorspace);
+
 BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3]);
 BLI_INLINE unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char[3]);
 BLI_INLINE void IMB_colormangement_xyz_to_rgb(float rgb[3], const float xyz[3]);
@@ -124,6 +128,14 @@ void IMB_colormanagement_colorspace_to_scene_linear(float *buffer,
                                                     struct ColorSpace *colorspace,
                                                     bool predivide);
 
+void IMB_colormanagement_imbuf_to_srgb_texture(unsigned char *rect,
+                                               const int x,
+                                               const int y,
+                                               const int width,
+                                               const int height,
+                                               const struct ImBuf *ibuf,
+                                               const bool compress_as_srgb);
+
 void IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3]);
 void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3]);
 
@@ -340,6 +352,7 @@ enum {
   COLOR_ROLE_DEFAULT_SEQUENCER,
   COLOR_ROLE_DEFAULT_BYTE,
   COLOR_ROLE_DEFAULT_FLOAT,
+  COLOR_ROLE_DATA,
 };
 
 #include "intern/colormanagement_inline.c"
index 2566016ffdd69bd37b200452c63ad2d4bbe61d45..a83f2d60b8c8e8c47bc7cd1fec6fd89f8cf03085 100644 (file)
@@ -48,6 +48,13 @@ typedef struct ColorSpace {
 
   bool is_invertible;
   bool is_data;
+
+  /* Additional info computed only when needed since it's not cheap. */
+  struct {
+    bool cached;
+    bool is_srgb;
+    bool is_scene_linear;
+  } info;
 } ColorSpace;
 
 typedef struct ColorManagedDisplay {
index f31d4ede693eec416281fba1e843c072ba2811cf..b460d268d38099f0956727c95200148babbf1bd5 100644 (file)
@@ -65,6 +65,7 @@
 #define DISPLAY_BUFFER_CHANNELS 4
 
 /* ** list of all supported color spaces, displays and views */
+static char global_role_data[MAX_COLORSPACE_NAME];
 static char global_role_scene_linear[MAX_COLORSPACE_NAME];
 static char global_role_color_picking[MAX_COLORSPACE_NAME];
 static char global_role_texture_painting[MAX_COLORSPACE_NAME];
@@ -488,6 +489,7 @@ static void colormanage_load_config(OCIO_ConstConfigRcPtr *config)
   const char *name;
 
   /* get roles */
+  colormanage_role_color_space_name_get(config, global_role_data, OCIO_ROLE_DATA, NULL);
   colormanage_role_color_space_name_get(
       config, global_role_scene_linear, OCIO_ROLE_SCENE_LINEAR, NULL);
   colormanage_role_color_space_name_get(
@@ -1260,6 +1262,8 @@ void IMB_colormanagement_validate_settings(const ColorManagedDisplaySettings *di
 const char *IMB_colormanagement_role_colorspace_name_get(int role)
 {
   switch (role) {
+    case COLOR_ROLE_DATA:
+      return global_role_data;
     case COLOR_ROLE_SCENE_LINEAR:
       return global_role_scene_linear;
     case COLOR_ROLE_COLOR_PICKING:
@@ -1341,6 +1345,42 @@ const char *IMB_colormanagement_get_rect_colorspace(ImBuf *ibuf)
   }
 }
 
+bool IMB_colormanagement_space_is_data(ColorSpace *colorspace)
+{
+  return (colorspace && colorspace->is_data);
+}
+
+static void colormanage_ensure_srgb_scene_linear_info(ColorSpace *colorspace)
+{
+  if (!colorspace->info.cached) {
+    OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
+    OCIO_ConstColorSpaceRcPtr *ocio_colorspace = OCIO_configGetColorSpace(config,
+                                                                          colorspace->name);
+
+    bool is_scene_linear, is_srgb;
+    OCIO_colorSpaceIsBuiltin(config, ocio_colorspace, &is_scene_linear, &is_srgb);
+
+    OCIO_colorSpaceRelease(ocio_colorspace);
+    OCIO_configRelease(config);
+
+    colorspace->info.is_scene_linear = is_scene_linear;
+    colorspace->info.is_srgb = is_srgb;
+    colorspace->info.cached = true;
+  }
+}
+
+bool IMB_colormanagement_space_is_scene_linear(ColorSpace *colorspace)
+{
+  colormanage_ensure_srgb_scene_linear_info(colorspace);
+  return (colorspace && colorspace->info.is_scene_linear);
+}
+
+bool IMB_colormanagement_space_is_srgb(ColorSpace *colorspace)
+{
+  colormanage_ensure_srgb_scene_linear_info(colorspace);
+  return (colorspace && colorspace->info.is_srgb);
+}
+
 /*********************** Threaded display buffer transform routines *************************/
 
 typedef struct DisplayBufferThread {
@@ -2111,6 +2151,57 @@ void IMB_colormanagement_colorspace_to_scene_linear(float *buffer,
   }
 }
 
+void IMB_colormanagement_imbuf_to_srgb_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)
+{
+  /* Convert byte buffer for texture storage on the GPU. These have builtin
+   * support for converting sRGB to linear, which allows us to store textures
+   * without precision or performance loss at minimal memory usage. */
+  BLI_assert(ibuf->rect && ibuf->rect_float == NULL);
+
+  OCIO_ConstProcessorRcPtr *processor = NULL;
+  if (compress_as_srgb && ibuf->rect_colorspace &&
+      !IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace)) {
+    processor = colorspace_to_scene_linear_processor(ibuf->rect_colorspace);
+  }
+
+  /* TODO(brecht): make this multithreaded, or at least process in batches. */
+  const unsigned char *in_buffer = (unsigned char *)ibuf->rect;
+
+  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 unsigned char *in = in_buffer + in_offset * 4;
+    unsigned char *out = out_buffer + out_offset * 4;
+
+    if (processor) {
+      /* Convert to scene linear, to sRGB and premultiply. */
+      for (int x = 0; x < width; x++, in += 4, out += 4) {
+        float pixel[4];
+        rgba_uchar_to_float(pixel, in);
+        OCIO_processorApplyRGB(processor, pixel);
+        linearrgb_to_srgb_v3_v3(pixel, pixel);
+        mul_v3_fl(pixel, pixel[3]);
+        rgba_float_to_uchar(out, pixel);
+      }
+    }
+    else {
+      /* Premultiply only. */
+      for (int x = 0; x < width; x++, in += 4, out += 4) {
+        out[0] = (in[0] * in[3]) >> 8;
+        out[1] = (in[1] * in[3]) >> 8;
+        out[2] = (in[2] * in[3]) >> 8;
+        out[3] = in[3];
+      }
+    }
+  }
+}
+
 /* Conversion between color picking role. Typically we would expect such a
  * requirements:
  * - It is approximately perceptually linear, so that the HSV numbers and
index fec5f96b4f3f46b9d00233ca59f045ec36ad772a..0aceeda20d5917f0595abee76496cf72ec78026c 100644 (file)
@@ -191,8 +191,6 @@ enum {
   IMA_GPU_REFRESH = (1 << 0),
   /** All mipmap levels in OpenGL texture set? */
   IMA_GPU_MIPMAP_COMPLETE = (1 << 1),
-  /** OpenGL image texture bound as non-color data. */
-  IMA_GPU_IS_DATA = (1 << 2),
 };
 
 /* ima->type and ima->source moved to BKE_image.h, for API */
index 226e1d2f841ae53c48fd8ad220243df5524a92a6..3890fc63f3fb8bdc8f867be262fbea5fe379311d 100644 (file)
@@ -832,7 +832,7 @@ typedef struct NodeTexSky {
 typedef struct NodeTexImage {
   NodeTexBase base;
   ImageUser iuser;
-  int color_space;
+  int color_space DNA_DEPRECATED;
   int projection;
   float projection_blend;
   int interpolation;
@@ -853,7 +853,7 @@ typedef struct NodeTexBrick {
 typedef struct NodeTexEnvironment {
   NodeTexBase base;
   ImageUser iuser;
-  int color_space;
+  int color_space DNA_DEPRECATED;
   int projection;
   int interpolation;
   char _pad[4];
@@ -1121,10 +1121,6 @@ typedef struct NodeCryptomatte {
 #define SHD_SKY_OLD 0
 #define SHD_SKY_NEW 1
 
-/* image/environment texture */
-#define SHD_COLORSPACE_NONE 0
-#define SHD_COLORSPACE_COLOR 1
-
 /* environment texture */
 #define SHD_PROJ_EQUIRECTANGULAR 0
 #define SHD_PROJ_MIRROR_BALL 1
index 40ee069657c5ccfa8d4c83cb6075edaef805b0dc..8ba1f5440be59a61b13dcf8fa69492d98694a50c 100644 (file)
@@ -525,6 +525,22 @@ static char *rna_ColorManagedViewSettings_path(PointerRNA *UNUSED(ptr))
   return BLI_strdup("view_settings");
 }
 
+static bool rna_ColorManagedColorspaceSettings_is_data_get(struct PointerRNA *ptr)
+{
+  ColorManagedColorspaceSettings *colorspace = (ColorManagedColorspaceSettings *)ptr->data;
+  const char *data_name = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DATA);
+  return STREQ(colorspace->name, data_name);
+}
+
+static void rna_ColorManagedColorspaceSettings_is_data_set(struct PointerRNA *ptr, bool value)
+{
+  ColorManagedColorspaceSettings *colorspace = (ColorManagedColorspaceSettings *)ptr->data;
+  if (value) {
+    const char *data_name = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DATA);
+    STRNCPY(colorspace->name, data_name);
+  }
+}
+
 static int rna_ColorManagedColorspaceSettings_colorspace_get(struct PointerRNA *ptr)
 {
   ColorManagedColorspaceSettings *colorspace = (ColorManagedColorspaceSettings *)ptr->data;
@@ -1227,6 +1243,7 @@ static void rna_def_colormanage(BlenderRNA *brna)
 
   prop = RNA_def_property(srna, "name", PROP_ENUM, PROP_NONE);
   RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
+  RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
   RNA_def_property_enum_items(prop, color_space_items);
   RNA_def_property_enum_funcs(prop,
                               "rna_ColorManagedColorspaceSettings_colorspace_get",
@@ -1235,6 +1252,17 @@ static void rna_def_colormanage(BlenderRNA *brna)
   RNA_def_property_ui_text(prop, "Input Color Space", "Color space of the image or movie on disk");
   RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagedColorspaceSettings_reload_update");
 
+  prop = RNA_def_property(srna, "is_data", PROP_BOOLEAN, PROP_NONE);
+  RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+  RNA_def_property_boolean_funcs(prop,
+                                 "rna_ColorManagedColorspaceSettings_is_data_get",
+                                 "rna_ColorManagedColorspaceSettings_is_data_set");
+  RNA_def_property_ui_text(
+      prop,
+      "Is Data",
+      "Treat image as non-color data without color management, like normal or displacement maps");
+  RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update");
+
   //
   srna = RNA_def_struct(brna, "ColorManagedSequencerColorspaceSettings", NULL);
   RNA_def_struct_path_func(srna, "rna_ColorManagedSequencerColorspaceSettings_path");
index ca75f862b11749ddd13be69c498457357908353e..553dbeeb97b68247447775ff8fa01f77a0274537 100644 (file)
@@ -211,69 +211,30 @@ static void rna_Image_scale(Image *image, ReportList *reports, int width, int he
   }
 }
 
-static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int filter, int mag)
+static int rna_Image_gl_load(Image *image, ReportList *reports, int frame)
 {
-  GPUTexture *tex = image->gputexture[TEXTARGET_TEXTURE_2D];
-  int error = GL_NO_ERROR;
-
-  if (tex)
-    return error;
-
   ImageUser iuser = {NULL};
   iuser.framenr = frame;
   iuser.ok = true;
 
-  void *lock;
-  ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
-
-  /* clean glError buffer */
-  while (glGetError() != GL_NO_ERROR) {
-  }
+  GPUTexture *tex = GPU_texture_from_blender(image, &iuser, GL_TEXTURE_2D);
 
-  if (ibuf == NULL || ibuf->rect == NULL) {
-    BKE_reportf(reports, RPT_ERROR, "Image '%s' does not have any image data", image->id.name + 2);
-    BKE_image_release_ibuf(image, ibuf, lock);
+  if (tex == NULL) {
+    BKE_reportf(reports, RPT_ERROR, "Failed to load image texture '%s'", image->id.name + 2);
     return (int)GL_INVALID_OPERATION;
   }
 
-  unsigned int bindcode = 0;
-  GPU_create_gl_tex(&bindcode,
-                    ibuf->rect,
-                    ibuf->rect_float,
-                    ibuf->x,
-                    ibuf->y,
-                    GL_TEXTURE_2D,
-                    (filter != GL_NEAREST && filter != GL_LINEAR),
-                    false,
-                    image);
-
-  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLint)filter);
-  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLint)mag);
-
-  /* TODO(merwin): validate input (dimensions, filter, mag) before calling OpenGL
-   *               instead of trusting input & testing for error after */
-  error = glGetError();
-
-  if (error) {
-    glDeleteTextures(1, (GLuint *)&bindcode);
-  }
-  else {
-    image->gputexture[TEXTARGET_TEXTURE_2D] = GPU_texture_from_bindcode(GL_TEXTURE_2D, bindcode);
-  }
-
-  BKE_image_release_ibuf(image, ibuf, lock);
-
-  return error;
+  return GL_NO_ERROR;
 }
 
-static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame, int filter, int mag)
+static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame)
 {
   int error = GL_NO_ERROR;
 
   BKE_image_tag_time(image);
 
   if (image->gputexture[TEXTARGET_TEXTURE_2D] == NULL)
-    error = rna_Image_gl_load(image, reports, frame, filter, mag);
+    error = rna_Image_gl_load(image, reports, frame);
 
   return error;
 }
@@ -367,52 +328,20 @@ void RNA_api_image(StructRNA *srna)
   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);
-  RNA_def_int(func,
-              "filter",
-              GL_LINEAR_MIPMAP_NEAREST,
-              -INT_MAX,
-              INT_MAX,
-              "Filter",
-              "The texture minifying function to use if the image wasn't loaded",
-              -INT_MAX,
-              INT_MAX);
-  RNA_def_int(func,
-              "mag",
-              GL_LINEAR,
-              -INT_MAX,
-              INT_MAX,
-              "Magnification",
-              "The texture magnification function to use if the image wasn't loaded",
-              -INT_MAX,
-              INT_MAX);
   /* return value */
   parm = RNA_def_int(
       func, "error", 0, -INT_MAX, INT_MAX, "Error", "OpenGL error value", -INT_MAX, INT_MAX);
   RNA_def_function_return(func, parm);
 
   func = RNA_def_function(srna, "gl_load", "rna_Image_gl_load");
-  RNA_def_function_ui_description(func, "Load the image into OpenGL graphics memory");
+  RNA_def_function_ui_description(
+      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.");
   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);
-  RNA_def_int(func,
-              "filter",
-              GL_LINEAR_MIPMAP_NEAREST,
-              -INT_MAX,
-              INT_MAX,
-              "Filter",
-              "The texture minifying function",
-              -INT_MAX,
-              INT_MAX);
-  RNA_def_int(func,
-              "mag",
-              GL_LINEAR,
-              -INT_MAX,
-              INT_MAX,
-              "Magnification",
-              "The texture magnification function",
-              -INT_MAX,
-              INT_MAX);
   /* return value */
   parm = RNA_def_int(
       func, "error", 0, -INT_MAX, INT_MAX, "Error", "OpenGL error value", -INT_MAX, INT_MAX);
index e4270f3854e366fca9c1bf5255428c8d3fae5e41..4906f8ac28e06447c919393ff9cb177fa2fe1186 100644 (file)
@@ -3987,21 +3987,6 @@ static void def_sh_tex_sky(StructRNA *srna)
   RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
 }
 
-static const EnumPropertyItem sh_tex_prop_color_space_items[] = {
-    {SHD_COLORSPACE_COLOR,
-     "COLOR",
-     0,
-     "Color",
-     "Image contains color data, and will be converted to linear color for rendering"},
-    {SHD_COLORSPACE_NONE,
-     "NONE",
-     0,
-     "Non-Color Data",
-     "Image contains non-color data, for example a displacement or normal map, "
-     "and will not be converted"},
-    {0, NULL, 0, NULL, NULL},
-};
-
 static const EnumPropertyItem sh_tex_prop_interpolation_items[] = {
     {SHD_INTERP_LINEAR, "Linear", 0, "Linear", "Linear interpolation"},
     {SHD_INTERP_CLOSEST, "Closest", 0, "Closest", "No interpolation (sample closest texel)"},
@@ -4038,12 +4023,6 @@ static void def_sh_tex_environment(StructRNA *srna)
   RNA_def_struct_sdna_from(srna, "NodeTexEnvironment", "storage");
   def_sh_tex(srna);
 
-  prop = RNA_def_property(srna, "color_space", PROP_ENUM, PROP_NONE);
-  RNA_def_property_enum_items(prop, sh_tex_prop_color_space_items);
-  RNA_def_property_enum_default(prop, SHD_COLORSPACE_COLOR);
-  RNA_def_property_ui_text(prop, "Color Space", "Image file color space");
-  RNA_def_property_update(prop, 0, "rna_Node_update");
-
   prop = RNA_def_property(srna, "projection", PROP_ENUM, PROP_NONE);
   RNA_def_property_enum_items(prop, prop_projection_items);
   RNA_def_property_ui_text(prop, "Projection", "Projection of the input image");
@@ -4122,12 +4101,6 @@ static void def_sh_tex_image(StructRNA *srna)
   RNA_def_struct_sdna_from(srna, "NodeTexImage", "storage");
   def_sh_tex(srna);
 
-  prop = RNA_def_property(srna, "color_space", PROP_ENUM, PROP_NONE);
-  RNA_def_property_enum_items(prop, sh_tex_prop_color_space_items);
-  RNA_def_property_enum_default(prop, SHD_COLORSPACE_COLOR);
-  RNA_def_property_ui_text(prop, "Color Space", "Image file color space");
-  RNA_def_property_update(prop, 0, "rna_Node_update");
-
   prop = RNA_def_property(srna, "projection", PROP_ENUM, PROP_NONE);
   RNA_def_property_enum_items(prop, prop_projection_items);
   RNA_def_property_ui_text(
index 916cb81953e79641ed09cda987ee84168dd4091b..165ada05a56025ca71478c64db1a35e9f01f73cc 100644 (file)
@@ -67,6 +67,7 @@
 #include "RE_shader_ext.h"
 
 #include "GPU_material.h"
+#include "GPU_texture.h"
 #include "GPU_uniformbuffer.h"
 
 bool sh_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
index c3b8bc8bfd0c12d0904af063bcfd05f6d32d9364..615f55e43506780780cfebbc25531dfc03a63bcc 100644 (file)
@@ -46,7 +46,6 @@ static void node_shader_init_tex_environment(bNodeTree *UNUSED(ntree), bNode *no
   NodeTexEnvironment *tex = MEM_callocN(sizeof(NodeTexEnvironment), "NodeTexEnvironment");
   BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
   BKE_texture_colormapping_default(&tex->base.color_mapping);
-  tex->color_space = SHD_COLORSPACE_COLOR;
   tex->projection = SHD_PROJ_EQUIRECTANGULAR;
   BKE_imageuser_default(&tex->iuser);
 
@@ -68,7 +67,6 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
   NodeTexImage *tex_original = node_original->storage;
   ImageUser *iuser = &tex_original->iuser;
 
-  int isdata = tex->color_space == SHD_COLORSPACE_NONE;
   GPUNodeLink *outalpha;
 
   if (!ima) {
@@ -89,7 +87,7 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
              "node_tex_environment_equirectangular",
              in[0].link,
              GPU_constant(&clamp_size),
-             GPU_image(ima, iuser, isdata),
+             GPU_image(ima, iuser),
              &in[0].link);
   }
   else {
@@ -104,7 +102,7 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
       GPU_link(mat,
                "node_tex_image_linear_no_mip",
                in[0].link,
-               GPU_image(ima, iuser, isdata),
+               GPU_image(ima, iuser),
                &out[0].link,
                &outalpha);
       break;
@@ -112,26 +110,19 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
       GPU_link(mat,
                "node_tex_image_nearest",
                in[0].link,
-               GPU_image(ima, iuser, isdata),
+               GPU_image(ima, iuser),
                &out[0].link,
                &outalpha);
       break;
     default:
-      GPU_link(mat,
-               "node_tex_image_cubic",
-               in[0].link,
-               GPU_image(ima, iuser, isdata),
-               &out[0].link,
-               &outalpha);
+      GPU_link(
+          mat, "node_tex_image_cubic", in[0].link, GPU_image(ima, iuser), &out[0].link, &outalpha);
       break;
   }
 
-  ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
-  if (ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 &&
-      GPU_material_do_color_management(mat)) {
-    GPU_link(mat, "srgb_to_linearrgb", out[0].link, &out[0].link);
+  if (out[0].hasoutput) {
+    GPU_link(mat, "tex_color_alpha_clear", out[0].link, &out[0].link);
   }
-  BKE_image_release_ibuf(ima, ibuf, NULL);
 
   return true;
 }
index 722434ce783fb212d6b02b096d7d03ff610d4b26..95e76491b4a3ad87bdf6742b83a86343d347d130 100644 (file)
@@ -57,7 +57,6 @@ static void node_shader_init_tex_image(bNodeTree *UNUSED(ntree), bNode *node)
   NodeTexImage *tex = MEM_callocN(sizeof(NodeTexImage), "NodeTexImage");
   BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
   BKE_texture_colormapping_default(&tex->base.color_mapping);
-  tex->color_space = SHD_COLORSPACE_COLOR;
   BKE_imageuser_default(&tex->iuser);
 
   node->storage = tex;
@@ -99,7 +98,6 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
 
   const char *gpu_node_name = (tex->projection == SHD_PROJ_BOX) ? names_box[tex->interpolation] :
                                                                   names[tex->interpolation];
-  bool do_color_correction = false;
   bool do_texco_extend = (tex->extension != SHD_IMAGE_EXTENSION_REPEAT);
   const bool do_texco_clip = (tex->extension == SHD_IMAGE_EXTENSION_CLIP);
 
@@ -114,20 +112,10 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
   GPUNodeLink *vnor, *ob_mat, *blend;
   GPUNodeLink **texco = &in[0].link;
 
-  int isdata = tex->color_space == SHD_COLORSPACE_NONE;
-
   if (!ima) {
     return GPU_stack_link(mat, node, "node_tex_image_empty", in, out);
   }
 
-  ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
-  if ((tex->color_space == SHD_COLORSPACE_COLOR) && ibuf &&
-      (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 &&
-      GPU_material_do_color_management(mat)) {
-    do_color_correction = true;
-  }
-  BKE_image_release_ibuf(ima, ibuf, NULL);
-
   if (!*texco) {
     *texco = GPU_attribute(CD_MTFACE, "");
   }
@@ -140,26 +128,20 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
         GPU_link(mat, "set_rgb", *texco, &input_coords);
       }
       if (do_texco_extend) {
-        GPU_link(mat, "point_texco_clamp", *texco, GPU_image(ima, iuser, isdata), texco);
+        GPU_link(mat, "point_texco_clamp", *texco, GPU_image(ima, iuser), texco);
       }
-      GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata));
+      GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser));
       break;
 
     case SHD_PROJ_BOX:
       vnor = GPU_builtin(GPU_WORLD_NORMAL);
       ob_mat = GPU_builtin(GPU_OBJECT_MATRIX);
       blend = GPU_uniform(&tex->projection_blend);
-      gpu_image = GPU_image(ima, iuser, isdata);
+      gpu_image = GPU_image(ima, iuser);
 
       /* equivalent to normal_world_to_object */
       GPU_link(mat, "normal_transform_transposed_m4v3", vnor, ob_mat, &norm);
-      GPU_link(
-          mat, gpu_node_name, *texco, norm, GPU_image(ima, iuser, isdata), &col1, &col2, &col3);
-      if (do_color_correction) {
-        GPU_link(mat, "srgb_to_linearrgb", col1, &col1);
-        GPU_link(mat, "srgb_to_linearrgb", col2, &col2);
-        GPU_link(mat, "srgb_to_linearrgb", col3, &col3);
-      }
+      GPU_link(mat, gpu_node_name, *texco, norm, GPU_image(ima, iuser), &col1, &col2, &col3);
       GPU_stack_link(
           mat, node, "node_tex_image_box", in, out, norm, col1, col2, col3, gpu_image, blend);
       break;
@@ -171,9 +153,9 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
         GPU_link(mat, "set_rgb", *texco, &input_coords);
       }
       if (do_texco_extend) {
-        GPU_link(mat, "point_texco_clamp", *texco, GPU_image(ima, iuser, isdata), texco);
+        GPU_link(mat, "point_texco_clamp", *texco, GPU_image(ima, iuser), texco);
       }
-      GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata));
+      GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser));
       break;
 
     case SHD_PROJ_TUBE:
@@ -183,20 +165,25 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
         GPU_link(mat, "set_rgb", *texco, &input_coords);
       }
       if (do_texco_extend) {
-        GPU_link(mat, "point_texco_clamp", *texco, GPU_image(ima, iuser, isdata), texco);
+        GPU_link(mat, "point_texco_clamp", *texco, GPU_image(ima, iuser), texco);
       }
-      GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata));
+      GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser));
       break;
   }
 
   if (tex->projection != SHD_PROJ_BOX) {
     if (do_texco_clip) {
       gpu_node_name = names_clip[tex->interpolation];
-      GPU_stack_link(
-          mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata), out[0].link);
+      GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser), out[0].link);
+    }
+  }
+
+  if (out[0].hasoutput) {
+    if (out[1].hasoutput) {
+      GPU_link(mat, "tex_color_alpha_unpremultiply", out[0].link, &out[0].link);
     }
-    if (do_color_correction) {
-      GPU_link(mat, "srgb_to_linearrgb", out[0].link, &out[0].link);
+    else {
+      GPU_link(mat, "tex_color_alpha_clear", out[0].link, &out[0].link);
     }
   }