Skip to content

Commit 7c964e7

Browse files
Jeff BrownAndroid (Google) Code Review
authored andcommitted
Merge changes I39804ee6,I6a5a7ea2 into jb-mr1-dev
* changes: Use spline interpolation for auto-brightness. Add FloatMath.hypot.
2 parents e663975 + 1a30b55 commit 7c964e7

File tree

6 files changed

+235
-41
lines changed

6 files changed

+235
-41
lines changed

api/current.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22876,6 +22876,7 @@ package android.util {
2287622876
method public static float cos(float);
2287722877
method public static float exp(float);
2287822878
method public static float floor(float);
22879+
method public static float hypot(float, float);
2287922880
method public static float sin(float);
2288022881
method public static float sqrt(float);
2288122882
}

core/java/android/util/FloatMath.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,14 @@ private FloatMath() {}
8080
* @return the exponential of value
8181
*/
8282
public static native float exp(float value);
83+
84+
/**
85+
* Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
86+
* {@code y}</i><sup>{@code 2}</sup>{@code )}.
87+
*
88+
* @param x a float number
89+
* @param y a float number
90+
* @return the hypotenuse
91+
*/
92+
public static native float hypot(float x, float y);
8393
}

core/java/android/util/Spline.java

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright (C) 2012 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 android.util;
18+
19+
/**
20+
* Performs spline interpolation given a set of control points.
21+
* @hide
22+
*/
23+
public final class Spline {
24+
private final float[] mX;
25+
private final float[] mY;
26+
private final float[] mM;
27+
28+
private Spline(float[] x, float[] y, float[] m) {
29+
mX = x;
30+
mY = y;
31+
mM = m;
32+
}
33+
34+
/**
35+
* Creates a monotone cubic spline from a given set of control points.
36+
*
37+
* The spline is guaranteed to pass through each control point exactly.
38+
* Moreover, assuming the control points are monotonic (Y is non-decreasing or
39+
* non-increasing) then the interpolated values will also be monotonic.
40+
*
41+
* This function uses the Fritsch-Carlson method for computing the spline parameters.
42+
* http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
43+
*
44+
* @param x The X component of the control points, strictly increasing.
45+
* @param y The Y component of the control points, monotonic.
46+
* @return
47+
*
48+
* @throws IllegalArgumentException if the X or Y arrays are null, have
49+
* different lengths or have fewer than 2 values.
50+
* @throws IllegalArgumentException if the control points are not monotonic.
51+
*/
52+
public static Spline createMonotoneCubicSpline(float[] x, float[] y) {
53+
if (x == null || y == null || x.length != y.length || x.length < 2) {
54+
throw new IllegalArgumentException("There must be at least two control "
55+
+ "points and the arrays must be of equal length.");
56+
}
57+
58+
final int n = x.length;
59+
float[] d = new float[n - 1]; // could optimize this out
60+
float[] m = new float[n];
61+
62+
// Compute slopes of secant lines between successive points.
63+
for (int i = 0; i < n - 1; i++) {
64+
float h = x[i + 1] - x[i];
65+
if (h <= 0f) {
66+
throw new IllegalArgumentException("The control points must all "
67+
+ "have strictly increasing X values.");
68+
}
69+
d[i] = (y[i + 1] - y[i]) / h;
70+
}
71+
72+
// Initialize the tangents as the average of the secants.
73+
m[0] = d[0];
74+
for (int i = 1; i < n - 1; i++) {
75+
m[i] = (d[i - 1] + d[i]) * 0.5f;
76+
}
77+
m[n - 1] = d[n - 2];
78+
79+
// Update the tangents to preserve monotonicity.
80+
for (int i = 0; i < n - 1; i++) {
81+
if (d[i] == 0f) { // successive Y values are equal
82+
m[i] = 0f;
83+
m[i + 1] = 0f;
84+
} else {
85+
float a = m[i] / d[i];
86+
float b = m[i + 1] / d[i];
87+
if (a < 0f || b < 0f) {
88+
throw new IllegalArgumentException("The control points must have "
89+
+ "monotonic Y values.");
90+
}
91+
float h = FloatMath.hypot(a, b);
92+
if (h > 9f) {
93+
float t = 3f / h;
94+
m[i] = t * a * d[i];
95+
m[i + 1] = t * b * d[i];
96+
}
97+
}
98+
}
99+
return new Spline(x, y, m);
100+
}
101+
102+
/**
103+
* Interpolates the value of Y = f(X) for given X.
104+
* Clamps X to the domain of the spline.
105+
*
106+
* @param x The X value.
107+
* @return The interpolated Y = f(X) value.
108+
*/
109+
public float interpolate(float x) {
110+
// Handle the boundary cases.
111+
final int n = mX.length;
112+
if (Float.isNaN(x)) {
113+
return x;
114+
}
115+
if (x <= mX[0]) {
116+
return mY[0];
117+
}
118+
if (x >= mX[n - 1]) {
119+
return mY[n - 1];
120+
}
121+
122+
// Find the index 'i' of the last point with smaller X.
123+
// We know this will be within the spline due to the boundary tests.
124+
int i = 0;
125+
while (x >= mX[i + 1]) {
126+
i += 1;
127+
if (x == mX[i]) {
128+
return mY[i];
129+
}
130+
}
131+
132+
// Perform cubic Hermite spline interpolation.
133+
float h = mX[i + 1] - mX[i];
134+
float t = (x - mX[i]) / h;
135+
return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
136+
+ (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
137+
}
138+
139+
// For debugging.
140+
@Override
141+
public String toString() {
142+
StringBuilder str = new StringBuilder();
143+
final int n = mX.length;
144+
str.append("[");
145+
for (int i = 0; i < n; i++) {
146+
if (i != 0) {
147+
str.append(", ");
148+
}
149+
str.append("(").append(mX[i]);
150+
str.append(", ").append(mY[i]);
151+
str.append(": ").append(mM[i]).append(")");
152+
}
153+
str.append("]");
154+
return str.toString();
155+
}
156+
}

core/jni/android_util_FloatMath.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ class MathUtilsGlue {
2929
static float ExpF(JNIEnv* env, jobject clazz, float x) {
3030
return expf(x);
3131
}
32+
33+
static float HypotF(JNIEnv* env, jobject clazz, float x, float y) {
34+
return hypotf(x, y);
35+
}
3236
};
3337

3438
static JNINativeMethod gMathUtilsMethods[] = {
@@ -38,6 +42,7 @@ static JNINativeMethod gMathUtilsMethods[] = {
3842
{"cos", "(F)F", (void*) MathUtilsGlue::CosF},
3943
{"sqrt", "(F)F", (void*) MathUtilsGlue::SqrtF},
4044
{"exp", "(F)F", (void*) MathUtilsGlue::ExpF},
45+
{"hypot", "(FF)F", (void*) MathUtilsGlue::HypotF},
4146
};
4247

4348
int register_android_util_FloatMath(JNIEnv* env)

core/res/res/values/config.xml

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -533,13 +533,22 @@
533533
<integer name="config_longPressOnHomeBehavior">2</integer>
534534

535535
<!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
536-
The N entries of this array define N + 1 zones as follows:
536+
The N entries of this array define N + 1 control points as follows:
537537
538-
Zone 0: 0 <= LUX < array[0]
539-
Zone 1: array[0] <= LUX < array[1]
538+
Point 1: LUX <= 0 (implicit)
539+
Point 2: 0 < level[1] == LUX < level[2]
540540
...
541-
Zone N: array[N - 1] <= LUX < array[N]
542-
Zone N + 1: array[N] <= LUX < infinity
541+
Point N: level[N - 1] == LUX < level[N]
542+
Point N + 1: level[N] <= LUX < infinity
543+
544+
The control points must be strictly increasing. Each control point
545+
corresponds to an entry in the brightness backlight values arrays.
546+
For example, if LUX == level[1] (first element of the levels array)
547+
then the brightness will be determined by value[1] (first element
548+
of the brightness values array).
549+
550+
Spline interpolation is used to determine the auto-brightness
551+
backlight values for LUX levels between these control points.
543552
544553
Must be overridden in platform specific overlays -->
545554
<integer-array name="config_autoBrightnessLevels">
@@ -552,20 +561,23 @@
552561
<!-- Array of output values for LCD backlight corresponding to the LUX values
553562
in the config_autoBrightnessLevels array. This array should have size one greater
554563
than the size of the config_autoBrightnessLevels array.
564+
The brightness values must be between 0 and 255 and be non-decreasing.
555565
This must be overridden in platform specific overlays -->
556566
<integer-array name="config_autoBrightnessLcdBacklightValues">
557567
</integer-array>
558568

559569
<!-- Array of output values for button backlight corresponding to the LUX values
560570
in the config_autoBrightnessLevels array. This array should have size one greater
561571
than the size of the config_autoBrightnessLevels array.
572+
The brightness values must be between 0 and 255 and be non-decreasing.
562573
This must be overridden in platform specific overlays -->
563574
<integer-array name="config_autoBrightnessButtonBacklightValues">
564575
</integer-array>
565576

566577
<!-- Array of output values for keyboard backlight corresponding to the LUX values
567578
in the config_autoBrightnessLevels array. This array should have size one greater
568579
than the size of the config_autoBrightnessLevels array.
580+
The brightness values must be between 0 and 255 and be non-decreasing.
569581
This must be overridden in platform specific overlays -->
570582
<integer-array name="config_autoBrightnessKeyboardBacklightValues">
571583
</integer-array>

services/java/com/android/server/power/DisplayPowerController.java

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@
3333
import android.os.Message;
3434
import android.os.SystemClock;
3535
import android.util.Slog;
36+
import android.util.Spline;
3637
import android.util.TimeUtils;
3738

3839
import java.io.PrintWriter;
3940
import java.io.StringWriter;
40-
import java.util.Arrays;
4141
import java.util.concurrent.CountDownLatch;
4242
import java.util.concurrent.Executor;
4343

@@ -98,9 +98,9 @@ final class DisplayPowerController {
9898
// average of light samples. Different constants are used
9999
// to calculate the average light level when adapting to brighter or
100100
// dimmer environments.
101-
// This parameter only controls the averaging of light samples.
102-
private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 1500;
103-
private static final long DIMMING_LIGHT_TIME_CONSTANT = 3000;
101+
// This parameter only controls the filtering of light samples.
102+
private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 500;
103+
private static final long DIMMING_LIGHT_TIME_CONSTANT = 2000;
104104

105105
// Stability requirements in milliseconds for accepting a new brightness
106106
// level. This is used for debouncing the light sensor. Different constants
@@ -144,8 +144,7 @@ final class DisplayPowerController {
144144

145145
// Auto-brightness.
146146
private boolean mUseSoftwareAutoBrightnessConfig;
147-
private int[] mAutoBrightnessLevelsConfig;
148-
private int[] mAutoBrightnessLcdBacklightValuesConfig;
147+
private Spline mScreenAutoBrightnessSpline;
149148

150149
// Amount of time to delay auto-brightness after screen on while waiting for
151150
// the light sensor to warm-up in milliseconds.
@@ -289,17 +288,18 @@ public DisplayPowerController(Looper looper, Context context, Notifier notifier,
289288
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
290289
com.android.internal.R.bool.config_automatic_brightness_available);
291290
if (mUseSoftwareAutoBrightnessConfig) {
292-
mAutoBrightnessLevelsConfig = resources.getIntArray(
291+
int[] lux = resources.getIntArray(
293292
com.android.internal.R.array.config_autoBrightnessLevels);
294-
mAutoBrightnessLcdBacklightValuesConfig = resources.getIntArray(
293+
int[] screenBrightness = resources.getIntArray(
295294
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
296-
if (mAutoBrightnessLcdBacklightValuesConfig.length
297-
!= mAutoBrightnessLevelsConfig.length + 1) {
295+
296+
mScreenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
297+
if (mScreenAutoBrightnessSpline == null) {
298298
Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues "
299-
+ "(size " + mAutoBrightnessLcdBacklightValuesConfig.length + ") "
300-
+ "should have exactly one more entry than "
301-
+ "config_autoBrightnessLevels (size "
302-
+ mAutoBrightnessLevelsConfig.length + "). "
299+
+ "(size " + screenBrightness.length + ") "
300+
+ "must be monotic and have exactly one more entry than "
301+
+ "config_autoBrightnessLevels (size " + lux.length + ") "
302+
+ "which must be strictly increasing. "
303303
+ "Auto-brightness will be disabled.");
304304
mUseSoftwareAutoBrightnessConfig = false;
305305
}
@@ -322,6 +322,31 @@ public DisplayPowerController(Looper looper, Context context, Notifier notifier,
322322
}
323323
}
324324

325+
private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
326+
try {
327+
final int n = brightness.length;
328+
float[] x = new float[n];
329+
float[] y = new float[n];
330+
y[0] = brightness[0];
331+
for (int i = 1; i < n; i++) {
332+
x[i] = lux[i - 1];
333+
y[i] = brightness[i];
334+
}
335+
336+
Spline spline = Spline.createMonotoneCubicSpline(x, y);
337+
if (false) {
338+
Slog.d(TAG, "Auto-brightness spline: " + spline);
339+
for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
340+
Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v)));
341+
}
342+
}
343+
return spline;
344+
} catch (IllegalArgumentException ex) {
345+
Slog.e(TAG, "Could not create auto-brightness spline.", ex);
346+
return null;
347+
}
348+
}
349+
325350
/**
326351
* Returns true if the proximity sensor screen-off function is available.
327352
*/
@@ -768,13 +793,13 @@ private void updateAutoBrightness(boolean sendUpdate) {
768793
return;
769794
}
770795

771-
final int newScreenAutoBrightness = mapLuxToBrightness(mLightMeasurement,
772-
mAutoBrightnessLevelsConfig,
773-
mAutoBrightnessLcdBacklightValuesConfig);
796+
final int newScreenAutoBrightness = interpolateBrightness(
797+
mScreenAutoBrightnessSpline, mLightMeasurement);
774798
if (mScreenAutoBrightness != newScreenAutoBrightness) {
775799
if (DEBUG) {
776800
Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
777-
+ mScreenAutoBrightness);
801+
+ mScreenAutoBrightness + "newScreenAutoBrightness="
802+
+ newScreenAutoBrightness);
778803
}
779804

780805
mScreenAutoBrightness = newScreenAutoBrightness;
@@ -784,20 +809,8 @@ private void updateAutoBrightness(boolean sendUpdate) {
784809
}
785810
}
786811

787-
/**
788-
* Maps a light sensor measurement in lux to a brightness value given
789-
* a table of lux breakpoint values and a table of brightnesses that
790-
* is one element larger.
791-
*/
792-
private static int mapLuxToBrightness(float lux,
793-
int[] fromLux, int[] toBrightness) {
794-
// TODO implement interpolation and possibly range expansion
795-
int level = 0;
796-
final int count = fromLux.length;
797-
while (level < count && lux >= fromLux[level]) {
798-
level += 1;
799-
}
800-
return toBrightness[level];
812+
private static int interpolateBrightness(Spline spline, float lux) {
813+
return Math.min(255, Math.max(0, (int)Math.round(spline.interpolate(lux))));
801814
}
802815

803816
private void sendOnStateChanged() {
@@ -839,10 +852,7 @@ public void dump(PrintWriter pw) {
839852
pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
840853
pw.println(" mUseSoftwareAutoBrightnessConfig="
841854
+ mUseSoftwareAutoBrightnessConfig);
842-
pw.println(" mAutoBrightnessLevelsConfig="
843-
+ Arrays.toString(mAutoBrightnessLevelsConfig));
844-
pw.println(" mAutoBrightnessLcdBacklightValuesConfig="
845-
+ Arrays.toString(mAutoBrightnessLcdBacklightValuesConfig));
855+
pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
846856
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
847857

848858
if (Looper.myLooper() == mHandler.getLooper()) {

0 commit comments

Comments
 (0)