blob: 8ac8205579b22d44cd7398711180934515532c4d [file] [log] [blame]
/*
* Copyright 2015 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.support.v7.graphics;
import android.support.annotation.FloatRange;
/**
* A class which allows custom selection of colors in a {@link Palette}'s generation. Instances
* can be created via the {@link Builder} class.
*
* <p>To use the target, use the {@link Palette.Builder#addTarget(Target)} API when building a
* Palette.</p>
*/
public final class Target {
private static final float TARGET_DARK_LUMA = 0.26f;
private static final float MAX_DARK_LUMA = 0.45f;
private static final float MIN_LIGHT_LUMA = 0.55f;
private static final float TARGET_LIGHT_LUMA = 0.74f;
private static final float MIN_NORMAL_LUMA = 0.3f;
private static final float TARGET_NORMAL_LUMA = 0.5f;
private static final float MAX_NORMAL_LUMA = 0.7f;
private static final float TARGET_MUTED_SATURATION = 0.3f;
private static final float MAX_MUTED_SATURATION = 0.4f;
private static final float TARGET_VIBRANT_SATURATION = 1f;
private static final float MIN_VIBRANT_SATURATION = 0.35f;
private static final float WEIGHT_SATURATION = 0.24f;
private static final float WEIGHT_LUMA = 0.52f;
private static final float WEIGHT_POPULATION = 0.24f;
private static final int INDEX_MIN = 0;
private static final int INDEX_TARGET = 1;
private static final int INDEX_MAX = 2;
private static final int INDEX_WEIGHT_SAT = 0;
private static final int INDEX_WEIGHT_LUMA = 1;
private static final int INDEX_WEIGHT_POP = 2;
/**
* A target which has the characteristics of a vibrant color which is light in luminance.
*/
public static final Target LIGHT_VIBRANT;
/**
* A target which has the characteristics of a vibrant color which is neither light or dark.
*/
public static final Target VIBRANT;
/**
* A target which has the characteristics of a vibrant color which is dark in luminance.
*/
public static final Target DARK_VIBRANT;
/**
* A target which has the characteristics of a muted color which is light in luminance.
*/
public static final Target LIGHT_MUTED;
/**
* A target which has the characteristics of a muted color which is neither light or dark.
*/
public static final Target MUTED;
/**
* A target which has the characteristics of a muted color which is dark in luminance.
*/
public static final Target DARK_MUTED;
static {
LIGHT_VIBRANT = new Target();
setDefaultLightLightnessValues(LIGHT_VIBRANT);
setDefaultVibrantSaturationValues(LIGHT_VIBRANT);
VIBRANT = new Target();
setDefaultNormalLightnessValues(VIBRANT);
setDefaultVibrantSaturationValues(VIBRANT);
DARK_VIBRANT = new Target();
setDefaultDarkLightnessValues(DARK_VIBRANT);
setDefaultVibrantSaturationValues(DARK_VIBRANT);
LIGHT_MUTED = new Target();
setDefaultLightLightnessValues(LIGHT_MUTED);
setDefaultMutedSaturationValues(LIGHT_MUTED);
MUTED = new Target();
setDefaultNormalLightnessValues(MUTED);
setDefaultMutedSaturationValues(MUTED);
DARK_MUTED = new Target();
setDefaultDarkLightnessValues(DARK_MUTED);
setDefaultMutedSaturationValues(DARK_MUTED);
}
private final float[] mSaturationTargets = new float[3];
private final float[] mLightnessTargets = new float[3];
private final float[] mWeights = new float[3];
private boolean mIsExclusive = true; // default to true
private Target() {
setTargetDefaultValues(mSaturationTargets);
setTargetDefaultValues(mLightnessTargets);
setDefaultWeights();
}
private Target(Target from) {
System.arraycopy(from.mSaturationTargets, 0, mSaturationTargets, 0,
mSaturationTargets.length);
System.arraycopy(from.mLightnessTargets, 0, mLightnessTargets, 0,
mLightnessTargets.length);
System.arraycopy(from.mWeights, 0, mWeights, 0, mWeights.length);
}
/**
* The minimum saturation value for this target.
*/
@FloatRange(from = 0, to = 1)
public float getMinimumSaturation() {
return mSaturationTargets[INDEX_MIN];
}
/**
* The target saturation value for this target.
*/
@FloatRange(from = 0, to = 1)
public float getTargetSaturation() {
return mSaturationTargets[INDEX_TARGET];
}
/**
* The maximum saturation value for this target.
*/
@FloatRange(from = 0, to = 1)
public float getMaximumSaturation() {
return mSaturationTargets[INDEX_MAX];
}
/**
* The minimum lightness value for this target.
*/
@FloatRange(from = 0, to = 1)
public float getMinimumLightness() {
return mLightnessTargets[INDEX_MIN];
}
/**
* The target lightness value for this target.
*/
@FloatRange(from = 0, to = 1)
public float getTargetLightness() {
return mLightnessTargets[INDEX_TARGET];
}
/**
* The maximum lightness value for this target.
*/
@FloatRange(from = 0, to = 1)
public float getMaximumLightness() {
return mLightnessTargets[INDEX_MAX];
}
/**
* The weight of important that a color's saturation value has on selection.
*/
public float getSaturationWeight() {
return mWeights[INDEX_WEIGHT_SAT];
}
/**
* The weight of important that a color's lightness value has on selection.
*/
public float getLightnessWeight() {
return mWeights[INDEX_WEIGHT_LUMA];
}
/**
* The weight of important that a color's population value has on selection.
*/
public float getPopulationWeight() {
return mWeights[INDEX_WEIGHT_POP];
}
/**
* Returns whether any color selected for this target is exclusive for this target only.
*
* <p>If false, then the color can be selected for other targets.</p>
*/
public boolean isExclusive() {
return mIsExclusive;
}
private static void setTargetDefaultValues(final float[] values) {
values[INDEX_MIN] = 0f;
values[INDEX_TARGET] = 0.5f;
values[INDEX_MAX] = 1f;
}
private void setDefaultWeights() {
mWeights[INDEX_WEIGHT_SAT] = WEIGHT_SATURATION;
mWeights[INDEX_WEIGHT_LUMA] = WEIGHT_LUMA;
mWeights[INDEX_WEIGHT_POP] = WEIGHT_POPULATION;
}
void normalizeWeights() {
float sum = 0;
for (int i = 0, z = mWeights.length; i < z; i++) {
float weight = mWeights[i];
if (weight > 0) {
sum += weight;
}
}
if (sum != 0) {
for (int i = 0, z = mWeights.length; i < z; i++) {
if (mWeights[i] > 0) {
mWeights[i] /= sum;
}
}
}
}
private static void setDefaultDarkLightnessValues(Target target) {
target.mLightnessTargets[INDEX_TARGET] = TARGET_DARK_LUMA;
target.mLightnessTargets[INDEX_MAX] = MAX_DARK_LUMA;
}
private static void setDefaultNormalLightnessValues(Target target) {
target.mLightnessTargets[INDEX_MIN] = MIN_NORMAL_LUMA;
target.mLightnessTargets[INDEX_TARGET] = TARGET_NORMAL_LUMA;
target.mLightnessTargets[INDEX_MAX] = MAX_NORMAL_LUMA;
}
private static void setDefaultLightLightnessValues(Target target) {
target.mLightnessTargets[INDEX_MIN] = MIN_LIGHT_LUMA;
target.mLightnessTargets[INDEX_TARGET] = TARGET_LIGHT_LUMA;
}
private static void setDefaultVibrantSaturationValues(Target target) {
target.mSaturationTargets[INDEX_MIN] = MIN_VIBRANT_SATURATION;
target.mSaturationTargets[INDEX_TARGET] = TARGET_VIBRANT_SATURATION;
}
private static void setDefaultMutedSaturationValues(Target target) {
target.mSaturationTargets[INDEX_TARGET] = TARGET_MUTED_SATURATION;
target.mSaturationTargets[INDEX_MAX] = MAX_MUTED_SATURATION;
}
/**
* Builder class for generating custom {@link Target} instances.
*/
public final 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(Target target) {
mTarget = new Target(target);
}
/**
* Set the minimum saturation value for this target.
*/
public Builder setMinimumSaturation(@FloatRange(from = 0, to = 1) float value) {
mTarget.mSaturationTargets[INDEX_MIN] = value;
return this;
}
/**
* Set the target/ideal saturation value for this target.
*/
public Builder setTargetSaturation(@FloatRange(from = 0, to = 1) float value) {
mTarget.mSaturationTargets[INDEX_TARGET] = value;
return this;
}
/**
* Set the maximum saturation value for this target.
*/
public Builder setMaximumSaturation(@FloatRange(from = 0, to = 1) float value) {
mTarget.mSaturationTargets[INDEX_MAX] = value;
return this;
}
/**
* Set the minimum lightness value for this target.
*/
public Builder setMinimumLightness(@FloatRange(from = 0, to = 1) float value) {
mTarget.mLightnessTargets[INDEX_MIN] = value;
return this;
}
/**
* Set the target/ideal lightness value for this target.
*/
public Builder setTargetLightness(@FloatRange(from = 0, to = 1) float value) {
mTarget.mLightnessTargets[INDEX_TARGET] = value;
return this;
}
/**
* Set the maximum lightness value for this target.
*/
public Builder setMaximumLightness(@FloatRange(from = 0, to = 1) float value) {
mTarget.mLightnessTargets[INDEX_MAX] = value;
return this;
}
/**
* Set the weight of important that a color's saturation value has on selection. A weight
* of <= 0 means that it has no weight and is ignored.
*/
public Builder setSaturationWeight(@FloatRange(from = 0) float weight) {
mTarget.mWeights[INDEX_WEIGHT_SAT] = weight;
return this;
}
/**
* Set the weight of important that a color's lightness value has on selection. A weight
* of <= 0 means that it has no weight and is ignored.
*/
public Builder setLightnessWeight(@FloatRange(from = 0) float weight) {
mTarget.mWeights[INDEX_WEIGHT_LUMA] = weight;
return this;
}
/**
* Set the weight of important that a color's population value has on selection. A weight
* of <= 0 means that it has no weight and is ignored.
*/
public Builder setPopulationWeight(@FloatRange(from = 0) float weight) {
mTarget.mWeights[INDEX_WEIGHT_POP] = weight;
return this;
}
/**
* Set whether any color selected for this target is exclusive to this target only.
* Defaults to true.
*
* @param exclusive true if any the color is exclusive to this target, or false is the
* color can be selected for other targets.
*/
public Builder setExclusive(boolean exclusive) {
mTarget.mIsExclusive = exclusive;
return this;
}
/**
* Builds and returns the resulting {@link Target}.
*/
public Target build() {
return mTarget;
}
}
}