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

Commit 669b15a9 authored by Derek Sollenberger's avatar Derek Sollenberger
Browse files

Fix HWUI/Skia Gradients to premultiply the colors prior to interpolation

This is fixed in Skia by passing the appropriate flag when the shader is
generated.  The fix in HWUI is to reverse the premultiplication and
interpolation steps.

Test: bit CtsUiRenderingTestCases:.testclasses.ShaderTests
Bug: 34323783
Change-Id: I3417141949f62fcc696b6d8213a4b446d7d0cbf8
parent 3f2bbcbe
Loading
Loading
Loading
Loading
+15 −7
Original line number Diff line number Diff line
@@ -10,6 +10,14 @@

using namespace android::uirenderer;

/**
 * By default Skia gradients will interpolate their colors in unpremul space
 * and then premultiply each of the results. We must set this flag to preserve
 * backwards compatiblity by premultiplying the colors of the gradient first,
 * and then interpolating between them.
 */
static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;

static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
    if (NULL == ptr) {
        doThrowIAE(env);
@@ -89,7 +97,7 @@ static jlong LinearGradient_create1(JNIEnv* env, jobject o, jlong matrixPtr,

    SkShader* shader = SkGradientShader::MakeLinear(pts,
            reinterpret_cast<const SkColor*>(colorValues), pos, count,
            static_cast<SkShader::TileMode>(tileMode), /* flags */ 0, matrix).release();
            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release();

    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
    ThrowIAE_IfNull(env, shader);
@@ -109,7 +117,7 @@ static jlong LinearGradient_create2(JNIEnv* env, jobject o, jlong matrixPtr,
    colors[1] = color1;

    SkShader* s = SkGradientShader::MakeLinear(pts, colors, NULL, 2,
            (SkShader::TileMode)tileMode, /* flags */ 0, matrix).release();
            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release();

    ThrowIAE_IfNull(env, s);
    return reinterpret_cast<jlong>(s);
@@ -135,7 +143,7 @@ static jlong RadialGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloa

    SkShader* shader = SkGradientShader::MakeRadial(center, radius,
            reinterpret_cast<const SkColor*>(colorValues), pos, count,
            static_cast<SkShader::TileMode>(tileMode), /* flags */ 0, matrix).release();
            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release();
    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues),
                                 JNI_ABORT);

@@ -154,7 +162,7 @@ static jlong RadialGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloa
    colors[1] = color1;

    SkShader* s = SkGradientShader::MakeRadial(center, radius, colors, NULL, 2,
            (SkShader::TileMode)tileMode, /* flags */ 0, matrix).release();
            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release();
    ThrowIAE_IfNull(env, s);
    return reinterpret_cast<jlong>(s);
}
@@ -174,8 +182,8 @@ static jlong SweepGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloat
    #error Need to convert float array to SkScalar array before calling the following function.
#endif

    SkShader* shader = SkGradientShader::MakeSweep(x, y,
            reinterpret_cast<const SkColor*>(colors), pos, count, /* flags */ 0, matrix).release();
    SkShader* shader = SkGradientShader::MakeSweep(x, y, reinterpret_cast<const SkColor*>(colors),
            pos, count, sGradientShaderFlags, matrix).release();
    env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors),
                                 JNI_ABORT);
    ThrowIAE_IfNull(env, shader);
@@ -189,7 +197,7 @@ static jlong SweepGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloat
    colors[0] = color0;
    colors[1] = color1;
    SkShader* s = SkGradientShader::MakeSweep(x, y, colors, NULL, 2,
            /* flags */ 0, matrix).release();
            sGradientShaderFlags, matrix).release();
    ThrowIAE_IfNull(env, s);
    return reinterpret_cast<jlong>(s);
}
+12 −12
Original line number Diff line number Diff line
@@ -189,9 +189,9 @@ void GradientCache::mixBytes(const FloatColor& start, const FloatColor& end,
        float amount, uint8_t*& dst) const {
    float oppAmount = 1.0f - amount;
    float a = start.a * oppAmount + end.a * amount;
    *dst++ = uint8_t(a * OECF(start.r * oppAmount + end.r * amount) * 255.0f);
    *dst++ = uint8_t(a * OECF(start.g * oppAmount + end.g * amount) * 255.0f);
    *dst++ = uint8_t(a * OECF(start.b * oppAmount + end.b * amount) * 255.0f);
    *dst++ = uint8_t(OECF(start.r * oppAmount + end.r * amount) * 255.0f);
    *dst++ = uint8_t(OECF(start.g * oppAmount + end.g * amount) * 255.0f);
    *dst++ = uint8_t(OECF(start.b * oppAmount + end.b * amount) * 255.0f);
    *dst++ = uint8_t(a * 255.0f);
}

@@ -202,13 +202,13 @@ void GradientCache::mixFloats(const FloatColor& start, const FloatColor& end,
    float* d = (float*) dst;
#ifdef ANDROID_ENABLE_LINEAR_BLENDING
    // We want to stay linear
    *d++ = a * (start.r * oppAmount + end.r * amount);
    *d++ = a * (start.g * oppAmount + end.g * amount);
    *d++ = a * (start.b * oppAmount + end.b * amount);
    *d++ = (start.r * oppAmount + end.r * amount);
    *d++ = (start.g * oppAmount + end.g * amount);
    *d++ = (start.b * oppAmount + end.b * amount);
#else
    *d++ = a * OECF(start.r * oppAmount + end.r * amount);
    *d++ = a * OECF(start.g * oppAmount + end.g * amount);
    *d++ = a * OECF(start.b * oppAmount + end.b * amount);
    *d++ = OECF(start.r * oppAmount + end.r * amount);
    *d++ = OECF(start.g * oppAmount + end.g * amount);
    *d++ = OECF(start.b * oppAmount + end.b * amount);
#endif
    *d++ = a;
    dst += 4 * sizeof(float);
@@ -229,10 +229,10 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions,
    ChannelMixer mix = gMixers[mUseFloatTexture];

    FloatColor start;
    start.setUnPreMultiplied(colors[0]);
    start.set(colors[0]);

    FloatColor end;
    end.setUnPreMultiplied(colors[1]);
    end.set(colors[1]);

    int currentPos = 1;
    float startPos = positions[0];
@@ -247,7 +247,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions,

            currentPos++;

            end.setUnPreMultiplied(colors[currentPos]);
            end.set(colors[currentPos]);
            distance = positions[currentPos] - startPos;
        }

+3 −11
Original line number Diff line number Diff line
@@ -295,10 +295,6 @@ const char* gFS_GradientPreamble[2] = {
        vec4 dither(const vec4 color) {
            return color + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);
        }
        vec4 gradientMix(const vec4 a, const vec4 b, float v) {
            vec4 c = mix(a, b, v);
            return vec4(c.rgb * c.a, c.a);
        }
        )__SHADER__",
        // sRGB framebuffer
        R"__SHADER__(
@@ -306,10 +302,6 @@ const char* gFS_GradientPreamble[2] = {
            vec3 dithered = sqrt(color.rgb) + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);
            return vec4(dithered * dithered, color.a);
        }
        vec4 gradientMixMix(const vec4 a, const vec4 b, float v) {
            vec4 c = mix(a, b, v);
            return vec4(c.rgb * c.a, c.a);
        }
        )__SHADER__",
};

@@ -364,19 +356,19 @@ const char* gFS_Main_FetchGradient[6] = {
        // Linear
        "    vec4 gradientColor = texture2D(gradientSampler, linear);\n",

        "    vec4 gradientColor = gradientMix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",
        "    vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",

        // Circular
        "    vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n",

        "    vec4 gradientColor = gradientMix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",
        "    vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",

        // Sweep
        "    highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
        "    vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n",

        "    highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
        "    vec4 gradientColor = gradientMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
        "    vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
};
const char* gFS_Main_FetchBitmap =
        "    vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, outBitmapTexCoords));\n";
+2 −2
Original line number Diff line number Diff line
@@ -173,8 +173,8 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode
        outData->gradientSampler = 0;
        outData->gradientTexture = nullptr;

        outData->startColor.setUnPreMultiplied(gradInfo.fColors[0]);
        outData->endColor.setUnPreMultiplied(gradInfo.fColors[1]);
        outData->startColor.set(gradInfo.fColors[0]);
        outData->endColor.set(gradInfo.fColors[1]);
    }

    return true;