org.projectbuendia.client.utils.Colorizer.java Source code

Java tutorial

Introduction

Here is the source code for org.projectbuendia.client.utils.Colorizer.java

Source

// Copyright 2015 The Project Buendia Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License.  You may obtain a copy
// of the License at: http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distrib-
// uted under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
// OR CONDITIONS OF ANY KIND, either express or implied.  See the License for
// specific language governing permissions and limitations under the License.

package org.projectbuendia.client.utils;

import android.support.v4.util.LruCache;

import java.util.Arrays;
import java.util.Random;

/** A class that generates aesthetically pleasing colors based on a color wheel. */
public class Colorizer {

    private static final int[] sDefaultPalette = new int[] { 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFF00FFFF,
            0xFFFF00FF, 0xFFFFFF00, 0xFFFF7000, 0xFF7FFF00, 0xFF00FF7F, 0xFF007FFF, 0xFF7F00FF, 0xFFFF007F };

    private static final int[][] sDefaultPaletteBytes = getPaletteBytes(sDefaultPalette);

    /** A {@link Colorizer} that has 12 colors. */
    public static final Colorizer C_12 = new Colorizer(0);

    /** A {@link Colorizer} that has 24 colors. */
    public static final Colorizer C_24 = new Colorizer(1);

    /** A {@link Colorizer} that has 48 colors. */
    public static final Colorizer C_48 = new Colorizer(2);

    /** A {@link Colorizer} that has 96 colors. */
    public static final Colorizer C_96 = new Colorizer(3);

    public static Colorizer withPalette(int... palette) {
        return new Colorizer(Arrays.copyOf(palette, palette.length));
    }

    private final LruCache<Integer, Integer> mCache = new LruCache<>(128);

    private final int[][] mPaletteBytes;
    private final int mInterpolations;
    private final int mInterpolationMultiplier;
    private final int mColorCount;
    private final double mShift;

    private final Random mRandom;

    private Colorizer(int[] palette) {
        this(getPaletteBytes(palette), 0, 0.);
    }

    private Colorizer(int interpolations) {
        this(sDefaultPaletteBytes, interpolations, 0.);
    }

    private Colorizer(Colorizer colorizer, int interpolations) {
        this(colorizer.mPaletteBytes, colorizer.mInterpolations + interpolations, colorizer.mShift);
    }

    private Colorizer(Colorizer colorizer, double shiftOffset) {
        this(colorizer.mPaletteBytes, colorizer.mInterpolations,
                Math.max(Math.min(colorizer.mShift + shiftOffset, 1.), -1.));
    }

    private Colorizer(int[][] paletteBytes, int interpolations, double shift) {
        mPaletteBytes = paletteBytes;
        mInterpolations = interpolations;
        mInterpolationMultiplier = 1 << interpolations;
        mColorCount = paletteBytes.length * mInterpolationMultiplier;
        mShift = shift;
        mRandom = new Random();
    }

    public Colorizer interpolate(int interpolation) {
        return new Colorizer(this, interpolation);
    }

    /**
     * Creates a new {@link Colorizer} based on this one but with the specified tint applied to each
     * calculated color.
     */
    public Colorizer withTint(double tint) {
        if (tint < 0. || tint > 1.) {
            throw new IllegalArgumentException("Tint must be between 0 and 1.");
        }

        return new Colorizer(this, tint);
    }

    /**
     * Creates a new {@link Colorizer} based on this one but with the specified shade applied to
     * each calculated color.
     */
    public Colorizer withShade(double shade) {
        if (shade < 0. || shade > 1.) {
            throw new IllegalArgumentException("Shade must be between 0 and 1.");
        }

        return new Colorizer(this, -shade);
    }

    /** Gets an ARGB color value for the specified integer. */
    public int getColorArgb(int i) {
        Integer cachedValue = mCache.get(i);
        if (cachedValue != null) {
            return cachedValue;
        }

        int colorIndex = i % mColorCount;
        if (colorIndex < 0) {
            colorIndex += mColorCount;
        }

        int[] rgb;
        if (mInterpolations == 0) {
            rgb = Arrays.copyOf(mPaletteBytes[colorIndex], 3);
        } else {
            int basePaletteIndex = colorIndex / mInterpolationMultiplier;
            int basePaletteOffset = colorIndex - basePaletteIndex * mInterpolationMultiplier;
            double offsetFraction = (double) basePaletteOffset / mInterpolationMultiplier;

            int[] startRgb = mPaletteBytes[basePaletteIndex];
            int[] endRgb = mPaletteBytes[(basePaletteIndex + 1) % mPaletteBytes.length];

            // TODO: Consider doing interpolations in another color space.
            rgb = new int[] { (int) (startRgb[0] + offsetFraction * (endRgb[0] - startRgb[0])),
                    (int) (startRgb[1] + offsetFraction * (endRgb[1] - startRgb[1])),
                    (int) (startRgb[2] + offsetFraction * (endRgb[2] - startRgb[2])) };
        }

        double shift = mShift * 0xFF;

        int value = 0xFF000000 | (int) Math.max(Math.min(rgb[0] + shift, 255.), 0) << 16
                | (int) Math.max(Math.min(rgb[1] + shift, 255.), 0) << 8
                | (int) Math.max(Math.min(rgb[2] + shift, 255.), 0);
        mCache.put(i, value);
        return value;
    }

    /** Gets an ARGB value for the specified object. */
    public int getColorArgb(Object o) {
        return getColorArgb(mix(o == null ? 0 : o.hashCode()));
    }

    private static int[][] getPaletteBytes(int[] palette) {
        int[][] paletteBytes = new int[palette.length][];
        for (int i = 0; i < palette.length; i++) {
            paletteBytes[i] = new int[] { ((palette[i] >>> 16) & 0xff), ((palette[i] >>> 8) & 0xff),
                    ((palette[i]) & 0xff) };
        }

        return paletteBytes;
    }

    private int mix(int value) {
        mRandom.setSeed(value);
        return mRandom.nextInt();
    }
}