Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 427bb80e authored by Romain Guy's avatar Romain Guy Committed by android-build-merger
Browse files

Merge "Convert bitmaps to sRGB/scRGB when they have a color profile" into oc-dev am: 33813bd4

am: 6869c551

Change-Id: I69b4e1ba160032255891fbb2656445badd5b936e
parents db1251e1 6869c551
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ hwui_src_files := \
    service/GraphicsStatsService.cpp \
    thread/TaskManager.cpp \
    utils/Blur.cpp \
    utils/Color.cpp \
    utils/GLUtils.cpp \
    utils/LinearAllocator.cpp \
    utils/StringUtils.cpp \
+1 −1
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ public:
    }

    void setSize(uint32_t width, uint32_t height) override {
        texture.updateSize(width, height, texture.internalFormat(), texture.format(),
        texture.updateLayout(width, height, texture.internalFormat(), texture.format(),
                texture.target());
    }

+5 −1
Original line number Diff line number Diff line
@@ -605,7 +605,11 @@ void GlopBuilder::build() {
        } else {
            mDescription.hasExternalTexture = true;
        }
        mDescription.hasLinearTexture = mOutGlop->fill.texture.texture->isLinear();
        Texture* texture = mOutGlop->fill.texture.texture;
        mDescription.hasLinearTexture = texture->isLinear();
        mDescription.hasColorSpaceConversion = texture->hasColorSpaceConversion();
        mDescription.transferFunction = texture->getTransferFunctionType();
        mDescription.hasTranslucentConversion = texture->blend;
    }

    mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Color;
+35 −18
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include "FloatColor.h"
#include "Matrix.h"
#include "Properties.h"
#include "utils/Color.h"

namespace android {
namespace uirenderer {
@@ -56,11 +57,11 @@ namespace uirenderer {
#define PROGRAM_KEY_BITMAP_NPOT         0x80
#define PROGRAM_KEY_BITMAP_EXTERNAL    0x100

#define PROGRAM_KEY_SWAP_SRC_DST      0x2000

#define PROGRAM_KEY_BITMAP_WRAPS_MASK  0x600
#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800

#define PROGRAM_KEY_SWAP_SRC_DST_SHIFT 13

// Encode the xfermodes on 6 bits
#define PROGRAM_MAX_XFERMODE 0x1f
#define PROGRAM_XFERMODE_SHADER_SHIFT 26
@@ -89,6 +90,10 @@ namespace uirenderer {
#define PROGRAM_HAS_GAMMA_CORRECTION 44
#define PROGRAM_HAS_LINEAR_TEXTURE 45

#define PROGRAM_HAS_COLOR_SPACE_CONVERSION 46
#define PROGRAM_TRANSFER_FUNCTION 47 // 2 bits for transfer function
#define PROGRAM_HAS_TRANSLUCENT_CONVERSION 49

///////////////////////////////////////////////////////////////////////////////
// Types
///////////////////////////////////////////////////////////////////////////////
@@ -105,13 +110,13 @@ typedef uint64_t programid;
 * A ProgramDescription must be used in conjunction with a ProgramCache.
 */
struct ProgramDescription {
    enum class ColorFilterMode {
    enum class ColorFilterMode : int8_t {
        None = 0,
        Matrix,
        Blend
    };

    enum Gradient {
    enum Gradient : int8_t {
        kGradientLinear = 0,
        kGradientCircular,
        kGradientSweep
@@ -168,6 +173,11 @@ struct ProgramDescription {
    // Set when sampling an image in linear space
    bool hasLinearTexture;

    bool hasColorSpaceConversion;
    TransferFunctionType transferFunction;
    // Indicates whether the bitmap to convert between color spaces is translucent
    bool hasTranslucentConversion;

    /**
     * Resets this description. All fields are reset back to the default
     * values they hold after building a new instance.
@@ -210,6 +220,10 @@ struct ProgramDescription {

        hasGammaCorrection = false;
        hasLinearTexture = false;

        hasColorSpaceConversion = false;
        transferFunction = TransferFunctionType::None;
        hasTranslucentConversion = false;
    }

    /**
@@ -269,18 +283,21 @@ struct ProgramDescription {
                break;
        }
        key |= ((int) framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
        if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
        if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
        if (hasVertexAlpha) key |= programid(0x1) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT;
        if (useShadowAlphaInterp) key |= programid(0x1) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT;
        if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
        if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
        if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
        if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS;
        if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
        if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP;
        if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
        if (hasLinearTexture) key |= programid(0x1) << PROGRAM_HAS_LINEAR_TEXTURE;
        key |= programid(swapSrcDst) << PROGRAM_KEY_SWAP_SRC_DST_SHIFT;
        key |= programid(modulate) << PROGRAM_MODULATE_SHIFT;
        key |= programid(hasVertexAlpha) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT;
        key |= programid(useShadowAlphaInterp) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT;
        key |= programid(hasExternalTexture) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
        key |= programid(hasTextureTransform) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
        key |= programid(isSimpleGradient) << PROGRAM_IS_SIMPLE_GRADIENT;
        key |= programid(hasColors) << PROGRAM_HAS_COLORS;
        key |= programid(hasDebugHighlight) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
        key |= programid(hasRoundRectClip) << PROGRAM_HAS_ROUND_RECT_CLIP;
        key |= programid(hasGammaCorrection) << PROGRAM_HAS_GAMMA_CORRECTION;
        key |= programid(hasLinearTexture) << PROGRAM_HAS_LINEAR_TEXTURE;
        key |= programid(hasColorSpaceConversion) << PROGRAM_HAS_COLOR_SPACE_CONVERSION;
        key |= programid(transferFunction) << PROGRAM_TRANSFER_FUNCTION;
        key |= programid(hasTranslucentConversion) << PROGRAM_HAS_TRANSLUCENT_CONVERSION;
        return key;
    }

+118 −19
Original line number Diff line number Diff line
@@ -161,17 +161,61 @@ const char* gFS_Uniforms_HasRoundRectClip =
        "uniform vec4 roundRectInnerRectLTRB;\n"
        "uniform float roundRectRadius;\n";

const char* gFS_Uniforms_ColorSpaceConversion =
        // TODO: Should we use a 3D LUT to combine the matrix and transfer functions?
        // 32x32x32 fp16 LUTs (for scRGB output) are large and heavy to generate...
        "uniform mat3 colorSpaceMatrix;\n";

const char* gFS_Uniforms_TransferFunction[4] = {
        // In this order: g, a, b, c, d, e, f
        // See ColorSpace::TransferParameters
        // We'll use hardware sRGB conversion as much as possible
        "",
        "uniform float transferFunction[7];\n",
        "uniform float transferFunction[5];\n",
        "uniform float transferFunctionGamma;\n"
};

const char* gFS_OETF[2] = {
         "\nvec4 OETF(const vec4 linear) {\n"
         "    return linear;\n"
         "}\n",
        R"__SHADER__(
        vec4 OETF(const vec4 linear) {
            return linear;
        }
        )__SHADER__",
        // We expect linear data to be scRGB so we mirror the gamma function
         "\nvec4 OETF(const vec4 linear) {"
          "    return vec4(sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)), linear.a);\n"
          "}\n",
        R"__SHADER__(
        vec4 OETF(const vec4 linear) {
            return vec4(sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)), linear.a);
        }
        )__SHADER__"
};

const char* gFS_ColorConvert[3] = {
        // Just OETF
        R"__SHADER__(
        vec4 colorConvert(const vec4 color) {
            return OETF(color);
        }
        )__SHADER__",
        // Full color conversion for opaque bitmaps
        R"__SHADER__(
        vec4 colorConvert(const vec4 color) {
            return OETF(vec4(colorSpaceMatrix * EOTF_Parametric(color.rgb), color.a));
        }
        )__SHADER__",
        // Full color conversion for translucent bitmaps
        // Note: 0.5/256=0.0019
        R"__SHADER__(
        vec4 colorConvert(in vec4 color) {
            color.rgb /= color.a + 0.0019;
            color = OETF(vec4(colorSpaceMatrix * EOTF_Parametric(color.rgb), color.a));
            color.rgb *= color.a + 0.0019;
            return color;
        }
        )__SHADER__",
};

const char* gFS_Transfer_Functions = R"__SHADER__(
const char* gFS_sRGB_TransferFunctions = R"__SHADER__(
        float OETF_sRGB(const float linear) {
            // IEC 61966-2-1:1999
            return linear <= 0.0031308 ? linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
@@ -187,12 +231,56 @@ const char* gFS_Transfer_Functions = R"__SHADER__(
        }
)__SHADER__";

const char* gFS_TransferFunction[4] = {
        // Conversion done by the texture unit (sRGB)
        R"__SHADER__(
        vec3 EOTF_Parametric(const vec3 x) {
            return x;
        }
        )__SHADER__",
        // Full transfer function
        // TODO: We should probably use a 1D LUT (256x1 with texelFetch() since input is 8 bit)
        // TODO: That would cause 3 dependent texture fetches. Is it worth it?
        R"__SHADER__(
        float EOTF_Parametric(float x) {
            return x <= transferFunction[4]
                  ? transferFunction[3] * x + transferFunction[6]
                  : pow(transferFunction[1] * x + transferFunction[2], transferFunction[0])
                          + transferFunction[5];
        }

        vec3 EOTF_Parametric(const vec3 x) {
            return vec3(EOTF_Parametric(x.r), EOTF_Parametric(x.g), EOTF_Parametric(x.b));
        }
        )__SHADER__",
        // Limited transfer function, e = f = 0.0
        R"__SHADER__(
        float EOTF_Parametric(float x) {
            return x <= transferFunction[4]
                  ? transferFunction[3] * x
                  : pow(transferFunction[1] * x + transferFunction[2], transferFunction[0]);
        }

        vec3 EOTF_Parametric(const vec3 x) {
            return vec3(EOTF_Parametric(x.r), EOTF_Parametric(x.g), EOTF_Parametric(x.b));
        }
        )__SHADER__",
        // Gamma transfer function, e = f = 0.0
        R"__SHADER__(
        vec3 EOTF_Parametric(const vec3 x) {
            return vec3(pow(x.r, transferFunctionGamma),
                        pow(x.g, transferFunctionGamma),
                        pow(x.b, transferFunctionGamma));
        }
        )__SHADER__"
};

// Dithering must be done in the quantization space
// When we are writing to an sRGB framebuffer, we must do the following:
//     EOTF(OETF(color) + dither)
// The dithering pattern is generated with a triangle noise generator in the range [-1.0,1.0]
// TODO: Handle linear fp16 render targets
const char* gFS_Gradient_Functions = R"__SHADER__(
const char* gFS_GradientFunctions = R"__SHADER__(
        float triangleNoise(const highp vec2 n) {
            highp vec2 p = fract(n * vec2(5.3987, 5.4421));
            p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
@@ -200,7 +288,8 @@ const char* gFS_Gradient_Functions = R"__SHADER__(
            return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
        }
)__SHADER__";
const char* gFS_Gradient_Preamble[2] = {

const char* gFS_GradientPreamble[2] = {
        // Linear framebuffer
        R"__SHADER__(
        vec4 dither(const vec4 color) {
@@ -259,9 +348,9 @@ const char* gFS_Main_ApplyVertexAlphaShadowInterp =
        "    fragColor *= texture2D(baseSampler, vec2(alpha, 0.5)).a;\n";
const char* gFS_Main_FetchTexture[2] = {
        // Don't modulate
        "    fragColor = OETF(texture2D(baseSampler, outTexCoords));\n",
        "    fragColor = colorConvert(texture2D(baseSampler, outTexCoords));\n",
        // Modulate
        "    fragColor = color * texture2D(baseSampler, outTexCoords);\n"
        "    fragColor = color * colorConvert(texture2D(baseSampler, outTexCoords));\n"
};
const char* gFS_Main_FetchA8Texture[4] = {
        // Don't modulate
@@ -290,9 +379,9 @@ const char* gFS_Main_FetchGradient[6] = {
        "    vec4 gradientColor = gradientMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
};
const char* gFS_Main_FetchBitmap =
        "    vec4 bitmapColor = OETF(texture2D(bitmapSampler, outBitmapTexCoords));\n";
        "    vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, outBitmapTexCoords));\n";
const char* gFS_Main_FetchBitmapNpot =
        "    vec4 bitmapColor = OETF(texture2D(bitmapSampler, wrap(outBitmapTexCoords)));\n";
        "    vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, wrap(outBitmapTexCoords)));\n";
const char* gFS_Main_BlendShadersBG =
        "    fragColor = blendShaders(gradientColor, bitmapColor)";
const char* gFS_Main_BlendShadersGB =
@@ -627,6 +716,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
    }
    shader.append(gFS_Uniforms_ColorOp[static_cast<int>(description.colorOp)]);

    if (description.hasColorSpaceConversion) {
        shader.append(gFS_Uniforms_ColorSpaceConversion);
    }
    shader.append(gFS_Uniforms_TransferFunction[static_cast<int>(description.transferFunction)]);

    // Generate required functions
    if (description.hasGradient && description.hasBitmap) {
        generateBlend(shader, "blendShaders", description.shadersMode);
@@ -640,16 +734,21 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
    if (description.useShaderBasedWrap) {
        generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
    }
    if (description.hasGradient || description.hasLinearTexture) {
        shader.append(gFS_Transfer_Functions);
    if (description.hasGradient || description.hasLinearTexture
            || description.hasColorSpaceConversion) {
        shader.append(gFS_sRGB_TransferFunctions);
    }
    if (description.hasBitmap || ((description.hasTexture || description.hasExternalTexture) &&
            !description.hasAlpha8Texture)) {
        shader.append(gFS_OETF[description.hasLinearTexture && !mHasLinearBlending]);
        shader.append(gFS_TransferFunction[static_cast<int>(description.transferFunction)]);
        shader.append(gFS_OETF[(description.hasLinearTexture || description.hasColorSpaceConversion)
                && !mHasLinearBlending]);
        shader.append(gFS_ColorConvert[description.hasColorSpaceConversion
                ? 1 + description.hasTranslucentConversion : 0]);
    }
    if (description.hasGradient) {
        shader.append(gFS_Gradient_Functions);
        shader.append(gFS_Gradient_Preamble[mHasLinearBlending]);
        shader.append(gFS_GradientFunctions);
        shader.append(gFS_GradientPreamble[mHasLinearBlending]);
    }

    // Begin the shader
Loading