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 }