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