Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c0a6c03c authored by Michael Wright's avatar Michael Wright Committed by Android (Google) Code Review
Browse files

Merge "Use a linear spline if there's a non-monotonic brightness curve" into lmp-dev

parents 8c94d9fb 3e9a1343
Loading
Loading
Loading
Loading
+233 −92
Original line number Diff line number Diff line
@@ -20,15 +20,34 @@ 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;
public abstract class Spline {

    private Spline(float[] x, float[] y, float[] m) {
        mX = x;
        mY = y;
        mM = 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 abstract float interpolate(float x);

    /**
     * Creates an appropriate spline based on the properties of the control points.
     *
     * If the control points are monotonic then the resulting spline will preserve that and
     * otherwise optimize for error bounds.
     */
    public static Spline createSpline(float[] x, float[] y) {
        if (!isStrictlyIncreasing(x)) {
            throw new IllegalArgumentException("The control points must all have strictly "
                    + "increasing X values.");
        }

        if (isMonotonic(y)) {
            return createMonotoneCubicSpline(x, y);
        } else {
            return createLinearSpline(x, y);
        }
    }

    /**
@@ -50,6 +69,64 @@ public final class Spline {
     * @throws IllegalArgumentException if the control points are not monotonic.
     */
    public static Spline createMonotoneCubicSpline(float[] x, float[] y) {
        return new MonotoneCubicSpline(x, y);
    }

    /**
     * Creates a linear spline from a given set of control points.
     *
     * Like a monotone cubic spline, the interpolated curve will be monotonic if the control points
     * are monotonic.
     *
     * @param x The X component of the control points, strictly increasing.
     * @param y The Y component of the control points.
     * @return
     *
     * @throws IllegalArgumentException if the X or Y arrays are null, have
     * different lengths or have fewer than 2 values.
     * @throws IllegalArgumentException if the X components of the control points are not strictly
     * increasing.
     */
    public static Spline createLinearSpline(float[] x, float[] y) {
        return new LinearSpline(x, y);
    }

    private static boolean isStrictlyIncreasing(float[] x) {
        if (x == null || x.length < 2) {
            throw new IllegalArgumentException("There must be at least two control points.");
        }
        float prev = x[0];
        for (int i = 1; i < x.length; i++) {
            float curr = x[i];
            if (curr <= prev) {
                return false;
            }
            prev = curr;
        }
        return true;
    }

    private static boolean isMonotonic(float[] x) {
        if (x == null || x.length < 2) {
            throw new IllegalArgumentException("There must be at least two control points.");
        }
        float prev = x[0];
        for (int i = 1; i < x.length; i++) {
            float curr = x[i];
            if (curr < prev) {
                return false;
            }
            prev = curr;
        }
        return true;
    }

    public static class MonotoneCubicSpline extends Spline {
        private float[] mX;
        private float[] mY;
        private float[] mM;

        public MonotoneCubicSpline(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.");
@@ -96,16 +173,13 @@ public final class Spline {
                    }
                }
            }
        return new Spline(x, y, m);

            mX = x;
            mY = y;
            mM = 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.
     */
        @Override
        public float interpolate(float x) {
            // Handle the boundary cases.
            final int n = mX.length;
@@ -141,7 +215,7 @@ public final class Spline {
        public String toString() {
            StringBuilder str = new StringBuilder();
            final int n = mX.length;
        str.append("[");
            str.append("MonotoneCubicSpline{[");
            for (int i = 0; i < n; i++) {
                if (i != 0) {
                    str.append(", ");
@@ -150,7 +224,74 @@ public final class Spline {
                str.append(", ").append(mY[i]);
                str.append(": ").append(mM[i]).append(")");
            }
        str.append("]");
            str.append("]}");
            return str.toString();
        }
    }

    public static class LinearSpline extends Spline {
        private final float[] mX;
        private final float[] mY;
        private final float[] mM;

        public LinearSpline(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;
            mM = new float[N-1];
            for (int i = 0; i < N-1; i++) {
                mM[i] = (y[i+1] - y[i]) / (x[i+1] - x[i]);
            }
            mX = x;
            mY = y;
        }

        @Override
        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];
                }
            }
            return mY[i] + mM[i] * (x - mX[i]);
        }

        @Override
        public String toString() {
            StringBuilder str = new StringBuilder();
            final int n = mX.length;
            str.append("LinearSpline{[");
            for (int i = 0; i < n; i++) {
                if (i != 0) {
                    str.append(", ");
                }
                str.append("(").append(mX[i]);
                str.append(", ").append(mY[i]);
                if (i < n-1) {
                    str.append(": ").append(mM[i]);
                }
                str.append(")");
            }
            str.append("]}");
            return str.toString();
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -1009,7 +1009,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
                y[i] = normalizeAbsoluteBrightness(brightness[i]);
            }

            Spline spline = Spline.createMonotoneCubicSpline(x, y);
            Spline spline = Spline.createSpline(x, y);
            if (DEBUG) {
                Slog.d(TAG, "Auto-brightness spline: " + spline);
                for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {