• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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