Merge changes I39804ee6,I6a5a7ea2 into jb-mr1-dev
* changes: Use spline interpolation for auto-brightness. Add FloatMath.hypot.
This commit is contained in:
@@ -22876,6 +22876,7 @@ package android.util {
|
||||
method public static float cos(float);
|
||||
method public static float exp(float);
|
||||
method public static float floor(float);
|
||||
method public static float hypot(float, float);
|
||||
method public static float sin(float);
|
||||
method public static float sqrt(float);
|
||||
}
|
||||
|
||||
@@ -80,4 +80,14 @@ public class FloatMath {
|
||||
* @return the exponential of value
|
||||
*/
|
||||
public static native float exp(float value);
|
||||
|
||||
/**
|
||||
* Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
|
||||
* {@code y}</i><sup>{@code 2}</sup>{@code )}.
|
||||
*
|
||||
* @param x a float number
|
||||
* @param y a float number
|
||||
* @return the hypotenuse
|
||||
*/
|
||||
public static native float hypot(float x, float y);
|
||||
}
|
||||
|
||||
156
core/java/android/util/Spline.java
Normal file
156
core/java/android/util/Spline.java
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.util;
|
||||
|
||||
/**
|
||||
* Performs spline interpolation given a set of control points.
|
||||
* @hide
|
||||
*/
|
||||
public final class Spline {
|
||||
private final float[] mX;
|
||||
private final float[] mY;
|
||||
private final float[] mM;
|
||||
|
||||
private Spline(float[] x, float[] y, float[] m) {
|
||||
mX = x;
|
||||
mY = y;
|
||||
mM = m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a monotone cubic spline from a given set of control points.
|
||||
*
|
||||
* The spline is guaranteed to pass through each control point exactly.
|
||||
* Moreover, assuming the control points are monotonic (Y is non-decreasing or
|
||||
* non-increasing) then the interpolated values will also be monotonic.
|
||||
*
|
||||
* This function uses the Fritsch-Carlson method for computing the spline parameters.
|
||||
* http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
|
||||
*
|
||||
* @param x The X component of the control points, strictly increasing.
|
||||
* @param y The Y component of the control points, monotonic.
|
||||
* @return
|
||||
*
|
||||
* @throws IllegalArgumentException if the X or Y arrays are null, have
|
||||
* different lengths or have fewer than 2 values.
|
||||
* @throws IllegalArgumentException if the control points are not monotonic.
|
||||
*/
|
||||
public static Spline createMonotoneCubicSpline(float[] x, float[] y) {
|
||||
if (x == null || y == null || x.length != y.length || x.length < 2) {
|
||||
throw new IllegalArgumentException("There must be at least two control "
|
||||
+ "points and the arrays must be of equal length.");
|
||||
}
|
||||
|
||||
final int n = x.length;
|
||||
float[] d = new float[n - 1]; // could optimize this out
|
||||
float[] m = new float[n];
|
||||
|
||||
// Compute slopes of secant lines between successive points.
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
float h = x[i + 1] - x[i];
|
||||
if (h <= 0f) {
|
||||
throw new IllegalArgumentException("The control points must all "
|
||||
+ "have strictly increasing X values.");
|
||||
}
|
||||
d[i] = (y[i + 1] - y[i]) / h;
|
||||
}
|
||||
|
||||
// Initialize the tangents as the average of the secants.
|
||||
m[0] = d[0];
|
||||
for (int i = 1; i < n - 1; i++) {
|
||||
m[i] = (d[i - 1] + d[i]) * 0.5f;
|
||||
}
|
||||
m[n - 1] = d[n - 2];
|
||||
|
||||
// Update the tangents to preserve monotonicity.
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
if (d[i] == 0f) { // successive Y values are equal
|
||||
m[i] = 0f;
|
||||
m[i + 1] = 0f;
|
||||
} else {
|
||||
float a = m[i] / d[i];
|
||||
float b = m[i + 1] / d[i];
|
||||
if (a < 0f || b < 0f) {
|
||||
throw new IllegalArgumentException("The control points must have "
|
||||
+ "monotonic Y values.");
|
||||
}
|
||||
float h = FloatMath.hypot(a, b);
|
||||
if (h > 9f) {
|
||||
float t = 3f / h;
|
||||
m[i] = t * a * d[i];
|
||||
m[i + 1] = t * b * d[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Spline(x, y, m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolates the value of Y = f(X) for given X.
|
||||
* Clamps X to the domain of the spline.
|
||||
*
|
||||
* @param x The X value.
|
||||
* @return The interpolated Y = f(X) value.
|
||||
*/
|
||||
public float interpolate(float x) {
|
||||
// Handle the boundary cases.
|
||||
final int n = mX.length;
|
||||
if (Float.isNaN(x)) {
|
||||
return x;
|
||||
}
|
||||
if (x <= mX[0]) {
|
||||
return mY[0];
|
||||
}
|
||||
if (x >= mX[n - 1]) {
|
||||
return mY[n - 1];
|
||||
}
|
||||
|
||||
// Find the index 'i' of the last point with smaller X.
|
||||
// We know this will be within the spline due to the boundary tests.
|
||||
int i = 0;
|
||||
while (x >= mX[i + 1]) {
|
||||
i += 1;
|
||||
if (x == mX[i]) {
|
||||
return mY[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Perform cubic Hermite spline interpolation.
|
||||
float h = mX[i + 1] - mX[i];
|
||||
float t = (x - mX[i]) / h;
|
||||
return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
|
||||
+ (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
|
||||
}
|
||||
|
||||
// For debugging.
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
final int n = mX.length;
|
||||
str.append("[");
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (i != 0) {
|
||||
str.append(", ");
|
||||
}
|
||||
str.append("(").append(mX[i]);
|
||||
str.append(", ").append(mY[i]);
|
||||
str.append(": ").append(mM[i]).append(")");
|
||||
}
|
||||
str.append("]");
|
||||
return str.toString();
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,10 @@ public:
|
||||
static float ExpF(JNIEnv* env, jobject clazz, float x) {
|
||||
return expf(x);
|
||||
}
|
||||
|
||||
static float HypotF(JNIEnv* env, jobject clazz, float x, float y) {
|
||||
return hypotf(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
static JNINativeMethod gMathUtilsMethods[] = {
|
||||
@@ -38,6 +42,7 @@ static JNINativeMethod gMathUtilsMethods[] = {
|
||||
{"cos", "(F)F", (void*) MathUtilsGlue::CosF},
|
||||
{"sqrt", "(F)F", (void*) MathUtilsGlue::SqrtF},
|
||||
{"exp", "(F)F", (void*) MathUtilsGlue::ExpF},
|
||||
{"hypot", "(FF)F", (void*) MathUtilsGlue::HypotF},
|
||||
};
|
||||
|
||||
int register_android_util_FloatMath(JNIEnv* env)
|
||||
|
||||
@@ -533,13 +533,22 @@
|
||||
<integer name="config_longPressOnHomeBehavior">2</integer>
|
||||
|
||||
<!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
|
||||
The N entries of this array define N + 1 zones as follows:
|
||||
The N entries of this array define N + 1 control points as follows:
|
||||
|
||||
Zone 0: 0 <= LUX < array[0]
|
||||
Zone 1: array[0] <= LUX < array[1]
|
||||
Point 1: LUX <= 0 (implicit)
|
||||
Point 2: 0 < level[1] == LUX < level[2]
|
||||
...
|
||||
Zone N: array[N - 1] <= LUX < array[N]
|
||||
Zone N + 1: array[N] <= LUX < infinity
|
||||
Point N: level[N - 1] == LUX < level[N]
|
||||
Point N + 1: level[N] <= LUX < infinity
|
||||
|
||||
The control points must be strictly increasing. Each control point
|
||||
corresponds to an entry in the brightness backlight values arrays.
|
||||
For example, if LUX == level[1] (first element of the levels array)
|
||||
then the brightness will be determined by value[1] (first element
|
||||
of the brightness values array).
|
||||
|
||||
Spline interpolation is used to determine the auto-brightness
|
||||
backlight values for LUX levels between these control points.
|
||||
|
||||
Must be overridden in platform specific overlays -->
|
||||
<integer-array name="config_autoBrightnessLevels">
|
||||
@@ -552,6 +561,7 @@
|
||||
<!-- Array of output values for LCD backlight corresponding to the LUX values
|
||||
in the config_autoBrightnessLevels array. This array should have size one greater
|
||||
than the size of the config_autoBrightnessLevels array.
|
||||
The brightness values must be between 0 and 255 and be non-decreasing.
|
||||
This must be overridden in platform specific overlays -->
|
||||
<integer-array name="config_autoBrightnessLcdBacklightValues">
|
||||
</integer-array>
|
||||
@@ -559,6 +569,7 @@
|
||||
<!-- Array of output values for button backlight corresponding to the LUX values
|
||||
in the config_autoBrightnessLevels array. This array should have size one greater
|
||||
than the size of the config_autoBrightnessLevels array.
|
||||
The brightness values must be between 0 and 255 and be non-decreasing.
|
||||
This must be overridden in platform specific overlays -->
|
||||
<integer-array name="config_autoBrightnessButtonBacklightValues">
|
||||
</integer-array>
|
||||
@@ -566,6 +577,7 @@
|
||||
<!-- Array of output values for keyboard backlight corresponding to the LUX values
|
||||
in the config_autoBrightnessLevels array. This array should have size one greater
|
||||
than the size of the config_autoBrightnessLevels array.
|
||||
The brightness values must be between 0 and 255 and be non-decreasing.
|
||||
This must be overridden in platform specific overlays -->
|
||||
<integer-array name="config_autoBrightnessKeyboardBacklightValues">
|
||||
</integer-array>
|
||||
|
||||
@@ -33,11 +33,11 @@ import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Slog;
|
||||
import android.util.Spline;
|
||||
import android.util.TimeUtils;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -98,9 +98,9 @@ final class DisplayPowerController {
|
||||
// average of light samples. Different constants are used
|
||||
// to calculate the average light level when adapting to brighter or
|
||||
// dimmer environments.
|
||||
// This parameter only controls the averaging of light samples.
|
||||
private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 1500;
|
||||
private static final long DIMMING_LIGHT_TIME_CONSTANT = 3000;
|
||||
// This parameter only controls the filtering of light samples.
|
||||
private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 500;
|
||||
private static final long DIMMING_LIGHT_TIME_CONSTANT = 2000;
|
||||
|
||||
// Stability requirements in milliseconds for accepting a new brightness
|
||||
// level. This is used for debouncing the light sensor. Different constants
|
||||
@@ -144,8 +144,7 @@ final class DisplayPowerController {
|
||||
|
||||
// Auto-brightness.
|
||||
private boolean mUseSoftwareAutoBrightnessConfig;
|
||||
private int[] mAutoBrightnessLevelsConfig;
|
||||
private int[] mAutoBrightnessLcdBacklightValuesConfig;
|
||||
private Spline mScreenAutoBrightnessSpline;
|
||||
|
||||
// Amount of time to delay auto-brightness after screen on while waiting for
|
||||
// the light sensor to warm-up in milliseconds.
|
||||
@@ -289,17 +288,18 @@ final class DisplayPowerController {
|
||||
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
|
||||
com.android.internal.R.bool.config_automatic_brightness_available);
|
||||
if (mUseSoftwareAutoBrightnessConfig) {
|
||||
mAutoBrightnessLevelsConfig = resources.getIntArray(
|
||||
int[] lux = resources.getIntArray(
|
||||
com.android.internal.R.array.config_autoBrightnessLevels);
|
||||
mAutoBrightnessLcdBacklightValuesConfig = resources.getIntArray(
|
||||
int[] screenBrightness = resources.getIntArray(
|
||||
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
|
||||
if (mAutoBrightnessLcdBacklightValuesConfig.length
|
||||
!= mAutoBrightnessLevelsConfig.length + 1) {
|
||||
|
||||
mScreenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
|
||||
if (mScreenAutoBrightnessSpline == null) {
|
||||
Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues "
|
||||
+ "(size " + mAutoBrightnessLcdBacklightValuesConfig.length + ") "
|
||||
+ "should have exactly one more entry than "
|
||||
+ "config_autoBrightnessLevels (size "
|
||||
+ mAutoBrightnessLevelsConfig.length + "). "
|
||||
+ "(size " + screenBrightness.length + ") "
|
||||
+ "must be monotic and have exactly one more entry than "
|
||||
+ "config_autoBrightnessLevels (size " + lux.length + ") "
|
||||
+ "which must be strictly increasing. "
|
||||
+ "Auto-brightness will be disabled.");
|
||||
mUseSoftwareAutoBrightnessConfig = false;
|
||||
}
|
||||
@@ -322,6 +322,31 @@ final class DisplayPowerController {
|
||||
}
|
||||
}
|
||||
|
||||
private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
|
||||
try {
|
||||
final int n = brightness.length;
|
||||
float[] x = new float[n];
|
||||
float[] y = new float[n];
|
||||
y[0] = brightness[0];
|
||||
for (int i = 1; i < n; i++) {
|
||||
x[i] = lux[i - 1];
|
||||
y[i] = brightness[i];
|
||||
}
|
||||
|
||||
Spline spline = Spline.createMonotoneCubicSpline(x, y);
|
||||
if (false) {
|
||||
Slog.d(TAG, "Auto-brightness spline: " + spline);
|
||||
for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
|
||||
Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v)));
|
||||
}
|
||||
}
|
||||
return spline;
|
||||
} catch (IllegalArgumentException ex) {
|
||||
Slog.e(TAG, "Could not create auto-brightness spline.", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the proximity sensor screen-off function is available.
|
||||
*/
|
||||
@@ -768,13 +793,13 @@ final class DisplayPowerController {
|
||||
return;
|
||||
}
|
||||
|
||||
final int newScreenAutoBrightness = mapLuxToBrightness(mLightMeasurement,
|
||||
mAutoBrightnessLevelsConfig,
|
||||
mAutoBrightnessLcdBacklightValuesConfig);
|
||||
final int newScreenAutoBrightness = interpolateBrightness(
|
||||
mScreenAutoBrightnessSpline, mLightMeasurement);
|
||||
if (mScreenAutoBrightness != newScreenAutoBrightness) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
|
||||
+ mScreenAutoBrightness);
|
||||
+ mScreenAutoBrightness + "newScreenAutoBrightness="
|
||||
+ newScreenAutoBrightness);
|
||||
}
|
||||
|
||||
mScreenAutoBrightness = newScreenAutoBrightness;
|
||||
@@ -784,20 +809,8 @@ final class DisplayPowerController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a light sensor measurement in lux to a brightness value given
|
||||
* a table of lux breakpoint values and a table of brightnesses that
|
||||
* is one element larger.
|
||||
*/
|
||||
private static int mapLuxToBrightness(float lux,
|
||||
int[] fromLux, int[] toBrightness) {
|
||||
// TODO implement interpolation and possibly range expansion
|
||||
int level = 0;
|
||||
final int count = fromLux.length;
|
||||
while (level < count && lux >= fromLux[level]) {
|
||||
level += 1;
|
||||
}
|
||||
return toBrightness[level];
|
||||
private static int interpolateBrightness(Spline spline, float lux) {
|
||||
return Math.min(255, Math.max(0, (int)Math.round(spline.interpolate(lux))));
|
||||
}
|
||||
|
||||
private void sendOnStateChanged() {
|
||||
@@ -839,10 +852,7 @@ final class DisplayPowerController {
|
||||
pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
|
||||
pw.println(" mUseSoftwareAutoBrightnessConfig="
|
||||
+ mUseSoftwareAutoBrightnessConfig);
|
||||
pw.println(" mAutoBrightnessLevelsConfig="
|
||||
+ Arrays.toString(mAutoBrightnessLevelsConfig));
|
||||
pw.println(" mAutoBrightnessLcdBacklightValuesConfig="
|
||||
+ Arrays.toString(mAutoBrightnessLcdBacklightValuesConfig));
|
||||
pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
|
||||
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
|
||||
|
||||
if (Looper.myLooper() == mHandler.getLooper()) {
|
||||
|
||||
Reference in New Issue
Block a user