blob: ee8d8467d827549333a91ac7cc98df00879a7d8c [file] [log] [blame]
Dan Gittika5a2d632019-01-09 14:25:29 +00001/*
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
17package android.hardware.display;
18
Kenny Guy0a9e3412019-02-25 16:23:48 +000019import android.annotation.FloatRange;
Dan Gittika5a2d632019-01-09 14:25:29 +000020import android.annotation.NonNull;
21import android.annotation.SystemApi;
Kenny Guy9524ff92019-01-17 15:16:01 +000022import android.annotation.TestApi;
Dan Gittika5a2d632019-01-09 14:25:29 +000023import android.os.Parcel;
24import android.os.Parcelable;
25import android.util.MathUtils;
26
27import com.android.internal.util.XmlUtils;
28
29import org.xmlpull.v1.XmlPullParser;
30import org.xmlpull.v1.XmlPullParserException;
31import org.xmlpull.v1.XmlSerializer;
32
33import java.io.IOException;
34
35/**
36 * BrightnessCorrection encapsulates a correction to the brightness, without comitting to the
37 * actual correction scheme.
38 * It is used by the BrightnessConfiguration, which maps context (e.g. the foreground app's package
39 * name and category) to corrections that need to be applied to the brightness within that context.
40 * Corrections are currently done by the app that has the top activity of the focused stack, either
41 * by its package name, or (if its package name is not mapped to any correction) by its category.
42 *
43 * @hide
44 */
45@SystemApi
Kenny Guy9524ff92019-01-17 15:16:01 +000046@TestApi
Dan Gittika5a2d632019-01-09 14:25:29 +000047public final class BrightnessCorrection implements Parcelable {
48
49 private static final int SCALE_AND_TRANSLATE_LOG = 1;
50
51 private static final String TAG_SCALE_AND_TRANSLATE_LOG = "scale-and-translate-log";
52
53 private BrightnessCorrectionImplementation mImplementation;
54
55 // Parcelable classes must be final, and protected methods are not allowed in APIs, so we can't
56 // make this class abstract and use composition instead of inheritence.
57 private BrightnessCorrection(BrightnessCorrectionImplementation implementation) {
58 mImplementation = implementation;
59 }
60
61 /**
62 * Creates a BrightnessCorrection that given {@code brightness}, corrects it to be
Kenny Guy0a9e3412019-02-25 16:23:48 +000063 * {@code exp(scale * ln(brightness) + translate)}.
Dan Gittika5a2d632019-01-09 14:25:29 +000064 *
65 * @param scale
Kenny Guy0a9e3412019-02-25 16:23:48 +000066 * How much to scale the log (base e) brightness.
Dan Gittika5a2d632019-01-09 14:25:29 +000067 * @param translate
Kenny Guy0a9e3412019-02-25 16:23:48 +000068 * How much to translate the log (base e) brightness.
Dan Gittika5a2d632019-01-09 14:25:29 +000069 *
70 * @return A BrightnessCorrection that given {@code brightness}, corrects it to be
Kenny Guy0a9e3412019-02-25 16:23:48 +000071 * {@code exp(scale * ln(brightness) + translate)}.
Dan Gittika5a2d632019-01-09 14:25:29 +000072 *
73 * @throws IllegalArgumentException
74 * - scale or translate are NaN.
75 */
76 @NonNull
77 public static BrightnessCorrection createScaleAndTranslateLog(float scale, float translate) {
78 BrightnessCorrectionImplementation implementation =
79 new ScaleAndTranslateLog(scale, translate);
80 return new BrightnessCorrection(implementation);
81 }
82
83 /**
84 * Applies the brightness correction to a given brightness.
85 *
86 * @param brightness
87 * The brightness.
88 *
89 * @return The corrected brightness.
90 */
Kenny Guy0a9e3412019-02-25 16:23:48 +000091 @FloatRange(from = 0.0)
92 public float apply(@FloatRange(from = 0.0) float brightness) {
Dan Gittika5a2d632019-01-09 14:25:29 +000093 return mImplementation.apply(brightness);
94 }
95
96 /**
97 * Returns a string representation.
98 *
99 * @return A string representation.
100 */
101 public String toString() {
102 return mImplementation.toString();
103 }
104
Kenny Guy9524ff92019-01-17 15:16:01 +0000105
106 @Override
107 public boolean equals(Object o) {
108 if (o == this) {
109 return true;
110 }
111 if (!(o instanceof BrightnessCorrection)) {
112 return false;
113 }
114 BrightnessCorrection other = (BrightnessCorrection) o;
115 return other.mImplementation.equals(mImplementation);
116 }
117
118 @Override
119 public int hashCode() {
120 return mImplementation.hashCode();
121 }
122
Dan Gittika5a2d632019-01-09 14:25:29 +0000123 public static final Creator<BrightnessCorrection> CREATOR =
124 new Creator<BrightnessCorrection>() {
125 public BrightnessCorrection createFromParcel(Parcel in) {
126 final int type = in.readInt();
127 switch (type) {
128 case SCALE_AND_TRANSLATE_LOG:
129 return ScaleAndTranslateLog.readFromParcel(in);
130 }
131 return null;
132 }
133
134 public BrightnessCorrection[] newArray(int size) {
135 return new BrightnessCorrection[size];
136 }
137 };
138
139 @Override
140 public void writeToParcel(Parcel dest, int flags) {
141 mImplementation.writeToParcel(dest);
142 }
143
144 @Override
145 public int describeContents() {
146 return 0;
147 }
148
149 /**
150 * Writes the correction to an XML serializer.
151 *
152 * @param serializer
153 * The XML serializer.
154 *
155 * @hide
156 */
157 public void saveToXml(XmlSerializer serializer) throws IOException {
158 mImplementation.saveToXml(serializer);
159 }
160
161 /**
162 * Read a correction from an XML parser.
163 *
164 * @param parser
165 * The XML parser.
166 *
167 * @throws IOException
168 * The parser failed to read the XML file.
169 * @throws XmlPullParserException
170 * The parser failed to parse the XML file.
171 *
172 * @hide
173 */
174 public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
175 XmlPullParserException {
176 final int depth = parser.getDepth();
177 while (XmlUtils.nextElementWithin(parser, depth)) {
178 if (TAG_SCALE_AND_TRANSLATE_LOG.equals(parser.getName())) {
179 return ScaleAndTranslateLog.loadFromXml(parser);
180 }
181 }
182 return null;
183 }
184
185 private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
186 final String string = parser.getAttributeValue(null, attribute);
187 try {
188 return Float.parseFloat(string);
189 } catch (NullPointerException | NumberFormatException e) {
190 return Float.NaN;
191 }
192 }
193
194 private interface BrightnessCorrectionImplementation {
195 float apply(float brightness);
196 String toString();
197 void writeToParcel(Parcel dest);
198 void saveToXml(XmlSerializer serializer) throws IOException;
199 // Package-private static methods:
200 // static BrightnessCorrection readFromParcel(Parcel in);
201 // static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
202 // XmlPullParserException;
203 }
204
205 /**
206 * A BrightnessCorrection that given {@code brightness}, corrects it to be
Kenny Guy0a9e3412019-02-25 16:23:48 +0000207 * {@code exp(scale * ln(brightness) + translate)}.
Dan Gittika5a2d632019-01-09 14:25:29 +0000208 */
209 private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation {
210 private static final float MIN_SCALE = 0.5f;
211 private static final float MAX_SCALE = 2.0f;
212 private static final float MIN_TRANSLATE = -0.6f;
213 private static final float MAX_TRANSLATE = 0.7f;
214
215 private static final String ATTR_SCALE = "scale";
216 private static final String ATTR_TRANSLATE = "translate";
217
218 private final float mScale;
219 private final float mTranslate;
220
221 ScaleAndTranslateLog(float scale, float translate) {
222 if (Float.isNaN(scale) || Float.isNaN(translate)) {
223 throw new IllegalArgumentException("scale and translate must be numbers");
224 }
225 mScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
226 mTranslate = MathUtils.constrain(translate, MIN_TRANSLATE, MAX_TRANSLATE);
227 }
228
229 @Override
230 public float apply(float brightness) {
231 return MathUtils.exp(mScale * MathUtils.log(brightness) + mTranslate);
232 }
233
234 @Override
235 public String toString() {
236 return "ScaleAndTranslateLog(" + mScale + ", " + mTranslate + ")";
237 }
238
239 @Override
Kenny Guy9524ff92019-01-17 15:16:01 +0000240 public boolean equals(Object o) {
241 if (o == this) {
242 return true;
243 }
244 if (!(o instanceof ScaleAndTranslateLog)) {
245 return false;
246 }
247 ScaleAndTranslateLog other = (ScaleAndTranslateLog) o;
248 return other.mScale == mScale && other.mTranslate == mTranslate;
249 }
250
251 @Override
252 public int hashCode() {
253 int result = 1;
254 result = result * 31 + Float.hashCode(mScale);
255 result = result * 31 + Float.hashCode(mTranslate);
256 return result;
257 }
258
259 @Override
Dan Gittika5a2d632019-01-09 14:25:29 +0000260 public void writeToParcel(Parcel dest) {
261 dest.writeInt(SCALE_AND_TRANSLATE_LOG);
262 dest.writeFloat(mScale);
263 dest.writeFloat(mTranslate);
264 }
265
266 @Override
267 public void saveToXml(XmlSerializer serializer) throws IOException {
268 serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
269 serializer.attribute(null, ATTR_SCALE, Float.toString(mScale));
270 serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate));
271 serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
272 }
273
274 static BrightnessCorrection readFromParcel(Parcel in) {
275 float scale = in.readFloat();
276 float translate = in.readFloat();
277 return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
278 }
279
280 static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
281 XmlPullParserException {
282 final float scale = loadFloatFromXml(parser, ATTR_SCALE);
283 final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE);
284 return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
285 }
286 }
287}