Merge changes I39804ee6,I6a5a7ea2 into jb-mr1-dev

* changes:
  Use spline interpolation for auto-brightness.
  Add FloatMath.hypot.
This commit is contained in:
Jeff Brown
2012-08-16 02:06:39 -07:00
committed by Android (Google) Code Review
6 changed files with 235 additions and 41 deletions

View File

@@ -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);
}

View File

@@ -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);
}

View 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();
}
}

View File

@@ -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)

View File

@@ -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>

View File

@@ -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()) {