234 lines
7.7 KiB
Java
234 lines
7.7 KiB
Java
/*
|
|
* SPDX-FileCopyrightText: 2015 The Android Open Source Project
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package lineageos.util.palette;
|
|
|
|
import lineageos.util.palette.Palette.Swatch;
|
|
|
|
import java.util.List;
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
class DefaultGenerator extends Palette.Generator {
|
|
|
|
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 = 3f;
|
|
private static final float WEIGHT_LUMA = 6f;
|
|
private static final float WEIGHT_POPULATION = 1f;
|
|
|
|
private List<Swatch> mSwatches;
|
|
|
|
private int mHighestPopulation;
|
|
|
|
private Swatch mVibrantSwatch;
|
|
private Swatch mMutedSwatch;
|
|
private Swatch mDarkVibrantSwatch;
|
|
private Swatch mDarkMutedSwatch;
|
|
private Swatch mLightVibrantSwatch;
|
|
private Swatch mLightMutedSwatch;
|
|
|
|
@Override
|
|
public void generate(final List<Swatch> swatches) {
|
|
mSwatches = swatches;
|
|
|
|
mHighestPopulation = findMaxPopulation();
|
|
|
|
generateVariationColors();
|
|
|
|
// Now try and generate any missing colors
|
|
generateEmptySwatches();
|
|
}
|
|
|
|
@Override
|
|
public Swatch getVibrantSwatch() {
|
|
return mVibrantSwatch;
|
|
}
|
|
|
|
@Override
|
|
public Swatch getLightVibrantSwatch() {
|
|
return mLightVibrantSwatch;
|
|
}
|
|
|
|
@Override
|
|
public Swatch getDarkVibrantSwatch() {
|
|
return mDarkVibrantSwatch;
|
|
}
|
|
|
|
@Override
|
|
public Swatch getMutedSwatch() {
|
|
return mMutedSwatch;
|
|
}
|
|
|
|
@Override
|
|
public Swatch getLightMutedSwatch() {
|
|
return mLightMutedSwatch;
|
|
}
|
|
|
|
@Override
|
|
public Swatch getDarkMutedSwatch() {
|
|
return mDarkMutedSwatch;
|
|
}
|
|
|
|
private void generateVariationColors() {
|
|
mVibrantSwatch = findColorVariation(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA,
|
|
TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);
|
|
|
|
mLightVibrantSwatch = findColorVariation(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f,
|
|
TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);
|
|
|
|
mDarkVibrantSwatch = findColorVariation(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA,
|
|
TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);
|
|
|
|
mMutedSwatch = findColorVariation(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA,
|
|
TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);
|
|
|
|
mLightMutedSwatch = findColorVariation(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f,
|
|
TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);
|
|
|
|
mDarkMutedSwatch = findColorVariation(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA,
|
|
TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);
|
|
}
|
|
|
|
/**
|
|
* Try and generate any missing swatches from the swatches we did find.
|
|
*/
|
|
private void generateEmptySwatches() {
|
|
if (mVibrantSwatch == null) {
|
|
// If we do not have a vibrant color...
|
|
if (mDarkVibrantSwatch != null) {
|
|
// ...but we do have a dark vibrant, generate the value by modifying the luma
|
|
final float[] newHsl = copyHslValues(mDarkVibrantSwatch);
|
|
newHsl[2] = TARGET_NORMAL_LUMA;
|
|
mVibrantSwatch = new Swatch(ColorUtils.HSLToColor(newHsl), 0);
|
|
}
|
|
}
|
|
|
|
if (mDarkVibrantSwatch == null) {
|
|
// If we do not have a dark vibrant color...
|
|
if (mVibrantSwatch != null) {
|
|
// ...but we do have a vibrant, generate the value by modifying the luma
|
|
final float[] newHsl = copyHslValues(mVibrantSwatch);
|
|
newHsl[2] = TARGET_DARK_LUMA;
|
|
mDarkVibrantSwatch = new Swatch(ColorUtils.HSLToColor(newHsl), 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find the {@link Palette.Swatch} with the highest population value and return the population.
|
|
*/
|
|
private int findMaxPopulation() {
|
|
int population = 0;
|
|
for (Swatch swatch : mSwatches) {
|
|
population = Math.max(population, swatch.getPopulation());
|
|
}
|
|
return population;
|
|
}
|
|
|
|
private Swatch findColorVariation(float targetLuma, float minLuma, float maxLuma,
|
|
float targetSaturation, float minSaturation, float maxSaturation) {
|
|
Swatch max = null;
|
|
float maxValue = 0f;
|
|
|
|
for (Swatch swatch : mSwatches) {
|
|
final float sat = swatch.getHsl()[1];
|
|
final float luma = swatch.getHsl()[2];
|
|
|
|
if (sat >= minSaturation && sat <= maxSaturation &&
|
|
luma >= minLuma && luma <= maxLuma &&
|
|
!isAlreadySelected(swatch)) {
|
|
float value = createComparisonValue(sat, targetSaturation, luma, targetLuma,
|
|
swatch.getPopulation(), mHighestPopulation);
|
|
if (max == null || value > maxValue) {
|
|
max = swatch;
|
|
maxValue = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return max;
|
|
}
|
|
|
|
/**
|
|
* @return true if we have already selected {@code swatch}
|
|
*/
|
|
private boolean isAlreadySelected(Swatch swatch) {
|
|
return mVibrantSwatch == swatch || mDarkVibrantSwatch == swatch ||
|
|
mLightVibrantSwatch == swatch || mMutedSwatch == swatch ||
|
|
mDarkMutedSwatch == swatch || mLightMutedSwatch == swatch;
|
|
}
|
|
|
|
private static float createComparisonValue(float saturation, float targetSaturation,
|
|
float luma, float targetLuma,
|
|
int population, int maxPopulation) {
|
|
return createComparisonValue(saturation, targetSaturation, WEIGHT_SATURATION,
|
|
luma, targetLuma, WEIGHT_LUMA,
|
|
population, maxPopulation, WEIGHT_POPULATION);
|
|
}
|
|
|
|
private static float createComparisonValue(
|
|
float saturation, float targetSaturation, float saturationWeight,
|
|
float luma, float targetLuma, float lumaWeight,
|
|
int population, int maxPopulation, float populationWeight) {
|
|
return weightedMean(
|
|
invertDiff(saturation, targetSaturation), saturationWeight,
|
|
invertDiff(luma, targetLuma), lumaWeight,
|
|
population / (float) maxPopulation, populationWeight
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Copy a {@link Swatch}'s HSL values into a new float[].
|
|
*/
|
|
private static float[] copyHslValues(Swatch color) {
|
|
final float[] newHsl = new float[3];
|
|
System.arraycopy(color.getHsl(), 0, newHsl, 0, 3);
|
|
return newHsl;
|
|
}
|
|
|
|
/**
|
|
* Returns a value in the range 0-1. 1 is returned when {@code value} equals the
|
|
* {@code targetValue} and then decreases as the absolute difference between {@code value} and
|
|
* {@code targetValue} increases.
|
|
*
|
|
* @param value the item's value
|
|
* @param targetValue the value which we desire
|
|
*/
|
|
private static float invertDiff(float value, float targetValue) {
|
|
return 1f - Math.abs(value - targetValue);
|
|
}
|
|
|
|
private static float weightedMean(float... values) {
|
|
float sum = 0f;
|
|
float sumWeight = 0f;
|
|
|
|
for (int i = 0; i < values.length; i += 2) {
|
|
float value = values[i];
|
|
float weight = values[i + 1];
|
|
|
|
sum += (value * weight);
|
|
sumWeight += weight;
|
|
}
|
|
|
|
return sum / sumWeight;
|
|
}
|
|
}
|