blob: 22df778a1368e0f0174eb5483489b8c017b3a4a3 [file] [log] [blame]
/*
* 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.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
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 * ln(brightness) + translate)}.
*
* @param scale
* How much to scale the log (base e) brightness.
* @param translate
* How much to translate the log (base e) brightness.
*
* @return A BrightnessCorrection that given {@code brightness}, corrects it to be
* {@code exp(scale * ln(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.
*/
@FloatRange(from = 0.0)
public float apply(@FloatRange(from = 0.0) float brightness) {
return mImplementation.apply(brightness);
}
/**
* Returns a string representation.
*
* @return A string representation.
*/
@NonNull
public String toString() {
return mImplementation.toString();
}
@Override
public boolean equals(@Nullable 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 @android.annotation.NonNull 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 * ln(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);
}
}
}