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

Commit 1ec2d9a0 authored by Isaac Chai's avatar Isaac Chai
Browse files

Add color correction level API to native

Test: Locally tested with other cls of the same topic, and also added unit tests and ran 'atest SurfaceFlinger_test'
Bug: 322829049

Change-Id: I494ce39608ddf84fb74b60291511477672ca4e36
parent f55e4459
Loading
Loading
Loading
Loading
+25 −14
Original line number Diff line number Diff line
@@ -37,6 +37,18 @@ void Daltonizer::setMode(ColorBlindnessMode mode) {
    }
}

void Daltonizer::setLevel(int32_t level) {
    if (level < 0 || level > 10) {
        return;
    }

    float newLevel = level / 10.0f;
    if (std::fabs(mLevel - newLevel) > 0.09f) {
        mDirty = true;
    }
    mLevel = newLevel;
}

const mat4& Daltonizer::operator()() {
    if (mDirty) {
        mDirty = false;
@@ -117,25 +129,24 @@ void Daltonizer::update() {
    // a color blind user and "spread" this error onto the healthy cones.
    // The matrices below perform this last step and have been chosen arbitrarily.

    // The amount of correction can be adjusted here.

    // Scale 0 represents no change (mColorTransform is identical matrix).
    // error spread for protanopia
    const mat4 errp(    1.0, 0.7, 0.7, 0,
                        0.0, 1.0, 0.0, 0,
                        0.0, 0.0, 1.0, 0,
                          0,   0,   0, 1);
    const mat4 errp(1.0, mLevel, mLevel, 0.0,
                    0.0,    1.0,    0.0, 0.0,
                    0.0,    0.0,    1.0, 0.0,
                    0.0,    0.0,    0.0, 1.0);

    // error spread for deuteranopia
    const mat4 errd(    1.0, 0.0, 0.0, 0,
                        0.7, 1.0, 0.7, 0,
                        0.0, 0.0, 1.0, 0,
                          0,   0,   0, 1);
    const mat4 errd(   1.0, 0.0,    0.0, 0.0,
                    mLevel, 1.0, mLevel, 0.0,
                       0.0, 0.0,    1.0, 0.0,
                       0.0, 0.0,    0.0, 1.0);

    // error spread for tritanopia
    const mat4 errt(    1.0, 0.0, 0.0, 0,
                        0.0, 1.0, 0.0, 0,
                        0.7, 0.7, 1.0, 0,
                          0,   0,   0, 1);
    const mat4 errt(   1.0,    0.0, 0.0, 0.0,
                       0.0,    1.0, 0.0, 0.0,
                    mLevel, mLevel, 1.0, 0.0,
                       0.0,    0.0, 0.0, 1.0);

    // And the magic happens here...
    // We construct the matrix that will perform the whole correction.
+10 −0
Original line number Diff line number Diff line
@@ -21,6 +21,9 @@

namespace android {

// Forward declare test class
class DaltonizerTest;

enum class ColorBlindnessType {
    None,               // Disables the Daltonizer
    Protanomaly,        // L (red) cone deficient
@@ -37,10 +40,15 @@ class Daltonizer {
public:
    void setType(ColorBlindnessType type);
    void setMode(ColorBlindnessMode mode);
    // sets level for correction saturation, [0-10].
    void setLevel(int32_t level);

    // returns the color transform to apply in the shader
    const mat4& operator()();

    // For testing.
    friend class DaltonizerTest;

private:
    void update();

@@ -48,6 +56,8 @@ private:
    ColorBlindnessMode mMode = ColorBlindnessMode::Simulation;
    bool mDirty = true;
    mat4 mColorTransform;
    // level of error spreading, [0.0-1.0].
    float mLevel = 0.7f;
};

} /* namespace android */
+1 −0
Original line number Diff line number Diff line
@@ -7154,6 +7154,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r
                Mutex::Autolock _l(mStateLock);
                // daltonize
                n = data.readInt32();
                mDaltonizer.setLevel(data.readInt32());
                switch (n % 10) {
                    case 1:
                        mDaltonizer.setType(ColorBlindnessType::Protanomaly);
+1 −0
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ cc_test {
        "BackgroundExecutorTest.cpp",
        "CommitTest.cpp",
        "CompositionTest.cpp",
        "DaltonizerTest.cpp",
        "DisplayIdGeneratorTest.cpp",
        "DisplayTransactionTest.cpp",
        "DisplayDevice_GetBestColorModeTest.cpp",
+126 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <gtest/gtest.h>
#include <math/mat4.h>
#include <cmath>
#include "Effects/Daltonizer.h"

namespace android {

class DaltonizerTest {
private:
    Daltonizer& mDaltonizer;

public:
    DaltonizerTest(Daltonizer& daltonizer) : mDaltonizer(daltonizer) {}

    bool isDirty() const { return mDaltonizer.mDirty; }

    float getLevel() const { return mDaltonizer.mLevel; }

    ColorBlindnessType getType() const { return mDaltonizer.mType; }
};

constexpr float TOLERANCE = 0.01f;

static bool isIdentityMatrix(mat4& matrix) {
    for (size_t i = 0; i < 4; ++i) {
        for (size_t j = 0; j < 4; ++j) {
            if (i == j) {
                // Check diagonal elements
                if (std::fabs(matrix[i][j] - 1.0f) > TOLERANCE) {
                    return false;
                }
            } else {
                // Check off-diagonal elements
                if (std::fabs(matrix[i][j]) > TOLERANCE) {
                    return false;
                }
            }
        }
    }
    return true;
}

// Test Suite Name : DaltonizerTest, Test name: ConstructionDefaultValues
TEST(DaltonizerTest, ConstructionDefaultValues) {
    Daltonizer daltonizer;
    DaltonizerTest test(daltonizer);

    EXPECT_EQ(test.getLevel(), 0.7f);
    ASSERT_TRUE(test.isDirty());
    EXPECT_EQ(test.getType(), ColorBlindnessType::None);
    mat4 matrix = daltonizer();
    ASSERT_TRUE(isIdentityMatrix(matrix));
}

TEST(DaltonizerTest, NotDirtyAfterColorMatrixReturned) {
    Daltonizer daltonizer;

    mat4 matrix = daltonizer();
    DaltonizerTest test(daltonizer);

    ASSERT_FALSE(test.isDirty());
    ASSERT_TRUE(isIdentityMatrix(matrix));
}

TEST(DaltonizerTest, LevelOutOfRangeTooLowIgnored) {
    Daltonizer daltonizer;
    // Get matrix to reset isDirty == false.
    mat4 matrix = daltonizer();

    daltonizer.setLevel(-1);
    DaltonizerTest test(daltonizer);

    EXPECT_EQ(test.getLevel(), 0.7f);
    ASSERT_FALSE(test.isDirty());
}

TEST(DaltonizerTest, LevelOutOfRangeTooHighIgnored) {
    Daltonizer daltonizer;
    // Get matrix to reset isDirty == false.
    mat4 matrix = daltonizer();

    daltonizer.setLevel(11);
    DaltonizerTest test(daltonizer);

    EXPECT_EQ(test.getLevel(), 0.7f);
    ASSERT_FALSE(test.isDirty());
}

TEST(DaltonizerTest, ColorCorrectionMatrixNonIdentical) {
    Daltonizer daltonizer;
    daltonizer.setType(ColorBlindnessType::Protanomaly);
    daltonizer.setMode(ColorBlindnessMode::Correction);

    mat4 matrix = daltonizer();

    ASSERT_FALSE(isIdentityMatrix(matrix));
}

TEST(DaltonizerTest, LevelZeroColorMatrixEqIdentityMatrix) {
    Daltonizer daltonizer;
    daltonizer.setType(ColorBlindnessType::Protanomaly);
    daltonizer.setMode(ColorBlindnessMode::Correction);
    daltonizer.setLevel(0);

    mat4 matrix = daltonizer();

    ASSERT_TRUE(isIdentityMatrix(matrix));
}

} /* namespace android */