0001: /*
0002:
0003: Copyright 2001-2003 The Apache Software Foundation
0004:
0005: Licensed under the Apache License, Version 2.0 (the "License");
0006: you may not use this file except in compliance with the License.
0007: You may obtain a copy of the License at
0008:
0009: http://www.apache.org/licenses/LICENSE-2.0
0010:
0011: Unless required by applicable law or agreed to in writing, software
0012: distributed under the License is distributed on an "AS IS" BASIS,
0013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: See the License for the specific language governing permissions and
0015: limitations under the License.
0016:
0017: */
0018: package com.xoetrope.batik.ext.awt;
0019:
0020: import java.awt.Color;
0021: import java.awt.PaintContext;
0022: import java.awt.Rectangle;
0023: import java.awt.RenderingHints;
0024: import java.awt.color.ColorSpace;
0025: import java.awt.geom.AffineTransform;
0026: import java.awt.geom.NoninvertibleTransformException;
0027: import java.awt.geom.Rectangle2D;
0028: import java.awt.image.ColorModel;
0029: import java.awt.image.DataBuffer;
0030: import java.awt.image.DataBufferInt;
0031: import java.awt.image.DirectColorModel;
0032: import java.awt.image.Raster;
0033: import java.awt.image.SinglePixelPackedSampleModel;
0034: import java.awt.image.WritableRaster;
0035: import java.lang.ref.WeakReference;
0036:
0037: import com.xoetrope.batik.ext.awt.image.GraphicsUtil;
0038:
0039: /** This is the superclass for all PaintContexts which use a multiple color
0040: * gradient to fill in their raster. It provides the actual color interpolation
0041: * functionality. Subclasses only have to deal with using the gradient to fill
0042: * pixels in a raster.
0043: *
0044: * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
0045: * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
0046: * @version $Id: MultipleGradientPaintContext.java,v 1.2 2006/08/31 09:28:47 val Exp $
0047: *
0048: */
0049: abstract class MultipleGradientPaintContext implements PaintContext {
0050:
0051: protected final static boolean DEBUG = false;
0052:
0053: /**
0054: * The color model data is generated in (always un premult).
0055: */
0056: protected ColorModel dataModel;
0057: /**
0058: * PaintContext's output ColorModel ARGB if colors are not all
0059: * opaque, RGB otherwise. Linear and premult are matched to
0060: * output ColorModel.
0061: */
0062: protected ColorModel model;
0063:
0064: /** Color model used if gradient colors are all opaque */
0065: private static ColorModel lrgbmodel_NA = new DirectColorModel(
0066: ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), 24,
0067: 0xff0000, 0xFF00, 0xFF, 0x0, false, DataBuffer.TYPE_INT);
0068:
0069: private static ColorModel srgbmodel_NA = new DirectColorModel(
0070: ColorSpace.getInstance(ColorSpace.CS_sRGB), 24, 0xff0000,
0071: 0xFF00, 0xFF, 0x0, false, DataBuffer.TYPE_INT);
0072:
0073: /** Color model used if some gradient colors are transparent */
0074: private static ColorModel lrgbmodel_A = new DirectColorModel(
0075: ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), 32,
0076: 0xff0000, 0xFF00, 0xFF, 0xFF000000, false,
0077: DataBuffer.TYPE_INT);
0078:
0079: private static ColorModel srgbmodel_A = new DirectColorModel(
0080: ColorSpace.getInstance(ColorSpace.CS_sRGB), 32, 0xff0000,
0081: 0xFF00, 0xFF, 0xFF000000, false, DataBuffer.TYPE_INT);
0082:
0083: /** The cached colorModel */
0084: protected static ColorModel cachedModel;
0085:
0086: /** The cached raster, which is reusable among instances */
0087: protected static WeakReference cached;
0088:
0089: /** Raster is reused whenever possible */
0090: protected WritableRaster saved;
0091:
0092: /** The method to use when painting out of the gradient bounds. */
0093: protected MultipleGradientPaint.CycleMethodEnum cycleMethod;
0094:
0095: /** The colorSpace in which to perform the interpolation */
0096: protected MultipleGradientPaint.ColorSpaceEnum colorSpace;
0097:
0098: /** Elements of the inverse transform matrix. */
0099: protected float a00, a01, a10, a11, a02, a12;
0100:
0101: /** This boolean specifies wether we are in simple lookup mode, where an
0102: * input value between 0 and 1 may be used to directly index into a single
0103: * array of gradient colors. If this boolean value is false, then we have
0104: * to use a 2-step process where we have to determine which gradient array
0105: * we fall into, then determine the index into that array.
0106: */
0107: protected boolean isSimpleLookup = true;
0108:
0109: /** This boolean indicates if the gradient appears to have sudden
0110: * discontinuities in it, this may be because of multiple stops
0111: * at the same location or use of the REPEATE mode.
0112: */
0113: protected boolean hasDiscontinuity = false;
0114:
0115: /** Size of gradients array for scaling the 0-1 index when looking up
0116: * colors the fast way. */
0117: protected int fastGradientArraySize;
0118:
0119: /**
0120: * Array which contains the interpolated color values for each interval,
0121: * used by calculateSingleArrayGradient(). It is protected for possible
0122: * direct access by subclasses.
0123: */
0124: protected int[] gradient;
0125:
0126: /** Array of gradient arrays, one array for each interval. Used by
0127: * calculateMultipleArrayGradient().
0128: */
0129: protected int[][] gradients;
0130:
0131: /** This holds the blend of all colors in the gradient.
0132: * we use this at extreamly low resolutions to ensure we
0133: * get a decent blend of the colors.
0134: */
0135: protected int gradientAverage;
0136:
0137: /** This holds the color to use when we are off the bottom of the
0138: * gradient */
0139: protected int gradientUnderflow;
0140:
0141: /** This holds the color to use when we are off the top of the
0142: * gradient */
0143: protected int gradientOverflow;
0144:
0145: /** Length of the 2D slow lookup gradients array. */
0146: protected int gradientsLength;
0147:
0148: /** Normalized intervals array */
0149: protected float[] normalizedIntervals;
0150:
0151: /** fractions array */
0152: protected float[] fractions;
0153:
0154: /** Used to determine if gradient colors are all opaque */
0155: private int transparencyTest;
0156:
0157: /** Colorspace conversion lookup tables */
0158: private static final int SRGBtoLinearRGB[] = new int[256];
0159: private static final int LinearRGBtoSRGB[] = new int[256];
0160:
0161: //build the tables
0162: static {
0163: for (int k = 0; k < 256; k++) {
0164: SRGBtoLinearRGB[k] = convertSRGBtoLinearRGB(k);
0165: LinearRGBtoSRGB[k] = convertLinearRGBtoSRGB(k);
0166: }
0167: }
0168:
0169: /** Constant number of max colors between any 2 arbitrary colors.
0170: * Used for creating and indexing gradients arrays.
0171: */
0172: protected static final int GRADIENT_SIZE = 256;
0173: protected static final int GRADIENT_SIZE_INDEX = GRADIENT_SIZE - 1;
0174:
0175: /** Maximum length of the fast single-array. If the estimated array size
0176: * is greater than this, switch over to the slow lookup method.
0177: * No particular reason for choosing this number, but it seems to provide
0178: * satisfactory performance for the common case (fast lookup).
0179: */
0180: private static final int MAX_GRADIENT_ARRAY_SIZE = 5000;
0181:
0182: /** Constructor for superclass. Does some initialization, but leaves most
0183: * of the heavy-duty math for calculateGradient(), so the subclass may do
0184: * some other manipulation beforehand if necessary. This is not possible
0185: * if this computation is done in the superclass constructor which always
0186: * gets called first.
0187: **/
0188: public MultipleGradientPaintContext(ColorModel cm,
0189: Rectangle deviceBounds, Rectangle2D userBounds,
0190: AffineTransform t, RenderingHints hints, float[] fractions,
0191: Color[] colors,
0192: MultipleGradientPaint.CycleMethodEnum cycleMethod,
0193: MultipleGradientPaint.ColorSpaceEnum colorSpace)
0194: throws NoninvertibleTransformException {
0195: //We have to deal with the cases where the 1st gradient stop is not
0196: //equal to 0 and/or the last gradient stop is not equal to 1.
0197: //In both cases, create a new point and replicate the previous
0198: //extreme point's color.
0199:
0200: boolean fixFirst = false;
0201: boolean fixLast = false;
0202: int len = fractions.length;
0203:
0204: //if the first gradient stop is not equal to zero, fix this condition
0205: if (fractions[0] != 0f) {
0206: fixFirst = true;
0207: len++;
0208: }
0209:
0210: //if the last gradient stop is not equal to one, fix this condition
0211: if (fractions[fractions.length - 1] != 1f) {
0212: fixLast = true;
0213: len++;
0214: }
0215:
0216: for (int i = 0; i < fractions.length - 1; i++)
0217: if (fractions[i] == fractions[i + 1])
0218: len--;
0219:
0220: this .fractions = new float[len];
0221: Color[] loColors = new Color[len - 1];
0222: Color[] hiColors = new Color[len - 1];
0223: normalizedIntervals = new float[len - 1];
0224:
0225: gradientUnderflow = colors[0].getRGB();
0226: gradientOverflow = colors[colors.length - 1].getRGB();
0227:
0228: int idx = 0;
0229: if (fixFirst) {
0230: this .fractions[0] = 0;
0231: loColors[0] = colors[0];
0232: hiColors[0] = colors[0];
0233: normalizedIntervals[0] = fractions[0];
0234: idx++;
0235: }
0236:
0237: for (int i = 0; i < fractions.length - 1; i++) {
0238: if (fractions[i] == fractions[i + 1]) {
0239: // System.out.println("EQ Fracts");
0240: if (!colors[i].equals(colors[i + 1])) {
0241: hasDiscontinuity = true;
0242: }
0243: continue;
0244: }
0245: this .fractions[idx] = fractions[i];
0246: loColors[idx] = colors[i];
0247: hiColors[idx] = colors[i + 1];
0248: normalizedIntervals[idx] = fractions[i + 1] - fractions[i];
0249: idx++;
0250: }
0251:
0252: this .fractions[idx] = fractions[fractions.length - 1];
0253:
0254: if (fixLast) {
0255: loColors[idx] = hiColors[idx] = colors[colors.length - 1];
0256: normalizedIntervals[idx] = 1 - fractions[fractions.length - 1];
0257: idx++;
0258: this .fractions[idx] = 1;
0259: }
0260:
0261: // The inverse transform is needed to from device to user space.
0262: // Get all the components of the inverse transform matrix.
0263: AffineTransform tInv = t.createInverse();
0264:
0265: double m[] = new double[6];
0266: tInv.getMatrix(m);
0267: a00 = (float) m[0];
0268: a10 = (float) m[1];
0269: a01 = (float) m[2];
0270: a11 = (float) m[3];
0271: a02 = (float) m[4];
0272: a12 = (float) m[5];
0273:
0274: //copy some flags
0275: this .cycleMethod = cycleMethod;
0276: this .colorSpace = colorSpace;
0277:
0278: // Setup an example Model, we may refine it later.
0279: if (cm.getColorSpace() == lrgbmodel_A.getColorSpace())
0280: dataModel = lrgbmodel_A;
0281: else if (cm.getColorSpace() == srgbmodel_A.getColorSpace())
0282: dataModel = srgbmodel_A;
0283: else
0284: throw new IllegalArgumentException(
0285: "Unsupported ColorSpace for interpolation");
0286:
0287: calculateGradientFractions(loColors, hiColors);
0288:
0289: model = GraphicsUtil.coerceColorModel(dataModel, cm
0290: .isAlphaPremultiplied());
0291: }
0292:
0293: /** This function is the meat of this class. It calculates an array of
0294: * gradient colors based on an array of fractions and color values at those
0295: * fractions.
0296: */
0297: protected final void calculateGradientFractions(Color[] loColors,
0298: Color[] hiColors) {
0299:
0300: //if interpolation should occur in Linear RGB space, convert the
0301: //colors using the lookup table
0302: if (colorSpace == LinearGradientPaint.LINEAR_RGB) {
0303: for (int i = 0; i < loColors.length; i++) {
0304: loColors[i] = new Color(SRGBtoLinearRGB[loColors[i]
0305: .getRed()], SRGBtoLinearRGB[loColors[i]
0306: .getGreen()], SRGBtoLinearRGB[loColors[i]
0307: .getBlue()], loColors[i].getAlpha());
0308:
0309: hiColors[i] = new Color(SRGBtoLinearRGB[hiColors[i]
0310: .getRed()], SRGBtoLinearRGB[hiColors[i]
0311: .getGreen()], SRGBtoLinearRGB[hiColors[i]
0312: .getBlue()], hiColors[i].getAlpha());
0313: }
0314: }
0315:
0316: //initialize to be fully opaque for ANDing with colors
0317: transparencyTest = 0xff000000;
0318: if (cycleMethod == MultipleGradientPaint.NO_CYCLE) {
0319: // Include overflow and underflow colors in transparency
0320: // test.
0321: transparencyTest &= gradientUnderflow;
0322: transparencyTest &= gradientOverflow;
0323: }
0324:
0325: //array of interpolation arrays
0326: gradients = new int[fractions.length - 1][];
0327: gradientsLength = gradients.length;
0328:
0329: // Find smallest interval
0330: int n = normalizedIntervals.length;
0331:
0332: float Imin = 1;
0333:
0334: for (int i = 0; i < n; i++) {
0335: Imin = (Imin > normalizedIntervals[i]) ? normalizedIntervals[i]
0336: : Imin;
0337: }
0338:
0339: //estimate the size of the entire gradients array.
0340: //This is to prevent a tiny interval from causing the size of array to
0341: //explode. If the estimated size is too large, break to using
0342: //seperate arrays for each interval, and using an indexing scheme at
0343: //look-up time.
0344: int estimatedSize = 0;
0345:
0346: if (Imin == 0) {
0347: estimatedSize = Integer.MAX_VALUE;
0348: hasDiscontinuity = true;
0349: } else {
0350: for (int i = 0; i < normalizedIntervals.length; i++) {
0351: estimatedSize += (normalizedIntervals[i] / Imin)
0352: * GRADIENT_SIZE;
0353: }
0354: }
0355:
0356: if (estimatedSize > MAX_GRADIENT_ARRAY_SIZE) {
0357: //slow method
0358: calculateMultipleArrayGradient(loColors, hiColors);
0359: if ((cycleMethod == MultipleGradientPaint.REPEAT)
0360: && (gradients[0][0] != gradients[gradients.length - 1][GRADIENT_SIZE_INDEX]))
0361: hasDiscontinuity = true;
0362: } else {
0363: //fast method
0364: calculateSingleArrayGradient(loColors, hiColors, Imin);
0365: if ((cycleMethod == MultipleGradientPaint.REPEAT)
0366: && (gradient[0] != gradient[fastGradientArraySize]))
0367: hasDiscontinuity = true;
0368: }
0369:
0370: // Use the most 'economical' model (no alpha).
0371: if ((transparencyTest >>> 24) == 0xff) {
0372: if (dataModel.getColorSpace() == lrgbmodel_NA
0373: .getColorSpace())
0374: dataModel = lrgbmodel_NA;
0375: else if (dataModel.getColorSpace() == srgbmodel_NA
0376: .getColorSpace())
0377: dataModel = srgbmodel_NA;
0378: model = dataModel;
0379: }
0380: }
0381:
0382: /**
0383: * FAST LOOKUP METHOD
0384: *
0385: * This method calculates the gradient color values and places them in a
0386: * single int array, gradient[]. It does this by allocating space for
0387: * each interval based on its size relative to the smallest interval in
0388: * the array. The smallest interval is allocated 255 interpolated values
0389: * (the maximum number of unique in-between colors in a 24 bit color
0390: * system), and all other intervals are allocated
0391: * size = (255 * the ratio of their size to the smallest interval).
0392: *
0393: * This scheme expedites a speedy retrieval because the colors are
0394: * distributed along the array according to their user-specified
0395: * distribution. All that is needed is a relative index from 0 to 1.
0396: *
0397: * The only problem with this method is that the possibility exists for
0398: * the array size to balloon in the case where there is a
0399: * disproportionately small gradient interval. In this case the other
0400: * intervals will be allocated huge space, but much of that data is
0401: * redundant. We thus need to use the space conserving scheme below.
0402: *
0403: * @param Imin the size of the smallest interval
0404: *
0405: */
0406: private void calculateSingleArrayGradient(Color[] loColors,
0407: Color[] hiColors, float Imin) {
0408:
0409: //set the flag so we know later it is a non-simple lookup
0410: isSimpleLookup = true;
0411:
0412: int rgb1; //2 colors to interpolate
0413: int rgb2;
0414:
0415: int gradientsTot = 1; //the eventual size of the single array
0416:
0417: // These are fixed point 8.16 (start with 0.5)
0418: int aveA = 0x008000;
0419: int aveR = 0x008000;
0420: int aveG = 0x008000;
0421: int aveB = 0x008000;
0422:
0423: //for every interval (transition between 2 colors)
0424: for (int i = 0; i < gradients.length; i++) {
0425:
0426: //create an array whose size is based on the ratio to the
0427: //smallest interval.
0428: int nGradients = (int) ((normalizedIntervals[i] / Imin) * 255f);
0429: gradientsTot += nGradients;
0430: gradients[i] = new int[nGradients];
0431:
0432: //the the 2 colors (keyframes) to interpolate between
0433: rgb1 = loColors[i].getRGB();
0434: rgb2 = hiColors[i].getRGB();
0435:
0436: //fill this array with the colors in between rgb1 and rgb2
0437: interpolate(rgb1, rgb2, gradients[i]);
0438:
0439: // Calculate Average of two colors...
0440: int argb = gradients[i][GRADIENT_SIZE / 2];
0441: float norm = normalizedIntervals[i];
0442: aveA += (int) (((argb >> 8) & 0xFF0000) * norm);
0443: aveR += (int) (((argb) & 0xFF0000) * norm);
0444: aveG += (int) (((argb << 8) & 0xFF0000) * norm);
0445: aveB += (int) (((argb << 16) & 0xFF0000) * norm);
0446:
0447: //if the colors are opaque, transparency should still be 0xff000000
0448: transparencyTest &= rgb1;
0449: transparencyTest &= rgb2;
0450: }
0451:
0452: gradientAverage = (((aveA & 0xFF0000) << 8)
0453: | ((aveR & 0xFF0000)) | ((aveG & 0xFF0000) >> 8) | ((aveB & 0xFF0000) >> 16));
0454:
0455: // Put all gradients in a single array
0456: gradient = new int[gradientsTot];
0457: int curOffset = 0;
0458: for (int i = 0; i < gradients.length; i++) {
0459: System.arraycopy(gradients[i], 0, gradient, curOffset,
0460: gradients[i].length);
0461: curOffset += gradients[i].length;
0462: }
0463: gradient[gradient.length - 1] = hiColors[hiColors.length - 1]
0464: .getRGB();
0465:
0466: //if interpolation occurred in Linear RGB space, convert the
0467: //gradients back to SRGB using the lookup table
0468: if (colorSpace == LinearGradientPaint.LINEAR_RGB) {
0469: if (dataModel.getColorSpace() == ColorSpace
0470: .getInstance(ColorSpace.CS_sRGB)) {
0471: for (int i = 0; i < gradient.length; i++) {
0472: gradient[i] = convertEntireColorLinearRGBtoSRGB(gradient[i]);
0473: }
0474: gradientAverage = convertEntireColorLinearRGBtoSRGB(gradientAverage);
0475: }
0476: } else {
0477: if (dataModel.getColorSpace() == ColorSpace
0478: .getInstance(ColorSpace.CS_LINEAR_RGB)) {
0479: for (int i = 0; i < gradient.length; i++) {
0480: gradient[i] = convertEntireColorSRGBtoLinearRGB(gradient[i]);
0481: }
0482: gradientAverage = convertEntireColorSRGBtoLinearRGB(gradientAverage);
0483: }
0484: }
0485:
0486: fastGradientArraySize = gradient.length - 1;
0487: }
0488:
0489: /**
0490: * SLOW LOOKUP METHOD
0491: *
0492: * This method calculates the gradient color values for each interval and
0493: * places each into its own 255 size array. The arrays are stored in
0494: * gradients[][]. (255 is used because this is the maximum number of
0495: * unique colors between 2 arbitrary colors in a 24 bit color system)
0496: *
0497: * This method uses the minimum amount of space (only 255 * number of
0498: * intervals), but it aggravates the lookup procedure, because now we
0499: * have to find out which interval to select, then calculate the index
0500: * within that interval. This causes a significant performance hit,
0501: * because it requires this calculation be done for every point in
0502: * the rendering loop.
0503: *
0504: * For those of you who are interested, this is a classic example of the
0505: * time-space tradeoff.
0506: *
0507: */
0508: private void calculateMultipleArrayGradient(Color[] loColors,
0509: Color[] hiColors) {
0510:
0511: //set the flag so we know later it is a non-simple lookup
0512: isSimpleLookup = false;
0513:
0514: int rgb1; //2 colors to interpolate
0515: int rgb2;
0516:
0517: // These are fixed point 8.16 (start with 0.5)
0518: int aveA = 0x008000;
0519: int aveR = 0x008000;
0520: int aveG = 0x008000;
0521: int aveB = 0x008000;
0522:
0523: //for every interval (transition between 2 colors)
0524: for (int i = 0; i < gradients.length; i++) {
0525:
0526: // This interval will never actually be used (zero size)
0527: if (normalizedIntervals[i] == 0)
0528: continue;
0529:
0530: //create an array of the maximum theoretical size for each interval
0531: gradients[i] = new int[GRADIENT_SIZE];
0532:
0533: //get the the 2 colors
0534: rgb1 = loColors[i].getRGB();
0535: rgb2 = hiColors[i].getRGB();
0536:
0537: //fill this array with the colors in between rgb1 and rgb2
0538: interpolate(rgb1, rgb2, gradients[i]);
0539:
0540: // Calculate Average of two colors...
0541: int argb = gradients[i][GRADIENT_SIZE / 2];
0542: float norm = normalizedIntervals[i];
0543: aveA += (int) (((argb >> 8) & 0xFF0000) * norm);
0544: aveR += (int) (((argb) & 0xFF0000) * norm);
0545: aveG += (int) (((argb << 8) & 0xFF0000) * norm);
0546: aveB += (int) (((argb << 16) & 0xFF0000) * norm);
0547:
0548: //if the colors are opaque, transparency should still be 0xff000000
0549: transparencyTest &= rgb1;
0550: transparencyTest &= rgb2;
0551: }
0552:
0553: gradientAverage = (((aveA & 0xFF0000) << 8)
0554: | ((aveR & 0xFF0000)) | ((aveG & 0xFF0000) >> 8) | ((aveB & 0xFF0000) >> 16));
0555:
0556: //if interpolation occurred in Linear RGB space, convert the
0557: //gradients back to SRGB using the lookup table
0558: if (colorSpace == LinearGradientPaint.LINEAR_RGB) {
0559: if (dataModel.getColorSpace() == ColorSpace
0560: .getInstance(ColorSpace.CS_sRGB)) {
0561: for (int j = 0; j < gradients.length; j++) {
0562: for (int i = 0; i < gradients[j].length; i++) {
0563: gradients[j][i] = convertEntireColorLinearRGBtoSRGB(gradients[j][i]);
0564: }
0565: }
0566: gradientAverage = convertEntireColorLinearRGBtoSRGB(gradientAverage);
0567: }
0568: } else {
0569: if (dataModel.getColorSpace() == ColorSpace
0570: .getInstance(ColorSpace.CS_LINEAR_RGB)) {
0571: for (int j = 0; j < gradients.length; j++) {
0572: for (int i = 0; i < gradients[j].length; i++) {
0573: gradients[j][i] = convertEntireColorSRGBtoLinearRGB(gradients[j][i]);
0574: }
0575: }
0576: gradientAverage = convertEntireColorSRGBtoLinearRGB(gradientAverage);
0577: }
0578: }
0579: }
0580:
0581: /** Yet another helper function. This one linearly interpolates between
0582: * 2 colors, filling up the output array.
0583: *
0584: * @param rgb1 the start color
0585: * @param rgb2 the end color
0586: * @param output the output array of colors... assuming this is not null.
0587: *
0588: */
0589: private void interpolate(int rgb1, int rgb2, int[] output) {
0590:
0591: int a1, r1, g1, b1, da, dr, dg, db; //color components
0592:
0593: //step between interpolated values.
0594: float stepSize = 1 / (float) output.length;
0595:
0596: //extract color components from packed integer
0597: a1 = (rgb1 >> 24) & 0xff;
0598: r1 = (rgb1 >> 16) & 0xff;
0599: g1 = (rgb1 >> 8) & 0xff;
0600: b1 = (rgb1) & 0xff;
0601: //calculate the total change in alpha, red, green, blue
0602: da = ((rgb2 >> 24) & 0xff) - a1;
0603: dr = ((rgb2 >> 16) & 0xff) - r1;
0604: dg = ((rgb2 >> 8) & 0xff) - g1;
0605: db = ((rgb2) & 0xff) - b1;
0606:
0607: //for each step in the interval calculate the in-between color by
0608: //multiplying the normalized current position by the total color change
0609: //(.5 is added to prevent truncation round-off error)
0610: for (int i = 0; i < output.length; i++) {
0611: output[i] = (((int) ((a1 + i * da * stepSize) + .5) << 24))
0612: | (((int) ((r1 + i * dr * stepSize) + .5) << 16))
0613: | (((int) ((g1 + i * dg * stepSize) + .5) << 8))
0614: | (((int) ((b1 + i * db * stepSize) + .5)));
0615: }
0616: }
0617:
0618: /** Yet another helper function. This one extracts the color components
0619: * of an integer RGB triple, converts them from LinearRGB to SRGB, then
0620: * recompacts them into an int.
0621: */
0622: private int convertEntireColorLinearRGBtoSRGB(int rgb) {
0623:
0624: int a1, r1, g1, b1; //color components
0625:
0626: //extract red, green, blue components
0627: a1 = (rgb >> 24) & 0xff;
0628: r1 = (rgb >> 16) & 0xff;
0629: g1 = (rgb >> 8) & 0xff;
0630: b1 = rgb & 0xff;
0631:
0632: //use the lookup table
0633: r1 = LinearRGBtoSRGB[r1];
0634: g1 = LinearRGBtoSRGB[g1];
0635: b1 = LinearRGBtoSRGB[b1];
0636:
0637: //re-compact the components
0638: return ((a1 << 24) | (r1 << 16) | (g1 << 8) | b1);
0639: }
0640:
0641: /** Yet another helper function. This one extracts the color components
0642: * of an integer RGB triple, converts them from LinearRGB to SRGB, then
0643: * recompacts them into an int.
0644: */
0645: private int convertEntireColorSRGBtoLinearRGB(int rgb) {
0646:
0647: int a1, r1, g1, b1; //color components
0648:
0649: //extract red, green, blue components
0650: a1 = (rgb >> 24) & 0xff;
0651: r1 = (rgb >> 16) & 0xff;
0652: g1 = (rgb >> 8) & 0xff;
0653: b1 = rgb & 0xff;
0654:
0655: //use the lookup table
0656: r1 = SRGBtoLinearRGB[r1];
0657: g1 = SRGBtoLinearRGB[g1];
0658: b1 = SRGBtoLinearRGB[b1];
0659:
0660: //re-compact the components
0661: return ((a1 << 24) | (r1 << 16) | (g1 << 8) | b1);
0662: }
0663:
0664: /** Helper function to index into the gradients array. This is necessary
0665: * because each interval has an array of colors with uniform size 255.
0666: * However, the color intervals are not necessarily of uniform length, so
0667: * a conversion is required.
0668: *
0669: * @param position the unmanipulated position. want to map this into the
0670: * range 0 to 1
0671: *
0672: * @returns integer color to display
0673: *
0674: */
0675: protected final int indexIntoGradientsArrays(float position) {
0676:
0677: //first, manipulate position value depending on the cycle method.
0678:
0679: if (cycleMethod == MultipleGradientPaint.NO_CYCLE) {
0680:
0681: if (position >= 1) { //upper bound is 1
0682: return gradientOverflow;
0683: }
0684:
0685: else if (position <= 0) { //lower bound is 0
0686: return gradientUnderflow;
0687: }
0688: }
0689:
0690: else if (cycleMethod == MultipleGradientPaint.REPEAT) {
0691: //get the fractional part
0692: //(modulo behavior discards integer component)
0693: position = position - (int) position;
0694:
0695: //position now be between -1 and 1
0696:
0697: if (position < 0) {
0698: position = position + 1; //force it to be in the range 0-1
0699: }
0700:
0701: int w = 0, c1 = 0, c2 = 0;
0702: if (isSimpleLookup) {
0703: position *= gradient.length;
0704: int idx1 = (int) (position);
0705: if (idx1 + 1 < gradient.length)
0706: return gradient[idx1];
0707:
0708: w = (int) ((position - idx1) * (1 << 16));
0709: c1 = gradient[idx1];
0710: c2 = gradient[0];
0711: } else {
0712: //for all the gradient interval arrays
0713: for (int i = 0; i < gradientsLength; i++) {
0714:
0715: if (position < fractions[i + 1]) { //this is the array we want
0716:
0717: float delta = position - fractions[i];
0718:
0719: delta = ((delta / normalizedIntervals[i]) * GRADIENT_SIZE);
0720: //this is the interval we want.
0721: int index = (int) delta;
0722: if ((index + 1 < gradients[i].length)
0723: || (i + 1 < gradientsLength))
0724: return gradients[i][index];
0725:
0726: w = (int) ((delta - index) * (1 << 16));
0727: c1 = gradients[i][index];
0728: c2 = gradients[0][0];
0729: break;
0730: }
0731: }
0732: }
0733:
0734: return ((((((c1 >> 8) & 0xFF0000) + ((((c2 >>> 24)) - ((c1 >>> 24))) * w)) & 0xFF0000) << 8)
0735: |
0736:
0737: (((((c1) & 0xFF0000) + ((((c2 >> 16) & 0xFF) - ((c1 >> 16) & 0xFF)) * w)) & 0xFF0000))
0738: |
0739:
0740: (((((c1 << 8) & 0xFF0000) + ((((c2 >> 8) & 0xFF) - ((c1 >> 8) & 0xFF)) * w)) & 0xFF0000) >> 8) |
0741:
0742: (((((c1 << 16) & 0xFF0000) + ((((c2) & 0xFF) - ((c1) & 0xFF)) * w)) & 0xFF0000) >> 16));
0743:
0744: // return c1 +
0745: // ((( ((((c2>>>24) )-((c1>>>24) ))*w)&0xFF0000)<< 8) |
0746: // (( ((((c2>> 16)&0xFF)-((c1>> 16)&0xFF))*w)&0xFF0000) ) |
0747: // (( ((((c2>> 8)&0xFF)-((c1>> 8)&0xFF))*w)&0xFF0000)>> 8) |
0748: // (( ((((c2 )&0xFF)-((c1 )&0xFF))*w)&0xFF0000)>>16));
0749: }
0750:
0751: else { //cycleMethod == MultipleGradientPaint.REFLECT
0752:
0753: if (position < 0) {
0754: position = -position; //take absolute value
0755: }
0756:
0757: int part = (int) position; //take the integer part
0758:
0759: position = position - part; //get the fractional part
0760:
0761: if ((part & 0x00000001) == 1) { //if integer part is odd
0762: position = 1 - position; //want the reflected color instead
0763: }
0764: }
0765:
0766: //now, get the color based on this 0-1 position:
0767:
0768: if (isSimpleLookup) { //easy to compute: just scale index by array size
0769: return gradient[(int) (position * fastGradientArraySize)];
0770: }
0771:
0772: else { //more complicated computation, to save space
0773:
0774: //for all the gradient interval arrays
0775: for (int i = 0; i < gradientsLength; i++) {
0776:
0777: if (position < fractions[i + 1]) { //this is the array we want
0778:
0779: float delta = position - fractions[i];
0780:
0781: //this is the interval we want.
0782: int index = (int) ((delta / normalizedIntervals[i]) * (GRADIENT_SIZE_INDEX));
0783:
0784: return gradients[i][index];
0785: }
0786: }
0787:
0788: }
0789:
0790: return gradientOverflow;
0791: }
0792:
0793: /** Helper function to index into the gradients array. This is necessary
0794: * because each interval has an array of colors with uniform size 255.
0795: * However, the color intervals are not necessarily of uniform length, so
0796: * a conversion is required. This version also does anti-aliasing by
0797: * averaging the gradient over position+/-(sz/2).
0798: *
0799: * @param position the unmanipulated position. want to map this into the
0800: * range 0 to 1
0801: * @param sz the size in gradient space to average.
0802: *
0803: * @returns ARGB integer color to display
0804: */
0805: protected final int indexGradientAntiAlias(float position, float sz) {
0806: //first, manipulate position value depending on the cycle method.
0807: if (cycleMethod == MultipleGradientPaint.NO_CYCLE) {
0808: if (DEBUG)
0809: System.out.println("NO_CYCLE");
0810: float p1 = position - (sz / 2);
0811: float p2 = position + (sz / 2);
0812:
0813: if (p1 >= 1)
0814: return gradientOverflow;
0815:
0816: if (p2 <= 0)
0817: return gradientUnderflow;
0818:
0819: int interior;
0820: float top_weight = 0, bottom_weight = 0, frac;
0821: if (p2 >= 1) {
0822: top_weight = (p2 - 1) / sz;
0823: if (p1 <= 0) {
0824: bottom_weight = -p1 / sz;
0825: frac = 1;
0826: interior = gradientAverage;
0827: } else {
0828: frac = 1 - p1;
0829: interior = getAntiAlias(p1, true, 1, false, 1 - p1,
0830: 1);
0831: }
0832: } else if (p1 <= 0) {
0833: bottom_weight = -p1 / sz;
0834: frac = p2;
0835: interior = getAntiAlias(0, true, p2, false, p2, 1);
0836: } else
0837: return getAntiAlias(p1, true, p2, false, sz, 1);
0838:
0839: int norm = (int) ((1 << 16) * frac / sz);
0840: int pA = (((interior >>> 20) & 0xFF0) * norm) >> 16;
0841: int pR = (((interior >> 12) & 0xFF0) * norm) >> 16;
0842: int pG = (((interior >> 4) & 0xFF0) * norm) >> 16;
0843: int pB = (((interior << 4) & 0xFF0) * norm) >> 16;
0844:
0845: if (bottom_weight != 0) {
0846: int bPix = gradientUnderflow;
0847: // System.out.println("ave: " + gradientAverage);
0848: norm = (int) ((1 << 16) * bottom_weight);
0849: pA += (((bPix >>> 20) & 0xFF0) * norm) >> 16;
0850: pR += (((bPix >> 12) & 0xFF0) * norm) >> 16;
0851: pG += (((bPix >> 4) & 0xFF0) * norm) >> 16;
0852: pB += (((bPix << 4) & 0xFF0) * norm) >> 16;
0853: }
0854:
0855: if (top_weight != 0) {
0856: int tPix = gradientOverflow;
0857:
0858: norm = (int) ((1 << 16) * top_weight);
0859: pA += (((tPix >>> 20) & 0xFF0) * norm) >> 16;
0860: pR += (((tPix >> 12) & 0xFF0) * norm) >> 16;
0861: pG += (((tPix >> 4) & 0xFF0) * norm) >> 16;
0862: pB += (((tPix << 4) & 0xFF0) * norm) >> 16;
0863: }
0864:
0865: return (((pA & 0xFF0) << 20) | ((pR & 0xFF0) << 12)
0866: | ((pG & 0xFF0) << 4) | ((pB & 0xFF0) >> 4));
0867: }
0868:
0869: // See how many times we are going to "wrap around" the gradient,
0870: // array.
0871: int intSz = (int) sz;
0872:
0873: float weight = 1f;
0874: if (intSz != 0) {
0875: // We need to make sure that sz is < 1.0 otherwise
0876: // p1 and p2 my pass each other which will cause no end of
0877: // trouble.
0878: sz -= intSz;
0879: weight = sz / (intSz + sz);
0880: if (weight < 0.1)
0881: // The part of the color from the location will be swamped
0882: // by the averaged part of the gradient so just use the
0883: // average color for the gradient.
0884: return gradientAverage;
0885: }
0886:
0887: // So close to full gradient just use the average value...
0888: if (sz > 0.99)
0889: return gradientAverage;
0890:
0891: // Go up and down from position by 1/2 sz.
0892: float p1 = position - (sz / 2);
0893: float p2 = position + (sz / 2);
0894: if (DEBUG)
0895: System.out.println("P1: " + p1 + " P2: " + p2);
0896:
0897: // These indicate the direction to go from p1 and p2 when
0898: // averaging...
0899: boolean p1_up = true;
0900: boolean p2_up = false;
0901:
0902: if (cycleMethod == MultipleGradientPaint.REPEAT) {
0903: if (DEBUG)
0904: System.out.println("REPEAT");
0905:
0906: // Get positions between -1 and 1
0907: p1 = p1 - (int) p1;
0908: p2 = p2 - (int) p2;
0909:
0910: // force to be in rage 0-1.
0911: if (p1 < 0)
0912: p1 += 1;
0913: if (p2 < 0)
0914: p2 += 1;
0915: }
0916:
0917: else { //cycleMethod == MultipleGradientPaint.REFLECT
0918: if (DEBUG)
0919: System.out.println("REFLECT");
0920:
0921: //take absolute values
0922: // Note when we reflect we change sense of p1/2_up.
0923: if (p2 < 0) {
0924: p1 = -p1;
0925: p1_up = !p1_up;
0926: p2 = -p2;
0927: p2_up = !p2_up;
0928: } else if (p1 < 0) {
0929: p1 = -p1;
0930: p1_up = !p1_up;
0931: }
0932:
0933: int part1, part2;
0934: part1 = (int) p1; // take the integer part
0935: p1 = p1 - part1; // get the fractional part
0936:
0937: part2 = (int) p2; // take the integer part
0938: p2 = p2 - part2; // get the fractional part
0939:
0940: // if integer part is odd we want the reflected color instead.
0941: // Note when we reflect we change sense of p1/2_up.
0942: if ((part1 & 0x01) == 1) {
0943: p1 = 1 - p1;
0944: p1_up = !p1_up;
0945: }
0946:
0947: if ((part2 & 0x01) == 1) {
0948: p2 = 1 - p2;
0949: p2_up = !p2_up;
0950: }
0951:
0952: // Check if in the end they just got switched around.
0953: // this commonly happens if they both end up negative.
0954: if ((p1 > p2) && !p1_up && p2_up) {
0955: float t = p1;
0956: p1 = p2;
0957: p2 = t;
0958: p1_up = true;
0959: p2_up = false;
0960: }
0961: }
0962:
0963: return getAntiAlias(p1, p1_up, p2, p2_up, sz, weight);
0964: }
0965:
0966: private final int getAntiAlias(float p1, boolean p1_up, float p2,
0967: boolean p2_up, float sz, float weight) {
0968:
0969: // Until the last set of ops these are 28.4 fixed point values.
0970: int ach = 0, rch = 0, gch = 0, bch = 0;
0971: if (isSimpleLookup) {
0972: p1 *= fastGradientArraySize;
0973: p2 *= fastGradientArraySize;
0974:
0975: int idx1 = (int) p1;
0976: int idx2 = (int) p2;
0977:
0978: int i, pix;
0979:
0980: if (p1_up && !p2_up && (idx1 <= idx2)) {
0981:
0982: if (idx1 == idx2)
0983: return gradient[idx1];
0984:
0985: // Sum between idx1 and idx2.
0986: for (i = idx1 + 1; i < idx2; i++) {
0987: pix = gradient[i];
0988: ach += ((pix >>> 20) & 0xFF0);
0989: rch += ((pix >>> 12) & 0xFF0);
0990: gch += ((pix >>> 4) & 0xFF0);
0991: bch += ((pix << 4) & 0xFF0);
0992: }
0993: } else {
0994: // Do the bulk of the work, all the whole gradient entries
0995: // for idx1 and idx2.
0996: if (p1_up) {
0997: for (i = idx1 + 1; i < fastGradientArraySize; i++) {
0998: pix = gradient[i];
0999: ach += ((pix >>> 20) & 0xFF0);
1000: rch += ((pix >>> 12) & 0xFF0);
1001: gch += ((pix >>> 4) & 0xFF0);
1002: bch += ((pix << 4) & 0xFF0);
1003: }
1004: } else {
1005: for (i = 0; i < idx1; i++) {
1006: pix = gradient[i];
1007: ach += ((pix >>> 20) & 0xFF0);
1008: rch += ((pix >>> 12) & 0xFF0);
1009: gch += ((pix >>> 4) & 0xFF0);
1010: bch += ((pix << 4) & 0xFF0);
1011: }
1012: }
1013:
1014: if (p2_up) {
1015: for (i = idx2 + 1; i < fastGradientArraySize; i++) {
1016: pix = gradient[i];
1017: ach += ((pix >>> 20) & 0xFF0);
1018: rch += ((pix >>> 12) & 0xFF0);
1019: gch += ((pix >>> 4) & 0xFF0);
1020: bch += ((pix << 4) & 0xFF0);
1021: }
1022: } else {
1023: for (i = 0; i < idx2; i++) {
1024: pix = gradient[i];
1025: ach += ((pix >>> 20) & 0xFF0);
1026: rch += ((pix >>> 12) & 0xFF0);
1027: gch += ((pix >>> 4) & 0xFF0);
1028: bch += ((pix << 4) & 0xFF0);
1029: }
1030: }
1031: }
1032:
1033: int norm, isz;
1034:
1035: // Normalize the summation so far...
1036: isz = (int) ((1 << 16) / (sz * fastGradientArraySize));
1037: ach = (ach * isz) >> 16;
1038: rch = (rch * isz) >> 16;
1039: gch = (gch * isz) >> 16;
1040: bch = (bch * isz) >> 16;
1041:
1042: // Clean up with the partial buckets at each end.
1043: if (p1_up)
1044: norm = (int) ((1 - (p1 - idx1)) * isz);
1045: else
1046: norm = (int) ((p1 - idx1) * isz);
1047: pix = gradient[idx1];
1048: ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
1049: rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
1050: gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
1051: bch += (((pix << 4) & 0xFF0) * norm) >> 16;
1052:
1053: if (p2_up)
1054: norm = (int) ((1 - (p2 - idx2)) * isz);
1055: else
1056: norm = (int) ((p2 - idx2) * isz);
1057: pix = gradient[idx2];
1058: ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
1059: rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
1060: gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
1061: bch += (((pix << 4) & 0xFF0) * norm) >> 16;
1062:
1063: // Round and drop the 4bits frac.
1064: ach = (ach + 0x08) >> 4;
1065: rch = (rch + 0x08) >> 4;
1066: gch = (gch + 0x08) >> 4;
1067: bch = (bch + 0x08) >> 4;
1068:
1069: } else {
1070: int idx1 = 0, idx2 = 0;
1071: int i1 = -1, i2 = -1;
1072: float f1 = 0, f2 = 0;
1073: // Find which gradient interval our points fall into.
1074: for (int i = 0; i < gradientsLength; i++) {
1075: if ((p1 < fractions[i + 1]) && (i1 == -1)) {
1076: //this is the array we want
1077: i1 = i;
1078: f1 = p1 - fractions[i];
1079:
1080: f1 = ((f1 / normalizedIntervals[i]) * GRADIENT_SIZE_INDEX);
1081: //this is the interval we want.
1082: idx1 = (int) f1;
1083: if (i2 != -1)
1084: break;
1085: }
1086: if ((p2 < fractions[i + 1]) && (i2 == -1)) {
1087: //this is the array we want
1088: i2 = i;
1089: f2 = p2 - fractions[i];
1090:
1091: f2 = ((f2 / normalizedIntervals[i]) * GRADIENT_SIZE_INDEX);
1092: //this is the interval we want.
1093: idx2 = (int) f2;
1094: if (i1 != -1)
1095: break;
1096: }
1097: }
1098:
1099: if (i1 == -1) {
1100: i1 = gradients.length - 1;
1101: f1 = idx1 = GRADIENT_SIZE_INDEX;
1102: }
1103:
1104: if (i2 == -1) {
1105: i2 = gradients.length - 1;
1106: f2 = idx2 = GRADIENT_SIZE_INDEX;
1107: }
1108:
1109: if (DEBUG)
1110: System.out.println("I1: " + i1 + " Idx1: " + idx1
1111: + " I2: " + i2 + " Idx2: " + idx2);
1112:
1113: // Simple case within one gradient array (so the average
1114: // of the two idx gives us the true average of colors).
1115: if ((i1 == i2) && (idx1 <= idx2) && p1_up && !p2_up)
1116: return gradients[i1][(idx1 + idx2 + 1) >> 1];
1117:
1118: // i1 != i2
1119:
1120: int pix, norm;
1121: int base = (int) ((1 << 16) / sz);
1122: if ((i1 < i2) && p1_up && !p2_up) {
1123: norm = (int) ((base * normalizedIntervals[i1] * (GRADIENT_SIZE_INDEX - f1)) / GRADIENT_SIZE_INDEX);
1124: pix = gradients[i1][(idx1 + GRADIENT_SIZE) >> 1];
1125: ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
1126: rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
1127: gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
1128: bch += (((pix << 4) & 0xFF0) * norm) >> 16;
1129:
1130: for (int i = i1 + 1; i < i2; i++) {
1131: norm = (int) (base * normalizedIntervals[i]);
1132: pix = gradients[i][GRADIENT_SIZE >> 1];
1133:
1134: ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
1135: rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
1136: gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
1137: bch += (((pix << 4) & 0xFF0) * norm) >> 16;
1138: }
1139:
1140: norm = (int) ((base * normalizedIntervals[i2] * f2) / GRADIENT_SIZE_INDEX);
1141: pix = gradients[i2][(idx2 + 1) >> 1];
1142: ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
1143: rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
1144: gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
1145: bch += (((pix << 4) & 0xFF0) * norm) >> 16;
1146: } else {
1147: if (p1_up) {
1148: norm = (int) ((base * normalizedIntervals[i1] * (GRADIENT_SIZE_INDEX - f1)) / GRADIENT_SIZE_INDEX);
1149: pix = gradients[i1][(idx1 + GRADIENT_SIZE) >> 1];
1150: } else {
1151: norm = (int) ((base * normalizedIntervals[i1] * f1) / GRADIENT_SIZE_INDEX);
1152: pix = gradients[i1][(idx1 + 1) >> 1];
1153: }
1154: ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
1155: rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
1156: gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
1157: bch += (((pix << 4) & 0xFF0) * norm) >> 16;
1158:
1159: if (p2_up) {
1160: norm = (int) ((base * normalizedIntervals[i2] * (GRADIENT_SIZE_INDEX - f2)) / GRADIENT_SIZE_INDEX);
1161: pix = gradients[i2][(idx2 + GRADIENT_SIZE) >> 1];
1162: } else {
1163: norm = (int) ((base * normalizedIntervals[i2] * f2) / GRADIENT_SIZE_INDEX);
1164: pix = gradients[i2][(idx2 + 1) >> 1];
1165: }
1166: ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
1167: rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
1168: gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
1169: bch += (((pix << 4) & 0xFF0) * norm) >> 16;
1170:
1171: if (p1_up) {
1172: for (int i = i1 + 1; i < gradientsLength; i++) {
1173: norm = (int) (base * normalizedIntervals[i]);
1174: pix = gradients[i][GRADIENT_SIZE >> 1];
1175:
1176: ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
1177: rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
1178: gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
1179: bch += (((pix << 4) & 0xFF0) * norm) >> 16;
1180: }
1181: } else {
1182: for (int i = 0; i < i1; i++) {
1183: norm = (int) (base * normalizedIntervals[i]);
1184: pix = gradients[i][GRADIENT_SIZE >> 1];
1185:
1186: ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
1187: rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
1188: gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
1189: bch += (((pix << 4) & 0xFF0) * norm) >> 16;
1190: }
1191: }
1192:
1193: if (p2_up) {
1194: for (int i = i2 + 1; i < gradientsLength; i++) {
1195: norm = (int) (base * normalizedIntervals[i]);
1196: pix = gradients[i][GRADIENT_SIZE >> 1];
1197:
1198: ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
1199: rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
1200: gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
1201: bch += (((pix << 4) & 0xFF0) * norm) >> 16;
1202: }
1203: } else {
1204: for (int i = 0; i < i2; i++) {
1205: norm = (int) (base * normalizedIntervals[i]);
1206: pix = gradients[i][GRADIENT_SIZE >> 1];
1207:
1208: ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
1209: rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
1210: gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
1211: bch += (((pix << 4) & 0xFF0) * norm) >> 16;
1212: }
1213: }
1214:
1215: }
1216: ach = (ach + 0x08) >> 4;
1217: rch = (rch + 0x08) >> 4;
1218: gch = (gch + 0x08) >> 4;
1219: bch = (bch + 0x08) >> 4;
1220: if (DEBUG)
1221: System.out.println("Pix: [" + ach + ", " + rch + ", "
1222: + gch + ", " + bch + "]");
1223: }
1224:
1225: if (weight != 1) {
1226: // System.out.println("ave: " + gradientAverage);
1227: int aveW = (int) ((1 << 16) * (1 - weight));
1228: int aveA = ((gradientAverage >>> 24) & 0xFF) * aveW;
1229: int aveR = ((gradientAverage >> 16) & 0xFF) * aveW;
1230: int aveG = ((gradientAverage >> 8) & 0xFF) * aveW;
1231: int aveB = ((gradientAverage) & 0xFF) * aveW;
1232:
1233: int iw = (int) (weight * (1 << 16));
1234: ach = ((ach * iw) + aveA) >> 16;
1235: rch = ((rch * iw) + aveR) >> 16;
1236: gch = ((gch * iw) + aveG) >> 16;
1237: bch = ((bch * iw) + aveB) >> 16;
1238: }
1239:
1240: return ((ach << 24) | (rch << 16) | (gch << 8) | bch);
1241: }
1242:
1243: /** Helper function to convert a color component in sRGB space to linear
1244: * RGB space. Used to build a static lookup table.
1245: */
1246: private static int convertSRGBtoLinearRGB(int color) {
1247:
1248: float input, output;
1249:
1250: input = color / 255.0f;
1251: if (input <= 0.04045f) {
1252: output = input / 12.92f;
1253: } else {
1254: output = (float) Math.pow((input + 0.055) / 1.055, 2.4);
1255: }
1256: int o = Math.round(output * 255.0f);
1257:
1258: return o;
1259: }
1260:
1261: /** Helper function to convert a color component in linear RGB space to
1262: * SRGB space. Used to build a static lookup table.
1263: */
1264: private static int convertLinearRGBtoSRGB(int color) {
1265:
1266: float input, output;
1267:
1268: input = color / 255.0f;
1269:
1270: if (input <= 0.0031308) {
1271: output = input * 12.92f;
1272: } else {
1273: output = (1.055f * ((float) Math.pow(input, (1.0 / 2.4)))) - 0.055f;
1274: }
1275:
1276: int o = Math.round(output * 255.0f);
1277:
1278: return o;
1279: }
1280:
1281: /** Superclass getRaster... */
1282: public final Raster getRaster(int x, int y, int w, int h) {
1283: if (w == 0 || h == 0) {
1284: return null;
1285: }
1286:
1287: //
1288: // If working raster is big enough, reuse it. Otherwise,
1289: // build a large enough new one.
1290: //
1291: WritableRaster raster = saved;
1292: if (raster == null || raster.getWidth() < w
1293: || raster.getHeight() < h) {
1294: raster = getCachedRaster(dataModel, w, h);
1295: saved = raster;
1296: }
1297:
1298: // Access raster internal int array. Because we use a DirectColorModel,
1299: // we know the DataBuffer is of type DataBufferInt and the SampleModel
1300: // is SinglePixelPackedSampleModel.
1301: // Adjust for initial offset in DataBuffer and also for the scanline
1302: // stride.
1303: //
1304: DataBufferInt rasterDB = (DataBufferInt) raster.getDataBuffer();
1305: int[] pixels = rasterDB.getBankData()[0];
1306: int off = rasterDB.getOffset();
1307: int scanlineStride = ((SinglePixelPackedSampleModel) raster
1308: .getSampleModel()).getScanlineStride();
1309: int adjust = scanlineStride - w;
1310:
1311: fillRaster(pixels, off, adjust, x, y, w, h); //delegate to subclass.
1312:
1313: GraphicsUtil.coerceData(raster, dataModel, model
1314: .isAlphaPremultiplied());
1315:
1316: return raster;
1317: }
1318:
1319: /** Subclasses should implement this. */
1320: protected abstract void fillRaster(int pixels[], int off,
1321: int adjust, int x, int y, int w, int h);
1322:
1323: /** Took this cacheRaster code from GradientPaint. It appears to recycle
1324: * rasters for use by any other instance, as long as they are sufficiently
1325: * large.
1326: */
1327: protected final static synchronized WritableRaster getCachedRaster(
1328: ColorModel cm, int w, int h) {
1329: if (cm == cachedModel) {
1330: if (cached != null) {
1331: WritableRaster ras = (WritableRaster) cached.get();
1332: if (ras != null && ras.getWidth() >= w
1333: && ras.getHeight() >= h) {
1334: cached = null;
1335: return ras;
1336: }
1337: }
1338: }
1339: // Don't create rediculously small rasters...
1340: if (w < 32)
1341: w = 32;
1342: if (h < 32)
1343: h = 32;
1344: return cm.createCompatibleWritableRaster(w, h);
1345: }
1346:
1347: /** Took this cacheRaster code from GradientPaint. It appears to recycle
1348: * rasters for use by any other instance, as long as they are sufficiently
1349: * large.
1350: */
1351: protected final static synchronized void putCachedRaster(
1352: ColorModel cm, WritableRaster ras) {
1353: if (cached != null) {
1354: WritableRaster cras = (WritableRaster) cached.get();
1355: if (cras != null) {
1356: int cw = cras.getWidth();
1357: int ch = cras.getHeight();
1358: int iw = ras.getWidth();
1359: int ih = ras.getHeight();
1360: if (cw >= iw && ch >= ih) {
1361: return;
1362: }
1363: if (cw * ch >= iw * ih) {
1364: return;
1365: }
1366: }
1367: }
1368: cachedModel = cm;
1369: cached = new WeakReference(ras);
1370: }
1371:
1372: /**
1373: * Release the resources allocated for the operation.
1374: */
1375: public final void dispose() {
1376: if (saved != null) {
1377: putCachedRaster(model, saved);
1378: saved = null;
1379: }
1380: }
1381:
1382: /**
1383: * Return the ColorModel of the output.
1384: */
1385: public final ColorModel getColorModel() {
1386: return model;
1387: }
1388: }
|