/* * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package com.sun.pisces; public final class GradientColorMap { /** * @defgroup CycleMethods Gradient cycle methods * Gradient cycle methods. Specifies wheteher to repeat gradient fill in cycle * or not. We will explain possible methods on linear gradient behaviour. * @see setLinearGradient, setRadialGradient * @def CYCLE_NONE * @ingroup CycleMethods * Gradient without repetition. Imagine linear gradient from blue to red color. * Color of start point (line perpendicular to vector(start,end)) will be blue. * Color of end point (line) will be red. Between these two points (lines), * there will be smooth color gradient. Outside gradient area everything will be * blue or red when CYCLE_NONE used. It works similar way with radial gradient. * @def CYCLE_REPEAT * @ingroup CycleMethods * Gradient with repetition. Gradient fill is repeated with period given by * start,end distance. * @def CYCLE_REFLECT * @ingroup CycleMethods * Gradient is repeated. Start and end color in new cycle are swaped. Gradient * fill is repeated with period given by start,end distance. You can imagine * this as if you'd put mirror to end point (line). */ public static final int CYCLE_NONE = 0; public static final int CYCLE_REPEAT = 1; public static final int CYCLE_REFLECT = 2; int cycleMethod; private static final int LG_RAMP_SIZE = 8; private static final int RAMP_SIZE = 1 << LG_RAMP_SIZE; int[] fractions = null; int[] rgba = null; int[] colors = null; GradientColorMap(int[] fractions, int[] rgba, int cycleMethod) { this.cycleMethod = cycleMethod; int numStops = fractions.length; if (fractions[0] != 0) { int[] nfractions = new int[numStops + 1]; int[] nrgba = new int[numStops + 1]; System.arraycopy(fractions, 0, nfractions, 1, numStops); System.arraycopy(rgba, 0, nrgba, 1, numStops); nfractions[0] = 0; nrgba[0] = rgba[0]; fractions = nfractions; rgba = nrgba; ++numStops; } if (fractions[numStops - 1] != 0x10000) { int[] nfractions = new int[numStops + 1]; int[] nrgba = new int[numStops + 1]; System.arraycopy(fractions, 0, nfractions, 0, numStops); System.arraycopy(rgba, 0, nrgba, 0, numStops); nfractions[numStops] = 0x10000; nrgba[numStops] = rgba[numStops - 1]; fractions = nfractions; rgba = nrgba; } this.fractions = new int[fractions.length]; System.arraycopy(fractions, 0, this.fractions, 0, fractions.length); this.rgba = new int[rgba.length]; System.arraycopy(rgba, 0, this.rgba, 0, rgba.length); createRamp(); } private int pad(int frac) { switch (cycleMethod) { case CYCLE_NONE: if (frac < 0) { return 0; } else if (frac > 0xffff) { return 0xffff; } else { return frac; } case CYCLE_REPEAT: return frac & 0xffff; case CYCLE_REFLECT: if (frac < 0) { frac = -frac; } frac = frac & 0x1ffff; if (frac > 0xffff) { frac = 0x1ffff - frac; } return frac; default: throw new RuntimeException("Unknown cycle method: " + cycleMethod); } } private int findStop(int frac) { int numStops = fractions.length; for (int i = 1; i < numStops; i++) { if (fractions[i] > frac) { return i; } } // should not reach here return 1; } private void accumColor(int frac, int[] r, int[] g, int[] b, int[] a, int[] red, int[] green, int[] blue, int[] alpha) { int stop = findStop(frac); frac -= fractions[stop - 1]; int delta = fractions[stop] - fractions[stop - 1]; red[0] += r[stop - 1] + (frac*(r[stop] - r[stop - 1]))/delta; green[0] += g[stop - 1] + (frac*(g[stop] - g[stop - 1]))/delta; blue[0] += b[stop - 1] + (frac*(b[stop] - b[stop - 1]))/delta; alpha[0] += a[stop - 1] + (frac*(a[stop] - a[stop - 1]))/delta; } private int getColorAA(int frac, int[] r, int[] g, int[] b, int[] a, int[] red, int[] green, int[] blue, int[] alpha) { int stop = findStop(frac); int delta = 192; if (fractions[stop-1] < pad(frac-delta) && pad(frac+delta) < fractions[stop]) { // we only need to antialias if a stop is within range delta = 0; } int step = 64; int total = 0; for (int i = -delta; i <= delta; i += step) { int f = pad(frac + i); accumColor(f, r, g, b, a, red, green, blue, alpha); ++total; } alpha[0] /= total; red[0] /= total; green[0] /= total; blue[0] /= total; return (alpha[0] << 24) | (red[0] << 16) | (green[0] << 8) | blue[0]; } private void createRamp() { this.colors = new int[RAMP_SIZE]; int[] alpha = new int[1]; int[] red = new int[1]; int[] green = new int[1]; int[] blue = new int[1]; int numStops = fractions.length; int[] a = new int[numStops]; int[] r = new int[numStops]; int[] g = new int[numStops]; int[] b = new int[numStops]; for (int i = 0; i < numStops; i++) { a[i] = (rgba[i] >> 24) & 0xff; r[i] = (rgba[i] >> 16) & 0xff; g[i] = (rgba[i] >> 8) & 0xff; b[i] = rgba[i] & 0xff; } int lastColorIndex = RAMP_SIZE - 1; int shift = (16 - LG_RAMP_SIZE); colors[0] = rgba[0]; colors[lastColorIndex] = rgba[numStops - 1]; for (int i = 1; i < lastColorIndex; i++) { red[0] = green[0] = blue[0] = alpha[0] = 0; colors[i] = getColorAA(i << shift, r, g, b, a, red, green, blue, alpha); } } }