blob: 3f1c222ab52004d7a6d1b732c13319a9b39cb330 [file] [log] [blame]
Christine Franks6caba0f2019-03-07 17:48:25 -08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.display.color;
18
19import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
20
21import android.content.Context;
22import android.content.res.Resources;
23import android.graphics.ColorSpace;
24import android.hardware.display.ColorDisplayManager;
25import android.opengl.Matrix;
26import android.os.IBinder;
27import android.util.Slog;
28import android.view.SurfaceControl;
29import android.view.SurfaceControl.DisplayPrimaries;
30
31import com.android.internal.R;
32import com.android.internal.annotations.VisibleForTesting;
33
34import java.io.PrintWriter;
Daniel Solomonbd3d1d22019-04-15 20:36:02 -070035import java.lang.System;
Christine Franks6caba0f2019-03-07 17:48:25 -080036
37final class DisplayWhiteBalanceTintController extends TintController {
38
39 // Three chromaticity coordinates per color: X, Y, and Z
40 private static final int NUM_VALUES_PER_PRIMARY = 3;
41 // Four colors: red, green, blue, and white
42 private static final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
Daniel Solomonbd3d1d22019-04-15 20:36:02 -070043 private static final int COLORSPACE_MATRIX_LENGTH = 9;
Christine Franks6caba0f2019-03-07 17:48:25 -080044
45 private final Object mLock = new Object();
46 @VisibleForTesting
47 int mTemperatureMin;
48 @VisibleForTesting
49 int mTemperatureMax;
50 private int mTemperatureDefault;
Daniel Solomonbd3d1d22019-04-15 20:36:02 -070051 @VisibleForTesting
52 float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
Christine Franks6caba0f2019-03-07 17:48:25 -080053 @VisibleForTesting
54 ColorSpace.Rgb mDisplayColorSpaceRGB;
55 private float[] mChromaticAdaptationMatrix;
56 @VisibleForTesting
57 int mCurrentColorTemperature;
58 private float[] mCurrentColorTemperatureXYZ;
Daniel Solomonbd3d1d22019-04-15 20:36:02 -070059 @VisibleForTesting
60 boolean mSetUp = false;
Christine Franks6caba0f2019-03-07 17:48:25 -080061 private float[] mMatrixDisplayWhiteBalance = new float[16];
62 private Boolean mIsAvailable;
63
64 @Override
65 public void setUp(Context context, boolean needsLinear) {
66 mSetUp = false;
67 final Resources res = context.getResources();
68
69 ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl();
70 if (displayColorSpaceRGB == null) {
71 Slog.w(ColorDisplayService.TAG,
72 "Failed to get display color space from SurfaceControl, trying res");
73 displayColorSpaceRGB = getDisplayColorSpaceFromResources(res);
74 if (displayColorSpaceRGB == null) {
75 Slog.e(ColorDisplayService.TAG, "Failed to get display color space from resources");
76 return;
77 }
78 }
79
Daniel Solomonbd3d1d22019-04-15 20:36:02 -070080 // Make sure display color space is valid
81 if (!isColorMatrixValid(displayColorSpaceRGB.getTransform())) {
82 Slog.e(ColorDisplayService.TAG, "Invalid display color space RGB-to-XYZ transform");
83 return;
84 }
85 if (!isColorMatrixValid(displayColorSpaceRGB.getInverseTransform())) {
86 Slog.e(ColorDisplayService.TAG, "Invalid display color space XYZ-to-RGB transform");
87 return;
88 }
89
Christine Franks6caba0f2019-03-07 17:48:25 -080090 final String[] nominalWhiteValues = res.getStringArray(
91 R.array.config_displayWhiteBalanceDisplayNominalWhite);
92 float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
93 for (int i = 0; i < nominalWhiteValues.length; i++) {
94 displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
95 }
96
97 final int colorTemperatureMin = res.getInteger(
98 R.integer.config_displayWhiteBalanceColorTemperatureMin);
99 if (colorTemperatureMin <= 0) {
100 Slog.e(ColorDisplayService.TAG,
101 "Display white balance minimum temperature must be greater than 0");
102 return;
103 }
104
105 final int colorTemperatureMax = res.getInteger(
106 R.integer.config_displayWhiteBalanceColorTemperatureMax);
107 if (colorTemperatureMax < colorTemperatureMin) {
108 Slog.e(ColorDisplayService.TAG,
109 "Display white balance max temp must be greater or equal to min");
110 return;
111 }
112
113 final int colorTemperature = res.getInteger(
114 R.integer.config_displayWhiteBalanceColorTemperatureDefault);
115
116 synchronized (mLock) {
117 mDisplayColorSpaceRGB = displayColorSpaceRGB;
118 mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
119 mTemperatureMin = colorTemperatureMin;
120 mTemperatureMax = colorTemperatureMax;
121 mTemperatureDefault = colorTemperature;
122 mSetUp = true;
123 }
124
125 setMatrix(mTemperatureDefault);
126 }
127
128 @Override
129 public float[] getMatrix() {
130 return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance
131 : ColorDisplayService.MATRIX_IDENTITY;
132 }
133
134 @Override
135 public void setMatrix(int cct) {
136 if (!mSetUp) {
137 Slog.w(ColorDisplayService.TAG,
138 "Can't set display white balance temperature: uninitialized");
139 return;
140 }
141
142 if (cct < mTemperatureMin) {
143 Slog.w(ColorDisplayService.TAG,
144 "Requested display color temperature is below allowed minimum");
145 cct = mTemperatureMin;
146 } else if (cct > mTemperatureMax) {
147 Slog.w(ColorDisplayService.TAG,
148 "Requested display color temperature is above allowed maximum");
149 cct = mTemperatureMax;
150 }
151
Christine Franks6caba0f2019-03-07 17:48:25 -0800152 synchronized (mLock) {
153 mCurrentColorTemperature = cct;
154
155 // Adapt the display's nominal white point to match the requested CCT value
156 mCurrentColorTemperatureXYZ = ColorSpace.cctToXyz(cct);
157
158 mChromaticAdaptationMatrix =
159 ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
160 mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
161
162 // Convert the adaptation matrix to RGB space
163 float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
164 mDisplayColorSpaceRGB.getTransform());
165 result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
166
167 // Normalize the transform matrix to peak white value in RGB space
168 final float adaptedMaxR = result[0] + result[3] + result[6];
169 final float adaptedMaxG = result[1] + result[4] + result[7];
170 final float adaptedMaxB = result[2] + result[5] + result[8];
171 final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
Christine Franks6caba0f2019-03-07 17:48:25 -0800172
173 Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
Daniel Solomonbd3d1d22019-04-15 20:36:02 -0700174 for (int i = 0; i < result.length; i++) {
175 result[i] /= denum;
176 if (!isColorMatrixCoeffValid(result[i])) {
177 Slog.e(ColorDisplayService.TAG, "Invalid DWB color matrix");
178 return;
179 }
180 }
181
Christine Franks6caba0f2019-03-07 17:48:25 -0800182 java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
183 java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
184 java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
185 }
Anthony Han00a490b2019-08-26 14:00:28 -0700186
187 Slog.d(ColorDisplayService.TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct
188 + " matrix = " + matrixToString(mMatrixDisplayWhiteBalance, 16));
Christine Franks6caba0f2019-03-07 17:48:25 -0800189 }
190
191 @Override
192 public int getLevel() {
193 return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
194 }
195
196 @Override
197 public boolean isAvailable(Context context) {
198 if (mIsAvailable == null) {
199 mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context);
200 }
201 return mIsAvailable;
202 }
203
204 @Override
205 public void dump(PrintWriter pw) {
206 synchronized (mLock) {
207 pw.println(" mSetUp = " + mSetUp);
208 if (!mSetUp) {
209 return;
210 }
211
212 pw.println(" mTemperatureMin = " + mTemperatureMin);
213 pw.println(" mTemperatureMax = " + mTemperatureMax);
214 pw.println(" mTemperatureDefault = " + mTemperatureDefault);
215 pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature);
216 pw.println(" mCurrentColorTemperatureXYZ = "
217 + matrixToString(mCurrentColorTemperatureXYZ, 3));
218 pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = "
219 + matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
220 pw.println(" mChromaticAdaptationMatrix = "
221 + matrixToString(mChromaticAdaptationMatrix, 3));
222 pw.println(" mDisplayColorSpaceRGB XYZ-to-RGB = "
223 + matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
224 pw.println(" mMatrixDisplayWhiteBalance = "
225 + matrixToString(mMatrixDisplayWhiteBalance, 4));
226 }
227 }
228
Christine Franks6caba0f2019-03-07 17:48:25 -0800229 private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
230 return new ColorSpace.Rgb(
231 "Display Color Space",
232 redGreenBlueXYZ,
233 whiteXYZ,
234 2.2f // gamma, unused for display white balance
235 );
236 }
237
238 private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
239 final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
240 if (displayToken == null) {
241 return null;
242 }
243
244 DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
245 if (primaries == null || primaries.red == null || primaries.green == null
246 || primaries.blue == null || primaries.white == null) {
247 return null;
248 }
249
250 return makeRgbColorSpaceFromXYZ(
251 new float[]{
252 primaries.red.X, primaries.red.Y, primaries.red.Z,
253 primaries.green.X, primaries.green.Y, primaries.green.Z,
254 primaries.blue.X, primaries.blue.Y, primaries.blue.Z,
255 },
256 new float[]{primaries.white.X, primaries.white.Y, primaries.white.Z}
257 );
258 }
259
260 private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) {
261 final String[] displayPrimariesValues = res.getStringArray(
262 R.array.config_displayWhiteBalanceDisplayPrimaries);
263 float[] displayRedGreenBlueXYZ =
264 new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
265 float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
266
267 for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
268 displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
269 }
270
271 for (int i = 0; i < displayWhiteXYZ.length; i++) {
272 displayWhiteXYZ[i] = Float.parseFloat(
273 displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
274 }
275
276 return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ);
277 }
Daniel Solomonbd3d1d22019-04-15 20:36:02 -0700278
279 private boolean isColorMatrixCoeffValid(float coeff) {
280 if (Float.isNaN(coeff) || Float.isInfinite(coeff)) {
281 return false;
282 }
283
284 return true;
285 }
286
287 private boolean isColorMatrixValid(float[] matrix) {
288 if (matrix == null || matrix.length != COLORSPACE_MATRIX_LENGTH) {
289 return false;
290 }
291
292 for (int i = 0; i < matrix.length; i++) {
293 if (!isColorMatrixCoeffValid(matrix[i])) {
294 return false;
295 }
296 }
297
298 return true;
299 }
300
Christine Franks6caba0f2019-03-07 17:48:25 -0800301}