diff options
| -rw-r--r-- | services/surfaceflinger/Effects/Daltonizer.cpp | 39 | ||||
| -rw-r--r-- | services/surfaceflinger/Effects/Daltonizer.h | 10 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 1 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/Android.bp | 1 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/DaltonizerTest.cpp | 126 |
5 files changed, 163 insertions, 14 deletions
diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp index a7090c51f2..65f2605a1e 100644 --- a/services/surfaceflinger/Effects/Daltonizer.cpp +++ b/services/surfaceflinger/Effects/Daltonizer.cpp @@ -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. diff --git a/services/surfaceflinger/Effects/Daltonizer.h b/services/surfaceflinger/Effects/Daltonizer.h index 2fb60e96d2..f5eaae7ab4 100644 --- a/services/surfaceflinger/Effects/Daltonizer.h +++ b/services/surfaceflinger/Effects/Daltonizer.h @@ -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 */ diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index c237a7da3e..70b9e4361d 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -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); diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 0c13db3d94..5145e112d6 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -66,6 +66,7 @@ cc_test { "BackgroundExecutorTest.cpp", "CommitTest.cpp", "CompositionTest.cpp", + "DaltonizerTest.cpp", "DisplayIdGeneratorTest.cpp", "DisplayTransactionTest.cpp", "DisplayDevice_GetBestColorModeTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/DaltonizerTest.cpp b/services/surfaceflinger/tests/unittests/DaltonizerTest.cpp new file mode 100644 index 0000000000..9f632a1430 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/DaltonizerTest.cpp @@ -0,0 +1,126 @@ +/* + * 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 */ |