1 /* 2 * Copyright 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 static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotEquals; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.mockito.ArgumentMatchers.anyFloat; 25 import static org.mockito.ArgumentMatchers.anyInt; 26 import static org.mockito.Mockito.mock; 27 import static org.mockito.Mockito.when; 28 29 import android.content.res.Resources; 30 import android.content.res.TypedArray; 31 import android.hardware.display.BrightnessConfiguration; 32 import android.os.PowerManager; 33 import android.support.test.filters.SmallTest; 34 import android.support.test.runner.AndroidJUnit4; 35 import android.util.MathUtils; 36 import android.util.Spline; 37 import android.util.Slog; 38 39 import org.junit.Test; 40 import org.junit.runner.RunWith; 41 42 import java.util.Arrays; 43 44 @SmallTest 45 @RunWith(AndroidJUnit4.class) 46 public class BrightnessMappingStrategyTest { 47 48 private static final int[] LUX_LEVELS = { 49 0, 50 5, 51 20, 52 40, 53 100, 54 325, 55 600, 56 1250, 57 2200, 58 4000, 59 5000 60 }; 61 62 private static final float[] DISPLAY_LEVELS_NITS = { 63 13.25f, 64 54.0f, 65 78.85f, 66 105.02f, 67 132.7f, 68 170.12f, 69 212.1f, 70 265.2f, 71 335.8f, 72 415.2f, 73 478.5f, 74 }; 75 76 private static final int[] DISPLAY_LEVELS_BACKLIGHT = { 77 9, 78 30, 79 45, 80 62, 81 78, 82 96, 83 119, 84 146, 85 178, 86 221, 87 255 88 }; 89 90 private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f }; 91 private static final int[] BACKLIGHT_RANGE = { 1, 255 }; 92 93 private static final float[] EMPTY_FLOAT_ARRAY = new float[0]; 94 private static final int[] EMPTY_INT_ARRAY = new int[0]; 95 96 private static final float MAXIMUM_GAMMA = 3.0f; 97 private static final int[] GAMMA_CORRECTION_LUX = { 98 0, 99 100, 100 1000, 101 2500, 102 4000, 103 4900, 104 5000 105 }; 106 private static final float[] GAMMA_CORRECTION_NITS = { 107 1.0f, 108 10.55f, 109 96.5f, 110 239.75f, 111 383.0f, 112 468.95f, 113 478.5f, 114 }; 115 private static final Spline GAMMA_CORRECTION_SPLINE = Spline.createSpline( 116 new float[] { 0.0f, 100.0f, 1000.0f, 2500.0f, 4000.0f, 4900.0f, 5000.0f }, 117 new float[] { 0.035f, 0.035f, 0.221f, 0.523f, 0.797f, 0.980f, 1.0f }); 118 119 @Test 120 public void testSimpleStrategyMappingAtControlPoints() { 121 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); 122 BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res); 123 assertNotNull("BrightnessMappingStrategy should not be null", simple); 124 for (int i = 0; i < LUX_LEVELS.length; i++) { 125 final float expectedLevel = 126 (float) DISPLAY_LEVELS_BACKLIGHT[i] / PowerManager.BRIGHTNESS_ON; 127 assertEquals(expectedLevel, 128 simple.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/); 129 } 130 } 131 132 @Test 133 public void testSimpleStrategyMappingBetweenControlPoints() { 134 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); 135 BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res); 136 assertNotNull("BrightnessMappingStrategy should not be null", simple); 137 for (int i = 1; i < LUX_LEVELS.length; i++) { 138 final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; 139 final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON; 140 assertTrue("Desired brightness should be between adjacent control points.", 141 backlight > DISPLAY_LEVELS_BACKLIGHT[i - 1] 142 && backlight < DISPLAY_LEVELS_BACKLIGHT[i]); 143 } 144 } 145 146 @Test 147 public void testSimpleStrategyIgnoresNewConfiguration() { 148 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); 149 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); 150 151 final int N = LUX_LEVELS.length; 152 final float[] lux = { 0f, 1f }; 153 final float[] nits = { 0, PowerManager.BRIGHTNESS_ON }; 154 155 BrightnessConfiguration config = new BrightnessConfiguration.Builder() 156 .setCurve(lux, nits) 157 .build(); 158 strategy.setBrightnessConfiguration(config); 159 assertNotEquals(1.0f, strategy.getBrightness(1f), 0.01 /*tolerance*/); 160 } 161 162 @Test 163 public void testSimpleStrategyIgnoresNullConfiguration() { 164 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); 165 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); 166 167 strategy.setBrightnessConfiguration(null); 168 final int N = DISPLAY_LEVELS_BACKLIGHT.length; 169 final float expectedBrightness = 170 (float) DISPLAY_LEVELS_BACKLIGHT[N - 1] / PowerManager.BRIGHTNESS_ON; 171 assertEquals(expectedBrightness, 172 strategy.getBrightness(LUX_LEVELS[N - 1]), 0.01 /*tolerance*/); 173 } 174 175 @Test 176 public void testPhysicalStrategyMappingAtControlPoints() { 177 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, 178 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); 179 BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res); 180 assertNotNull("BrightnessMappingStrategy should not be null", physical); 181 for (int i = 0; i < LUX_LEVELS.length; i++) { 182 final float expectedLevel = DISPLAY_LEVELS_NITS[i] / DISPLAY_RANGE_NITS[1]; 183 assertEquals(expectedLevel, 184 physical.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/); 185 } 186 } 187 188 @Test 189 public void testPhysicalStrategyMappingBetweenControlPoints() { 190 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, 191 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); 192 BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res); 193 assertNotNull("BrightnessMappingStrategy should not be null", physical); 194 Spline backlightToBrightness = 195 Spline.createSpline(toFloatArray(BACKLIGHT_RANGE), DISPLAY_RANGE_NITS); 196 for (int i = 1; i < LUX_LEVELS.length; i++) { 197 final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; 198 final float backlight = physical.getBrightness(lux) * PowerManager.BRIGHTNESS_ON; 199 final float nits = backlightToBrightness.interpolate(backlight); 200 assertTrue("Desired brightness should be between adjacent control points.", 201 nits > DISPLAY_LEVELS_NITS[i - 1] && nits < DISPLAY_LEVELS_NITS[i]); 202 } 203 } 204 205 @Test 206 public void testPhysicalStrategyUsesNewConfigurations() { 207 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, 208 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); 209 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); 210 211 final float[] lux = { 0f, 1f }; 212 final float[] nits = { 213 DISPLAY_RANGE_NITS[0], 214 DISPLAY_RANGE_NITS[DISPLAY_RANGE_NITS.length - 1] 215 }; 216 217 BrightnessConfiguration config = new BrightnessConfiguration.Builder() 218 .setCurve(lux, nits) 219 .build(); 220 strategy.setBrightnessConfiguration(config); 221 assertEquals(1.0f, strategy.getBrightness(1f), 0.01 /*tolerance*/); 222 223 // Check that null returns us to the default configuration. 224 strategy.setBrightnessConfiguration(null); 225 final int N = DISPLAY_LEVELS_NITS.length; 226 final float expectedBrightness = DISPLAY_LEVELS_NITS[N - 1] / DISPLAY_RANGE_NITS[1]; 227 assertEquals(expectedBrightness, 228 strategy.getBrightness(LUX_LEVELS[N - 1]), 0.01f /*tolerance*/); 229 } 230 231 @Test 232 public void testDefaultStrategyIsPhysical() { 233 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT, 234 DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); 235 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); 236 assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy); 237 } 238 239 @Test 240 public void testNonStrictlyIncreasingLuxLevelsFails() { 241 final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length); 242 final int idx = lux.length / 2; 243 int tmp = lux[idx]; 244 lux[idx] = lux[idx+1]; 245 lux[idx+1] = tmp; 246 Resources res = createResources(lux, DISPLAY_LEVELS_NITS, 247 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); 248 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); 249 assertNull(strategy); 250 251 // And make sure we get the same result even if it's monotone but not increasing. 252 lux[idx] = lux[idx+1]; 253 res = createResources(lux, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); 254 strategy = BrightnessMappingStrategy.create(res); 255 assertNull(strategy); 256 } 257 258 @Test 259 public void testDifferentNumberOfControlPointValuesFails() { 260 //Extra lux level 261 final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length+1); 262 // Make sure it's strictly increasing so that the only failure is the differing array 263 // lengths 264 lux[lux.length - 1] = lux[lux.length - 2] + 1; 265 Resources res = createResources(lux, DISPLAY_LEVELS_NITS, 266 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); 267 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); 268 assertNull(strategy); 269 270 res = createResources(lux, DISPLAY_LEVELS_BACKLIGHT); 271 strategy = BrightnessMappingStrategy.create(res); 272 assertNull(strategy); 273 274 // Extra backlight level 275 final int[] backlight = Arrays.copyOf( 276 DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1); 277 backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1; 278 res = createResources(LUX_LEVELS, backlight); 279 strategy = BrightnessMappingStrategy.create(res); 280 assertNull(strategy); 281 282 // Extra nits level 283 final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1); 284 nits[nits.length - 1] = nits[nits.length - 2] + 1; 285 res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); 286 strategy = BrightnessMappingStrategy.create(res); 287 assertNull(strategy); 288 } 289 290 @Test 291 public void testPhysicalStrategyRequiresNitsMapping() { 292 Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, 293 DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/, BACKLIGHT_RANGE); 294 BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res); 295 assertNull(physical); 296 297 res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, 298 DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, EMPTY_INT_ARRAY /*backlightRange*/); 299 physical = BrightnessMappingStrategy.create(res); 300 assertNull(physical); 301 302 res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, 303 DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/, 304 EMPTY_INT_ARRAY /*backlightRange*/); 305 physical = BrightnessMappingStrategy.create(res); 306 assertNull(physical); 307 } 308 309 @Test 310 public void testStrategiesAdaptToUserDataPoint() { 311 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, 312 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); 313 assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res)); 314 res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); 315 assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res)); 316 } 317 318 private static void assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy strategy) { 319 // Save out all of the initial brightness data for comparison after reset. 320 float[] initialBrightnessLevels = new float[LUX_LEVELS.length]; 321 for (int i = 0; i < LUX_LEVELS.length; i++) { 322 initialBrightnessLevels[i] = strategy.getBrightness(LUX_LEVELS[i]); 323 } 324 325 // Add a data point in the middle of the curve where the user has set the brightness max 326 final int idx = LUX_LEVELS.length / 2; 327 strategy.addUserDataPoint(LUX_LEVELS[idx], 1.0f); 328 329 // Then make sure that all control points after the middle lux level are also set to max... 330 for (int i = idx; i < LUX_LEVELS.length; i++) { 331 assertEquals(strategy.getBrightness(LUX_LEVELS[idx]), 1.0, 0.01 /*tolerance*/); 332 } 333 334 // ...and that all control points before the middle lux level are strictly less than the 335 // previous one still. 336 float prevBrightness = strategy.getBrightness(LUX_LEVELS[idx]); 337 for (int i = idx - 1; i >= 0; i--) { 338 float brightness = strategy.getBrightness(LUX_LEVELS[i]); 339 assertTrue("Brightness levels must be monotonic after adapting to user data", 340 prevBrightness >= brightness); 341 prevBrightness = brightness; 342 } 343 344 // Now reset the curve and make sure we go back to the initial brightness levels recorded 345 // before adding the user data point. 346 strategy.clearUserDataPoints(); 347 for (int i = 0; i < LUX_LEVELS.length; i++) { 348 assertEquals(initialBrightnessLevels[i], strategy.getBrightness(LUX_LEVELS[i]), 349 0.01 /*tolerance*/); 350 } 351 352 // Now set the middle of the lux range to something just above the minimum. 353 float minBrightness = strategy.getBrightness(LUX_LEVELS[0]); 354 strategy.addUserDataPoint(LUX_LEVELS[idx], minBrightness + 0.01f); 355 356 // Then make sure the curve is still monotonic. 357 prevBrightness = 0f; 358 for (float lux : LUX_LEVELS) { 359 float brightness = strategy.getBrightness(lux); 360 assertTrue("Brightness levels must be monotonic after adapting to user data", 361 prevBrightness <= brightness); 362 prevBrightness = brightness; 363 } 364 365 // And that the lowest lux level still gives the absolute minimum brightness. This should 366 // be true assuming that there are more than two lux levels in the curve since we picked a 367 // brightness just barely above the minimum for the middle of the curve. 368 minBrightness = (float) MathUtils.pow(minBrightness, MAXIMUM_GAMMA); // Gamma correction. 369 assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.01 /*tolerance*/); 370 } 371 372 private static float[] toFloatArray(int[] vals) { 373 float[] newVals = new float[vals.length]; 374 for (int i = 0; i < vals.length; i++) { 375 newVals[i] = (float) vals[i]; 376 } 377 return newVals; 378 } 379 380 private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight) { 381 return createResources(luxLevels, brightnessLevelsBacklight, 382 EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/, EMPTY_FLOAT_ARRAY /*nitsRange*/, 383 EMPTY_INT_ARRAY /*backlightRange*/); 384 } 385 386 private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits, 387 float[] nitsRange, int[] backlightRange) { 388 return createResources(luxLevels, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, 389 brightnessLevelsNits, nitsRange, backlightRange); 390 } 391 392 private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight, 393 float[] brightnessLevelsNits, float[] nitsRange, int[] backlightRange) { 394 Resources mockResources = mock(Resources.class); 395 // For historical reasons, the lux levels resource implicitly defines the first point as 0, 396 // so we need to chop it off of the array the mock resource object returns. 397 int[] luxLevelsResource = Arrays.copyOfRange(luxLevels, 1, luxLevels.length); 398 when(mockResources.getIntArray(com.android.internal.R.array.config_autoBrightnessLevels)) 399 .thenReturn(luxLevelsResource); 400 401 when(mockResources.getIntArray( 402 com.android.internal.R.array.config_autoBrightnessLcdBacklightValues)) 403 .thenReturn(brightnessLevelsBacklight); 404 405 TypedArray mockBrightnessLevelNits = createFloatTypedArray(brightnessLevelsNits); 406 when(mockResources.obtainTypedArray( 407 com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)) 408 .thenReturn(mockBrightnessLevelNits); 409 410 TypedArray mockNitsRange = createFloatTypedArray(nitsRange); 411 when(mockResources.obtainTypedArray( 412 com.android.internal.R.array.config_screenBrightnessNits)) 413 .thenReturn(mockNitsRange); 414 415 when(mockResources.getIntArray( 416 com.android.internal.R.array.config_screenBrightnessBacklight)) 417 .thenReturn(backlightRange); 418 419 when(mockResources.getInteger( 420 com.android.internal.R.integer.config_screenBrightnessSettingMinimum)) 421 .thenReturn(1); 422 when(mockResources.getInteger( 423 com.android.internal.R.integer.config_screenBrightnessSettingMaximum)) 424 .thenReturn(255); 425 when(mockResources.getFraction( 426 com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 1, 1)) 427 .thenReturn(MAXIMUM_GAMMA); 428 return mockResources; 429 } 430 431 private TypedArray createFloatTypedArray(float[] vals) { 432 TypedArray mockArray = mock(TypedArray.class); 433 when(mockArray.length()).thenAnswer(invocation -> { 434 return vals.length; 435 }); 436 when(mockArray.getFloat(anyInt(), anyFloat())).thenAnswer(invocation -> { 437 final float def = (float) invocation.getArguments()[1]; 438 if (vals == null) { 439 return def; 440 } 441 int idx = (int) invocation.getArguments()[0]; 442 if (idx >= 0 && idx < vals.length) { 443 return vals[idx]; 444 } else { 445 return def; 446 } 447 }); 448 return mockArray; 449 } 450 451 // Gamma correction tests. 452 // x0 = 100 y0 = ~0.01 453 // x1 = 1000 y1 = ~0.20 454 // x2 = 2500 y2 = ~0.50 455 // x3 = 4000 y3 = ~0.80 456 // x4 = 4900 y4 = ~0.99 457 458 @Test 459 public void testGammaCorrectionLowChangeAtCenter() { 460 // If we set a user data point at (x2, y2^0.5), i.e. gamma = 0.5, it should bump the rest 461 // of the spline accordingly. 462 final int x1 = 1000; 463 final int x2 = 2500; 464 final int x3 = 4000; 465 final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1); 466 final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); 467 final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3); 468 Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, 469 DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); 470 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); 471 // Let's start with a sanity check: 472 assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */); 473 assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */); 474 assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */); 475 // OK, let's roll: 476 float gamma = 0.5f; 477 strategy.addUserDataPoint(x2, (float) MathUtils.pow(y2, gamma)); 478 assertEquals(MathUtils.pow(y1, gamma), strategy.getBrightness(x1), 0.01f /* tolerance */); 479 assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */); 480 assertEquals(MathUtils.pow(y3, gamma), strategy.getBrightness(x3), 0.01f /* tolerance */); 481 // The adjustment should be +0.63 (manual calculation). 482 assertEquals(+0.63f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); 483 } 484 485 @Test 486 public void testGammaCorrectionHighChangeAtCenter() { 487 // This time we set a user data point at (x2, y2^0.25), i.e. gamma = 0.3 (the minimum), 488 // which should bump the rest of the spline accordingly, and further correct x2 to hit 489 // y2^0.25 (not y2^0.3). 490 final int x1 = 1000; 491 final int x2 = 2500; 492 final int x3 = 4000; 493 final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1); 494 final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); 495 final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3); 496 Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, 497 DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); 498 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); 499 // Sanity check: 500 assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */); 501 assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */); 502 assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */); 503 // Let's roll: 504 float gamma = 0.25f; 505 final float minGamma = 1.0f / MAXIMUM_GAMMA; 506 strategy.addUserDataPoint(x2, (float) MathUtils.pow(y2, gamma)); 507 assertEquals(MathUtils.pow(y1, minGamma), strategy.getBrightness(x1), 508 0.01f /* tolerance */); 509 assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 510 0.01f /* tolerance */); 511 assertEquals(MathUtils.pow(y3, minGamma), strategy.getBrightness(x3), 512 0.01f /* tolerance */); 513 // The adjustment should be +1.0 (maximum adjustment). 514 assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); 515 } 516 517 @Test 518 public void testGammaCorrectionExtremeChangeAtCenter() { 519 // Extreme changes (e.g. setting brightness to 0.0 or 1.0) can't be gamma corrected, so we 520 // just make sure the adjustment reflects the change. 521 Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, 522 DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); 523 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); 524 assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); 525 strategy.addUserDataPoint(2500, 1.0f); 526 assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); 527 strategy.addUserDataPoint(2500, 0.0f); 528 assertEquals(-1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); 529 } 530 531 @Test 532 public void testGammaCorrectionChangeAtEdges() { 533 // The algorithm behaves differently at the edges, because gamma correction there tends to 534 // be extreme. If we add a user data point at (x0, y0+0.3), the adjustment should be 535 // 0.3*2 = 0.6, resulting in a gamma of 3**-0.6 = ~0.52. 536 final int x0 = 100; 537 final int x2 = 2500; 538 final int x4 = 4900; 539 final float y0 = GAMMA_CORRECTION_SPLINE.interpolate(x0); 540 final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); 541 final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4); 542 Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, 543 DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); 544 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); 545 // Sanity, as per tradition: 546 assertEquals(y0, strategy.getBrightness(x0), 0.01f /* tolerance */); 547 assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */); 548 assertEquals(y4, strategy.getBrightness(x4), 0.01f /* tolerance */); 549 // Rollin': 550 float increase = 0.3f; 551 float adjustment = increase * 2; 552 float gamma = (float) MathUtils.pow(MAXIMUM_GAMMA, -adjustment); 553 strategy.addUserDataPoint(x0, y0 + increase); 554 assertEquals(y0 + increase, strategy.getBrightness(x0), 0.01f /* tolerance */); 555 assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */); 556 assertEquals(MathUtils.pow(y4, gamma), strategy.getBrightness(x4), 0.01f /* tolerance */); 557 assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); 558 // Similarly, if we set a user data point at (x4, 1.0), the adjustment should be (1-y4)*2. 559 increase = 1.0f - y4; 560 adjustment = increase * 2; 561 gamma = (float) MathUtils.pow(MAXIMUM_GAMMA, -adjustment); 562 strategy.addUserDataPoint(x4, 1.0f); 563 assertEquals(MathUtils.pow(y0, gamma), strategy.getBrightness(x0), 0.01f /* tolerance */); 564 assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */); 565 assertEquals(1.0f, strategy.getBrightness(x4), 0.01f /* tolerance */); 566 assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); 567 } 568 } 569