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
diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp
index a7090c5..65f2605 100644
--- a/services/surfaceflinger/Effects/Daltonizer.cpp
+++ b/services/surfaceflinger/Effects/Daltonizer.cpp
@@ -37,6 +37,18 @@
}
}
+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 @@
// 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 2fb60e9..f5eaae7 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 @@
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 @@
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 c237a7d..70b9e43 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -7154,6 +7154,7 @@
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 0c13db3..5145e11 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -66,6 +66,7 @@
"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 0000000..9f632a1
--- /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 */