| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.hardware.display; |
| |
| import android.annotation.NonNull; |
| import android.annotation.SystemApi; |
| import android.annotation.TestApi; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.util.MathUtils; |
| |
| import com.android.internal.util.XmlUtils; |
| |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| import org.xmlpull.v1.XmlSerializer; |
| |
| import java.io.IOException; |
| |
| /** |
| * BrightnessCorrection encapsulates a correction to the brightness, without comitting to the |
| * actual correction scheme. |
| * It is used by the BrightnessConfiguration, which maps context (e.g. the foreground app's package |
| * name and category) to corrections that need to be applied to the brightness within that context. |
| * Corrections are currently done by the app that has the top activity of the focused stack, either |
| * by its package name, or (if its package name is not mapped to any correction) by its category. |
| * |
| * @hide |
| */ |
| @SystemApi |
| @TestApi |
| public final class BrightnessCorrection implements Parcelable { |
| |
| private static final int SCALE_AND_TRANSLATE_LOG = 1; |
| |
| private static final String TAG_SCALE_AND_TRANSLATE_LOG = "scale-and-translate-log"; |
| |
| private BrightnessCorrectionImplementation mImplementation; |
| |
| // Parcelable classes must be final, and protected methods are not allowed in APIs, so we can't |
| // make this class abstract and use composition instead of inheritence. |
| private BrightnessCorrection(BrightnessCorrectionImplementation implementation) { |
| mImplementation = implementation; |
| } |
| |
| /** |
| * Creates a BrightnessCorrection that given {@code brightness}, corrects it to be |
| * {@code exp(scale * log(brightness) + translate)}. |
| * |
| * @param scale |
| * How much to scale the log brightness. |
| * @param translate |
| * How much to translate the log brightness. |
| * |
| * @return A BrightnessCorrection that given {@code brightness}, corrects it to be |
| * {@code exp(scale * log(brightness) + translate)}. |
| * |
| * @throws IllegalArgumentException |
| * - scale or translate are NaN. |
| */ |
| @NonNull |
| public static BrightnessCorrection createScaleAndTranslateLog(float scale, float translate) { |
| BrightnessCorrectionImplementation implementation = |
| new ScaleAndTranslateLog(scale, translate); |
| return new BrightnessCorrection(implementation); |
| } |
| |
| /** |
| * Applies the brightness correction to a given brightness. |
| * |
| * @param brightness |
| * The brightness. |
| * |
| * @return The corrected brightness. |
| */ |
| public float apply(float brightness) { |
| return mImplementation.apply(brightness); |
| } |
| |
| /** |
| * Returns a string representation. |
| * |
| * @return A string representation. |
| */ |
| public String toString() { |
| return mImplementation.toString(); |
| } |
| |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == this) { |
| return true; |
| } |
| if (!(o instanceof BrightnessCorrection)) { |
| return false; |
| } |
| BrightnessCorrection other = (BrightnessCorrection) o; |
| return other.mImplementation.equals(mImplementation); |
| } |
| |
| @Override |
| public int hashCode() { |
| return mImplementation.hashCode(); |
| } |
| |
| public static final Creator<BrightnessCorrection> CREATOR = |
| new Creator<BrightnessCorrection>() { |
| public BrightnessCorrection createFromParcel(Parcel in) { |
| final int type = in.readInt(); |
| switch (type) { |
| case SCALE_AND_TRANSLATE_LOG: |
| return ScaleAndTranslateLog.readFromParcel(in); |
| } |
| return null; |
| } |
| |
| public BrightnessCorrection[] newArray(int size) { |
| return new BrightnessCorrection[size]; |
| } |
| }; |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| mImplementation.writeToParcel(dest); |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** |
| * Writes the correction to an XML serializer. |
| * |
| * @param serializer |
| * The XML serializer. |
| * |
| * @hide |
| */ |
| public void saveToXml(XmlSerializer serializer) throws IOException { |
| mImplementation.saveToXml(serializer); |
| } |
| |
| /** |
| * Read a correction from an XML parser. |
| * |
| * @param parser |
| * The XML parser. |
| * |
| * @throws IOException |
| * The parser failed to read the XML file. |
| * @throws XmlPullParserException |
| * The parser failed to parse the XML file. |
| * |
| * @hide |
| */ |
| public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException, |
| XmlPullParserException { |
| final int depth = parser.getDepth(); |
| while (XmlUtils.nextElementWithin(parser, depth)) { |
| if (TAG_SCALE_AND_TRANSLATE_LOG.equals(parser.getName())) { |
| return ScaleAndTranslateLog.loadFromXml(parser); |
| } |
| } |
| return null; |
| } |
| |
| private static float loadFloatFromXml(XmlPullParser parser, String attribute) { |
| final String string = parser.getAttributeValue(null, attribute); |
| try { |
| return Float.parseFloat(string); |
| } catch (NullPointerException | NumberFormatException e) { |
| return Float.NaN; |
| } |
| } |
| |
| private interface BrightnessCorrectionImplementation { |
| float apply(float brightness); |
| String toString(); |
| void writeToParcel(Parcel dest); |
| void saveToXml(XmlSerializer serializer) throws IOException; |
| // Package-private static methods: |
| // static BrightnessCorrection readFromParcel(Parcel in); |
| // static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException, |
| // XmlPullParserException; |
| } |
| |
| /** |
| * A BrightnessCorrection that given {@code brightness}, corrects it to be |
| * {@code exp(scale * log(brightness) + translate)}. |
| */ |
| private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation { |
| private static final float MIN_SCALE = 0.5f; |
| private static final float MAX_SCALE = 2.0f; |
| private static final float MIN_TRANSLATE = -0.6f; |
| private static final float MAX_TRANSLATE = 0.7f; |
| |
| private static final String ATTR_SCALE = "scale"; |
| private static final String ATTR_TRANSLATE = "translate"; |
| |
| private final float mScale; |
| private final float mTranslate; |
| |
| ScaleAndTranslateLog(float scale, float translate) { |
| if (Float.isNaN(scale) || Float.isNaN(translate)) { |
| throw new IllegalArgumentException("scale and translate must be numbers"); |
| } |
| mScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); |
| mTranslate = MathUtils.constrain(translate, MIN_TRANSLATE, MAX_TRANSLATE); |
| } |
| |
| @Override |
| public float apply(float brightness) { |
| return MathUtils.exp(mScale * MathUtils.log(brightness) + mTranslate); |
| } |
| |
| @Override |
| public String toString() { |
| return "ScaleAndTranslateLog(" + mScale + ", " + mTranslate + ")"; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == this) { |
| return true; |
| } |
| if (!(o instanceof ScaleAndTranslateLog)) { |
| return false; |
| } |
| ScaleAndTranslateLog other = (ScaleAndTranslateLog) o; |
| return other.mScale == mScale && other.mTranslate == mTranslate; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = 1; |
| result = result * 31 + Float.hashCode(mScale); |
| result = result * 31 + Float.hashCode(mTranslate); |
| return result; |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest) { |
| dest.writeInt(SCALE_AND_TRANSLATE_LOG); |
| dest.writeFloat(mScale); |
| dest.writeFloat(mTranslate); |
| } |
| |
| @Override |
| public void saveToXml(XmlSerializer serializer) throws IOException { |
| serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG); |
| serializer.attribute(null, ATTR_SCALE, Float.toString(mScale)); |
| serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate)); |
| serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG); |
| } |
| |
| static BrightnessCorrection readFromParcel(Parcel in) { |
| float scale = in.readFloat(); |
| float translate = in.readFloat(); |
| return BrightnessCorrection.createScaleAndTranslateLog(scale, translate); |
| } |
| |
| static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException, |
| XmlPullParserException { |
| final float scale = loadFloatFromXml(parser, ATTR_SCALE); |
| final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE); |
| return BrightnessCorrection.createScaleAndTranslateLog(scale, translate); |
| } |
| } |
| } |