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

Commit da2845cb authored by KaiChieh Chuang's avatar KaiChieh Chuang Committed by Alec Mouri
Browse files

Fix the order of applying layer alpha and color matrices in GLESRenderEngine

If the system want to do a color inversion,
there is an unexpected result due to applying the layer alpha first.
Solution: RenderEngine should apply
the layer's alpha after multiplying by the color matrix.

For example, given the following color inversion matrix:
| r.r  r.g  r.b |   | -1.0   0.0   0.0 |
| g.r  g.g  g.b | = |  0.0  -1.0   0.0 |
| b.r  b.g  b.b |   |  0.0   0.0  -1.0 |
| Tr   Tg   Tb  |   |  1.0   1.0   1.0 |

We want to get a black layer when applying the color transform to a white layer;
However, the white layer applies layer alpha before color matrix. We can get
the below calculation: When layer alpha is 0, R_out value was always Tr.
Then we do not get a black layer when the system applies color inversion to a
white layer.
R_out = (R_in * layer_alpha) * r.r + Tr

Therefore we apply the color matrix first, so we can get the below calculation.
When the layer alpha is 0, we can get a black layer.
R_out = (R_in * r.r + Tr) * layer_alpha
      = (R_in * r.r * layer_alpha) + (Tr * layer_alpha)

adds RenderEngineTest::fillBufferColorTransformZeroLayerAlpha

Bug: 157204341
Test: color inversion when switching pages in the Setting app
Test: RenderEngineTest
Change-Id: I206184aae84042290b00e411709e04e54579780a
parent 9a715075
Loading
Loading
Loading
Loading
+17 −9
Original line number Diff line number Diff line
@@ -740,15 +740,6 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) {
        if (needs.isOpaque()) {
            fs << "gl_FragColor.a = 1.0;";
        }
        if (needs.hasAlpha()) {
            // modulate the current alpha value with alpha set
            if (needs.isPremultiplied()) {
                // ... and the color too if we're premultiplied
                fs << "gl_FragColor *= color.a;";
            } else {
                fs << "gl_FragColor.a *= color.a;";
            }
        }
    }

    if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
@@ -768,6 +759,23 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) {
        }
    }

    /*
     * Whether applying layer alpha before or after color transform doesn't matter,
     * as long as we can undo premultiplication. But we cannot un-premultiply
     * for color transform if the layer alpha = 0, e.g. 0 / (0 + 0.0019) = 0.
     */
    if (!needs.drawShadows()) {
        if (needs.hasAlpha()) {
            // modulate the current alpha value with alpha set
            if (needs.isPremultiplied()) {
                // ... and the color too if we're premultiplied
                fs << "gl_FragColor *= color.a;";
            } else {
                fs << "gl_FragColor.a *= color.a;";
            }
        }
    }

    if (needs.hasRoundedCorners()) {
        if (needs.isPremultiplied()) {
            fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));";
+56 −0
Original line number Diff line number Diff line
@@ -405,6 +405,12 @@ public:
    template <typename SourceVariant>
    void fillBufferColorTransform();

    template <typename SourceVariant>
    void fillBufferWithColorTransformZeroLayerAlpha();

    template <typename SourceVariant>
    void fillBufferColorTransformZeroLayerAlpha();

    template <typename SourceVariant>
    void fillRedBufferWithRoundedCorners();

@@ -764,6 +770,36 @@ void RenderEngineTest::fillBufferColorTransform() {
    expectBufferColor(fullscreenRect(), 172, 0, 0, 255, 1);
}

template <typename SourceVariant>
void RenderEngineTest::fillBufferWithColorTransformZeroLayerAlpha() {
    renderengine::DisplaySettings settings;
    settings.physicalDisplay = fullscreenRect();
    settings.clip = Rect(1, 1);

    std::vector<const renderengine::LayerSettings*> layers;

    renderengine::LayerSettings layer;
    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
    SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
    layer.alpha = 0;

    // construct a fake color matrix
    // simple inverse color
    settings.colorTransform = mat4(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 1, 1, 1, 1);

    layer.geometry.boundaries = Rect(1, 1).toFloatRect();

    layers.push_back(&layer);

    invokeDraw(settings, layers, mBuffer);
}

template <typename SourceVariant>
void RenderEngineTest::fillBufferColorTransformZeroLayerAlpha() {
    fillBufferWithColorTransformZeroLayerAlpha<SourceVariant>();
    expectBufferColor(fullscreenRect(), 0, 0, 0, 0);
}

template <typename SourceVariant>
void RenderEngineTest::fillRedBufferWithRoundedCorners() {
    renderengine::DisplaySettings settings;
@@ -1240,6 +1276,13 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
    fillBufferWithRoundedCorners<ColorSourceVariant>();
}

TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) {
    const auto& renderEngineFactory = GetParam();
    mRE = renderEngineFactory->createRenderEngine();

    fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>();
}

TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
    const auto& renderEngineFactory = GetParam();
    mRE = renderEngineFactory->createRenderEngine();
@@ -1337,6 +1380,12 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource)

    fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) {
    const auto& renderEngineFactory = GetParam();
    mRE = renderEngineFactory->createRenderEngine();

    fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}

TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
    const auto& renderEngineFactory = GetParam();
@@ -1436,6 +1485,13 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
    fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}

TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) {
    const auto& renderEngineFactory = GetParam();
    mRE = renderEngineFactory->createRenderEngine();

    fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}

TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
    const auto& renderEngineFactory = GetParam();
    mRE = renderEngineFactory->createRenderEngine();