diff --git a/src/main/java/com/sangupta/colors/extract/android/AndroidColorExtractor.java b/src/main/java/com/sangupta/colors/extract/android/AndroidColorExtractor.java index 6c4cc54..2550ce7 100644 --- a/src/main/java/com/sangupta/colors/extract/android/AndroidColorExtractor.java +++ b/src/main/java/com/sangupta/colors/extract/android/AndroidColorExtractor.java @@ -20,10 +20,8 @@ import java.util.ArrayList; import java.util.List; -import com.sangupta.colors.extract.android.Bitmap; -import com.sangupta.colors.extract.android.ColorCutQuantizer; -import com.sangupta.colors.extract.android.Palette; -import com.sangupta.colors.extract.android.Target; +import com.sangupta.colors.Swatch; +import com.sangupta.colors.model.RGB; /** * This class is based on the Android source code to extract colors from a given @@ -39,12 +37,12 @@ public class AndroidColorExtractor { static final int DEFAULT_RESIZE_BITMAP_AREA = 112 * 112; - + static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16; - private final List mSwatches; + private final List swatches; - private final Bitmap mBitmap; + private final Bitmap bitmapImage; private final List targets = new ArrayList(); @@ -57,23 +55,26 @@ public class AndroidColorExtractor { private final List filters = new ArrayList(); /** - * Construct a new {@link Builder} using a source {@link Bitmap} + * + * @param bitmap */ public AndroidColorExtractor(Bitmap bitmap) { if (bitmap == null) { throw new IllegalArgumentException("Bitmap is not valid"); } - filters.add(PaletteFilter.DEFAULT_FILTER); - mBitmap = bitmap; - mSwatches = null; + + this.filters.add(PaletteFilter.DEFAULT_FILTER); + + this.bitmapImage = bitmap; + this.swatches = null; // Add the default targets - targets.add(Target.LIGHT_VIBRANT); - targets.add(Target.VIBRANT); - targets.add(Target.DARK_VIBRANT); - targets.add(Target.LIGHT_MUTED); - targets.add(Target.MUTED); - targets.add(Target.DARK_MUTED); + this.targets.add(Target.LIGHT_VIBRANT); + this.targets.add(Target.VIBRANT); + this.targets.add(Target.DARK_VIBRANT); + this.targets.add(Target.LIGHT_MUTED); + this.targets.add(Target.MUTED); + this.targets.add(Target.DARK_MUTED); } /** @@ -85,7 +86,7 @@ public AndroidColorExtractor(Bitmap bitmap) { * faces then this value should be increased to ~24. */ public AndroidColorExtractor maximumColorCount(int colors) { - maxColors = colors; + this.maxColors = colors; return this; } @@ -104,8 +105,8 @@ public AndroidColorExtractor maximumColorCount(int colors) { * should cover, or any value <= 0 to disable resizing. */ public AndroidColorExtractor resizeBitmapArea(final int area) { - resizeArea = area; - resizeMaxDimension = -1; + this.resizeArea = area; + this.resizeMaxDimension = -1; return this; } @@ -114,7 +115,7 @@ public AndroidColorExtractor resizeBitmapArea(final int area) { * automatically by {@link Palette}. */ public AndroidColorExtractor clearFilters() { - filters.clear(); + this.filters.clear(); return this; } @@ -128,6 +129,7 @@ public AndroidColorExtractor addFilter(PaletteFilter filter) { if (filter != null) { filters.add(filter); } + return this; } @@ -139,8 +141,8 @@ public AndroidColorExtractor addFilter(PaletteFilter filter) { *

*/ public AndroidColorExtractor addTarget(final Target target) { - if (!targets.contains(target)) { - targets.add(target); + if (!this.targets.contains(target)) { + this.targets.add(target); } return this; } @@ -150,8 +152,8 @@ public AndroidColorExtractor addTarget(final Target target) { * automatically by {@link Palette}. */ public AndroidColorExtractor clearTargets() { - if (targets != null) { - targets.clear(); + if (this.targets != null) { + this.targets.clear(); } return this; } @@ -162,26 +164,26 @@ public AndroidColorExtractor clearTargets() { public Palette generate() { List swatches; - if (mBitmap != null) { + if (this.bitmapImage != null) { // We have a Bitmap so we need to use quantization to reduce the number of // colors // First we'll scale down the bitmap if needed - final Bitmap bitmap = scaleBitmapDown(mBitmap); + final Bitmap bitmap = scaleBitmapDown(this.bitmapImage); // Now generate a quantizer from the Bitmap - final ColorCutQuantizer quantizer = new ColorCutQuantizer(getPixelsFromBitmap(bitmap), maxColors, - filters.isEmpty() ? null : filters.toArray(new PaletteFilter[filters.size()])); + final ColorCutQuantizer quantizer = new ColorCutQuantizer(getPixelsFromBitmap(bitmap), this.maxColors, + this.filters.isEmpty() ? null : this.filters.toArray(new PaletteFilter[this.filters.size()])); swatches = quantizer.getQuantizedColors(); } else { // Else we're using the provided swatches - swatches = mSwatches; + swatches = this.swatches; } // Now create a Palette instance - final Palette palette = new Palette(swatches, targets); + final Palette palette = new Palette(swatches, this.targets); // And make it generate itself palette.generate(); @@ -198,15 +200,15 @@ private int[] getPixelsFromBitmap(Bitmap bitmap) { private Bitmap scaleBitmapDown(final Bitmap bitmap) { double scaleRatio = -1; - if (resizeArea > 0) { + if (this.resizeArea > 0) { final int bitmapArea = bitmap.getWidth() * bitmap.getHeight(); - if (bitmapArea > resizeArea) { - scaleRatio = Math.sqrt(resizeArea / (double) bitmapArea); + if (bitmapArea > this.resizeArea) { + scaleRatio = Math.sqrt(this.resizeArea / (double) bitmapArea); } - } else if (resizeMaxDimension > 0) { + } else if (this.resizeMaxDimension > 0) { final int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight()); - if (maxDimension > resizeMaxDimension) { - scaleRatio = resizeMaxDimension / (double) maxDimension; + if (maxDimension > this.resizeMaxDimension) { + scaleRatio = this.resizeMaxDimension / (double) maxDimension; } } @@ -215,7 +217,8 @@ private Bitmap scaleBitmapDown(final Bitmap bitmap) { return bitmap; } - return Bitmap.createScaledBitmap(bitmap, (int) Math.ceil(bitmap.getWidth() * scaleRatio), (int) Math.ceil(bitmap.getHeight() * scaleRatio)); + return Bitmap.createScaledBitmap(bitmap, (int) Math.ceil(bitmap.getWidth() * scaleRatio), + (int) Math.ceil(bitmap.getHeight() * scaleRatio)); } /** @@ -229,10 +232,10 @@ private Bitmap scaleBitmapDown(final Bitmap bitmap) { * @return A Color Hex String */ public static int getColor(BufferedImage image) { - if(image == null) { + if (image == null) { throw new IllegalArgumentException("Image cannot be null"); } - + AndroidColorExtractor extractor = new AndroidColorExtractor(new Bitmap(image)); Palette palette = extractor.generate(); PaletteSwatch swatch = palette.getVibrantSwatch(); @@ -248,5 +251,40 @@ public static int getColor(BufferedImage image) { int color = swatch.getRgb(); return color; } - + + /** + * Get a {@link Swatch} of all dominant colors. + * + * @param image the {@link BufferedImage} to use + * + * @return + */ + public static Swatch getSwatch(BufferedImage image) { + if (image == null) { + throw new IllegalArgumentException("Image cannot be null"); + } + + AndroidColorExtractor extractor = new AndroidColorExtractor(new Bitmap(image)); + Palette palette = extractor.generate(); + + Swatch swatch = new Swatch<>(); + addToSwatch(swatch, palette.getVibrantColor()); + + return swatch; + } + + /** + * Convert android swatch color to {@link RGB} color and add it to the + * {@link Swatch}. + * + * @param swatch + * @param color + */ + private static void addToSwatch(Swatch swatch, int color) { + if (color < 0) { + return; + } + + swatch.add(new RGB(color)); + } } diff --git a/src/main/java/com/sangupta/colors/extract/android/Palette.java b/src/main/java/com/sangupta/colors/extract/android/Palette.java index 60a8e2a..5376f85 100644 --- a/src/main/java/com/sangupta/colors/extract/android/Palette.java +++ b/src/main/java/com/sangupta/colors/extract/android/Palette.java @@ -62,38 +62,38 @@ public final class Palette { static final boolean LOG_TIMINGS = false; - private final List mSwatches; + private final List swatches; - private final List mTargets; + private final List targets; - private final Map mSelectedSwatches; + private final Map selectedSwatches; - private final SparseBooleanArray mUsedColors; + private final SparseBooleanArray usedColors; - private final PaletteSwatch mDominantSwatch; + private final PaletteSwatch dominantSwatch; Palette(List swatches, List targets) { - mSwatches = swatches; - mTargets = targets; + this.swatches = swatches; + this.targets = targets; - mUsedColors = new SparseBooleanArray(); - mSelectedSwatches = new LinkedHashMap(); + this.usedColors = new SparseBooleanArray(); + this.selectedSwatches = new LinkedHashMap(); - mDominantSwatch = findDominantSwatch(); + this.dominantSwatch = findDominantSwatch(); } /** * Returns all of the swatches which make up the palette. */ public List getSwatches() { - return Collections.unmodifiableList(mSwatches); + return Collections.unmodifiableList(this.swatches); } /** * Returns the targets used to generate this palette. */ public List getTargets() { - return Collections.unmodifiableList(mTargets); + return Collections.unmodifiableList(this.targets); } /** @@ -156,8 +156,8 @@ public PaletteSwatch getDarkMutedSwatch() { * @param defaultColor value to return if the swatch isn't available * @see #getVibrantSwatch() */ - public int getVibrantColor(final int defaultColor) { - return getColorForTarget(Target.VIBRANT, defaultColor); + public int getVibrantColor() { + return getColorForTarget(Target.VIBRANT); } /** @@ -166,8 +166,8 @@ public int getVibrantColor(final int defaultColor) { * @param defaultColor value to return if the swatch isn't available * @see #getLightVibrantSwatch() */ - public int getLightVibrantColor(final int defaultColor) { - return getColorForTarget(Target.LIGHT_VIBRANT, defaultColor); + public int getLightVibrantColor() { + return getColorForTarget(Target.LIGHT_VIBRANT); } /** @@ -176,8 +176,8 @@ public int getLightVibrantColor(final int defaultColor) { * @param defaultColor value to return if the swatch isn't available * @see #getDarkVibrantSwatch() */ - public int getDarkVibrantColor(final int defaultColor) { - return getColorForTarget(Target.DARK_VIBRANT, defaultColor); + public int getDarkVibrantColor() { + return getColorForTarget(Target.DARK_VIBRANT); } /** @@ -186,8 +186,8 @@ public int getDarkVibrantColor(final int defaultColor) { * @param defaultColor value to return if the swatch isn't available * @see #getMutedSwatch() */ - public int getMutedColor(final int defaultColor) { - return getColorForTarget(Target.MUTED, defaultColor); + public int getMutedColor() { + return getColorForTarget(Target.MUTED); } /** @@ -196,8 +196,8 @@ public int getMutedColor(final int defaultColor) { * @param defaultColor value to return if the swatch isn't available * @see #getLightMutedSwatch() */ - public int getLightMutedColor(final int defaultColor) { - return getColorForTarget(Target.LIGHT_MUTED, defaultColor); + public int getLightMutedColor() { + return getColorForTarget(Target.LIGHT_MUTED); } /** @@ -206,8 +206,8 @@ public int getLightMutedColor(final int defaultColor) { * @param defaultColor value to return if the swatch isn't available * @see #getDarkMutedSwatch() */ - public int getDarkMutedColor(final int defaultColor) { - return getColorForTarget(Target.DARK_MUTED, defaultColor); + public int getDarkMutedColor() { + return getColorForTarget(Target.DARK_MUTED); } /** @@ -215,7 +215,7 @@ public int getDarkMutedColor(final int defaultColor) { * {@code null} if one could not be found. */ public PaletteSwatch getSwatchForTarget(final Target target) { - return mSelectedSwatches.get(target); + return this.selectedSwatches.get(target); } /** @@ -224,9 +224,9 @@ public PaletteSwatch getSwatchForTarget(final Target target) { * * @param defaultColor value to return if the swatch isn't available */ - public int getColorForTarget(final Target target, final int defaultColor) { + public int getColorForTarget(final Target target) { PaletteSwatch swatch = getSwatchForTarget(target); - return swatch != null ? swatch.getRgb() : defaultColor; + return swatch != null ? swatch.getRgb() : -1; } /** @@ -238,7 +238,7 @@ public int getColorForTarget(final Target target, final int defaultColor) { *

*/ public PaletteSwatch getDominantSwatch() { - return mDominantSwatch; + return this.dominantSwatch; } /** @@ -249,20 +249,21 @@ public PaletteSwatch getDominantSwatch() { * @see #getDominantSwatch() */ public int getDominantColor(int defaultColor) { - return mDominantSwatch != null ? mDominantSwatch.getRgb() : defaultColor; + return this.dominantSwatch != null ? this.dominantSwatch.getRgb() : defaultColor; } void generate() { // We need to make sure that the scored targets are generated first. This is so // that // inherited targets have something to inherit from - for (int i = 0, count = mTargets.size(); i < count; i++) { - final Target target = mTargets.get(i); + for (int i = 0, count = this.targets.size(); i < count; i++) { + final Target target = this.targets.get(i); target.normalizeWeights(); - mSelectedSwatches.put(target, generateScoredTarget(target)); + this.selectedSwatches.put(target, generateScoredTarget(target)); } + // We now clear out the used colors - mUsedColors.clear(); + this.usedColors.clear(); } private PaletteSwatch generateScoredTarget(final Target target) { @@ -270,7 +271,7 @@ private PaletteSwatch generateScoredTarget(final Target target) { if (maxScoreSwatch != null && target.isExclusive()) { // If we have a swatch, and the target is exclusive, add the color to the used // list - mUsedColors.append(maxScoreSwatch.getRgb(), true); + this.usedColors.append(maxScoreSwatch.getRgb(), true); } return maxScoreSwatch; } @@ -278,8 +279,8 @@ private PaletteSwatch generateScoredTarget(final Target target) { private PaletteSwatch getMaxScoredSwatchForTarget(final Target target) { float maxScore = 0; PaletteSwatch maxScoreSwatch = null; - for (int i = 0, count = mSwatches.size(); i < count; i++) { - final PaletteSwatch swatch = mSwatches.get(i); + for (int i = 0, count = this.swatches.size(); i < count; i++) { + final PaletteSwatch swatch = this.swatches.get(i); if (shouldBeScoredForTarget(swatch, target)) { final float score = generateScore(swatch, target); if (maxScoreSwatch == null || score > maxScore) { @@ -298,7 +299,7 @@ private boolean shouldBeScoredForTarget(final PaletteSwatch swatch, final Target final float hsl[] = swatch.getHsl(); return hsl[1] >= target.getMinimumSaturation() && hsl[1] <= target.getMaximumSaturation() && hsl[2] >= target.getMinimumLightness() && hsl[2] <= target.getMaximumLightness() - && !mUsedColors.get(swatch.getRgb()); + && !this.usedColors.get(swatch.getRgb()); } private float generateScore(PaletteSwatch swatch, Target target) { @@ -308,7 +309,7 @@ private float generateScore(PaletteSwatch swatch, Target target) { float luminanceScore = 0; float populationScore = 0; - final int maxPopulation = mDominantSwatch != null ? mDominantSwatch.getPopulation() : 1; + final int maxPopulation = this.dominantSwatch != null ? this.dominantSwatch.getPopulation() : 1; if (target.getSaturationWeight() > 0) { saturationScore = target.getSaturationWeight() * (1f - Math.abs(hsl[1] - target.getTargetSaturation())); @@ -326,8 +327,8 @@ private float generateScore(PaletteSwatch swatch, Target target) { private PaletteSwatch findDominantSwatch() { int maxPop = Integer.MIN_VALUE; PaletteSwatch maxSwatch = null; - for (int i = 0, count = mSwatches.size(); i < count; i++) { - PaletteSwatch swatch = mSwatches.get(i); + for (int i = 0, count = this.swatches.size(); i < count; i++) { + PaletteSwatch swatch = this.swatches.get(i); if (swatch.getPopulation() > maxPop) { maxSwatch = swatch; maxPop = swatch.getPopulation();