blob: 96e7faa81c3ae80ca102e83637f7fa6321341d34 [file] [log] [blame]
/*
* Copyright (C) 2021 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 com.android.internal.graphics.palette;
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
/**
* A class which allows custom selection of colors in a {@link Palette}'s generation. Instances can
* be created via the {@link Builder} class.
*/
public final class Target {
private static final float WEIGHT_CHROMA = 0.5f;
private static final float WEIGHT_RELATIVE_LUMINANCE = 0.5f;
private static final float WEIGHT_POPULATION = 0.3f;
private static final float WEIGHT_HUE = 0.2f;
// Arbitrarily chosen, except max - CAM16 chroma has a ceiling of 130, based on unit testing.
private static final float DEFAULT_CHROMA_MIN = 0.f;
private static final float DEFAULT_CHROMA_MAX = 130.f;
private static final float DEFAULT_CHROMA_TARGET = 30.f;
private float mTargetRelativeLuminance = -1.0f;
private float mChromaWeight;
private float mChromaTarget;
private float mChromaMin;
private float mChromaMax;
private float mRelativeLuminanceWeight;
private float mPopulationWeight;
private float mHueWeight;
private float mTargetHue;
Target() {
mChromaMax = DEFAULT_CHROMA_MAX;
mChromaMin = DEFAULT_CHROMA_MIN;
mChromaTarget = DEFAULT_CHROMA_TARGET;
mChromaWeight = WEIGHT_CHROMA;
mRelativeLuminanceWeight = WEIGHT_RELATIVE_LUMINANCE;
mPopulationWeight = WEIGHT_POPULATION;
mHueWeight = WEIGHT_HUE;
}
Target(@NonNull Target from) {
mTargetRelativeLuminance = from.mTargetRelativeLuminance;
mChromaWeight = from.mChromaWeight;
mRelativeLuminanceWeight = from.mRelativeLuminanceWeight;
mPopulationWeight = from.mPopulationWeight;
mHueWeight = from.mHueWeight;
mChromaTarget = from.mChromaTarget;
mChromaMin = from.mChromaMin;
mChromaMax = from.mChromaMax;
}
/** The relative luminance value for this target. */
@FloatRange(from = 0, to = 100)
public float getTargetRelativeLuminance() {
return mTargetRelativeLuminance;
}
/** The relative luminance value for this target. */
@FloatRange(from = 0, to = 100)
public float getTargetPerceptualLuminance() {
return Contrast.yToLstar(mTargetRelativeLuminance);
}
/** The minimum chroma value for this target. */
@FloatRange(from = 0, to = 100)
public float getMinimumChroma() {
return mChromaMin;
}
/** The target chroma value for this target. */
@FloatRange(from = 0, to = 100)
public float getTargetChroma() {
return mChromaTarget;
}
/** The maximum chroma value for this target. */
@FloatRange(from = 0, to = 130)
public float getMaximumChroma() {
return mChromaMax;
}
/** The target hue value for this target. */
@FloatRange(from = 0, to = 100)
public float getTargetHue() {
return mTargetHue;
}
/**
* Returns the weight of importance that this target places on a color's chroma within the
* image.
*
* <p>The larger the weight, relative to the other weights, the more important that a color
* being
* close to the target value has on selection.
*
* @see #getTargetChroma()
*/
public float getChromaWeight() {
return mChromaWeight;
}
/**
* Returns the weight of importance that this target places on a color's lightness within the
* image.
*
* <p>The larger the weight, relative to the other weights, the more important that a color
* being
* close to the target value has on selection.
*
* @see #getTargetRelativeLuminance()
*/
public float getLightnessWeight() {
return mRelativeLuminanceWeight;
}
/**
* Returns the weight of importance that this target places on a color's population within the
* image.
*
* <p>The larger the weight, relative to the other weights, the more important that a color's
* population being close to the most populous has on selection.
*/
public float getPopulationWeight() {
return mPopulationWeight;
}
/**
* Returns the weight of importance that this target places on a color's hue.
*
* <p>The larger the weight, relative to the other weights, the more important that a color's
* hue being close to the desired hue has on selection.
*/
public float getHueWeight() {
return mHueWeight;
}
/** Builder class for generating custom {@link Target} instances. */
public static class Builder {
private final Target mTarget;
/** Create a new {@link Target} builder from scratch. */
public Builder() {
mTarget = new Target();
}
/** Create a new builder based on an existing {@link Target}. */
public Builder(@NonNull Target target) {
mTarget = new Target(target);
}
/** Set the minimum chroma value for this target. */
@NonNull
public Builder setMinimumChroma(@FloatRange(from = 0, to = 100) float value) {
mTarget.mChromaMin = value;
return this;
}
/** Set the target/ideal chroma value for this target. */
@NonNull
public Builder setTargetChroma(@FloatRange(from = 0, to = 100) float value) {
mTarget.mChromaTarget = value;
return this;
}
/** Set the maximum chroma value for this target. */
@NonNull
public Builder setMaximumChroma(@FloatRange(from = 0, to = 100) float value) {
mTarget.mChromaMax = value;
return this;
}
/** Set the minimum lightness value for this target, using Y in XYZ color space. */
@NonNull
public Builder setTargetRelativeLuminance(@FloatRange(from = 0, to = 100) float value) {
mTarget.mTargetRelativeLuminance = value;
return this;
}
/** Set the minimum lightness value for this target, using L* in LAB color space. */
@NonNull
public Builder setTargetPerceptualLuminance(@FloatRange(from = 0, to = 100) float value) {
mTarget.mTargetRelativeLuminance = Contrast.lstarToY(value);
return this;
}
/**
* Set the hue desired from the target. This hue is not enforced, the only consequence
* is points will be awarded to seed colors the closer they are to this hue.
*/
@NonNull
public Builder setTargetHue(@IntRange(from = 0, to = 360) int hue) {
mTarget.mTargetHue = hue;
return this;
}
/** Sets lightness value for this target. */
@NonNull
public Builder setContrastRatio(
@FloatRange(from = 1, to = 21) float value,
@FloatRange(from = 0, to = 100) float relativeLuminance) {
float counterpartY = relativeLuminance;
float lstar = Contrast.yToLstar(counterpartY);
float targetY;
if (lstar < 50) {
targetY = Contrast.lighterY(counterpartY, value);
} else {
targetY = Contrast.darkerY(counterpartY, value);
}
mTarget.mTargetRelativeLuminance = targetY;
return this;
}
/**
* Set the weight of importance that this target will place on chroma values.
*
* <p>The larger the weight, relative to the other weights, the more important that a color
* being close to the target value has on selection.
*
* <p>A weight of 0 means that it has no weight, and thus has no bearing on the selection.
*
* @see #setTargetChroma(float)
*/
@NonNull
public Builder setChromaWeight(@FloatRange(from = 0) float weight) {
mTarget.mChromaWeight = weight;
return this;
}
/**
* Set the weight of importance that this target will place on lightness values.
*
* <p>The larger the weight, relative to the other weights, the more important that a color
* being close to the target value has on selection.
*
* <p>A weight of 0 means that it has no weight, and thus has no bearing on the selection.
*
* @see #setTargetRelativeLuminance(float)
*/
@NonNull
public Builder setLightnessWeight(@FloatRange(from = 0) float weight) {
mTarget.mRelativeLuminanceWeight = weight;
return this;
}
/**
* Set the weight of importance that this target will place on a color's population within
* the
* image.
*
* <p>The larger the weight, relative to the other weights, the more important that a
* color's
* population being close to the most populous has on selection.
*
* <p>A weight of 0 means that it has no weight, and thus has no bearing on the selection.
*/
@NonNull
public Builder setPopulationWeight(@FloatRange(from = 0) float weight) {
mTarget.mPopulationWeight = weight;
return this;
}
/** Builds and returns the resulting {@link Target}. */
@NonNull
public Target build() {
return mTarget;
}
}
}