/* * 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. * *

To use the target, use the {@link Palette.Builder#addTarget(Target)} API when building a * Palette.

*/ 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]; } /** * Returns the weight of importance that this target places on a color's saturation within * the image. * *

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 #getTargetSaturation() */ public float getSaturationWeight() { return mWeights[INDEX_WEIGHT_SAT]; } /** * Returns the weight of importance that this target places on a color's lightness within * the image. * *

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 #getTargetLightness() */ public float getLightnessWeight() { return mWeights[INDEX_WEIGHT_LUMA]; } /** * Returns the weight of importance that this target places on a color's population within * the image. * *

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 mWeights[INDEX_WEIGHT_POP]; } /** * Returns whether any color selected for this target is exclusive for this target only. * *

If false, then the color can be selected for other targets.

*/ 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 importance that this target will place on saturation values. * *

The larger the weight, relative to the other weights, the more important that a color * being close to the target value has on selection.

* *

A weight of 0 means that it has no weight, and thus has no * bearing on the selection.

* * @see #setTargetSaturation(float) */ public Builder setSaturationWeight(@FloatRange(from = 0) float weight) { mTarget.mWeights[INDEX_WEIGHT_SAT] = weight; return this; } /** * Set the weight of importance that this target will place on lightness values. * *

The larger the weight, relative to the other weights, the more important that a color * being close to the target value has on selection.

* *

A weight of 0 means that it has no weight, and thus has no * bearing on the selection.

* * @see #setTargetLightness(float) */ public Builder setLightnessWeight(@FloatRange(from = 0) float weight) { mTarget.mWeights[INDEX_WEIGHT_LUMA] = weight; return this; } /** * Set the weight of importance that this target will place on a color's population within * the image. * *

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.

* *

A weight of 0 means that it has no weight, and thus has no * bearing on the selection.

*/ 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; } } }