Loading services/surfaceflinger/Effects/Daltonizer.cpp +25 −14 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading services/surfaceflinger/Effects/Daltonizer.h +10 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,9 @@ namespace android { // Forward declare test class class DaltonizerTest; enum class ColorBlindnessType { None, // Disables the Daltonizer Protanomaly, // L (red) cone deficient Loading @@ -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(); Loading @@ -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 */ Loading services/surfaceflinger/SurfaceFlinger.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -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); Loading services/surfaceflinger/tests/unittests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ cc_test { "BackgroundExecutorTest.cpp", "CommitTest.cpp", "CompositionTest.cpp", "DaltonizerTest.cpp", "DisplayIdGeneratorTest.cpp", "DisplayTransactionTest.cpp", "DisplayDevice_GetBestColorModeTest.cpp", Loading services/surfaceflinger/tests/unittests/DaltonizerTest.cpp 0 → 100644 +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 */ Loading
services/surfaceflinger/Effects/Daltonizer.cpp +25 −14 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading
services/surfaceflinger/Effects/Daltonizer.h +10 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,9 @@ namespace android { // Forward declare test class class DaltonizerTest; enum class ColorBlindnessType { None, // Disables the Daltonizer Protanomaly, // L (red) cone deficient Loading @@ -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(); Loading @@ -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 */ Loading
services/surfaceflinger/SurfaceFlinger.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -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); Loading
services/surfaceflinger/tests/unittests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ cc_test { "BackgroundExecutorTest.cpp", "CommitTest.cpp", "CompositionTest.cpp", "DaltonizerTest.cpp", "DisplayIdGeneratorTest.cpp", "DisplayTransactionTest.cpp", "DisplayDevice_GetBestColorModeTest.cpp", Loading
services/surfaceflinger/tests/unittests/DaltonizerTest.cpp 0 → 100644 +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 */