Target.java revision 440e8e9dbc4ed4ecb20284607251f746833cd472
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
17package 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
35import 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 */
46public 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
137    Target() {
138        setTargetDefaultValues(mSaturationTargets);
139        setTargetDefaultValues(mLightnessTargets);
140        setDefaultWeights();
141    }
142
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)
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)
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)
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)
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)
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)
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     */
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     */
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     */
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     */
241    public boolean isExclusive() {
242        return mIsExclusive;
243    }
244
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
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
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
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
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
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
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
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         */
309        public Builder() {
310            mTarget = new Target();
311        }
312
313        /**
314         * Create a new builder based on an existing {@link Target}.
315         */
316        public Builder(Target target) {
317            mTarget = new Target(target);
318        }
319
320        /**
321         * Set the minimum saturation value for this target.
322         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
430        public Target build() {
431            return mTarget;
432        }
433    }
434
435}