1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.util; 18 19 /** 20 * This is the minimized version of {@code com.android.settingslib.display.BrightnessUtils} not to 21 * depend on the library which uses the hidden api. 22 */ 23 public class BrightnessUtils { 24 25 public static final int GAMMA_SPACE_MIN = 0; 26 public static final int GAMMA_SPACE_MAX = 65535; 27 28 // Hybrid Log Gamma constant values 29 private static final float R = 0.5f; 30 private static final float A = 0.17883277f; 31 private static final float B = 0.28466892f; 32 private static final float C = 0.55991073f; 33 34 /** 35 * A function for converting from the gamma space that the slider works in to the 36 * linear space that the setting works in. 37 * 38 * The gamma space effectively provides us a way to make linear changes to the slider that 39 * result in linear changes in perception. If we made changes to the slider in the linear space 40 * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law). 41 * 42 * Internally, this implements the Hybrid Log Gamma electro-optical transfer function, which is 43 * a slight improvement to the typical gamma transfer function for displays whose max 44 * brightness exceeds the 120 nit reference point, but doesn't set a specific reference 45 * brightness like the PQ function does. 46 * 47 * Note that this transfer function is only valid if the display's backlight value is a linear 48 * control. If it's calibrated to be something non-linear, then a different transfer function 49 * should be used. 50 * 51 * @param val The slider value. 52 * @param min The minimum acceptable value for the setting. 53 * @param max The maximum acceptable value for the setting. 54 * @return The corresponding setting value. 55 */ convertGammaToLinear(int val, int min, int max)56 public static final int convertGammaToLinear(int val, int min, int max) { 57 final float normalizedVal = MathUtils.norm(GAMMA_SPACE_MIN, GAMMA_SPACE_MAX, val); 58 final float ret; 59 if (normalizedVal <= R) { 60 ret = MathUtils.sq(normalizedVal / R); 61 } else { 62 ret = MathUtils.exp((normalizedVal - C) / A) + B; 63 } 64 65 // HLG is normalized to the range [0, 12], so we need to re-normalize to the range [0, 1] 66 // in order to derive the correct setting value. 67 return Math.round(MathUtils.lerp(min, max, ret / 12)); 68 } 69 70 /** 71 * A function for converting from the linear space that the setting works in to the 72 * gamma space that the slider works in. 73 * 74 * The gamma space effectively provides us a way to make linear changes to the slider that 75 * result in linear changes in perception. If we made changes to the slider in the linear space 76 * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law). 77 * 78 * Internally, this implements the Hybrid Log Gamma opto-electronic transfer function, which is 79 * a slight improvement to the typical gamma transfer function for displays whose max 80 * brightness exceeds the 120 nit reference point, but doesn't set a specific reference 81 * brightness like the PQ function does. 82 * 83 * Note that this transfer function is only valid if the display's backlight value is a linear 84 * control. If it's calibrated to be something non-linear, then a different transfer function 85 * should be used. 86 * 87 * @param val The brightness setting value. 88 * @param min The minimum acceptable value for the setting. 89 * @param max The maximum acceptable value for the setting. 90 * @return The corresponding slider value 91 */ convertLinearToGamma(int val, int min, int max)92 public static final int convertLinearToGamma(int val, int min, int max) { 93 return convertLinearToGammaFloat((float) val, (float) min, (float) max); 94 } 95 96 /** 97 * Version of {@link #convertLinearToGamma} that takes float values. 98 * TODO: brightnessfloat merge with above method(?) 99 * @param val The brightness setting value. 100 * @param min The minimum acceptable value for the setting. 101 * @param max The maximum acceptable value for the setting. 102 * @return The corresponding slider value 103 */ convertLinearToGammaFloat(float val, float min, float max)104 public static final int convertLinearToGammaFloat(float val, float min, float max) { 105 // For some reason, HLG normalizes to the range [0, 12] rather than [0, 1] 106 final float normalizedVal = MathUtils.norm(min, max, val) * 12; 107 final float ret; 108 if (normalizedVal <= 1f) { 109 ret = MathUtils.sqrt(normalizedVal) * R; 110 } else { 111 ret = A * MathUtils.log(normalizedVal - B) + C; 112 } 113 114 return Math.round(MathUtils.lerp(GAMMA_SPACE_MIN, GAMMA_SPACE_MAX, ret)); 115 } 116 117 /** 118 * This is the minimized version of {@code android.util.MathUtils} which is the hidden api. 119 */ 120 private static final class MathUtils { MathUtils()121 private MathUtils() { 122 } 123 log(float a)124 public static float log(float a) { 125 return (float) Math.log(a); 126 } 127 exp(float a)128 public static float exp(float a) { 129 return (float) Math.exp(a); 130 } 131 sqrt(float a)132 public static float sqrt(float a) { 133 return (float) Math.sqrt(a); 134 } 135 sq(float v)136 public static float sq(float v) { 137 return v * v; 138 } 139 lerp(float start, float stop, float amount)140 public static float lerp(float start, float stop, float amount) { 141 return start + (stop - start) * amount; 142 } 143 norm(float start, float stop, float value)144 public static float norm(float start, float stop, float value) { 145 return (value - start) / (stop - start); 146 } 147 } 148 } 149