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

Commit c1ec07dc authored by Isaac Chai's avatar Isaac Chai Committed by Android (Google) Code Review
Browse files

Merge "Add color correction level API to native" into main

parents 01843d9d 1ec2d9a0
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
@@ -7137,6 +7137,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 */