Merge "Make DWB CCT changes follow a step function" into udc-dev

This commit is contained in:
Treehugger Robot
2023-05-03 00:50:59 +00:00
committed by Android (Google) Code Review
9 changed files with 529 additions and 60 deletions

View File

@@ -991,6 +991,25 @@
<!-- Nominal White Z --> <item>1.089058</item>
</string-array>
<!-- The CCT closest to the white coordinates (primary) above and in SurfaceControl. -->
<integer name="config_displayWhiteBalanceDisplayNominalWhiteCct">6500</integer>
<!-- Range minimums corresponding to config_displayWhiteBalanceDisplaySteps. For example, if the
range minimums are [0, 3000] and the steps are [10, 20] then between 0 and 3000, exclusive,
the step between them will be 10 (i.e. 0, 10, 20, etc.) and the step between 3000 and the
maximum value is 20 (i.e. 3000, 3020, 3040, etc.). -->
<integer-array name="config_displayWhiteBalanceDisplayRangeMinimums">
<item>0</item>
</integer-array>
<!-- Steps corresponding to config_displayWhiteBalanceDisplayRangeMinimums. For example, if the
range minimums are [0, 3000] and the steps are [10, 20] then between 0 and 3000, exclusive,
the step between them will be 10 (i.e. 0, 10, 20, etc.) and the step between 3000 and the
maximum value is 20 (i.e. 3000, 3020, 3040, etc.). -->
<integer-array name="config_displayWhiteBalanceDisplaySteps">
<item>1</item>
</integer-array>
<!-- Boolean indicating whether light mode is allowed when DWB is turned on. -->
<bool name="config_displayWhiteBalanceLightModeAllowed">true</bool>

View File

@@ -3423,6 +3423,9 @@
<java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureDefault" />
<java-symbol type="array" name="config_displayWhiteBalanceDisplayPrimaries" />
<java-symbol type="array" name="config_displayWhiteBalanceDisplayNominalWhite" />
<java-symbol type="integer" name="config_displayWhiteBalanceDisplayNominalWhiteCct" />
<java-symbol type="array" name="config_displayWhiteBalanceDisplayRangeMinimums" />
<java-symbol type="array" name="config_displayWhiteBalanceDisplaySteps" />
<java-symbol type="bool" name="config_displayWhiteBalanceLightModeAllowed" />
<java-symbol type="integer" name="config_displayWhiteBalanceTransitionTime" />

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2023 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.
*/
package com.android.server.display.color;
import android.animation.TypeEvaluator;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import java.util.Arrays;
/**
* Interpolates between CCT values by a given step.
*/
class CctEvaluator implements TypeEvaluator<Integer> {
private static final String TAG = "CctEvaluator";
/**
* The minimum input value, which will represent index 0 in the mValues array. Each
* subsequent input value is offset by this amount.
*/
private final int mIndexOffset;
/**
* Cached step values at each CCT value (offset by the {@link #mIndexOffset} above). For
* example, if the minimum CCT is 2000K (which is set to mIndexOffset), then the 0th index of
* this array is equivalent to the step value at 2000K, 1st index corresponds to 2001K, and so
* on.
*/
@VisibleForTesting
final int[] mStepsAtOffsetCcts;
/**
* Pre-computed stepped CCTs. These will be accessed frequently; the memory cost of caching them
* is well-spent.
*/
@VisibleForTesting
final int[] mSteppedCctsAtOffsetCcts;
CctEvaluator(int min, int max, int[] cctRangeMinimums, int[] steps) {
final int delta = max - min + 1;
mStepsAtOffsetCcts = new int[delta];
mSteppedCctsAtOffsetCcts = new int[delta];
mIndexOffset = min;
final int parallelArraysLength = cctRangeMinimums.length;
if (cctRangeMinimums.length != steps.length) {
Slog.e(TAG,
"Parallel arrays cctRangeMinimums and steps are different lengths; setting "
+ "step of 1");
setStepOfOne();
} else if (parallelArraysLength == 0) {
Slog.e(TAG, "No cctRangeMinimums or steps are set; setting step of 1");
setStepOfOne();
} else {
int parallelArraysIndex = 0;
int index = 0;
int lastSteppedCct = Integer.MIN_VALUE;
while (index < delta) {
final int cct = index + mIndexOffset;
int nextParallelArraysIndex = parallelArraysIndex + 1;
while (nextParallelArraysIndex < parallelArraysLength
&& cct >= cctRangeMinimums[nextParallelArraysIndex]) {
parallelArraysIndex = nextParallelArraysIndex;
nextParallelArraysIndex++;
}
mStepsAtOffsetCcts[index] = steps[parallelArraysIndex];
if (lastSteppedCct == Integer.MIN_VALUE
|| Math.abs(lastSteppedCct - cct) >= steps[parallelArraysIndex]) {
lastSteppedCct = cct;
}
mSteppedCctsAtOffsetCcts[index] = lastSteppedCct;
index++;
}
}
}
@Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
final int cct = (int) (startValue + fraction * (endValue - startValue));
final int index = cct - mIndexOffset;
if (index < 0 || index >= mSteppedCctsAtOffsetCcts.length) {
Slog.e(TAG, "steppedCctValueAt: returning same since invalid requested index=" + index);
return cct;
}
return mSteppedCctsAtOffsetCcts[index];
}
private void setStepOfOne() {
Arrays.fill(mStepsAtOffsetCcts, 1);
for (int i = 0; i < mSteppedCctsAtOffsetCcts.length; i++) {
mSteppedCctsAtOffsetCcts[i] = mIndexOffset + i;
}
}
}

View File

@@ -180,6 +180,8 @@ public final class ColorDisplayService extends SystemService {
*/
private SparseIntArray mColorModeCompositionColorSpaces = null;
private final Object mCctTintApplierLock = new Object();
public ColorDisplayService(Context context) {
super(context);
mHandler = new TintHandler(DisplayThread.get().getLooper());
@@ -698,6 +700,79 @@ public final class ColorDisplayService extends SystemService {
}
}
private void applyTintByCct(ColorTemperatureTintController tintController, boolean immediate) {
synchronized (mCctTintApplierLock) {
tintController.cancelAnimator();
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
final int from = tintController.getAppliedCct();
final int to = tintController.isActivated() ? tintController.getTargetCct()
: tintController.getDisabledCct();
if (immediate) {
Slog.d(TAG, tintController.getClass().getSimpleName()
+ " applied immediately: toCct=" + to + " fromCct=" + from);
dtm.setColorMatrix(tintController.getLevel(),
tintController.computeMatrixForCct(to));
tintController.setAppliedCct(to);
} else {
Slog.d(TAG, tintController.getClass().getSimpleName() + " animation started: toCct="
+ to + " fromCct=" + from);
ValueAnimator valueAnimator = ValueAnimator.ofInt(from, to);
tintController.setAnimator(valueAnimator);
final CctEvaluator evaluator = tintController.getEvaluator();
if (evaluator != null) {
valueAnimator.setEvaluator(evaluator);
}
valueAnimator.setDuration(tintController.getTransitionDurationMilliseconds());
valueAnimator.setInterpolator(AnimationUtils.loadInterpolator(
getContext(), android.R.interpolator.linear));
valueAnimator.addUpdateListener((ValueAnimator animator) -> {
synchronized (mCctTintApplierLock) {
final int value = (int) animator.getAnimatedValue();
if (value != tintController.getAppliedCct()) {
dtm.setColorMatrix(tintController.getLevel(),
tintController.computeMatrixForCct(value));
tintController.setAppliedCct(value);
}
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
private boolean mIsCancelled;
@Override
public void onAnimationCancel(Animator animator) {
Slog.d(TAG, tintController.getClass().getSimpleName()
+ " animation cancelled");
mIsCancelled = true;
}
@Override
public void onAnimationEnd(Animator animator) {
synchronized (mCctTintApplierLock) {
Slog.d(TAG, tintController.getClass().getSimpleName()
+ " animation ended: wasCancelled=" + mIsCancelled
+ " toCct=" + to
+ " fromCct=" + from);
if (!mIsCancelled) {
// Ensure final color matrix is set at the end of the animation.
// If the animation is cancelled then don't set the final color
// matrix so the new animator can pick up from where this one left
// off.
dtm.setColorMatrix(tintController.getLevel(),
tintController.computeMatrixForCct(to));
tintController.setAppliedCct(to);
}
tintController.setAnimator(null);
}
}
});
valueAnimator.start();
}
}
}
/**
* Returns the first date time corresponding to the local time that occurs before the provided
* date time.
@@ -747,7 +822,7 @@ public final class ColorDisplayService extends SystemService {
// If disabled, clear the tint. If enabled, do nothing more here and let the next
// temperature update set the correct tint.
if (!activated) {
if (oldActivated && !activated) {
mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
}
}
@@ -1452,7 +1527,7 @@ public final class ColorDisplayService extends SystemService {
public class ColorDisplayServiceInternal {
/** Sets whether DWB should be allowed in the current state. */
public void setDisplayWhiteBalanceAllowed(boolean allowed) {
public void setDisplayWhiteBalanceAllowed(boolean allowed) {
mDisplayWhiteBalanceTintController.setAllowed(allowed);
updateDisplayWhiteBalanceStatus();
}
@@ -1464,8 +1539,8 @@ public final class ColorDisplayService extends SystemService {
* @param cct the color temperature in Kelvin.
*/
public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
// Update the transform matrix even if it can't be applied.
mDisplayWhiteBalanceTintController.setMatrix(cct);
// Update the transform target CCT even if it can't be applied.
mDisplayWhiteBalanceTintController.setTargetCct(cct);
if (mDisplayWhiteBalanceTintController.isActivated()) {
mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
@@ -1601,7 +1676,7 @@ public final class ColorDisplayService extends SystemService {
applyTint(mNightDisplayTintController, false);
break;
case MSG_APPLY_DISPLAY_WHITE_BALANCE:
applyTint(mDisplayWhiteBalanceTintController, false);
applyTintByCct(mDisplayWhiteBalanceTintController, false);
break;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 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.
*/
package com.android.server.display.color;
abstract class ColorTemperatureTintController extends TintController {
abstract int getAppliedCct();
abstract void setAppliedCct(int cct);
abstract int getTargetCct();
abstract void setTargetCct(int cct);
/**
* Returns the CCT value most closely associated with the "disabled" (identity) matrix for
* this device, to use as the target when deactivating this transform.
*/
abstract int getDisabledCct();
abstract float[] computeMatrixForCct(int cct);
abstract CctEvaluator getEvaluator();
}

View File

@@ -21,6 +21,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
import android.content.Context;
import android.content.res.Resources;
@@ -36,7 +37,7 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
final class DisplayWhiteBalanceTintController extends TintController {
final class DisplayWhiteBalanceTintController extends ColorTemperatureTintController {
// Three chromaticity coordinates per color: X, Y, and Z
private static final int NUM_VALUES_PER_PRIMARY = 3;
@@ -52,9 +53,11 @@ final class DisplayWhiteBalanceTintController extends TintController {
private int mTemperatureDefault;
@VisibleForTesting
float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
private int mDisplayNominalWhiteCct;
@VisibleForTesting
ColorSpace.Rgb mDisplayColorSpaceRGB;
private float[] mChromaticAdaptationMatrix;
// The temperature currently represented in the matrix.
@VisibleForTesting
int mCurrentColorTemperature;
private float[] mCurrentColorTemperatureXYZ;
@@ -65,6 +68,9 @@ final class DisplayWhiteBalanceTintController extends TintController {
private Boolean mIsAvailable;
// This feature becomes disallowed if the device is in an unsupported strong/light state.
private boolean mIsAllowed = true;
private int mTargetCct;
private int mAppliedCct;
private CctEvaluator mCctEvaluator;
private final DisplayManagerInternal mDisplayManagerInternal;
@@ -108,6 +114,9 @@ final class DisplayWhiteBalanceTintController extends TintController {
displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
}
final int displayNominalWhiteCct = res.getInteger(
R.integer.config_displayWhiteBalanceDisplayNominalWhiteCct);
final int colorTemperatureMin = res.getInteger(
R.integer.config_displayWhiteBalanceColorTemperatureMin);
if (colorTemperatureMin <= 0) {
@@ -124,19 +133,28 @@ final class DisplayWhiteBalanceTintController extends TintController {
return;
}
final int colorTemperature = res.getInteger(
final int defaultTemperature = res.getInteger(
R.integer.config_displayWhiteBalanceColorTemperatureDefault);
mTransitionDuration = res.getInteger(
R.integer.config_displayWhiteBalanceTransitionTime);
int[] cctRangeMinimums = res.getIntArray(
R.array.config_displayWhiteBalanceDisplayRangeMinimums);
int[] steps = res.getIntArray(R.array.config_displayWhiteBalanceDisplaySteps);
synchronized (mLock) {
mDisplayColorSpaceRGB = displayColorSpaceRGB;
mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
mDisplayNominalWhiteCct = displayNominalWhiteCct;
mTargetCct = mDisplayNominalWhiteCct;
mAppliedCct = mDisplayNominalWhiteCct;
mTemperatureMin = colorTemperatureMin;
mTemperatureMax = colorTemperatureMax;
mTemperatureDefault = colorTemperature;
mTemperatureDefault = defaultTemperature;
mSetUp = true;
mCctEvaluator = new CctEvaluator(mTemperatureMin, mTemperatureMax,
cctRangeMinimums, steps);
}
setMatrix(mTemperatureDefault);
@@ -144,8 +162,16 @@ final class DisplayWhiteBalanceTintController extends TintController {
@Override
public float[] getMatrix() {
return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance
: ColorDisplayService.MATRIX_IDENTITY;
if (!mSetUp || !isActivated()) {
return ColorDisplayService.MATRIX_IDENTITY;
}
computeMatrixForCct(mAppliedCct);
return mMatrixDisplayWhiteBalance;
}
@Override
public int getTargetCct() {
return mTargetCct;
}
/**
@@ -174,6 +200,12 @@ final class DisplayWhiteBalanceTintController extends TintController {
@Override
public void setMatrix(int cct) {
setTargetCct(cct);
computeMatrixForCct(mTargetCct);
}
@Override
public void setTargetCct(int cct) {
if (!mSetUp) {
Slog.w(ColorDisplayService.TAG,
"Can't set display white balance temperature: uninitialized");
@@ -183,50 +215,93 @@ final class DisplayWhiteBalanceTintController extends TintController {
if (cct < mTemperatureMin) {
Slog.w(ColorDisplayService.TAG,
"Requested display color temperature is below allowed minimum");
cct = mTemperatureMin;
mTargetCct = mTemperatureMin;
} else if (cct > mTemperatureMax) {
Slog.w(ColorDisplayService.TAG,
"Requested display color temperature is above allowed maximum");
cct = mTemperatureMax;
mTargetCct = mTemperatureMax;
} else {
mTargetCct = cct;
}
}
@Override
public int getDisabledCct() {
return mDisplayNominalWhiteCct;
}
@Override
public float[] computeMatrixForCct(int cct) {
if (!mSetUp || cct == 0) {
Slog.w(ColorDisplayService.TAG, "Couldn't compute matrix for cct=" + cct);
return ColorDisplayService.MATRIX_IDENTITY;
}
synchronized (mLock) {
mCurrentColorTemperature = cct;
// Adapt the display's nominal white point to match the requested CCT value
mCurrentColorTemperatureXYZ = ColorSpace.cctToXyz(cct);
mChromaticAdaptationMatrix =
ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
// Convert the adaptation matrix to RGB space
float[] result = mul3x3(mChromaticAdaptationMatrix,
mDisplayColorSpaceRGB.getTransform());
result = mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
// Normalize the transform matrix to peak white value in RGB space
final float adaptedMaxR = result[0] + result[3] + result[6];
final float adaptedMaxG = result[1] + result[4] + result[7];
final float adaptedMaxB = result[2] + result[5] + result[8];
final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
for (int i = 0; i < result.length; i++) {
result[i] /= denum;
if (!isColorMatrixCoeffValid(result[i])) {
Slog.e(ColorDisplayService.TAG, "Invalid DWB color matrix");
return;
}
if (cct == mDisplayNominalWhiteCct && !isActivated()) {
// DWB is finished turning off. Clear the matrix.
Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
} else {
computeMatrixForCctLocked(cct);
}
java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
Slog.d(ColorDisplayService.TAG, "computeDisplayWhiteBalanceMatrix: cct =" + cct
+ " matrix =" + matrixToString(mMatrixDisplayWhiteBalance, 16));
return mMatrixDisplayWhiteBalance;
}
}
private void computeMatrixForCctLocked(int cct) {
// Adapt the display's nominal white point to match the requested CCT value
mCurrentColorTemperatureXYZ = ColorSpace.cctToXyz(cct);
mChromaticAdaptationMatrix =
ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
// Convert the adaptation matrix to RGB space
float[] result = mul3x3(mChromaticAdaptationMatrix,
mDisplayColorSpaceRGB.getTransform());
result = mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
// Normalize the transform matrix to peak white value in RGB space
final float adaptedMaxR = result[0] + result[3] + result[6];
final float adaptedMaxG = result[1] + result[4] + result[7];
final float adaptedMaxB = result[2] + result[5] + result[8];
final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
for (int i = 0; i < result.length; i++) {
result[i] /= denum;
if (!isColorMatrixCoeffValid(result[i])) {
Slog.e(ColorDisplayService.TAG, "Invalid DWB color matrix");
return;
}
}
Slog.d(ColorDisplayService.TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct
+ " matrix = " + matrixToString(mMatrixDisplayWhiteBalance, 16));
java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
}
@Override
int getAppliedCct() {
return mAppliedCct;
}
@Override
void setAppliedCct(int cct) {
mAppliedCct = cct;
}
@Override
@Nullable
CctEvaluator getEvaluator() {
return mCctEvaluator;
}
@Override
@@ -258,7 +333,10 @@ final class DisplayWhiteBalanceTintController extends TintController {
pw.println(" mTemperatureMin = " + mTemperatureMin);
pw.println(" mTemperatureMax = " + mTemperatureMax);
pw.println(" mTemperatureDefault = " + mTemperatureDefault);
pw.println(" mDisplayNominalWhiteCct = " + mDisplayNominalWhiteCct);
pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature);
pw.println(" mTargetCct = " + mTargetCct);
pw.println(" mAppliedCct = " + mAppliedCct);
pw.println(" mCurrentColorTemperatureXYZ = "
+ matrixToString(mCurrentColorTemperatureXYZ, 3));
pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = "
@@ -340,11 +418,7 @@ final class DisplayWhiteBalanceTintController extends TintController {
}
private boolean isColorMatrixCoeffValid(float coeff) {
if (Float.isNaN(coeff) || Float.isInfinite(coeff)) {
return false;
}
return true;
return !Float.isNaN(coeff) && !Float.isInfinite(coeff);
}
private boolean isColorMatrixValid(float[] matrix) {
@@ -352,8 +426,8 @@ final class DisplayWhiteBalanceTintController extends TintController {
return false;
}
for (int i = 0; i < matrix.length; i++) {
if (!isColorMatrixCoeffValid(matrix[i])) {
for (float value : matrix) {
if (!isColorMatrixCoeffValid(value)) {
return false;
}
}

View File

@@ -16,6 +16,7 @@
package com.android.server.display.color;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.Slog;
@@ -28,14 +29,14 @@ abstract class TintController {
*/
private static final long TRANSITION_DURATION = 3000L;
private ColorDisplayService.TintValueAnimator mAnimator;
private ValueAnimator mAnimator;
private Boolean mIsActivated;
public ColorDisplayService.TintValueAnimator getAnimator() {
public ValueAnimator getAnimator() {
return mAnimator;
}
public void setAnimator(ColorDisplayService.TintValueAnimator animator) {
public void setAnimator(ValueAnimator animator) {
mAnimator = animator;
}

View File

@@ -72,18 +72,28 @@ public class DisplayWhiteBalanceTintControllerTest {
mResources = InstrumentationRegistry.getContext().getResources();
// These Resources are common to all tests.
doReturn(mResources.getInteger(R.integer.config_displayWhiteBalanceColorTemperatureMin))
doReturn(4000)
.when(mMockedResources)
.getInteger(R.integer.config_displayWhiteBalanceColorTemperatureMin);
doReturn(mResources.getInteger(R.integer.config_displayWhiteBalanceColorTemperatureMax))
doReturn(8000)
.when(mMockedResources)
.getInteger(R.integer.config_displayWhiteBalanceColorTemperatureMax);
doReturn(mResources.getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault))
doReturn(6500)
.when(mMockedResources)
.getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault);
doReturn(mResources.getStringArray(R.array.config_displayWhiteBalanceDisplayNominalWhite))
.when(mMockedResources)
.getStringArray(R.array.config_displayWhiteBalanceDisplayNominalWhite);
doReturn(new String[] {"0.950456", "1.000000", "1.089058"})
.when(mMockedResources)
.getStringArray(R.array.config_displayWhiteBalanceDisplayNominalWhite);
doReturn(6500)
.when(mMockedResources)
.getInteger(R.integer.config_displayWhiteBalanceDisplayNominalWhiteCct);
doReturn(new int[] {0})
.when(mMockedResources)
.getIntArray(R.array.config_displayWhiteBalanceDisplaySteps);
doReturn(new int[] {20})
.when(mMockedResources)
.getIntArray(R.array.config_displayWhiteBalanceDisplayRangeMinimums);
doReturn(mMockedResources).when(mMockedContext).getResources();
mDisplayToken = new Binder();
@@ -195,7 +205,7 @@ public class DisplayWhiteBalanceTintControllerTest {
* Matrix should match the precalculated one for given cct and display primaries.
*/
@Test
public void displayWhiteBalance_validateTransformMatrix() {
public void displayWhiteBalance_getAndSetMatrix_validateTransformMatrix() {
DisplayPrimaries displayPrimaries = new DisplayPrimaries();
displayPrimaries.red = new CieXyz();
displayPrimaries.red.X = 0.412315f;
@@ -223,10 +233,12 @@ public class DisplayWhiteBalanceTintControllerTest {
final int cct = 6500;
mDisplayWhiteBalanceTintController.setMatrix(cct);
mDisplayWhiteBalanceTintController.setAppliedCct(
mDisplayWhiteBalanceTintController.getTargetCct());
assertWithMessage("Failed to set temperature")
.that(mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
.isEqualTo(cct);
float[] matrixDwb = mDisplayWhiteBalanceTintController.getMatrix();
final float[] expectedMatrixDwb = {
0.971848f, -0.001421f, 0.000491f, 0.0f,
@@ -238,6 +250,54 @@ public class DisplayWhiteBalanceTintControllerTest {
1e-6f /* tolerance */);
}
/**
* Matrix should match the precalculated one for given cct and display primaries.
*/
@Test
public void displayWhiteBalance_targetApplied_validateTransformMatrix() {
DisplayPrimaries displayPrimaries = new DisplayPrimaries();
displayPrimaries.red = new CieXyz();
displayPrimaries.red.X = 0.412315f;
displayPrimaries.red.Y = 0.212600f;
displayPrimaries.red.Z = 0.019327f;
displayPrimaries.green = new CieXyz();
displayPrimaries.green.X = 0.357600f;
displayPrimaries.green.Y = 0.715200f;
displayPrimaries.green.Z = 0.119200f;
displayPrimaries.blue = new CieXyz();
displayPrimaries.blue.X = 0.180500f;
displayPrimaries.blue.Y = 0.072200f;
displayPrimaries.blue.Z = 0.950633f;
displayPrimaries.white = new CieXyz();
displayPrimaries.white.X = 0.950456f;
displayPrimaries.white.Y = 1.000000f;
displayPrimaries.white.Z = 1.089058f;
when(mDisplayManagerInternal.getDisplayNativePrimaries(DEFAULT_DISPLAY))
.thenReturn(displayPrimaries);
setUpTintController();
assertWithMessage("Setup with valid SurfaceControl failed")
.that(mDisplayWhiteBalanceTintController.mSetUp)
.isTrue();
final int cct = 6500;
mDisplayWhiteBalanceTintController.setTargetCct(cct);
final float[] matrixDwb = mDisplayWhiteBalanceTintController.computeMatrixForCct(cct);
mDisplayWhiteBalanceTintController.setAppliedCct(cct);
assertWithMessage("Failed to set temperature")
.that(mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
.isEqualTo(cct);
final float[] expectedMatrixDwb = {
0.971848f, -0.001421f, 0.000491f, 0.0f,
0.028193f, 0.945798f, 0.003207f, 0.0f,
-0.000042f, -0.000989f, 0.988659f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
assertArrayEquals("Unexpected DWB matrix", expectedMatrixDwb, matrixDwb,
1e-6f /* tolerance */);
}
private void setUpTintController() {
mDisplayWhiteBalanceTintController = new DisplayWhiteBalanceTintController(
mDisplayManagerInternal);

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2023 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.
*/
package com.android.server.display.color;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class CctEvaluatorTest {
@Test
public void noEntriesInParallelArrays_setsEverythingToOne() {
final CctEvaluator evaluator = new CctEvaluator(0, 5, new int[]{}, new int[]{});
assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo(new int[]{1, 1, 1, 1, 1, 1});
assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo(
new int[]{0, 1, 2, 3, 4, 5});
}
@Test
public void unevenNumberOfEntriesInParallelArrays_setsEverythingToOne() {
final CctEvaluator evaluator = new CctEvaluator(0, 5, new int[]{0}, new int[]{});
assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo(new int[]{1, 1, 1, 1, 1, 1});
assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo(
new int[]{0, 1, 2, 3, 4, 5});
}
@Test
public void singleEntryInParallelArray_computesCorrectly() {
final CctEvaluator evaluator = new CctEvaluator(0, 5, new int[]{0}, new int[]{2});
assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo(new int[]{2, 2, 2, 2, 2, 2});
assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo(
new int[]{0, 0, 2, 2, 4, 4});
}
@Test
public void minimumIsBelowFirstRange_computesCorrectly() {
final CctEvaluator evaluator = new CctEvaluator(3000, 3005, new int[]{3002},
new int[]{20});
assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo(new int[]{20, 20, 20, 20, 20, 20});
assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo(
new int[]{3000, 3000, 3000, 3000, 3000, 3000});
}
@Test
public void minimumIsAboveFirstRange_computesCorrectly() {
final CctEvaluator evaluator = new CctEvaluator(3000, 3008, new int[]{3002},
new int[]{20});
assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo(
new int[]{20, 20, 20, 20, 20, 20, 20, 20, 20});
assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo(
new int[]{3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000});
}
@Test
public void multipleStepsStartsAtThreshold_computesCorrectly() {
final CctEvaluator evaluator = new CctEvaluator(5, 20, new int[]{0, 4, 5, 10, 18},
new int[]{11, 7, 2, 15, 9});
assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo(
new int[]{2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 9, 9, 9});
assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo(
new int[]{5, 5, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 18, 18});
}
@Test
public void multipleStepsStartsInBetween_computesCorrectly() {
final CctEvaluator evaluator = new CctEvaluator(4, 20, new int[]{0, 5, 10, 18},
new int[]{14, 2, 15, 9});
assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo(
new int[]{14, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 9, 9, 9});
assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo(
new int[]{4, 4, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 18, 18});
}
}