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; 20 import android.content.res.Resources; 21 import android.content.res.TypedArray; 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 import com.android.server.display.utils.Plog; 32 33 import java.io.PrintWriter; 34 import java.util.Arrays; 35 36 /** 37 * A utility to map from an ambient brightness to a display's "backlight" brightness based on the 38 * available display information and brightness configuration. 39 * 40 * Note that without a mapping from the nits to a display backlight level, any 41 * {@link BrightnessConfiguration}s that are set are just ignored. 42 */ 43 public abstract class BrightnessMappingStrategy { 44 private static final String TAG = "BrightnessMappingStrategy"; 45 private static final boolean DEBUG = false; 46 47 private static final float LUX_GRAD_SMOOTHING = 0.25f; 48 private static final float MAX_GRAD = 1.0f; 49 50 private static final Plog PLOG = Plog.createSystemPlog(TAG); 51 52 @Nullable 53 public static BrightnessMappingStrategy create(Resources resources) { 54 float[] luxLevels = getLuxLevels(resources.getIntArray( 55 com.android.internal.R.array.config_autoBrightnessLevels)); 56 int[] brightnessLevelsBacklight = resources.getIntArray( 57 com.android.internal.R.array.config_autoBrightnessLcdBacklightValues); 58 float[] brightnessLevelsNits = getFloatArray(resources.obtainTypedArray( 59 com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)); 60 float autoBrightnessAdjustmentMaxGamma = resources.getFraction( 61 com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 62 1, 1); 63 64 float[] nitsRange = getFloatArray(resources.obtainTypedArray( 65 com.android.internal.R.array.config_screenBrightnessNits)); 66 int[] backlightRange = resources.getIntArray( 67 com.android.internal.R.array.config_screenBrightnessBacklight); 68 69 if (isValidMapping(nitsRange, backlightRange) 70 && isValidMapping(luxLevels, brightnessLevelsNits)) { 71 int minimumBacklight = resources.getInteger( 72 com.android.internal.R.integer.config_screenBrightnessSettingMinimum); 73 int maximumBacklight = resources.getInteger( 74 com.android.internal.R.integer.config_screenBrightnessSettingMaximum); 75 if (backlightRange[0] > minimumBacklight 76 || backlightRange[backlightRange.length - 1] < maximumBacklight) { 77 Slog.w(TAG, "Screen brightness mapping does not cover whole range of available " + 78 "backlight values, autobrightness functionality may be impaired."); 79 } 80 BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); 81 builder.setCurve(luxLevels, brightnessLevelsNits); 82 return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange, 83 autoBrightnessAdjustmentMaxGamma); 84 } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) { 85 return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight, 86 autoBrightnessAdjustmentMaxGamma); 87 } else { 88 return null; 89 } 90 } 91 92 private static float[] getLuxLevels(int[] lux) { 93 // The first control point is implicit and always at 0 lux. 94 float[] levels = new float[lux.length + 1]; 95 for (int i = 0; i < lux.length; i++) { 96 levels[i + 1] = (float) lux[i]; 97 } 98 return levels; 99 } 100 101 private static float[] getFloatArray(TypedArray array) { 102 final int N = array.length(); 103 float[] vals = new float[N]; 104 for (int i = 0; i < N; i++) { 105 vals[i] = array.getFloat(i, -1.0f); 106 } 107 array.recycle(); 108 return vals; 109 } 110 111 private static boolean isValidMapping(float[] x, float[] y) { 112 if (x == null || y == null || x.length == 0 || y.length == 0) { 113 return false; 114 } 115 if (x.length != y.length) { 116 return false; 117 } 118 final int N = x.length; 119 float prevX = x[0]; 120 float prevY = y[0]; 121 if (prevX < 0 || prevY < 0 || Float.isNaN(prevX) || Float.isNaN(prevY)) { 122 return false; 123 } 124 for (int i = 1; i < N; i++) { 125 if (prevX >= x[i] || prevY > y[i]) { 126 return false; 127 } 128 if (Float.isNaN(x[i]) || Float.isNaN(y[i])) { 129 return false; 130 } 131 prevX = x[i]; 132 prevY = y[i]; 133 } 134 return true; 135 } 136 137 private static boolean isValidMapping(float[] x, int[] y) { 138 if (x == null || y == null || x.length == 0 || y.length == 0) { 139 return false; 140 } 141 if (x.length != y.length) { 142 return false; 143 } 144 final int N = x.length; 145 float prevX = x[0]; 146 int prevY = y[0]; 147 if (prevX < 0 || prevY < 0 || Float.isNaN(prevX)) { 148 return false; 149 } 150 for (int i = 1; i < N; i++) { 151 if (prevX >= x[i] || prevY > y[i]) { 152 return false; 153 } 154 if (Float.isNaN(x[i])) { 155 return false; 156 } 157 prevX = x[i]; 158 prevY = y[i]; 159 } 160 return true; 161 } 162 163 /** 164 * Sets the {@link BrightnessConfiguration}. 165 * 166 * @param config The new configuration. If {@code null} is passed, the default configuration is 167 * used. 168 * @return Whether the brightness configuration has changed. 169 */ 170 public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config); 171 172 /** 173 * Returns the desired brightness of the display based on the current ambient lux. 174 * 175 * The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max 176 * brightness and 0 is the display at minimum brightness. 177 * 178 * @param lux The current ambient brightness in lux. 179 * @return The desired brightness of the display normalized to the range [0, 1.0]. 180 */ 181 public abstract float getBrightness(float lux); 182 183 /** 184 * Returns the current auto-brightness adjustment. 185 * 186 * The returned adjustment is a value in the range [-1.0, 1.0] such that 187 * {@code config_autoBrightnessAdjustmentMaxGamma<sup>-adjustment</sup>} is used to gamma 188 * correct the brightness curve. 189 */ 190 public abstract float getAutoBrightnessAdjustment(); 191 192 /** 193 * Sets the auto-brightness adjustment. 194 * 195 * @param adjustment The desired auto-brightness adjustment. 196 * @return Whether the auto-brightness adjustment has changed. 197 * 198 * @Deprecated The auto-brightness adjustment should not be set directly, but rather inferred 199 * from user data points. 200 */ 201 public abstract boolean setAutoBrightnessAdjustment(float adjustment); 202 203 /** 204 * Converts the provided backlight value to nits if possible. 205 * 206 * Returns -1.0f if there's no available mapping for the backlight to nits. 207 */ 208 public abstract float convertToNits(int backlight); 209 210 /** 211 * Adds a user interaction data point to the brightness mapping. 212 * 213 * This data point <b>must</b> exist on the brightness curve as a result of this call. This is 214 * so that the next time we come to query what the screen brightness should be, we get what the 215 * user requested rather than immediately changing to some other value. 216 * 217 * Currently, we only keep track of one of these at a time to constrain what can happen to the 218 * curve. 219 */ 220 public abstract void addUserDataPoint(float lux, float brightness); 221 222 /** 223 * Removes any short term adjustments made to the curve from user interactions. 224 * 225 * Note that this does *not* reset the mapping to its initial state, any brightness 226 * configurations that have been applied will continue to be in effect. This solely removes the 227 * effects of user interactions on the model. 228 */ 229 public abstract void clearUserDataPoints(); 230 231 /** @return True if there are any short term adjustments applied to the curve. */ 232 public abstract boolean hasUserDataPoints(); 233 234 /** @return True if the current brightness configuration is the default one. */ 235 public abstract boolean isDefaultConfig(); 236 237 /** @return The default brightness configuration. */ 238 public abstract BrightnessConfiguration getDefaultConfig(); 239 240 public abstract void dump(PrintWriter pw); 241 242 private static float normalizeAbsoluteBrightness(int brightness) { 243 brightness = MathUtils.constrain(brightness, 244 PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); 245 return (float) brightness / PowerManager.BRIGHTNESS_ON; 246 } 247 248 private static Pair<float[], float[]> insertControlPoint( 249 float[] luxLevels, float[] brightnessLevels, float lux, float brightness) { 250 final int idx = findInsertionPoint(luxLevels, lux); 251 final float[] newLuxLevels; 252 final float[] newBrightnessLevels; 253 if (idx == luxLevels.length) { 254 newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + 1); 255 newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length + 1); 256 newLuxLevels[idx] = lux; 257 newBrightnessLevels[idx] = brightness; 258 } else if (luxLevels[idx] == lux) { 259 newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length); 260 newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length); 261 newBrightnessLevels[idx] = brightness; 262 } else { 263 newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + 1); 264 System.arraycopy(newLuxLevels, idx, newLuxLevels, idx+1, luxLevels.length - idx); 265 newLuxLevels[idx] = lux; 266 newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length + 1); 267 System.arraycopy(newBrightnessLevels, idx, newBrightnessLevels, idx+1, 268 brightnessLevels.length - idx); 269 newBrightnessLevels[idx] = brightness; 270 } 271 smoothCurve(newLuxLevels, newBrightnessLevels, idx); 272 return Pair.create(newLuxLevels, newBrightnessLevels); 273 } 274 275 /** 276 * Returns the index of the first value that's less than or equal to {@code val}. 277 * 278 * This assumes that {@code arr} is sorted. If all values in {@code arr} are greater 279 * than val, then it will return the length of arr as the insertion point. 280 */ 281 private static int findInsertionPoint(float[] arr, float val) { 282 for (int i = 0; i < arr.length; i++) { 283 if (val <= arr[i]) { 284 return i; 285 } 286 } 287 return arr.length; 288 } 289 290 private static void smoothCurve(float[] lux, float[] brightness, int idx) { 291 if (DEBUG) { 292 PLOG.logCurve("unsmoothed curve", lux, brightness); 293 } 294 float prevLux = lux[idx]; 295 float prevBrightness = brightness[idx]; 296 // Smooth curve for data points above the newly introduced point 297 for (int i = idx+1; i < lux.length; i++) { 298 float currLux = lux[i]; 299 float currBrightness = brightness[i]; 300 float maxBrightness = prevBrightness * permissibleRatio(currLux, prevLux); 301 float newBrightness = MathUtils.constrain( 302 currBrightness, prevBrightness, maxBrightness); 303 if (newBrightness == currBrightness) { 304 break; 305 } 306 prevLux = currLux; 307 prevBrightness = newBrightness; 308 brightness[i] = newBrightness; 309 } 310 // Smooth curve for data points below the newly introduced point 311 prevLux = lux[idx]; 312 prevBrightness = brightness[idx]; 313 for (int i = idx-1; i >= 0; i--) { 314 float currLux = lux[i]; 315 float currBrightness = brightness[i]; 316 float minBrightness = prevBrightness * permissibleRatio(currLux, prevLux); 317 float newBrightness = MathUtils.constrain( 318 currBrightness, minBrightness, prevBrightness); 319 if (newBrightness == currBrightness) { 320 break; 321 } 322 prevLux = currLux; 323 prevBrightness = newBrightness; 324 brightness[i] = newBrightness; 325 } 326 if (DEBUG) { 327 PLOG.logCurve("smoothed curve", lux, brightness); 328 } 329 } 330 331 private static float permissibleRatio(float currLux, float prevLux) { 332 return MathUtils.exp(MAX_GRAD 333 * (MathUtils.log(currLux + LUX_GRAD_SMOOTHING) 334 - MathUtils.log(prevLux + LUX_GRAD_SMOOTHING))); 335 } 336 337 private static float inferAutoBrightnessAdjustment(float maxGamma, 338 float desiredBrightness, float currentBrightness) { 339 float adjustment = 0; 340 float gamma = Float.NaN; 341 // Extreme edge cases: use a simpler heuristic, as proper gamma correction around the edges 342 // affects the curve rather drastically. 343 if (currentBrightness <= 0.1f || currentBrightness >= 0.9f) { 344 adjustment = (desiredBrightness - currentBrightness); 345 // Edge case: darkest adjustment possible. 346 } else if (desiredBrightness == 0) { 347 adjustment = -1; 348 // Edge case: brightest adjustment possible. 349 } else if (desiredBrightness == 1) { 350 adjustment = +1; 351 } else { 352 // current^gamma = desired => gamma = log[current](desired) 353 gamma = MathUtils.log(desiredBrightness) / MathUtils.log(currentBrightness); 354 // max^-adjustment = gamma => adjustment = -log[max](gamma) 355 adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma); 356 } 357 adjustment = MathUtils.constrain(adjustment, -1, +1); 358 if (DEBUG) { 359 Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" + 360 MathUtils.pow(maxGamma, -adjustment) + " == " + gamma); 361 Slog.d(TAG, "inferAutoBrightnessAdjustment: " + currentBrightness + "^" + gamma + "=" + 362 MathUtils.pow(currentBrightness, gamma) + " == " + desiredBrightness); 363 } 364 return adjustment; 365 } 366 367 private static Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness, 368 float userLux, float userBrightness, float adjustment, float maxGamma) { 369 float[] newLux = lux; 370 float[] newBrightness = Arrays.copyOf(brightness, brightness.length); 371 if (DEBUG) { 372 PLOG.logCurve("unadjusted curve", newLux, newBrightness); 373 } 374 adjustment = MathUtils.constrain(adjustment, -1, 1); 375 float gamma = MathUtils.pow(maxGamma, -adjustment); 376 if (DEBUG) { 377 Slog.d(TAG, "getAdjustedCurve: " + maxGamma + "^" + -adjustment + "=" + 378 MathUtils.pow(maxGamma, -adjustment) + " == " + gamma); 379 } 380 if (gamma != 1) { 381 for (int i = 0; i < newBrightness.length; i++) { 382 newBrightness[i] = MathUtils.pow(newBrightness[i], gamma); 383 } 384 } 385 if (DEBUG) { 386 PLOG.logCurve("gamma adjusted curve", newLux, newBrightness); 387 } 388 if (userLux != -1) { 389 Pair<float[], float[]> curve = insertControlPoint(newLux, newBrightness, userLux, 390 userBrightness); 391 newLux = curve.first; 392 newBrightness = curve.second; 393 if (DEBUG) { 394 PLOG.logCurve("gamma and user adjusted curve", newLux, newBrightness); 395 // This is done for comparison. 396 curve = insertControlPoint(lux, brightness, userLux, userBrightness); 397 PLOG.logCurve("user adjusted curve", curve.first ,curve.second); 398 } 399 } 400 return Pair.create(newLux, newBrightness); 401 } 402 403 /** 404 * A {@link BrightnessMappingStrategy} that maps from ambient room brightness directly to the 405 * backlight of the display. 406 * 407 * Since we don't have information about the display's physical brightness, any brightness 408 * configurations that are set are just ignored. 409 */ 410 private static class SimpleMappingStrategy extends BrightnessMappingStrategy { 411 // Lux control points 412 private final float[] mLux; 413 // Brightness control points normalized to [0, 1] 414 private final float[] mBrightness; 415 416 private Spline mSpline; 417 private float mMaxGamma; 418 private float mAutoBrightnessAdjustment; 419 private float mUserLux; 420 private float mUserBrightness; 421 422 public SimpleMappingStrategy(float[] lux, int[] brightness, float maxGamma) { 423 Preconditions.checkArgument(lux.length != 0 && brightness.length != 0, 424 "Lux and brightness arrays must not be empty!"); 425 Preconditions.checkArgument(lux.length == brightness.length, 426 "Lux and brightness arrays must be the same length!"); 427 Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux"); 428 Preconditions.checkArrayElementsInRange(brightness, 429 0, Integer.MAX_VALUE, "brightness"); 430 431 final int N = brightness.length; 432 mLux = new float[N]; 433 mBrightness = new float[N]; 434 for (int i = 0; i < N; i++) { 435 mLux[i] = lux[i]; 436 mBrightness[i] = normalizeAbsoluteBrightness(brightness[i]); 437 } 438 439 mMaxGamma = maxGamma; 440 mAutoBrightnessAdjustment = 0; 441 mUserLux = -1; 442 mUserBrightness = -1; 443 if (DEBUG) { 444 PLOG.start("simple mapping strategy"); 445 } 446 computeSpline(); 447 } 448 449 @Override 450 public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { 451 return false; 452 } 453 454 @Override 455 public float getBrightness(float lux) { 456 return mSpline.interpolate(lux); 457 } 458 459 @Override 460 public float getAutoBrightnessAdjustment() { 461 return mAutoBrightnessAdjustment; 462 } 463 464 @Override 465 public boolean setAutoBrightnessAdjustment(float adjustment) { 466 adjustment = MathUtils.constrain(adjustment, -1, 1); 467 if (adjustment == mAutoBrightnessAdjustment) { 468 return false; 469 } 470 if (DEBUG) { 471 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " + 472 adjustment); 473 PLOG.start("auto-brightness adjustment"); 474 } 475 mAutoBrightnessAdjustment = adjustment; 476 computeSpline(); 477 return true; 478 } 479 480 @Override 481 public float convertToNits(int backlight) { 482 return -1.0f; 483 } 484 485 @Override 486 public void addUserDataPoint(float lux, float brightness) { 487 float unadjustedBrightness = getUnadjustedBrightness(lux); 488 if (DEBUG){ 489 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")"); 490 PLOG.start("add user data point") 491 .logPoint("user data point", lux, brightness) 492 .logPoint("current brightness", lux, unadjustedBrightness); 493 } 494 float adjustment = inferAutoBrightnessAdjustment(mMaxGamma, 495 brightness /* desiredBrightness */, 496 unadjustedBrightness /* currentBrightness */); 497 if (DEBUG) { 498 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " + 499 adjustment); 500 } 501 mAutoBrightnessAdjustment = adjustment; 502 mUserLux = lux; 503 mUserBrightness = brightness; 504 computeSpline(); 505 } 506 507 @Override 508 public void clearUserDataPoints() { 509 if (mUserLux != -1) { 510 if (DEBUG) { 511 Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0"); 512 PLOG.start("clear user data points") 513 .logPoint("user data point", mUserLux, mUserBrightness); 514 } 515 mAutoBrightnessAdjustment = 0; 516 mUserLux = -1; 517 mUserBrightness = -1; 518 computeSpline(); 519 } 520 } 521 522 @Override 523 public boolean hasUserDataPoints() { 524 return mUserLux != -1; 525 } 526 527 @Override 528 public boolean isDefaultConfig() { 529 return true; 530 } 531 532 @Override 533 public BrightnessConfiguration getDefaultConfig() { 534 return null; 535 } 536 537 @Override 538 public void dump(PrintWriter pw) { 539 pw.println("SimpleMappingStrategy"); 540 pw.println(" mSpline=" + mSpline); 541 pw.println(" mMaxGamma=" + mMaxGamma); 542 pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); 543 pw.println(" mUserLux=" + mUserLux); 544 pw.println(" mUserBrightness=" + mUserBrightness); 545 } 546 547 private void computeSpline() { 548 Pair<float[], float[]> curve = getAdjustedCurve(mLux, mBrightness, mUserLux, 549 mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma); 550 mSpline = Spline.createSpline(curve.first, curve.second); 551 } 552 553 private float getUnadjustedBrightness(float lux) { 554 Spline spline = Spline.createSpline(mLux, mBrightness); 555 return spline.interpolate(lux); 556 } 557 } 558 559 /** A {@link BrightnessMappingStrategy} that maps from ambient room brightness to the physical 560 * range of the display, rather than to the range of the backlight control (typically 0-255). 561 * 562 * By mapping through the physical brightness, the curve becomes portable across devices and 563 * gives us more resolution in the resulting mapping. 564 */ 565 @VisibleForTesting 566 static class PhysicalMappingStrategy extends BrightnessMappingStrategy { 567 // The current brightness configuration. 568 private BrightnessConfiguration mConfig; 569 570 // A spline mapping from the current ambient light in lux to the desired display brightness 571 // in nits. 572 private Spline mBrightnessSpline; 573 574 // A spline mapping from nits to the corresponding backlight value, normalized to the range 575 // [0, 1.0]. 576 private final Spline mNitsToBacklightSpline; 577 578 // The default brightness configuration. 579 private final BrightnessConfiguration mDefaultConfig; 580 581 // A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to 582 // a brightness in nits. 583 private Spline mBacklightToNitsSpline; 584 585 private float mMaxGamma; 586 private float mAutoBrightnessAdjustment; 587 private float mUserLux; 588 private float mUserBrightness; 589 590 public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits, 591 int[] backlight, float maxGamma) { 592 Preconditions.checkArgument(nits.length != 0 && backlight.length != 0, 593 "Nits and backlight arrays must not be empty!"); 594 Preconditions.checkArgument(nits.length == backlight.length, 595 "Nits and backlight arrays must be the same length!"); 596 Preconditions.checkNotNull(config); 597 Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits"); 598 Preconditions.checkArrayElementsInRange(backlight, 599 PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON, "backlight"); 600 601 mMaxGamma = maxGamma; 602 mAutoBrightnessAdjustment = 0; 603 mUserLux = -1; 604 mUserBrightness = -1; 605 606 // Setup the backlight spline 607 final int N = nits.length; 608 float[] normalizedBacklight = new float[N]; 609 for (int i = 0; i < N; i++) { 610 normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]); 611 } 612 613 mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight); 614 mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits); 615 616 mDefaultConfig = config; 617 if (DEBUG) { 618 PLOG.start("physical mapping strategy"); 619 } 620 mConfig = config; 621 computeSpline(); 622 } 623 624 @Override 625 public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { 626 if (config == null) { 627 config = mDefaultConfig; 628 } 629 if (config.equals(mConfig)) { 630 return false; 631 } 632 if (DEBUG) { 633 PLOG.start("brightness configuration"); 634 } 635 mConfig = config; 636 computeSpline(); 637 return true; 638 } 639 640 @Override 641 public float getBrightness(float lux) { 642 float nits = mBrightnessSpline.interpolate(lux); 643 float backlight = mNitsToBacklightSpline.interpolate(nits); 644 return backlight; 645 } 646 647 @Override 648 public float getAutoBrightnessAdjustment() { 649 return mAutoBrightnessAdjustment; 650 } 651 652 @Override 653 public boolean setAutoBrightnessAdjustment(float adjustment) { 654 adjustment = MathUtils.constrain(adjustment, -1, 1); 655 if (adjustment == mAutoBrightnessAdjustment) { 656 return false; 657 } 658 if (DEBUG) { 659 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " + 660 adjustment); 661 PLOG.start("auto-brightness adjustment"); 662 } 663 mAutoBrightnessAdjustment = adjustment; 664 computeSpline(); 665 return true; 666 } 667 668 @Override 669 public float convertToNits(int backlight) { 670 return mBacklightToNitsSpline.interpolate(normalizeAbsoluteBrightness(backlight)); 671 } 672 673 @Override 674 public void addUserDataPoint(float lux, float brightness) { 675 float unadjustedBrightness = getUnadjustedBrightness(lux); 676 if (DEBUG){ 677 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")"); 678 PLOG.start("add user data point") 679 .logPoint("user data point", lux, brightness) 680 .logPoint("current brightness", lux, unadjustedBrightness); 681 } 682 float adjustment = inferAutoBrightnessAdjustment(mMaxGamma, 683 brightness /* desiredBrightness */, 684 unadjustedBrightness /* currentBrightness */); 685 if (DEBUG) { 686 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " + 687 adjustment); 688 } 689 mAutoBrightnessAdjustment = adjustment; 690 mUserLux = lux; 691 mUserBrightness = brightness; 692 computeSpline(); 693 } 694 695 @Override 696 public void clearUserDataPoints() { 697 if (mUserLux != -1) { 698 if (DEBUG) { 699 Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0"); 700 PLOG.start("clear user data points") 701 .logPoint("user data point", mUserLux, mUserBrightness); 702 } 703 mAutoBrightnessAdjustment = 0; 704 mUserLux = -1; 705 mUserBrightness = -1; 706 computeSpline(); 707 } 708 } 709 710 @Override 711 public boolean hasUserDataPoints() { 712 return mUserLux != -1; 713 } 714 715 @Override 716 public boolean isDefaultConfig() { 717 return mDefaultConfig.equals(mConfig); 718 } 719 720 @Override 721 public BrightnessConfiguration getDefaultConfig() { 722 return mDefaultConfig; 723 } 724 725 @Override 726 public void dump(PrintWriter pw) { 727 pw.println("PhysicalMappingStrategy"); 728 pw.println(" mConfig=" + mConfig); 729 pw.println(" mBrightnessSpline=" + mBrightnessSpline); 730 pw.println(" mNitsToBacklightSpline=" + mNitsToBacklightSpline); 731 pw.println(" mMaxGamma=" + mMaxGamma); 732 pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); 733 pw.println(" mUserLux=" + mUserLux); 734 pw.println(" mUserBrightness=" + mUserBrightness); 735 } 736 737 private void computeSpline() { 738 Pair<float[], float[]> defaultCurve = mConfig.getCurve(); 739 float[] defaultLux = defaultCurve.first; 740 float[] defaultNits = defaultCurve.second; 741 float[] defaultBacklight = new float[defaultNits.length]; 742 for (int i = 0; i < defaultBacklight.length; i++) { 743 defaultBacklight[i] = mNitsToBacklightSpline.interpolate(defaultNits[i]); 744 } 745 Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBacklight, mUserLux, 746 mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma); 747 float[] lux = curve.first; 748 float[] backlight = curve.second; 749 float[] nits = new float[backlight.length]; 750 for (int i = 0; i < nits.length; i++) { 751 nits[i] = mBacklightToNitsSpline.interpolate(backlight[i]); 752 } 753 mBrightnessSpline = Spline.createSpline(lux, nits); 754 } 755 756 private float getUnadjustedBrightness(float lux) { 757 Pair<float[], float[]> curve = mConfig.getCurve(); 758 Spline spline = Spline.createSpline(curve.first, curve.second); 759 return mNitsToBacklightSpline.interpolate(spline.interpolate(lux)); 760 } 761 } 762 } 763