From 86cae92291f728d0eca637573ecbe6e0a53ccf1a Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Mon, 18 Jul 2016 02:36:42 -0700 Subject: [PATCH] livedisplay: Add support for direct color balance control * We currently use the DisplayColorCalibration API for setting display temperature which makes a lot of guesses about what temperature the display really is. Some devices will support the new ColorBalance API (via QDCM or other mechanism), which offers a calibrated alternative. Add support for this, which will supercede DCC if available. * Additionally, define the available color temperature range as a set of overlayable values so this can be specified per-device. This range will be mapped to balance values using the power curve calculations in the new MathUtils class. Change-Id: I99608c09807b747d962680293c7b0cee8d669003 --- api/cm_current.txt | 13 +- .../platform/internal/CMHardwareService.java | 64 ++++++++++ .../display/ColorTemperatureController.java | 115 +++++++++++++++++- .../internal/display/LiveDisplayService.java | 5 +- cm/res/res/values/config.xml | 8 ++ cm/res/res/values/symbols.xml | 3 + .../hardware/CMHardwareManager.java | 39 ++++++ .../hardware/ICMHardwareService.aidl | 5 + .../hardware/LiveDisplayConfig.java | 44 ++++++- .../hardware/LiveDisplayManager.java | 9 +- .../cyanogenmod/providers/CMSettings.java | 4 +- .../cyanogenmod/internal/util/MathUtils.java | 67 ++++++++++ system-api/cm_system-current.txt | 13 +- 13 files changed, 373 insertions(+), 16 deletions(-) create mode 100644 sdk/src/java/org/cyanogenmod/internal/util/MathUtils.java diff --git a/api/cm_current.txt b/api/cm_current.txt index 87ba2c5..73de8ed 100644 --- a/api/cm_current.txt +++ b/api/cm_current.txt @@ -244,7 +244,7 @@ package cyanogenmod.app { field public static final int ZEN_MODE_OFF = 0; // 0x0 } - public final class Profile implements android.os.Parcelable { + public final class Profile implements java.lang.Comparable android.os.Parcelable { ctor public Profile(java.lang.String); method public void addSecondaryUuid(java.util.UUID); method public int compareTo(java.lang.Object); @@ -451,6 +451,8 @@ package cyanogenmod.hardware { public final class CMHardwareManager { method public boolean deletePersistentObject(java.lang.String); method public boolean get(int); + method public int getColorBalance(); + method public android.util.Range getColorBalanceRange(); method public cyanogenmod.hardware.DisplayMode getCurrentDisplayMode(); method public cyanogenmod.hardware.DisplayMode getDefaultDisplayMode(); method public int[] getDisplayColorCalibration(); @@ -483,6 +485,7 @@ package cyanogenmod.hardware { method public boolean registerThermalListener(cyanogenmod.hardware.ThermalListenerCallback); method public boolean requireAdaptiveBacklightForSunlightEnhancement(); method public boolean set(int, boolean); + method public boolean setColorBalance(int); method public boolean setDisplayColorCalibration(int[]); method public deprecated boolean setDisplayGammaCalibration(int, int[]); method public boolean setDisplayMode(cyanogenmod.hardware.DisplayMode, boolean); @@ -493,6 +496,7 @@ package cyanogenmod.hardware { method public boolean writePersistentString(java.lang.String, java.lang.String); field public static final int FEATURE_ADAPTIVE_BACKLIGHT = 1; // 0x1 field public static final int FEATURE_AUTO_CONTRAST = 4096; // 0x1000 + field public static final int FEATURE_COLOR_BALANCE = 131072; // 0x20000 field public static final int FEATURE_COLOR_ENHANCEMENT = 2; // 0x2 field public static final int FEATURE_DISPLAY_COLOR_CALIBRATION = 4; // 0x4 field public static final int FEATURE_DISPLAY_GAMMA_CALIBRATION = 8; // 0x8 @@ -530,8 +534,10 @@ package cyanogenmod.hardware { } public class LiveDisplayConfig implements android.os.Parcelable { - ctor public LiveDisplayConfig(java.util.BitSet, int, int, int, boolean, boolean, boolean, boolean); + ctor public LiveDisplayConfig(java.util.BitSet, int, int, int, boolean, boolean, boolean, boolean, android.util.Range, android.util.Range); method public int describeContents(); + method public android.util.Range getColorBalanceRange(); + method public android.util.Range getColorTemperatureRange(); method public boolean getDefaultAutoContrast(); method public boolean getDefaultAutoOutdoorMode(); method public boolean getDefaultCABC(); @@ -567,6 +573,7 @@ package cyanogenmod.hardware { field public static final int FEATURE_AUTO_CONTRAST = 11; // 0xb field public static final int FEATURE_CABC = 10; // 0xa field public static final int FEATURE_COLOR_ADJUSTMENT = 13; // 0xd + field public static final int FEATURE_COLOR_BALANCE = 16; // 0x10 field public static final int FEATURE_COLOR_ENHANCEMENT = 12; // 0xc field public static final int FEATURE_DISPLAY_MODES = 15; // 0xf field public static final int FEATURE_MANAGED_OUTDOOR_MODE = 14; // 0xe @@ -948,6 +955,7 @@ package cyanogenmod.providers { field public static final java.lang.String KEY_MENU_ACTION = "key_menu_action"; field public static final java.lang.String KEY_MENU_LONG_PRESS_ACTION = "key_menu_long_press_action"; field public static final java.lang.String LOCKSCREEN_PIN_SCRAMBLE_LAYOUT = "lockscreen_scramble_pin_layout"; + field public static final java.lang.String LOCKSCREEN_ROTATION = "lockscreen_rotation"; field public static final java.lang.String MENU_WAKE_SCREEN = "menu_wake_screen"; field public static final java.lang.String NAVBAR_LEFT_IN_LANDSCAPE = "navigation_bar_left"; field public static final java.lang.String NAVIGATION_BAR_MENU_ARROW_KEYS = "navigation_bar_menu_arrow_keys"; @@ -972,7 +980,6 @@ package cyanogenmod.providers { field public static final java.lang.String QS_SHOW_BRIGHTNESS_SLIDER = "qs_show_brightness_slider"; field public static final java.lang.String RECENTS_SHOW_SEARCH_BAR = "recents_show_search_bar"; field public static final java.lang.String REVERSE_LOOKUP_PROVIDER = "reverse_lookup_provider"; - field public static final java.lang.String LOCKSCREEN_ROTATION = "lockscreen_rotation"; field public static final java.lang.String SHOW_ALARM_ICON = "show_alarm_icon"; field public static final java.lang.String STATUS_BAR_AM_PM = "status_bar_am_pm"; field public static final java.lang.String STATUS_BAR_BATTERY_STYLE = "status_bar_battery_style"; diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/CMHardwareService.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/CMHardwareService.java index 7e79de4..6052383 100644 --- a/cm/lib/main/java/org/cyanogenmod/platform/internal/CMHardwareService.java +++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/CMHardwareService.java @@ -36,6 +36,7 @@ import java.util.Arrays; import org.cyanogenmod.hardware.AdaptiveBacklight; import org.cyanogenmod.hardware.AutoContrast; +import org.cyanogenmod.hardware.ColorBalance; import org.cyanogenmod.hardware.ColorEnhancement; import org.cyanogenmod.hardware.DisplayColorCalibration; import org.cyanogenmod.hardware.DisplayGammaCalibration; @@ -96,6 +97,11 @@ public class CMHardwareService extends CMSystemService implements ThermalUpdateC public boolean writePersistentBytes(String key, byte[] value); public byte[] readPersistentBytes(String key); + + public int getColorBalanceMin(); + public int getColorBalanceMax(); + public int getColorBalance(); + public boolean setColorBalance(int value); } private class LegacyCMHardware implements CMHardwareInterface { @@ -137,6 +143,8 @@ public class CMHardwareService extends CMSystemService implements ThermalUpdateC mSupportedFeatures |= CMHardwareManager.FEATURE_THERMAL_MONITOR; if (UniqueDeviceId.isSupported()) mSupportedFeatures |= CMHardwareManager.FEATURE_UNIQUE_DEVICE_ID; + if (ColorBalance.isSupported()) + mSupportedFeatures |= CMHardwareManager.FEATURE_COLOR_BALANCE; } public int getSupportedFeatures() { @@ -334,6 +342,22 @@ public class CMHardwareService extends CMSystemService implements ThermalUpdateC public byte[] readPersistentBytes(String key) { return PersistentStorage.get(key); } + + public int getColorBalanceMin() { + return ColorBalance.getMinValue(); + } + + public int getColorBalanceMax() { + return ColorBalance.getMaxValue(); + } + + public int getColorBalance() { + return ColorBalance.getValue(); + } + + public boolean setColorBalance(int value) { + return ColorBalance.setValue(value); + } } private CMHardwareInterface getImpl(Context context) { @@ -687,5 +711,45 @@ public class CMHardwareService extends CMSystemService implements ThermalUpdateC } return false; } + + @Override + public int getColorBalanceMin() { + mContext.enforceCallingOrSelfPermission( + cyanogenmod.platform.Manifest.permission.HARDWARE_ABSTRACTION_ACCESS, null); + if (isSupported(CMHardwareManager.FEATURE_COLOR_BALANCE)) { + return mCmHwImpl.getColorBalanceMin(); + } + return 0; + } + + @Override + public int getColorBalanceMax() { + mContext.enforceCallingOrSelfPermission( + cyanogenmod.platform.Manifest.permission.HARDWARE_ABSTRACTION_ACCESS, null); + if (isSupported(CMHardwareManager.FEATURE_COLOR_BALANCE)) { + return mCmHwImpl.getColorBalanceMax(); + } + return 0; + } + + @Override + public int getColorBalance() { + mContext.enforceCallingOrSelfPermission( + cyanogenmod.platform.Manifest.permission.HARDWARE_ABSTRACTION_ACCESS, null); + if (isSupported(CMHardwareManager.FEATURE_COLOR_BALANCE)) { + return mCmHwImpl.getColorBalance(); + } + return 0; + } + + @Override + public boolean setColorBalance(int value) { + mContext.enforceCallingOrSelfPermission( + cyanogenmod.platform.Manifest.permission.HARDWARE_ABSTRACTION_ACCESS, null); + if (isSupported(CMHardwareManager.FEATURE_COLOR_BALANCE)) { + return mCmHwImpl.setColorBalance(value); + } + return false; + } }; } diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/display/ColorTemperatureController.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/ColorTemperatureController.java index 3184d71..a72c3a2 100644 --- a/cm/lib/main/java/org/cyanogenmod/platform/internal/display/ColorTemperatureController.java +++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/ColorTemperatureController.java @@ -20,18 +20,24 @@ import static cyanogenmod.hardware.LiveDisplayManager.MODE_DAY; import static cyanogenmod.hardware.LiveDisplayManager.MODE_NIGHT; import static cyanogenmod.hardware.LiveDisplayManager.MODE_OFF; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; import android.content.Context; import android.net.Uri; import android.os.Handler; import android.text.format.DateUtils; import android.util.MathUtils; +import android.util.Range; import android.util.Slog; +import android.view.animation.LinearInterpolator; import com.android.server.twilight.TwilightState; import java.io.PrintWriter; import java.util.BitSet; +import cyanogenmod.hardware.CMHardwareManager; +import cyanogenmod.hardware.LiveDisplayManager; import cyanogenmod.providers.CMSettings; import cyanogenmod.util.ColorUtils; @@ -40,6 +46,10 @@ public class ColorTemperatureController extends LiveDisplayFeature { private final DisplayHardwareController mDisplayHardware; private final boolean mUseTemperatureAdjustment; + private final boolean mUseColorBalance; + private final Range mColorBalanceRange; + private final Range mColorTemperatureRange; + private final double[] mColorBalanceCurve; private final int mDefaultDayTemperature; private final int mDefaultNightTemperature; @@ -48,6 +58,10 @@ public class ColorTemperatureController extends LiveDisplayFeature { private int mDayTemperature; private int mNightTemperature; + private ValueAnimator mAnimator; + + private final CMHardwareManager mHardware; + private static final long TWILIGHT_ADJUSTMENT_TIME = DateUtils.HOUR_IN_MILLIS * 1; private static final Uri DISPLAY_TEMPERATURE_DAY = @@ -59,12 +73,30 @@ public class ColorTemperatureController extends LiveDisplayFeature { Handler handler, DisplayHardwareController displayHardware) { super(context, handler); mDisplayHardware = displayHardware; - mUseTemperatureAdjustment = mDisplayHardware.hasColorAdjustment(); + mHardware = CMHardwareManager.getInstance(mContext); + + mUseColorBalance = mHardware + .isSupported(CMHardwareManager.FEATURE_COLOR_BALANCE); + mColorBalanceRange = mHardware.getColorBalanceRange(); + + mUseTemperatureAdjustment = mUseColorBalance || + mDisplayHardware.hasColorAdjustment(); mDefaultDayTemperature = mContext.getResources().getInteger( org.cyanogenmod.platform.internal.R.integer.config_dayColorTemperature); mDefaultNightTemperature = mContext.getResources().getInteger( org.cyanogenmod.platform.internal.R.integer.config_nightColorTemperature); + + mColorTemperatureRange = Range.create( + mContext.getResources().getInteger( + org.cyanogenmod.platform.internal.R.integer.config_minColorTemperature), + mContext.getResources().getInteger( + org.cyanogenmod.platform.internal.R.integer.config_maxColorTemperature)); + + mColorBalanceCurve = org.cyanogenmod.internal.util.MathUtils.powerCurve( + mColorTemperatureRange.getLower(), + mDefaultDayTemperature, + mColorTemperatureRange.getUpper()); } @Override @@ -85,6 +117,9 @@ public class ColorTemperatureController extends LiveDisplayFeature { caps.set(MODE_AUTO); caps.set(MODE_DAY); caps.set(MODE_NIGHT); + if (mUseColorBalance) { + caps.set(LiveDisplayManager.FEATURE_COLOR_BALANCE); + } } return mUseTemperatureAdjustment; } @@ -96,7 +131,11 @@ public class ColorTemperatureController extends LiveDisplayFeature { @Override protected void onScreenStateChanged() { - updateColorTemperature(); + if (mAnimator != null && mAnimator.isRunning() && !isScreenOn()) { + mAnimator.cancel(); + } else { + updateColorTemperature(); + } } @Override @@ -168,17 +207,79 @@ public class ColorTemperatureController extends LiveDisplayFeature { } } + /** + * Smoothly animate the current display color balance + */ + private synchronized void animateColorBalance(int balance) { + + // always start with the current values in the hardware + int current = mHardware.getColorBalance(); + + if (current == balance) { + return; + } + + long duration = (long)(5 * Math.abs(current - balance)); + + + if (DEBUG) { + Slog.d(TAG, "animateDisplayColor current=" + current + + " target=" + balance + " duration=" + duration); + } + + if (mAnimator != null) { + mAnimator.cancel(); + mAnimator.removeAllUpdateListeners(); + } + + mAnimator = ValueAnimator.ofInt(current, balance); + mAnimator.setDuration(duration); + mAnimator.setInterpolator(new LinearInterpolator()); + mAnimator.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(final ValueAnimator animation) { + synchronized (ColorTemperatureController.this) { + if (isScreenOn()) { + int value = (int) animation.getAnimatedValue(); + mHardware.setColorBalance(value); + } + } + } + }); + mAnimator.start(); + } + + /* + * Map the color temperature to a color balance value using a power curve. This assumes the + * correct configuration at the device level! + */ + private int mapColorTemperatureToBalance(int temperature) { + double z = org.cyanogenmod.internal.util.MathUtils.powerCurveToLinear(mColorBalanceCurve, temperature); + return Math.round(MathUtils.lerp((float)mColorBalanceRange.getLower(), + (float)mColorBalanceRange.getUpper(), (float)z)); + } private synchronized void setDisplayTemperature(int temperature) { + if (!mColorTemperatureRange.contains(temperature)) { + Slog.e(TAG, "Color temperature out of range: " + temperature); + return; + } + mColorTemperature = temperature; + if (mUseColorBalance) { + int balance = mapColorTemperatureToBalance(temperature); + Slog.d(TAG, "Set color balance = " + balance + " (temperature=" + temperature + ")"); + animateColorBalance(balance); + return; + } + final float[] rgb = ColorUtils.temperatureToRGB(temperature); if (mDisplayHardware.setAdditionalAdjustment(rgb)) { if (DEBUG) { Slog.d(TAG, "Adjust display temperature to " + temperature + "K"); } } - } /** @@ -257,4 +358,12 @@ public class ColorTemperatureController extends LiveDisplayFeature { void setNightColorTemperature(int temperature) { putInt(CMSettings.System.DISPLAY_TEMPERATURE_NIGHT, temperature); } + + Range getColorTemperatureRange() { + return mColorTemperatureRange; + } + + Range getColorBalanceRange() { + return mColorBalanceRange; + } } diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/display/LiveDisplayService.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/LiveDisplayService.java index db2b1a6..129983b 100644 --- a/cm/lib/main/java/org/cyanogenmod/platform/internal/display/LiveDisplayService.java +++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/LiveDisplayService.java @@ -40,13 +40,11 @@ import android.os.IBinder; import android.os.PowerManagerInternal; import android.os.Process; import android.os.UserHandle; -import android.util.Log; import android.view.Display; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.ServiceThread; -import com.android.server.SystemService; import com.android.server.pm.UserContentObserver; import com.android.server.twilight.TwilightListener; import com.android.server.twilight.TwilightManager; @@ -188,7 +186,8 @@ public class LiveDisplayService extends CMSystemService { mConfig = new LiveDisplayConfig(capabilities, defaultMode, mCTC.getDefaultDayTemperature(), mCTC.getDefaultNightTemperature(), mOMC.getDefaultAutoOutdoorMode(), mDHC.getDefaultAutoContrast(), - mDHC.getDefaultCABC(), mDHC.getDefaultColorEnhancement()); + mDHC.getDefaultCABC(), mDHC.getDefaultColorEnhancement(), + mCTC.getColorTemperatureRange(), mCTC.getColorBalanceRange()); // listeners mDisplayManager = (DisplayManager) getContext().getSystemService( diff --git a/cm/res/res/values/config.xml b/cm/res/res/values/config.xml index f08a75f..9592bf1 100644 --- a/cm/res/res/values/config.xml +++ b/cm/res/res/values/config.xml @@ -57,6 +57,14 @@ 9000 0 + + 1000 + 10000 + false true true diff --git a/cm/res/res/values/symbols.xml b/cm/res/res/values/symbols.xml index 0e7143c..e79570b 100644 --- a/cm/res/res/values/symbols.xml +++ b/cm/res/res/values/symbols.xml @@ -73,6 +73,9 @@ + + + diff --git a/sdk/src/java/cyanogenmod/hardware/CMHardwareManager.java b/sdk/src/java/cyanogenmod/hardware/CMHardwareManager.java index 7765b94..d31ce9b 100644 --- a/sdk/src/java/cyanogenmod/hardware/CMHardwareManager.java +++ b/sdk/src/java/cyanogenmod/hardware/CMHardwareManager.java @@ -20,6 +20,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import android.util.Range; import cyanogenmod.app.CMContextConstants; @@ -129,6 +130,11 @@ public final class CMHardwareManager { */ public static final int FEATURE_UNIQUE_DEVICE_ID = 0x10000; + /** + * Color balance + */ + public static final int FEATURE_COLOR_BALANCE = 0x20000; + private static final List BOOLEAN_FEATURES = Arrays.asList( FEATURE_ADAPTIVE_BACKLIGHT, FEATURE_COLOR_ENHANCEMENT, @@ -803,6 +809,39 @@ public final class CMHardwareManager { return false; } + public Range getColorBalanceRange() { + int min = 0; + int max = 0; + try { + if (checkService()) { + min = sService.getColorBalanceMin(); + max = sService.getColorBalanceMax(); + } + } catch (RemoteException e) { + } + return new Range(min, max); + } + + public int getColorBalance() { + try { + if (checkService()) { + return sService.getColorBalance(); + } + } catch (RemoteException e) { + } + return 0; + } + + public boolean setColorBalance(int value) { + try { + if (checkService()) { + return sService.setColorBalance(value); + } + } catch (RemoteException e) { + } + return false; + } + /** * @return true if service is valid */ diff --git a/sdk/src/java/cyanogenmod/hardware/ICMHardwareService.aidl b/sdk/src/java/cyanogenmod/hardware/ICMHardwareService.aidl index a1ae65b..6fbaf66 100644 --- a/sdk/src/java/cyanogenmod/hardware/ICMHardwareService.aidl +++ b/sdk/src/java/cyanogenmod/hardware/ICMHardwareService.aidl @@ -58,4 +58,9 @@ interface ICMHardwareService { boolean isSunlightEnhancementSelfManaged(); String getUniqueDeviceId(); + + int getColorBalanceMin(); + int getColorBalanceMax(); + int getColorBalance(); + boolean setColorBalance(int value); } diff --git a/sdk/src/java/cyanogenmod/hardware/LiveDisplayConfig.java b/sdk/src/java/cyanogenmod/hardware/LiveDisplayConfig.java index 81945f7..81cdca3 100644 --- a/sdk/src/java/cyanogenmod/hardware/LiveDisplayConfig.java +++ b/sdk/src/java/cyanogenmod/hardware/LiveDisplayConfig.java @@ -23,6 +23,7 @@ import static cyanogenmod.hardware.LiveDisplayManager.MODE_OFF; import android.os.Parcel; import android.os.Parcelable; +import android.util.Range; import java.util.BitSet; @@ -50,10 +51,15 @@ public class LiveDisplayConfig implements Parcelable { private final boolean mDefaultCABC; private final boolean mDefaultColorEnhancement; + private final Range mColorTemperatureRange; + private final Range mColorBalanceRange; + public LiveDisplayConfig(BitSet capabilities, int defaultMode, int defaultDayTemperature, int defaultNightTemperature, boolean defaultAutoOutdoorMode, boolean defaultAutoContrast, - boolean defaultCABC, boolean defaultColorEnhancement) { + boolean defaultCABC, boolean defaultColorEnhancement, + Range colorTemperatureRange, + Range colorBalanceRange) { super(); mCapabilities = (BitSet) capabilities.clone(); mAllModes.set(MODE_FIRST, MODE_LAST); @@ -64,6 +70,8 @@ public class LiveDisplayConfig implements Parcelable { mDefaultAutoOutdoorMode = defaultAutoOutdoorMode; mDefaultCABC = defaultCABC; mDefaultColorEnhancement = defaultColorEnhancement; + mColorTemperatureRange = colorTemperatureRange; + mColorBalanceRange = colorBalanceRange; } private LiveDisplayConfig(Parcel parcel) { @@ -80,6 +88,10 @@ public class LiveDisplayConfig implements Parcelable { boolean defaultAutoOutdoorMode = false; boolean defaultCABC = false; boolean defaultColorEnhancement = false; + int minColorTemperature = 0; + int maxColorTemperature = 0; + int minColorBalance = 0; + int maxColorBalance = 0; if (parcelableVersion >= Build.CM_VERSION_CODES.FIG) { capabilities = parcel.readLong(); @@ -90,6 +102,10 @@ public class LiveDisplayConfig implements Parcelable { defaultAutoOutdoorMode = parcel.readInt() == 1; defaultCABC = parcel.readInt() == 1; defaultColorEnhancement = parcel.readInt() == 1; + minColorTemperature = parcel.readInt(); + maxColorTemperature = parcel.readInt(); + minColorBalance = parcel.readInt(); + maxColorBalance = parcel.readInt(); } // set temps @@ -102,6 +118,8 @@ public class LiveDisplayConfig implements Parcelable { mDefaultAutoOutdoorMode = defaultAutoOutdoorMode; mDefaultCABC = defaultCABC; mDefaultColorEnhancement = defaultColorEnhancement; + mColorTemperatureRange = Range.create(minColorTemperature, maxColorTemperature); + mColorBalanceRange = Range.create(minColorBalance, maxColorBalance); // Complete parcel info for the concierge parcelInfo.complete(); @@ -118,6 +136,8 @@ public class LiveDisplayConfig implements Parcelable { sb.append(" defaultAutoContrast=").append(mDefaultAutoContrast); sb.append(" defaultCABC=").append(mDefaultCABC); sb.append(" defaultColorEnhancement=").append(mDefaultColorEnhancement); + sb.append(" colorTemperatureRange=").append(mColorTemperatureRange); + sb.append(" colorBalanceRange=").append(mColorBalanceRange); return sb.toString(); } @@ -141,6 +161,10 @@ public class LiveDisplayConfig implements Parcelable { out.writeInt(mDefaultAutoOutdoorMode ? 1 : 0); out.writeInt(mDefaultCABC ? 1 : 0); out.writeInt(mDefaultColorEnhancement ? 1 : 0); + out.writeInt(mColorTemperatureRange.getLower()); + out.writeInt(mColorTemperatureRange.getUpper()); + out.writeInt(mColorBalanceRange.getLower()); + out.writeInt(mColorBalanceRange.getUpper()); // Complete the parcel info for the concierge parcelInfo.complete(); @@ -243,6 +267,24 @@ public class LiveDisplayConfig implements Parcelable { return mDefaultColorEnhancement; } + /** + * Get the range of supported color temperatures + * + * @return range in Kelvin + */ + public Range getColorTemperatureRange() { + return mColorTemperatureRange; + } + + /** + * Get the range of supported color balance + * + * @return linear range which maps into the temperature range curve + */ + public Range getColorBalanceRange() { + return mColorBalanceRange; + } + /** @hide */ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { diff --git a/sdk/src/java/cyanogenmod/hardware/LiveDisplayManager.java b/sdk/src/java/cyanogenmod/hardware/LiveDisplayManager.java index abfd158..5a39a98 100644 --- a/sdk/src/java/cyanogenmod/hardware/LiveDisplayManager.java +++ b/sdk/src/java/cyanogenmod/hardware/LiveDisplayManager.java @@ -102,10 +102,17 @@ public class LiveDisplayManager { */ public static final int FEATURE_DISPLAY_MODES = 15; + /** + * System supports direct range-based control of display + * color balance (temperature). This is preferred over + * simple RGB adjustment. + */ + public static final int FEATURE_COLOR_BALANCE = 16; + /** @hide */ public static final int FEATURE_FIRST = FEATURE_CABC; /** @hide */ - public static final int FEATURE_LAST = FEATURE_DISPLAY_MODES; + public static final int FEATURE_LAST = FEATURE_COLOR_BALANCE; private static final String TAG = "LiveDisplay"; diff --git a/sdk/src/java/cyanogenmod/providers/CMSettings.java b/sdk/src/java/cyanogenmod/providers/CMSettings.java index 38c8b67..528518f 100644 --- a/sdk/src/java/cyanogenmod/providers/CMSettings.java +++ b/sdk/src/java/cyanogenmod/providers/CMSettings.java @@ -1311,7 +1311,7 @@ public final class CMSettings { /** @hide */ public static final Validator DISPLAY_TEMPERATURE_DAY_VALIDATOR = - new InclusiveIntegerRangeValidator(1000, 10000); + new InclusiveIntegerRangeValidator(0, 100000); /** * Color temperature of the display at night @@ -1320,7 +1320,7 @@ public final class CMSettings { /** @hide */ public static final Validator DISPLAY_TEMPERATURE_NIGHT_VALIDATOR = - new InclusiveIntegerRangeValidator(1000, 10000); + new InclusiveIntegerRangeValidator(0, 100000); /** * Display color temperature adjustment mode, one of DAY (default), NIGHT, or AUTO. diff --git a/sdk/src/java/org/cyanogenmod/internal/util/MathUtils.java b/sdk/src/java/org/cyanogenmod/internal/util/MathUtils.java new file mode 100644 index 0000000..3a2ddf0 --- /dev/null +++ b/sdk/src/java/org/cyanogenmod/internal/util/MathUtils.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.internal.util; + +public final class MathUtils { + + /** + * Given a range of values which change continuously in a non-linear way, + * we can map back and forth to a linear scale using some quadratic equations. + * + * The linear scale ranges from 0 -> 1. This method will calculate the + * coefficients needed to solve the conversion functions in the next two methods. + * + * lower = actual value when linear value = 0 + * mid = actual value when linear value = .5 + * upper actual value when linear value = 1 + * + * @param lower + * @param mid + * @param upper + * @return array of coefficients + */ + public static double[] powerCurve(double lower, double mid, double upper) { + final double[] curve = new double[3]; + curve[0] = ((lower * upper) - (mid * mid)) / (lower - (2 * mid) + upper); + curve[1] = Math.pow((mid - lower), 2) / (lower - (2 * mid) + upper); + curve[2] = 2 * Math.log((upper - mid) / (mid - lower)); + return curve; + } + + /** + * Map a value on a power curve to a linear value + * + * @param curve obtained from powerCurve() + * @param value to convert to linear scale + * @return linear value from 0 -> 1 + */ + public static double powerCurveToLinear(final double[] curve, double value) { + return Math.log((value - curve[0]) / curve[1]) / curve[2]; + } + + /** + * Map a value on a linear scale to a value on a power curve + * + * @param curve obtained from powerCurve() + * @param value from 0 -> 1 to map onto power curve + * @return actual value on the given curve + */ + public static double linearToPowerCurve(final double[] curve, double value) { + return curve[0] + curve[1] * Math.exp(curve[2] * value); + } + + +} diff --git a/system-api/cm_system-current.txt b/system-api/cm_system-current.txt index 87ba2c5..73de8ed 100644 --- a/system-api/cm_system-current.txt +++ b/system-api/cm_system-current.txt @@ -244,7 +244,7 @@ package cyanogenmod.app { field public static final int ZEN_MODE_OFF = 0; // 0x0 } - public final class Profile implements android.os.Parcelable { + public final class Profile implements java.lang.Comparable android.os.Parcelable { ctor public Profile(java.lang.String); method public void addSecondaryUuid(java.util.UUID); method public int compareTo(java.lang.Object); @@ -451,6 +451,8 @@ package cyanogenmod.hardware { public final class CMHardwareManager { method public boolean deletePersistentObject(java.lang.String); method public boolean get(int); + method public int getColorBalance(); + method public android.util.Range getColorBalanceRange(); method public cyanogenmod.hardware.DisplayMode getCurrentDisplayMode(); method public cyanogenmod.hardware.DisplayMode getDefaultDisplayMode(); method public int[] getDisplayColorCalibration(); @@ -483,6 +485,7 @@ package cyanogenmod.hardware { method public boolean registerThermalListener(cyanogenmod.hardware.ThermalListenerCallback); method public boolean requireAdaptiveBacklightForSunlightEnhancement(); method public boolean set(int, boolean); + method public boolean setColorBalance(int); method public boolean setDisplayColorCalibration(int[]); method public deprecated boolean setDisplayGammaCalibration(int, int[]); method public boolean setDisplayMode(cyanogenmod.hardware.DisplayMode, boolean); @@ -493,6 +496,7 @@ package cyanogenmod.hardware { method public boolean writePersistentString(java.lang.String, java.lang.String); field public static final int FEATURE_ADAPTIVE_BACKLIGHT = 1; // 0x1 field public static final int FEATURE_AUTO_CONTRAST = 4096; // 0x1000 + field public static final int FEATURE_COLOR_BALANCE = 131072; // 0x20000 field public static final int FEATURE_COLOR_ENHANCEMENT = 2; // 0x2 field public static final int FEATURE_DISPLAY_COLOR_CALIBRATION = 4; // 0x4 field public static final int FEATURE_DISPLAY_GAMMA_CALIBRATION = 8; // 0x8 @@ -530,8 +534,10 @@ package cyanogenmod.hardware { } public class LiveDisplayConfig implements android.os.Parcelable { - ctor public LiveDisplayConfig(java.util.BitSet, int, int, int, boolean, boolean, boolean, boolean); + ctor public LiveDisplayConfig(java.util.BitSet, int, int, int, boolean, boolean, boolean, boolean, android.util.Range, android.util.Range); method public int describeContents(); + method public android.util.Range getColorBalanceRange(); + method public android.util.Range getColorTemperatureRange(); method public boolean getDefaultAutoContrast(); method public boolean getDefaultAutoOutdoorMode(); method public boolean getDefaultCABC(); @@ -567,6 +573,7 @@ package cyanogenmod.hardware { field public static final int FEATURE_AUTO_CONTRAST = 11; // 0xb field public static final int FEATURE_CABC = 10; // 0xa field public static final int FEATURE_COLOR_ADJUSTMENT = 13; // 0xd + field public static final int FEATURE_COLOR_BALANCE = 16; // 0x10 field public static final int FEATURE_COLOR_ENHANCEMENT = 12; // 0xc field public static final int FEATURE_DISPLAY_MODES = 15; // 0xf field public static final int FEATURE_MANAGED_OUTDOOR_MODE = 14; // 0xe @@ -948,6 +955,7 @@ package cyanogenmod.providers { field public static final java.lang.String KEY_MENU_ACTION = "key_menu_action"; field public static final java.lang.String KEY_MENU_LONG_PRESS_ACTION = "key_menu_long_press_action"; field public static final java.lang.String LOCKSCREEN_PIN_SCRAMBLE_LAYOUT = "lockscreen_scramble_pin_layout"; + field public static final java.lang.String LOCKSCREEN_ROTATION = "lockscreen_rotation"; field public static final java.lang.String MENU_WAKE_SCREEN = "menu_wake_screen"; field public static final java.lang.String NAVBAR_LEFT_IN_LANDSCAPE = "navigation_bar_left"; field public static final java.lang.String NAVIGATION_BAR_MENU_ARROW_KEYS = "navigation_bar_menu_arrow_keys"; @@ -972,7 +980,6 @@ package cyanogenmod.providers { field public static final java.lang.String QS_SHOW_BRIGHTNESS_SLIDER = "qs_show_brightness_slider"; field public static final java.lang.String RECENTS_SHOW_SEARCH_BAR = "recents_show_search_bar"; field public static final java.lang.String REVERSE_LOOKUP_PROVIDER = "reverse_lookup_provider"; - field public static final java.lang.String LOCKSCREEN_ROTATION = "lockscreen_rotation"; field public static final java.lang.String SHOW_ALARM_ICON = "show_alarm_icon"; field public static final java.lang.String STATUS_BAR_AM_PM = "status_bar_am_pm"; field public static final java.lang.String STATUS_BAR_BATTERY_STYLE = "status_bar_battery_style";