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.settingslib.display; 18 19 import android.util.MathUtils; 20 21 public class BrightnessUtils { 22 23 public static final int GAMMA_SPACE_MAX = 1023; 24 25 // Hybrid Log Gamma constant values 26 private static final float R = 0.5f; 27 private static final float A = 0.17883277f; 28 private static final float B = 0.28466892f; 29 private static final float C = 0.55991073f; 30 31 /** 32 * A function for converting from the gamma space that the slider works in to the 33 * linear space that the setting works in. 34 * 35 * The gamma space effectively provides us a way to make linear changes to the slider that 36 * result in linear changes in perception. If we made changes to the slider in the linear space 37 * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law). 38 * 39 * Internally, this implements the Hybrid Log Gamma electro-optical transfer function, which is 40 * a slight improvement to the typical gamma transfer function for displays whose max 41 * brightness exceeds the 120 nit reference point, but doesn't set a specific reference 42 * brightness like the PQ function does. 43 * 44 * Note that this transfer function is only valid if the display's backlight value is a linear 45 * control. If it's calibrated to be something non-linear, then a different transfer function 46 * should be used. 47 * 48 * @param val The slider value. 49 * @param min The minimum acceptable value for the setting. 50 * @param max The maximum acceptable value for the setting. 51 * @return The corresponding setting value. 52 */ convertGammaToLinear(int val, int min, int max)53 public static final int convertGammaToLinear(int val, int min, int max) { 54 final float normalizedVal = MathUtils.norm(0, GAMMA_SPACE_MAX, val); 55 final float ret; 56 if (normalizedVal <= R) { 57 ret = MathUtils.sq(normalizedVal / R); 58 } else { 59 ret = MathUtils.exp((normalizedVal - C) / A) + B; 60 } 61 62 // HLG is normalized to the range [0, 12], so we need to re-normalize to the range [0, 1] 63 // in order to derive the correct setting value. 64 return Math.round(MathUtils.lerp(min, max, ret / 12)); 65 } 66 67 /** 68 * A function for converting from the linear space that the setting works in to the 69 * gamma space that the slider works in. 70 * 71 * The gamma space effectively provides us a way to make linear changes to the slider that 72 * result in linear changes in perception. If we made changes to the slider in the linear space 73 * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law). 74 * 75 * Internally, this implements the Hybrid Log Gamma opto-electronic transfer function, which is 76 * a slight improvement to the typical gamma transfer function for displays whose max 77 * brightness exceeds the 120 nit reference point, but doesn't set a specific reference 78 * brightness like the PQ function does. 79 * 80 * Note that this transfer function is only valid if the display's backlight value is a linear 81 * control. If it's calibrated to be something non-linear, then a different transfer function 82 * should be used. 83 * 84 * @param val The brightness setting value. 85 * @param min The minimum acceptable value for the setting. 86 * @param max The maximum acceptable value for the setting. 87 * @return The corresponding slider value 88 */ convertLinearToGamma(int val, int min, int max)89 public static final int convertLinearToGamma(int val, int min, int max) { 90 // For some reason, HLG normalizes to the range [0, 12] rather than [0, 1] 91 final float normalizedVal = MathUtils.norm(min, max, val) * 12; 92 final float ret; 93 if (normalizedVal <= 1f) { 94 ret = MathUtils.sqrt(normalizedVal) * R; 95 } else { 96 ret = A * MathUtils.log(normalizedVal - B) + C; 97 } 98 99 return Math.round(MathUtils.lerp(0, GAMMA_SPACE_MAX, ret)); 100 } 101 } 102