• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.palette.graphics;
18 
19 import androidx.annotation.FloatRange;
20 import androidx.annotation.NonNull;
21 
22 /**
23  * A class which allows custom selection of colors in a {@link Palette}'s generation. Instances
24  * can be created via the {@link Builder} class.
25  *
26  * <p>To use the target, use the {@link Palette.Builder#addTarget(Target)} API when building a
27  * Palette.</p>
28  */
29 public final class Target {
30 
31     private static final float TARGET_DARK_LUMA = 0.26f;
32     private static final float MAX_DARK_LUMA = 0.45f;
33 
34     private static final float MIN_LIGHT_LUMA = 0.55f;
35     private static final float TARGET_LIGHT_LUMA = 0.74f;
36 
37     private static final float MIN_NORMAL_LUMA = 0.3f;
38     private static final float TARGET_NORMAL_LUMA = 0.5f;
39     private static final float MAX_NORMAL_LUMA = 0.7f;
40 
41     private static final float TARGET_MUTED_SATURATION = 0.3f;
42     private static final float MAX_MUTED_SATURATION = 0.4f;
43 
44     private static final float TARGET_VIBRANT_SATURATION = 1f;
45     private static final float MIN_VIBRANT_SATURATION = 0.35f;
46 
47     private static final float WEIGHT_SATURATION = 0.24f;
48     private static final float WEIGHT_LUMA = 0.52f;
49     private static final float WEIGHT_POPULATION = 0.24f;
50 
51     static final int INDEX_MIN = 0;
52     static final int INDEX_TARGET = 1;
53     static final int INDEX_MAX = 2;
54 
55     static final int INDEX_WEIGHT_SAT = 0;
56     static final int INDEX_WEIGHT_LUMA = 1;
57     static final int INDEX_WEIGHT_POP = 2;
58 
59     /**
60      * A target which has the characteristics of a vibrant color which is light in luminance.
61     */
62     public static final Target LIGHT_VIBRANT;
63 
64     /**
65      * A target which has the characteristics of a vibrant color which is neither light or dark.
66      */
67     public static final Target VIBRANT;
68 
69     /**
70      * A target which has the characteristics of a vibrant color which is dark in luminance.
71      */
72     public static final Target DARK_VIBRANT;
73 
74     /**
75      * A target which has the characteristics of a muted color which is light in luminance.
76      */
77     public static final Target LIGHT_MUTED;
78 
79     /**
80      * A target which has the characteristics of a muted color which is neither light or dark.
81      */
82     public static final Target MUTED;
83 
84     /**
85      * A target which has the characteristics of a muted color which is dark in luminance.
86      */
87     public static final Target DARK_MUTED;
88 
89     static {
90         LIGHT_VIBRANT = new Target();
91         setDefaultLightLightnessValues(LIGHT_VIBRANT);
92         setDefaultVibrantSaturationValues(LIGHT_VIBRANT);
93 
94         VIBRANT = new Target();
95         setDefaultNormalLightnessValues(VIBRANT);
96         setDefaultVibrantSaturationValues(VIBRANT);
97 
98         DARK_VIBRANT = new Target();
99         setDefaultDarkLightnessValues(DARK_VIBRANT);
100         setDefaultVibrantSaturationValues(DARK_VIBRANT);
101 
102         LIGHT_MUTED = new Target();
103         setDefaultLightLightnessValues(LIGHT_MUTED);
104         setDefaultMutedSaturationValues(LIGHT_MUTED);
105 
106         MUTED = new Target();
107         setDefaultNormalLightnessValues(MUTED);
108         setDefaultMutedSaturationValues(MUTED);
109 
110         DARK_MUTED = new Target();
111         setDefaultDarkLightnessValues(DARK_MUTED);
112         setDefaultMutedSaturationValues(DARK_MUTED);
113     }
114 
115     final float[] mSaturationTargets = new float[3];
116     final float[] mLightnessTargets = new float[3];
117     final float[] mWeights = new float[3];
118     boolean mIsExclusive = true; // default to true
119 
Target()120     Target() {
121         setTargetDefaultValues(mSaturationTargets);
122         setTargetDefaultValues(mLightnessTargets);
123         setDefaultWeights();
124     }
125 
Target(@onNull Target from)126     Target(@NonNull Target from) {
127         System.arraycopy(from.mSaturationTargets, 0, mSaturationTargets, 0,
128                 mSaturationTargets.length);
129         System.arraycopy(from.mLightnessTargets, 0, mLightnessTargets, 0,
130                 mLightnessTargets.length);
131         System.arraycopy(from.mWeights, 0, mWeights, 0, mWeights.length);
132     }
133 
134     /**
135      * The minimum saturation value for this target.
136      */
137     @FloatRange(from = 0, to = 1)
getMinimumSaturation()138     public float getMinimumSaturation() {
139         return mSaturationTargets[INDEX_MIN];
140     }
141 
142     /**
143      * The target saturation value for this target.
144      */
145     @FloatRange(from = 0, to = 1)
getTargetSaturation()146     public float getTargetSaturation() {
147         return mSaturationTargets[INDEX_TARGET];
148     }
149 
150     /**
151      * The maximum saturation value for this target.
152      */
153     @FloatRange(from = 0, to = 1)
getMaximumSaturation()154     public float getMaximumSaturation() {
155         return mSaturationTargets[INDEX_MAX];
156     }
157 
158     /**
159      * The minimum lightness value for this target.
160      */
161     @FloatRange(from = 0, to = 1)
getMinimumLightness()162     public float getMinimumLightness() {
163         return mLightnessTargets[INDEX_MIN];
164     }
165 
166     /**
167      * The target lightness value for this target.
168      */
169     @FloatRange(from = 0, to = 1)
getTargetLightness()170     public float getTargetLightness() {
171         return mLightnessTargets[INDEX_TARGET];
172     }
173 
174     /**
175      * The maximum lightness value for this target.
176      */
177     @FloatRange(from = 0, to = 1)
getMaximumLightness()178     public float getMaximumLightness() {
179         return mLightnessTargets[INDEX_MAX];
180     }
181 
182     /**
183      * Returns the weight of importance that this target places on a color's saturation within
184      * the image.
185      *
186      * <p>The larger the weight, relative to the other weights, the more important that a color
187      * being close to the target value has on selection.</p>
188      *
189      * @see #getTargetSaturation()
190      */
getSaturationWeight()191     public float getSaturationWeight() {
192         return mWeights[INDEX_WEIGHT_SAT];
193     }
194 
195     /**
196      * Returns the weight of importance that this target places on a color's lightness within
197      * the image.
198      *
199      * <p>The larger the weight, relative to the other weights, the more important that a color
200      * being close to the target value has on selection.</p>
201      *
202      * @see #getTargetLightness()
203      */
getLightnessWeight()204     public float getLightnessWeight() {
205         return mWeights[INDEX_WEIGHT_LUMA];
206     }
207 
208     /**
209      * Returns the weight of importance that this target places on a color's population within
210      * the image.
211      *
212      * <p>The larger the weight, relative to the other weights, the more important that a
213      * color's population being close to the most populous has on selection.</p>
214      */
getPopulationWeight()215     public float getPopulationWeight() {
216         return mWeights[INDEX_WEIGHT_POP];
217     }
218 
219     /**
220      * Returns whether any color selected for this target is exclusive for this target only.
221      *
222      * <p>If false, then the color can be selected for other targets.</p>
223      */
isExclusive()224     public boolean isExclusive() {
225         return mIsExclusive;
226     }
227 
setTargetDefaultValues(final float[] values)228     private static void setTargetDefaultValues(final float[] values) {
229         values[INDEX_MIN] = 0f;
230         values[INDEX_TARGET] = 0.5f;
231         values[INDEX_MAX] = 1f;
232     }
233 
setDefaultWeights()234     private void setDefaultWeights() {
235         mWeights[INDEX_WEIGHT_SAT] = WEIGHT_SATURATION;
236         mWeights[INDEX_WEIGHT_LUMA] = WEIGHT_LUMA;
237         mWeights[INDEX_WEIGHT_POP] = WEIGHT_POPULATION;
238     }
239 
normalizeWeights()240     void normalizeWeights() {
241         float sum = 0;
242         for (int i = 0, z = mWeights.length; i < z; i++) {
243             float weight = mWeights[i];
244             if (weight > 0) {
245                 sum += weight;
246             }
247         }
248         if (sum != 0) {
249             for (int i = 0, z = mWeights.length; i < z; i++) {
250                 if (mWeights[i] > 0) {
251                     mWeights[i] /= sum;
252                 }
253             }
254         }
255     }
256 
setDefaultDarkLightnessValues(Target target)257     private static void setDefaultDarkLightnessValues(Target target) {
258         target.mLightnessTargets[INDEX_TARGET] = TARGET_DARK_LUMA;
259         target.mLightnessTargets[INDEX_MAX] = MAX_DARK_LUMA;
260     }
261 
setDefaultNormalLightnessValues(Target target)262     private static void setDefaultNormalLightnessValues(Target target) {
263         target.mLightnessTargets[INDEX_MIN] = MIN_NORMAL_LUMA;
264         target.mLightnessTargets[INDEX_TARGET] = TARGET_NORMAL_LUMA;
265         target.mLightnessTargets[INDEX_MAX] = MAX_NORMAL_LUMA;
266     }
267 
setDefaultLightLightnessValues(Target target)268     private static void setDefaultLightLightnessValues(Target target) {
269         target.mLightnessTargets[INDEX_MIN] = MIN_LIGHT_LUMA;
270         target.mLightnessTargets[INDEX_TARGET] = TARGET_LIGHT_LUMA;
271     }
272 
setDefaultVibrantSaturationValues(Target target)273     private static void setDefaultVibrantSaturationValues(Target target) {
274         target.mSaturationTargets[INDEX_MIN] = MIN_VIBRANT_SATURATION;
275         target.mSaturationTargets[INDEX_TARGET] = TARGET_VIBRANT_SATURATION;
276     }
277 
setDefaultMutedSaturationValues(Target target)278     private static void setDefaultMutedSaturationValues(Target target) {
279         target.mSaturationTargets[INDEX_TARGET] = TARGET_MUTED_SATURATION;
280         target.mSaturationTargets[INDEX_MAX] = MAX_MUTED_SATURATION;
281     }
282 
283     /**
284      * Builder class for generating custom {@link Target} instances.
285      */
286     public final static class Builder {
287         private final Target mTarget;
288 
289         /**
290          * Create a new {@link Target} builder from scratch.
291          */
Builder()292         public Builder() {
293             mTarget = new Target();
294         }
295 
296         /**
297          * Create a new builder based on an existing {@link Target}.
298          */
Builder(@onNull Target target)299         public Builder(@NonNull Target target) {
300             mTarget = new Target(target);
301         }
302 
303         /**
304          * Set the minimum saturation value for this target.
305          */
306         @NonNull
setMinimumSaturation(@loatRangefrom = 0, to = 1) float value)307         public Builder setMinimumSaturation(@FloatRange(from = 0, to = 1) float value) {
308             mTarget.mSaturationTargets[INDEX_MIN] = value;
309             return this;
310         }
311 
312         /**
313          * Set the target/ideal saturation value for this target.
314          */
315         @NonNull
setTargetSaturation(@loatRangefrom = 0, to = 1) float value)316         public Builder setTargetSaturation(@FloatRange(from = 0, to = 1) float value) {
317             mTarget.mSaturationTargets[INDEX_TARGET] = value;
318             return this;
319         }
320 
321         /**
322          * Set the maximum saturation value for this target.
323          */
324         @NonNull
setMaximumSaturation(@loatRangefrom = 0, to = 1) float value)325         public Builder setMaximumSaturation(@FloatRange(from = 0, to = 1) float value) {
326             mTarget.mSaturationTargets[INDEX_MAX] = value;
327             return this;
328         }
329 
330         /**
331          * Set the minimum lightness value for this target.
332          */
333         @NonNull
setMinimumLightness(@loatRangefrom = 0, to = 1) float value)334         public Builder setMinimumLightness(@FloatRange(from = 0, to = 1) float value) {
335             mTarget.mLightnessTargets[INDEX_MIN] = value;
336             return this;
337         }
338 
339         /**
340          * Set the target/ideal lightness value for this target.
341          */
342         @NonNull
setTargetLightness(@loatRangefrom = 0, to = 1) float value)343         public Builder setTargetLightness(@FloatRange(from = 0, to = 1) float value) {
344             mTarget.mLightnessTargets[INDEX_TARGET] = value;
345             return this;
346         }
347 
348         /**
349          * Set the maximum lightness value for this target.
350          */
351         @NonNull
setMaximumLightness(@loatRangefrom = 0, to = 1) float value)352         public Builder setMaximumLightness(@FloatRange(from = 0, to = 1) float value) {
353             mTarget.mLightnessTargets[INDEX_MAX] = value;
354             return this;
355         }
356 
357         /**
358          * Set the weight of importance that this target will place on saturation values.
359          *
360          * <p>The larger the weight, relative to the other weights, the more important that a color
361          * being close to the target value has on selection.</p>
362          *
363          * <p>A weight of 0 means that it has no weight, and thus has no
364          * bearing on the selection.</p>
365          *
366          * @see #setTargetSaturation(float)
367          */
368         @NonNull
setSaturationWeight(@loatRangefrom = 0) float weight)369         public Builder setSaturationWeight(@FloatRange(from = 0) float weight) {
370             mTarget.mWeights[INDEX_WEIGHT_SAT] = weight;
371             return this;
372         }
373 
374         /**
375          * Set the weight of importance that this target will place on lightness values.
376          *
377          * <p>The larger the weight, relative to the other weights, the more important that a color
378          * being close to the target value has on selection.</p>
379          *
380          * <p>A weight of 0 means that it has no weight, and thus has no
381          * bearing on the selection.</p>
382          *
383          * @see #setTargetLightness(float)
384          */
385         @NonNull
setLightnessWeight(@loatRangefrom = 0) float weight)386         public Builder setLightnessWeight(@FloatRange(from = 0) float weight) {
387             mTarget.mWeights[INDEX_WEIGHT_LUMA] = weight;
388             return this;
389         }
390 
391         /**
392          * Set the weight of importance that this target will place on a color's population within
393          * the image.
394          *
395          * <p>The larger the weight, relative to the other weights, the more important that a
396          * color's population being close to the most populous has on selection.</p>
397          *
398          * <p>A weight of 0 means that it has no weight, and thus has no
399          * bearing on the selection.</p>
400          */
401         @NonNull
setPopulationWeight(@loatRangefrom = 0) float weight)402         public Builder setPopulationWeight(@FloatRange(from = 0) float weight) {
403             mTarget.mWeights[INDEX_WEIGHT_POP] = weight;
404             return this;
405         }
406 
407         /**
408          * Set whether any color selected for this target is exclusive to this target only.
409          * Defaults to true.
410          *
411          * @param exclusive true if any the color is exclusive to this target, or false is the
412          *                  color can be selected for other targets.
413          */
414         @NonNull
setExclusive(boolean exclusive)415         public Builder setExclusive(boolean exclusive) {
416             mTarget.mIsExclusive = exclusive;
417             return this;
418         }
419 
420         /**
421          * Builds and returns the resulting {@link Target}.
422          */
423         @NonNull
build()424         public Target build() {
425             return mTarget;
426         }
427     }
428 
429 }