Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 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 | |
| 17 | package com.android.server.display; |
| 18 | |
| 19 | import android.annotation.Nullable; |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 20 | import android.content.res.Resources; |
| 21 | import android.content.res.TypedArray; |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 22 | import android.hardware.display.BrightnessConfiguration; |
| 23 | import android.os.PowerManager; |
| 24 | import android.util.MathUtils; |
| 25 | import android.util.Pair; |
| 26 | import android.util.Slog; |
| 27 | import android.util.Spline; |
| 28 | |
| 29 | import com.android.internal.util.Preconditions; |
| 30 | import com.android.internal.annotations.VisibleForTesting; |
| 31 | |
| 32 | import java.io.PrintWriter; |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 33 | import java.util.Arrays; |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 34 | |
| 35 | /** |
| 36 | * A utility to map from an ambient brightness to a display's "backlight" brightness based on the |
| 37 | * available display information and brightness configuration. |
| 38 | * |
| 39 | * Note that without a mapping from the nits to a display backlight level, any |
| 40 | * {@link BrightnessConfiguration}s that are set are just ignored. |
| 41 | */ |
| 42 | public abstract class BrightnessMappingStrategy { |
| 43 | private static final String TAG = "BrightnessMappingStrategy"; |
| 44 | private static final boolean DEBUG = false; |
| 45 | |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 46 | private static final float LUX_GRAD_SMOOTHING = 0.25f; |
| 47 | private static final float MAX_GRAD = 1.0f; |
| 48 | |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 49 | @Nullable |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 50 | public static BrightnessMappingStrategy create(Resources resources) { |
| 51 | float[] luxLevels = getLuxLevels(resources.getIntArray( |
| 52 | com.android.internal.R.array.config_autoBrightnessLevels)); |
| 53 | int[] brightnessLevelsBacklight = resources.getIntArray( |
| 54 | com.android.internal.R.array.config_autoBrightnessLcdBacklightValues); |
| 55 | float[] brightnessLevelsNits = getFloatArray(resources.obtainTypedArray( |
| 56 | com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)); |
| 57 | |
| 58 | float[] nitsRange = getFloatArray(resources.obtainTypedArray( |
| 59 | com.android.internal.R.array.config_screenBrightnessNits)); |
| 60 | int[] backlightRange = resources.getIntArray( |
| 61 | com.android.internal.R.array.config_screenBrightnessBacklight); |
| 62 | |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 63 | if (isValidMapping(nitsRange, backlightRange) |
| 64 | && isValidMapping(luxLevels, brightnessLevelsNits)) { |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 65 | int minimumBacklight = resources.getInteger( |
| 66 | com.android.internal.R.integer.config_screenBrightnessSettingMinimum); |
| 67 | int maximumBacklight = resources.getInteger( |
| 68 | com.android.internal.R.integer.config_screenBrightnessSettingMaximum); |
| 69 | if (backlightRange[0] > minimumBacklight |
| 70 | || backlightRange[backlightRange.length - 1] < maximumBacklight) { |
| 71 | Slog.w(TAG, "Screen brightness mapping does not cover whole range of available" |
| 72 | + " backlight values, autobrightness functionality may be impaired."); |
| 73 | } |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 74 | BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); |
| 75 | builder.setCurve(luxLevels, brightnessLevelsNits); |
| 76 | return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange); |
| 77 | } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) { |
| 78 | return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight); |
| 79 | } else { |
| 80 | return null; |
| 81 | } |
| 82 | } |
| 83 | |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 84 | private static float[] getLuxLevels(int[] lux) { |
| 85 | // The first control point is implicit and always at 0 lux. |
| 86 | float[] levels = new float[lux.length + 1]; |
| 87 | for (int i = 0; i < lux.length; i++) { |
| 88 | levels[i + 1] = (float) lux[i]; |
| 89 | } |
| 90 | return levels; |
| 91 | } |
| 92 | |
| 93 | private static float[] getFloatArray(TypedArray array) { |
| 94 | final int N = array.length(); |
| 95 | float[] vals = new float[N]; |
| 96 | for (int i = 0; i < N; i++) { |
| 97 | vals[i] = array.getFloat(i, -1.0f); |
| 98 | } |
| 99 | array.recycle(); |
| 100 | return vals; |
| 101 | } |
| 102 | |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 103 | private static boolean isValidMapping(float[] x, float[] y) { |
| 104 | if (x == null || y == null || x.length == 0 || y.length == 0) { |
| 105 | return false; |
| 106 | } |
| 107 | if (x.length != y.length) { |
| 108 | return false; |
| 109 | } |
| 110 | final int N = x.length; |
| 111 | float prevX = x[0]; |
| 112 | float prevY = y[0]; |
| 113 | if (prevX < 0 || prevY < 0 || Float.isNaN(prevX) || Float.isNaN(prevY)) { |
| 114 | return false; |
| 115 | } |
| 116 | for (int i = 1; i < N; i++) { |
| 117 | if (prevX >= x[i] || prevY > y[i]) { |
| 118 | return false; |
| 119 | } |
| 120 | if (Float.isNaN(x[i]) || Float.isNaN(y[i])) { |
| 121 | return false; |
| 122 | } |
| 123 | prevX = x[i]; |
| 124 | prevY = y[i]; |
| 125 | } |
| 126 | return true; |
| 127 | } |
| 128 | |
| 129 | private static boolean isValidMapping(float[] x, int[] y) { |
| 130 | if (x == null || y == null || x.length == 0 || y.length == 0) { |
| 131 | return false; |
| 132 | } |
| 133 | if (x.length != y.length) { |
| 134 | return false; |
| 135 | } |
| 136 | final int N = x.length; |
| 137 | float prevX = x[0]; |
| 138 | int prevY = y[0]; |
| 139 | if (prevX < 0 || prevY < 0 || Float.isNaN(prevX)) { |
| 140 | return false; |
| 141 | } |
| 142 | for (int i = 1; i < N; i++) { |
| 143 | if (prevX >= x[i] || prevY > y[i]) { |
| 144 | return false; |
| 145 | } |
| 146 | if (Float.isNaN(x[i])) { |
| 147 | return false; |
| 148 | } |
| 149 | prevX = x[i]; |
| 150 | prevY = y[i]; |
| 151 | } |
| 152 | return true; |
| 153 | } |
| 154 | |
| 155 | /** |
| 156 | * Sets the {@link BrightnessConfiguration}. |
| 157 | * |
| 158 | * @param config The new configuration. If {@code null} is passed, the default configuration is |
| 159 | * used. |
| 160 | * @return Whether the brightness configuration has changed. |
| 161 | */ |
| 162 | public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config); |
| 163 | |
| 164 | /** |
| 165 | * Returns the desired brightness of the display based on the current ambient lux. |
| 166 | * |
| 167 | * The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max |
| 168 | * brightness and 0 is the display at minimum brightness. |
| 169 | * |
| 170 | * @param lux The current ambient brightness in lux. |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 171 | * @return The desired brightness of the display normalized to the range [0, 1.0]. |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 172 | */ |
| 173 | public abstract float getBrightness(float lux); |
| 174 | |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 175 | /** |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 176 | * Converts the provided backlight value to nits if possible. |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 177 | * |
| 178 | * Returns -1.0f if there's no available mapping for the backlight to nits. |
| 179 | */ |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 180 | public abstract float convertToNits(int backlight); |
| 181 | |
| 182 | /** |
| 183 | * Adds a user interaction data point to the brightness mapping. |
| 184 | * |
| 185 | * Currently, we only keep track of one of these at a time to constrain what can happen to the |
| 186 | * curve. |
| 187 | */ |
| 188 | public abstract void addUserDataPoint(float lux, float brightness); |
| 189 | |
| 190 | /** |
| 191 | * Removes any short term adjustments made to the curve from user interactions. |
| 192 | * |
| 193 | * Note that this does *not* reset the mapping to its initial state, any brightness |
| 194 | * configurations that have been applied will continue to be in effect. This solely removes the |
| 195 | * effects of user interactions on the model. |
| 196 | */ |
| 197 | public abstract void clearUserDataPoints(); |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 198 | |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 199 | public abstract void dump(PrintWriter pw); |
| 200 | |
| 201 | private static float normalizeAbsoluteBrightness(int brightness) { |
| 202 | brightness = MathUtils.constrain(brightness, |
| 203 | PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); |
| 204 | return (float) brightness / PowerManager.BRIGHTNESS_ON; |
| 205 | } |
| 206 | |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 207 | private static Spline createSpline(float[] x, float[] y) { |
| 208 | Spline spline = Spline.createSpline(x, y); |
| 209 | if (DEBUG) { |
| 210 | Slog.d(TAG, "Spline: " + spline); |
| 211 | for (float v = 1f; v < x[x.length - 1] * 1.25f; v *= 1.25f) { |
| 212 | Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v))); |
| 213 | } |
| 214 | } |
| 215 | return spline; |
| 216 | } |
| 217 | |
| 218 | private static Pair<float[], float[]> insertControlPoint( |
| 219 | float[] luxLevels, float[] brightnessLevels, float lux, float brightness) { |
| 220 | if (DEBUG) { |
| 221 | Slog.d(TAG, "Inserting new control point at (" + lux + ", " + brightness + ")"); |
| 222 | } |
| 223 | final int idx = findInsertionPoint(luxLevels, lux); |
| 224 | final float[] newLuxLevels; |
| 225 | final float[] newBrightnessLevels; |
| 226 | if (idx == luxLevels.length) { |
| 227 | newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + 1); |
| 228 | newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length + 1); |
| 229 | newLuxLevels[idx] = lux; |
| 230 | newBrightnessLevels[idx] = brightness; |
| 231 | } else if (luxLevels[idx] == lux) { |
| 232 | newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length); |
| 233 | newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length); |
| 234 | newBrightnessLevels[idx] = brightness; |
| 235 | } else { |
| 236 | newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + 1); |
| 237 | System.arraycopy(newLuxLevels, idx, newLuxLevels, idx+1, luxLevels.length - idx); |
| 238 | newLuxLevels[idx] = lux; |
| 239 | newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length + 1); |
| 240 | System.arraycopy(newBrightnessLevels, idx, newBrightnessLevels, idx+1, |
| 241 | brightnessLevels.length - idx); |
| 242 | newBrightnessLevels[idx] = brightness; |
| 243 | } |
| 244 | smoothCurve(newLuxLevels, newBrightnessLevels, idx); |
| 245 | return Pair.create(newLuxLevels, newBrightnessLevels); |
| 246 | } |
| 247 | |
| 248 | /** |
| 249 | * Returns the index of the first value that's less than or equal to {@code val}. |
| 250 | * |
| 251 | * This assumes that {@code arr} is sorted. If all values in {@code arr} are greater |
| 252 | * than val, then it will return the length of arr as the insertion point. |
| 253 | */ |
| 254 | private static int findInsertionPoint(float[] arr, float val) { |
| 255 | for (int i = 0; i < arr.length; i++) { |
| 256 | if (val <= arr[i]) { |
| 257 | return i; |
| 258 | } |
| 259 | } |
| 260 | return arr.length; |
| 261 | } |
| 262 | |
| 263 | private static void smoothCurve(float[] lux, float[] brightness, int idx) { |
| 264 | if (DEBUG) { |
| 265 | Slog.d(TAG, "smoothCurve(lux=" + Arrays.toString(lux) |
| 266 | + ", brightness=" + Arrays.toString(brightness) |
| 267 | + ", idx=" + idx + ")"); |
| 268 | } |
| 269 | float prevLux = lux[idx]; |
| 270 | float prevBrightness = brightness[idx]; |
| 271 | // Smooth curve for data points above the newly introduced point |
| 272 | for (int i = idx+1; i < lux.length; i++) { |
| 273 | float currLux = lux[i]; |
| 274 | float currBrightness = brightness[i]; |
| 275 | float maxBrightness = prevBrightness * permissibleRatio(currLux, prevLux); |
| 276 | float newBrightness = MathUtils.constrain( |
| 277 | currBrightness, prevBrightness, maxBrightness); |
| 278 | if (newBrightness == currBrightness) { |
| 279 | break; |
| 280 | } |
| 281 | prevLux = currLux; |
| 282 | prevBrightness = newBrightness; |
| 283 | brightness[i] = newBrightness; |
| 284 | } |
| 285 | |
| 286 | // Smooth curve for data points below the newly introduced point |
| 287 | prevLux = lux[idx]; |
| 288 | prevBrightness = brightness[idx]; |
| 289 | for (int i = idx-1; i >= 0; i--) { |
| 290 | float currLux = lux[i]; |
| 291 | float currBrightness = brightness[i]; |
| 292 | float minBrightness = prevBrightness * permissibleRatio(currLux, prevLux); |
| 293 | float newBrightness = MathUtils.constrain( |
| 294 | currBrightness, minBrightness, prevBrightness); |
| 295 | if (newBrightness == currBrightness) { |
| 296 | break; |
| 297 | } |
| 298 | prevLux = currLux; |
| 299 | prevBrightness = newBrightness; |
| 300 | brightness[i] = newBrightness; |
| 301 | } |
| 302 | if (DEBUG) { |
| 303 | Slog.d(TAG, "Smoothed Curve: lux=" + Arrays.toString(lux) |
| 304 | + ", brightness=" + Arrays.toString(brightness)); |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | private static float permissibleRatio(float currLux, float prevLux) { |
| 309 | return MathUtils.exp(MAX_GRAD |
| 310 | * (MathUtils.log(currLux + LUX_GRAD_SMOOTHING) |
| 311 | - MathUtils.log(prevLux + LUX_GRAD_SMOOTHING))); |
| 312 | } |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 313 | |
| 314 | /** |
| 315 | * A {@link BrightnessMappingStrategy} that maps from ambient room brightness directly to the |
| 316 | * backlight of the display. |
| 317 | * |
| 318 | * Since we don't have information about the display's physical brightness, any brightness |
| 319 | * configurations that are set are just ignored. |
| 320 | */ |
| 321 | private static class SimpleMappingStrategy extends BrightnessMappingStrategy { |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 322 | // Lux control points |
| 323 | private final float[] mLux; |
| 324 | // Brightness control points normalized to [0, 1] |
| 325 | private final float[] mBrightness; |
| 326 | |
| 327 | private Spline mSpline; |
| 328 | private float mUserLux; |
| 329 | private float mUserBrightness; |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 330 | |
| 331 | public SimpleMappingStrategy(float[] lux, int[] brightness) { |
| 332 | Preconditions.checkArgument(lux.length != 0 && brightness.length != 0, |
| 333 | "Lux and brightness arrays must not be empty!"); |
| 334 | Preconditions.checkArgument(lux.length == brightness.length, |
| 335 | "Lux and brightness arrays must be the same length!"); |
| 336 | Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux"); |
| 337 | Preconditions.checkArrayElementsInRange(brightness, |
| 338 | 0, Integer.MAX_VALUE, "brightness"); |
| 339 | |
| 340 | final int N = brightness.length; |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 341 | mLux = new float[N]; |
| 342 | mBrightness = new float[N]; |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 343 | for (int i = 0; i < N; i++) { |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 344 | mLux[i] = lux[i]; |
| 345 | mBrightness[i] = normalizeAbsoluteBrightness(brightness[i]); |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 346 | } |
| 347 | |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 348 | mSpline = createSpline(mLux, mBrightness); |
| 349 | mUserLux = -1; |
| 350 | mUserBrightness = -1; |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 351 | } |
| 352 | |
| 353 | @Override |
| 354 | public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 355 | return false; |
| 356 | } |
| 357 | |
| 358 | @Override |
| 359 | public float getBrightness(float lux) { |
| 360 | return mSpline.interpolate(lux); |
| 361 | } |
| 362 | |
| 363 | @Override |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 364 | public float convertToNits(int backlight) { |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 365 | return -1.0f; |
| 366 | } |
| 367 | |
| 368 | @Override |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 369 | public void addUserDataPoint(float lux, float brightness) { |
| 370 | if (DEBUG){ |
| 371 | Slog.d(TAG, "addUserDataPoint(lux=" + lux + ", brightness=" + brightness + ")"); |
| 372 | } |
| 373 | Pair<float[], float[]> curve = insertControlPoint(mLux, mBrightness, lux, brightness); |
| 374 | mSpline = createSpline(curve.first, curve.second); |
| 375 | mUserLux = lux; |
| 376 | mUserBrightness = brightness; |
| 377 | } |
| 378 | |
| 379 | @Override |
| 380 | public void clearUserDataPoints() { |
| 381 | if (mUserLux != -1) { |
| 382 | mSpline = createSpline(mLux, mBrightness); |
| 383 | mUserLux = -1; |
| 384 | mUserBrightness = -1; |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | @Override |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 389 | public void dump(PrintWriter pw) { |
| 390 | pw.println("SimpleMappingStrategy"); |
| 391 | pw.println(" mSpline=" + mSpline); |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 392 | pw.println(" mUserLux=" + mUserLux); |
| 393 | pw.println(" mUserBrightness=" + mUserBrightness); |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 394 | } |
| 395 | } |
| 396 | |
| 397 | /** A {@link BrightnessMappingStrategy} that maps from ambient room brightness to the physical |
| 398 | * range of the display, rather than to the range of the backlight control (typically 0-255). |
| 399 | * |
| 400 | * By mapping through the physical brightness, the curve becomes portable across devices and |
| 401 | * gives us more resolution in the resulting mapping. |
| 402 | */ |
| 403 | @VisibleForTesting |
| 404 | static class PhysicalMappingStrategy extends BrightnessMappingStrategy { |
| 405 | // The current brightness configuration. |
| 406 | private BrightnessConfiguration mConfig; |
| 407 | |
| 408 | // A spline mapping from the current ambient light in lux to the desired display brightness |
| 409 | // in nits. |
| 410 | private Spline mBrightnessSpline; |
| 411 | |
| 412 | // A spline mapping from nits to the corresponding backlight value, normalized to the range |
| 413 | // [0, 1.0]. |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 414 | private final Spline mNitsToBacklightSpline; |
| 415 | |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 416 | // The default brightness configuration. |
| 417 | private final BrightnessConfiguration mDefaultConfig; |
| 418 | |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 419 | // A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to |
| 420 | // a brightness in nits. |
| 421 | private Spline mBacklightToNitsSpline; |
| 422 | |
| 423 | private float mUserLux; |
| 424 | private float mUserBrightness; |
| 425 | |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 426 | public PhysicalMappingStrategy(BrightnessConfiguration config, |
| 427 | float[] nits, int[] backlight) { |
| 428 | Preconditions.checkArgument(nits.length != 0 && backlight.length != 0, |
| 429 | "Nits and backlight arrays must not be empty!"); |
| 430 | Preconditions.checkArgument(nits.length == backlight.length, |
| 431 | "Nits and backlight arrays must be the same length!"); |
| 432 | Preconditions.checkNotNull(config); |
| 433 | Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits"); |
| 434 | Preconditions.checkArrayElementsInRange(backlight, |
| 435 | PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON, "backlight"); |
| 436 | |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 437 | mUserLux = -1; |
| 438 | mUserBrightness = -1; |
| 439 | |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 440 | // Setup the backlight spline |
| 441 | final int N = nits.length; |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 442 | float[] normalizedBacklight = new float[N]; |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 443 | for (int i = 0; i < N; i++) { |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 444 | normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]); |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 445 | } |
| 446 | |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 447 | mNitsToBacklightSpline = createSpline(nits, normalizedBacklight); |
| 448 | mBacklightToNitsSpline = createSpline(normalizedBacklight, nits); |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 449 | |
| 450 | mDefaultConfig = config; |
| 451 | setBrightnessConfiguration(config); |
| 452 | } |
| 453 | |
| 454 | @Override |
| 455 | public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { |
| 456 | if (config == null) { |
| 457 | config = mDefaultConfig; |
| 458 | } |
| 459 | if (config.equals(mConfig)) { |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 460 | return false; |
| 461 | } |
| 462 | |
| 463 | Pair<float[], float[]> curve = config.getCurve(); |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 464 | mBrightnessSpline = createSpline(curve.first /*lux*/, curve.second /*nits*/); |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 465 | mConfig = config; |
| 466 | return true; |
| 467 | } |
| 468 | |
| 469 | @Override |
| 470 | public float getBrightness(float lux) { |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 471 | float nits = mBrightnessSpline.interpolate(lux); |
| 472 | float backlight = mNitsToBacklightSpline.interpolate(nits); |
| 473 | return backlight; |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 474 | } |
| 475 | |
| 476 | @Override |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 477 | public float convertToNits(int backlight) { |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 478 | return mBacklightToNitsSpline.interpolate(normalizeAbsoluteBrightness(backlight)); |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 479 | } |
| 480 | |
| 481 | @Override |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 482 | public void addUserDataPoint(float lux, float backlight) { |
| 483 | if (DEBUG){ |
| 484 | Slog.d(TAG, "addUserDataPoint(lux=" + lux + ", backlight=" + backlight + ")"); |
| 485 | } |
| 486 | float brightness = mBacklightToNitsSpline.interpolate(backlight); |
| 487 | Pair<float[], float[]> defaultCurve = mConfig.getCurve(); |
| 488 | Pair<float[], float[]> newCurve = |
| 489 | insertControlPoint(defaultCurve.first, defaultCurve.second, lux, brightness); |
| 490 | mBrightnessSpline = createSpline(newCurve.first, newCurve.second); |
| 491 | mUserLux = lux; |
| 492 | mUserBrightness = brightness; |
| 493 | } |
| 494 | |
| 495 | @Override |
| 496 | public void clearUserDataPoints() { |
| 497 | if (mUserLux != -1) { |
| 498 | Pair<float[], float[]> defaultCurve = mConfig.getCurve(); |
| 499 | mBrightnessSpline = createSpline(defaultCurve.first, defaultCurve.second); |
| 500 | mUserLux = -1; |
| 501 | mUserBrightness = -1; |
| 502 | } |
| 503 | } |
| 504 | |
| 505 | @Override |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 506 | public void dump(PrintWriter pw) { |
| 507 | pw.println("PhysicalMappingStrategy"); |
| 508 | pw.println(" mConfig=" + mConfig); |
| 509 | pw.println(" mBrightnessSpline=" + mBrightnessSpline); |
Michael Wright | 144aac9 | 2017-12-21 18:37:41 +0000 | [diff] [blame] | 510 | pw.println(" mNitsToBacklightSpline=" + mNitsToBacklightSpline); |
Michael Wright | d846023 | 2018-01-16 18:04:59 +0000 | [diff] [blame] | 511 | pw.println(" mUserLux=" + mUserLux); |
| 512 | pw.println(" mUserBrightness=" + mUserBrightness); |
Michael Wright | eef0e13 | 2017-11-21 17:57:52 +0000 | [diff] [blame] | 513 | } |
| 514 | } |
| 515 | } |