Dan Gittik | a5a2d63 | 2019-01-09 14:25:29 +0000 | [diff] [blame] | 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 android.hardware.display; |
| 18 | |
Kenny Guy | 0a9e341 | 2019-02-25 16:23:48 +0000 | [diff] [blame] | 19 | import android.annotation.FloatRange; |
Dan Gittik | a5a2d63 | 2019-01-09 14:25:29 +0000 | [diff] [blame] | 20 | import android.annotation.NonNull; |
| 21 | import android.annotation.SystemApi; |
Kenny Guy | 9524ff9 | 2019-01-17 15:16:01 +0000 | [diff] [blame] | 22 | import android.annotation.TestApi; |
Dan Gittik | a5a2d63 | 2019-01-09 14:25:29 +0000 | [diff] [blame] | 23 | import android.os.Parcel; |
| 24 | import android.os.Parcelable; |
| 25 | import android.util.MathUtils; |
| 26 | |
| 27 | import com.android.internal.util.XmlUtils; |
| 28 | |
| 29 | import org.xmlpull.v1.XmlPullParser; |
| 30 | import org.xmlpull.v1.XmlPullParserException; |
| 31 | import org.xmlpull.v1.XmlSerializer; |
| 32 | |
| 33 | import 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 Guy | 9524ff9 | 2019-01-17 15:16:01 +0000 | [diff] [blame] | 46 | @TestApi |
Dan Gittik | a5a2d63 | 2019-01-09 14:25:29 +0000 | [diff] [blame] | 47 | public 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 Guy | 0a9e341 | 2019-02-25 16:23:48 +0000 | [diff] [blame] | 63 | * {@code exp(scale * ln(brightness) + translate)}. |
Dan Gittik | a5a2d63 | 2019-01-09 14:25:29 +0000 | [diff] [blame] | 64 | * |
| 65 | * @param scale |
Kenny Guy | 0a9e341 | 2019-02-25 16:23:48 +0000 | [diff] [blame] | 66 | * How much to scale the log (base e) brightness. |
Dan Gittik | a5a2d63 | 2019-01-09 14:25:29 +0000 | [diff] [blame] | 67 | * @param translate |
Kenny Guy | 0a9e341 | 2019-02-25 16:23:48 +0000 | [diff] [blame] | 68 | * How much to translate the log (base e) brightness. |
Dan Gittik | a5a2d63 | 2019-01-09 14:25:29 +0000 | [diff] [blame] | 69 | * |
| 70 | * @return A BrightnessCorrection that given {@code brightness}, corrects it to be |
Kenny Guy | 0a9e341 | 2019-02-25 16:23:48 +0000 | [diff] [blame] | 71 | * {@code exp(scale * ln(brightness) + translate)}. |
Dan Gittik | a5a2d63 | 2019-01-09 14:25:29 +0000 | [diff] [blame] | 72 | * |
| 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 Guy | 0a9e341 | 2019-02-25 16:23:48 +0000 | [diff] [blame] | 91 | @FloatRange(from = 0.0) |
| 92 | public float apply(@FloatRange(from = 0.0) float brightness) { |
Dan Gittik | a5a2d63 | 2019-01-09 14:25:29 +0000 | [diff] [blame] | 93 | 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 Guy | 9524ff9 | 2019-01-17 15:16:01 +0000 | [diff] [blame] | 105 | |
| 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 | |
Jeff Sharkey | 9e8f83d | 2019-02-28 12:06:45 -0700 | [diff] [blame] | 123 | public static final @android.annotation.NonNull Creator<BrightnessCorrection> CREATOR = |
Dan Gittik | a5a2d63 | 2019-01-09 14:25:29 +0000 | [diff] [blame] | 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 Guy | 0a9e341 | 2019-02-25 16:23:48 +0000 | [diff] [blame] | 207 | * {@code exp(scale * ln(brightness) + translate)}. |
Dan Gittik | a5a2d63 | 2019-01-09 14:25:29 +0000 | [diff] [blame] | 208 | */ |
| 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 Guy | 9524ff9 | 2019-01-17 15:16:01 +0000 | [diff] [blame] | 240 | 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 Gittik | a5a2d63 | 2019-01-09 14:25:29 +0000 | [diff] [blame] | 260 | 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 | } |