Merge "Make DWB CCT changes follow a step function" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
0cdca7eb57
@@ -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>
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user