Loading core/java/android/util/MathUtils.java +4 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,10 @@ public final class MathUtils { return (float) Math.pow(a, b); } public static float sqrt(float a) { return (float) Math.sqrt(a); } public static float max(float a, float b) { return a > b ? a : b; } Loading packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +149 −44 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.provider.Settings; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.util.Log; import android.util.MathUtils; import android.widget.ImageView; import com.android.internal.logging.MetricsLogger; Loading @@ -49,8 +50,15 @@ public class BrightnessController implements ToggleSlider.Listener { private static final String TAG = "StatusBar.BrightnessController"; private static final boolean SHOW_AUTOMATIC_ICON = false; private static final int SLIDER_MAX = 1023; private static final int SLIDER_ANIMATION_DURATION = 3000; // Hybrid Log Gamma constant values private static final float R = 0.5f; private static final float A = 0.17883277f; private static final float B = 0.28466892f; private static final float C = 0.55991073f; private static final int MSG_UPDATE_ICON = 0; private static final int MSG_UPDATE_SLIDER = 1; private static final int MSG_SET_CHECKED = 2; Loading @@ -60,8 +68,10 @@ public class BrightnessController implements ToggleSlider.Listener { private final int mMinimumBacklight; private final int mMaximumBacklight; private final int mDefaultBacklight; private final int mMinimumBacklightForVr; private final int mMaximumBacklightForVr; private final int mDefaultBacklightForVr; private final Context mContext; private final ImageView mIcon; Loading Loading @@ -203,21 +213,18 @@ public class BrightnessController implements ToggleSlider.Listener { private final Runnable mUpdateSliderRunnable = new Runnable() { @Override public void run() { if (mIsVrModeEnabled) { int value = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mMaximumBacklight, final int val; final boolean inVrMode = mIsVrModeEnabled; if (inVrMode) { val = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mDefaultBacklightForVr, UserHandle.USER_CURRENT); mHandler.obtainMessage(MSG_UPDATE_SLIDER, mMaximumBacklightForVr - mMinimumBacklightForVr, value - mMinimumBacklightForVr).sendToTarget(); } else { int value; value = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, mMaximumBacklight, val = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, mDefaultBacklight, UserHandle.USER_CURRENT); mHandler.obtainMessage(MSG_UPDATE_SLIDER, mMaximumBacklight - mMinimumBacklight, value - mMinimumBacklight).sendToTarget(); } mHandler.obtainMessage(MSG_UPDATE_SLIDER, val, inVrMode ? 1 : 0).sendToTarget(); } }; Loading @@ -239,8 +246,7 @@ public class BrightnessController implements ToggleSlider.Listener { updateIcon(msg.arg1 != 0); break; case MSG_UPDATE_SLIDER: mControl.setMax(msg.arg1); animateSliderTo(msg.arg2); updateSlider(msg.arg1, msg.arg2 != 0); break; case MSG_SET_CHECKED: mControl.setChecked(msg.arg1 != 0); Loading @@ -267,6 +273,7 @@ public class BrightnessController implements ToggleSlider.Listener { mContext = context; mIcon = icon; mControl = control; mControl.setMax(SLIDER_MAX); mBackgroundHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER)); mUserTracker = new CurrentUserTracker(mContext) { @Override Loading @@ -277,11 +284,13 @@ public class BrightnessController implements ToggleSlider.Listener { }; mBrightnessObserver = new BrightnessObserver(mHandler); PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); PowerManager pm = context.getSystemService(PowerManager.class); mMinimumBacklight = pm.getMinimumScreenBrightnessSetting(); mMaximumBacklight = pm.getMaximumScreenBrightnessSetting(); mDefaultBacklight = pm.getDefaultScreenBrightnessSetting(); mMinimumBacklightForVr = pm.getMinimumScreenBrightnessForVrSetting(); mMaximumBacklightForVr = pm.getMaximumScreenBrightnessForVrSetting(); mDefaultBacklightForVr = pm.getDefaultScreenBrightnessForVrSetting(); mAutomaticAvailable = context.getResources().getBoolean( com.android.internal.R.bool.config_automatic_brightness_available); Loading Loading @@ -350,39 +359,40 @@ public class BrightnessController implements ToggleSlider.Listener { mSliderAnimator.cancel(); } final int min; final int max; final int metric; final String setting; if (mIsVrModeEnabled) { final int val = value + mMinimumBacklightForVr; if (stopTracking) { MetricsLogger.action(mContext, MetricsEvent.ACTION_BRIGHTNESS_FOR_VR, val); } setBrightness(val); if (!tracking) { AsyncTask.execute(new Runnable() { public void run() { Settings.System.putIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FOR_VR, val, UserHandle.USER_CURRENT); } }); } metric = MetricsEvent.ACTION_BRIGHTNESS_FOR_VR; min = mMinimumBacklightForVr; max = mMaximumBacklightForVr; setting = Settings.System.SCREEN_BRIGHTNESS_FOR_VR; } else { final int val = value + mMinimumBacklight; metric = mAutomatic ? MetricsEvent.ACTION_BRIGHTNESS_AUTO : MetricsEvent.ACTION_BRIGHTNESS; min = mMinimumBacklight; max = mMaximumBacklight; setting = Settings.System.SCREEN_BRIGHTNESS; } final int val = convertGammaToLinear(value, min, max); if (stopTracking) { final int metric = mAutomatic ? MetricsEvent.ACTION_BRIGHTNESS_AUTO : MetricsEvent.ACTION_BRIGHTNESS; MetricsLogger.action(mContext, metric, val); } setBrightness(val); if (!tracking) { AsyncTask.execute(new Runnable() { public void run() { Settings.System.putIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, val, UserHandle.USER_CURRENT); setting, val, UserHandle.USER_CURRENT); } }); } } for (BrightnessStateChangeCallback cb : mChangeCallbacks) { cb.onBrightnessLevelChanged(); Loading Loading @@ -430,6 +440,28 @@ public class BrightnessController implements ToggleSlider.Listener { } } private void updateSlider(int val, boolean inVrMode) { final int min; final int max; if (inVrMode) { min = mMinimumBacklightForVr; max = mMaximumBacklightForVr; } else { min = mMinimumBacklight; max = mMaximumBacklight; } if (val == convertGammaToLinear(mControl.getValue(), min, max)) { // If we have more resolution on the slider than we do in the actual setting, then // multiple slider positions will map to the same setting value. Thus, if we see a // setting value here that maps to the current slider position, we don't bother to // calculate the new slider position since it may differ and look like a brightness // change to the user even though it isn't one. return; } final int sliderVal = convertLinearToGamma(val, min, max); animateSliderTo(sliderVal); } private void animateSliderTo(int target) { if (!mControlValueInitialized) { // Don't animate the first value since it's default state isn't meaningful to users. Loading @@ -448,4 +480,77 @@ public class BrightnessController implements ToggleSlider.Listener { mSliderAnimator.setDuration(SLIDER_ANIMATION_DURATION); mSliderAnimator.start(); } /** * A function for converting from the linear space that the setting works in to the * gamma space that the slider works in. * * The gamma space effectively provides us a way to make linear changes to the slider that * result in linear changes in perception. If we made changes to the slider in the linear space * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law). * * Internally, this implements the Hybrid Log Gamma opto-electronic transfer function, which is * a slight improvement to the typical gamma transfer function for displays whose max * brightness exceeds the 120 nit reference point, but doesn't set a specific reference * brightness like the PQ function does. * * Note that this transfer function is only valid if the display's backlight value is a linear * control. If it's calibrated to be something non-linear, then a different transfer function * should be used. * * @param val The brightness setting value. * @param min The minimum acceptable value for the setting. * @param max The maximum acceptable value for the setting. * * @return The corresponding slider value */ private static final int convertLinearToGamma(int val, int min, int max) { // For some reason, HLG normalizes to the range [0, 12] rather than [0, 1] final float normalizedVal = MathUtils.norm(min, max, val) * 12; final float ret; if (normalizedVal <= 1f) { ret = MathUtils.sqrt(normalizedVal) * R; } else { ret = A * MathUtils.log(normalizedVal - B) + C; } return Math.round(MathUtils.lerp(0, SLIDER_MAX, ret)); } /** * A function for converting from the gamma space that the slider works in to the * linear space that the setting works in. * * The gamma space effectively provides us a way to make linear changes to the slider that * result in linear changes in perception. If we made changes to the slider in the linear space * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law). * * Internally, this implements the Hybrid Log Gamma electro-optical transfer function, which is * a slight improvement to the typical gamma transfer function for displays whose max * brightness exceeds the 120 nit reference point, but doesn't set a specific reference * brightness like the PQ function does. * * Note that this transfer function is only valid if the display's backlight value is a linear * control. If it's calibrated to be something non-linear, then a different transfer function * should be used. * * @param val The slider value. * @param min The minimum acceptable value for the setting. * @param max The maximum acceptable value for the setting. * * @return The corresponding setting value. */ private static final int convertGammaToLinear(int val, int min, int max) { final float normalizedVal = MathUtils.norm(0, SLIDER_MAX, val); final float ret; if (normalizedVal <= R) { ret = MathUtils.sq(normalizedVal/R); } else { ret = MathUtils.exp((normalizedVal - C) / A) + B; } // HLG is normalized to the range [0, 12], so we need to re-normalize to the range [0, 1] // in order to derive the correct setting value. return Math.round(MathUtils.lerp(min, max, ret / 12)); } } Loading
core/java/android/util/MathUtils.java +4 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,10 @@ public final class MathUtils { return (float) Math.pow(a, b); } public static float sqrt(float a) { return (float) Math.sqrt(a); } public static float max(float a, float b) { return a > b ? a : b; } Loading
packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +149 −44 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.provider.Settings; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.util.Log; import android.util.MathUtils; import android.widget.ImageView; import com.android.internal.logging.MetricsLogger; Loading @@ -49,8 +50,15 @@ public class BrightnessController implements ToggleSlider.Listener { private static final String TAG = "StatusBar.BrightnessController"; private static final boolean SHOW_AUTOMATIC_ICON = false; private static final int SLIDER_MAX = 1023; private static final int SLIDER_ANIMATION_DURATION = 3000; // Hybrid Log Gamma constant values private static final float R = 0.5f; private static final float A = 0.17883277f; private static final float B = 0.28466892f; private static final float C = 0.55991073f; private static final int MSG_UPDATE_ICON = 0; private static final int MSG_UPDATE_SLIDER = 1; private static final int MSG_SET_CHECKED = 2; Loading @@ -60,8 +68,10 @@ public class BrightnessController implements ToggleSlider.Listener { private final int mMinimumBacklight; private final int mMaximumBacklight; private final int mDefaultBacklight; private final int mMinimumBacklightForVr; private final int mMaximumBacklightForVr; private final int mDefaultBacklightForVr; private final Context mContext; private final ImageView mIcon; Loading Loading @@ -203,21 +213,18 @@ public class BrightnessController implements ToggleSlider.Listener { private final Runnable mUpdateSliderRunnable = new Runnable() { @Override public void run() { if (mIsVrModeEnabled) { int value = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mMaximumBacklight, final int val; final boolean inVrMode = mIsVrModeEnabled; if (inVrMode) { val = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mDefaultBacklightForVr, UserHandle.USER_CURRENT); mHandler.obtainMessage(MSG_UPDATE_SLIDER, mMaximumBacklightForVr - mMinimumBacklightForVr, value - mMinimumBacklightForVr).sendToTarget(); } else { int value; value = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, mMaximumBacklight, val = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, mDefaultBacklight, UserHandle.USER_CURRENT); mHandler.obtainMessage(MSG_UPDATE_SLIDER, mMaximumBacklight - mMinimumBacklight, value - mMinimumBacklight).sendToTarget(); } mHandler.obtainMessage(MSG_UPDATE_SLIDER, val, inVrMode ? 1 : 0).sendToTarget(); } }; Loading @@ -239,8 +246,7 @@ public class BrightnessController implements ToggleSlider.Listener { updateIcon(msg.arg1 != 0); break; case MSG_UPDATE_SLIDER: mControl.setMax(msg.arg1); animateSliderTo(msg.arg2); updateSlider(msg.arg1, msg.arg2 != 0); break; case MSG_SET_CHECKED: mControl.setChecked(msg.arg1 != 0); Loading @@ -267,6 +273,7 @@ public class BrightnessController implements ToggleSlider.Listener { mContext = context; mIcon = icon; mControl = control; mControl.setMax(SLIDER_MAX); mBackgroundHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER)); mUserTracker = new CurrentUserTracker(mContext) { @Override Loading @@ -277,11 +284,13 @@ public class BrightnessController implements ToggleSlider.Listener { }; mBrightnessObserver = new BrightnessObserver(mHandler); PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); PowerManager pm = context.getSystemService(PowerManager.class); mMinimumBacklight = pm.getMinimumScreenBrightnessSetting(); mMaximumBacklight = pm.getMaximumScreenBrightnessSetting(); mDefaultBacklight = pm.getDefaultScreenBrightnessSetting(); mMinimumBacklightForVr = pm.getMinimumScreenBrightnessForVrSetting(); mMaximumBacklightForVr = pm.getMaximumScreenBrightnessForVrSetting(); mDefaultBacklightForVr = pm.getDefaultScreenBrightnessForVrSetting(); mAutomaticAvailable = context.getResources().getBoolean( com.android.internal.R.bool.config_automatic_brightness_available); Loading Loading @@ -350,39 +359,40 @@ public class BrightnessController implements ToggleSlider.Listener { mSliderAnimator.cancel(); } final int min; final int max; final int metric; final String setting; if (mIsVrModeEnabled) { final int val = value + mMinimumBacklightForVr; if (stopTracking) { MetricsLogger.action(mContext, MetricsEvent.ACTION_BRIGHTNESS_FOR_VR, val); } setBrightness(val); if (!tracking) { AsyncTask.execute(new Runnable() { public void run() { Settings.System.putIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FOR_VR, val, UserHandle.USER_CURRENT); } }); } metric = MetricsEvent.ACTION_BRIGHTNESS_FOR_VR; min = mMinimumBacklightForVr; max = mMaximumBacklightForVr; setting = Settings.System.SCREEN_BRIGHTNESS_FOR_VR; } else { final int val = value + mMinimumBacklight; metric = mAutomatic ? MetricsEvent.ACTION_BRIGHTNESS_AUTO : MetricsEvent.ACTION_BRIGHTNESS; min = mMinimumBacklight; max = mMaximumBacklight; setting = Settings.System.SCREEN_BRIGHTNESS; } final int val = convertGammaToLinear(value, min, max); if (stopTracking) { final int metric = mAutomatic ? MetricsEvent.ACTION_BRIGHTNESS_AUTO : MetricsEvent.ACTION_BRIGHTNESS; MetricsLogger.action(mContext, metric, val); } setBrightness(val); if (!tracking) { AsyncTask.execute(new Runnable() { public void run() { Settings.System.putIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, val, UserHandle.USER_CURRENT); setting, val, UserHandle.USER_CURRENT); } }); } } for (BrightnessStateChangeCallback cb : mChangeCallbacks) { cb.onBrightnessLevelChanged(); Loading Loading @@ -430,6 +440,28 @@ public class BrightnessController implements ToggleSlider.Listener { } } private void updateSlider(int val, boolean inVrMode) { final int min; final int max; if (inVrMode) { min = mMinimumBacklightForVr; max = mMaximumBacklightForVr; } else { min = mMinimumBacklight; max = mMaximumBacklight; } if (val == convertGammaToLinear(mControl.getValue(), min, max)) { // If we have more resolution on the slider than we do in the actual setting, then // multiple slider positions will map to the same setting value. Thus, if we see a // setting value here that maps to the current slider position, we don't bother to // calculate the new slider position since it may differ and look like a brightness // change to the user even though it isn't one. return; } final int sliderVal = convertLinearToGamma(val, min, max); animateSliderTo(sliderVal); } private void animateSliderTo(int target) { if (!mControlValueInitialized) { // Don't animate the first value since it's default state isn't meaningful to users. Loading @@ -448,4 +480,77 @@ public class BrightnessController implements ToggleSlider.Listener { mSliderAnimator.setDuration(SLIDER_ANIMATION_DURATION); mSliderAnimator.start(); } /** * A function for converting from the linear space that the setting works in to the * gamma space that the slider works in. * * The gamma space effectively provides us a way to make linear changes to the slider that * result in linear changes in perception. If we made changes to the slider in the linear space * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law). * * Internally, this implements the Hybrid Log Gamma opto-electronic transfer function, which is * a slight improvement to the typical gamma transfer function for displays whose max * brightness exceeds the 120 nit reference point, but doesn't set a specific reference * brightness like the PQ function does. * * Note that this transfer function is only valid if the display's backlight value is a linear * control. If it's calibrated to be something non-linear, then a different transfer function * should be used. * * @param val The brightness setting value. * @param min The minimum acceptable value for the setting. * @param max The maximum acceptable value for the setting. * * @return The corresponding slider value */ private static final int convertLinearToGamma(int val, int min, int max) { // For some reason, HLG normalizes to the range [0, 12] rather than [0, 1] final float normalizedVal = MathUtils.norm(min, max, val) * 12; final float ret; if (normalizedVal <= 1f) { ret = MathUtils.sqrt(normalizedVal) * R; } else { ret = A * MathUtils.log(normalizedVal - B) + C; } return Math.round(MathUtils.lerp(0, SLIDER_MAX, ret)); } /** * A function for converting from the gamma space that the slider works in to the * linear space that the setting works in. * * The gamma space effectively provides us a way to make linear changes to the slider that * result in linear changes in perception. If we made changes to the slider in the linear space * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law). * * Internally, this implements the Hybrid Log Gamma electro-optical transfer function, which is * a slight improvement to the typical gamma transfer function for displays whose max * brightness exceeds the 120 nit reference point, but doesn't set a specific reference * brightness like the PQ function does. * * Note that this transfer function is only valid if the display's backlight value is a linear * control. If it's calibrated to be something non-linear, then a different transfer function * should be used. * * @param val The slider value. * @param min The minimum acceptable value for the setting. * @param max The maximum acceptable value for the setting. * * @return The corresponding setting value. */ private static final int convertGammaToLinear(int val, int min, int max) { final float normalizedVal = MathUtils.norm(0, SLIDER_MAX, val); final float ret; if (normalizedVal <= R) { ret = MathUtils.sq(normalizedVal/R); } else { ret = MathUtils.exp((normalizedVal - C) / A) + B; } // HLG is normalized to the range [0, 12], so we need to re-normalize to the range [0, 1] // in order to derive the correct setting value. return Math.round(MathUtils.lerp(min, max, ret / 12)); } }