KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > ext > awt > MultipleGradientPaintContext


1 /*
2
3    Copyright 2001-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.ext.awt;
19
20 import java.awt.Color JavaDoc;
21 import java.awt.PaintContext JavaDoc;
22 import java.awt.Rectangle JavaDoc;
23 import java.awt.RenderingHints JavaDoc;
24 import java.awt.color.ColorSpace JavaDoc;
25 import java.awt.geom.AffineTransform JavaDoc;
26 import java.awt.geom.NoninvertibleTransformException JavaDoc;
27 import java.awt.geom.Rectangle2D JavaDoc;
28 import java.awt.image.ColorModel JavaDoc;
29 import java.awt.image.DataBuffer JavaDoc;
30 import java.awt.image.DataBufferInt JavaDoc;
31 import java.awt.image.DirectColorModel JavaDoc;
32 import java.awt.image.Raster JavaDoc;
33 import java.awt.image.SinglePixelPackedSampleModel JavaDoc;
34 import java.awt.image.WritableRaster JavaDoc;
35 import java.lang.ref.WeakReference JavaDoc;
36
37 import org.apache.batik.ext.awt.image.GraphicsUtil;
38
39 /** This is the superclass for all PaintContexts which use a multiple color
40  * gradient to fill in their raster. It provides the actual color interpolation
41  * functionality. Subclasses only have to deal with using the gradient to fill
42  * pixels in a raster.
43  *
44  * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
45  * @author <a HREF="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
46  * @version $Id: MultipleGradientPaintContext.java,v 1.19 2004/08/18 07:13:41 vhardy Exp $
47  *
48  */

49 abstract class MultipleGradientPaintContext implements PaintContext JavaDoc {
50
51     protected final static boolean DEBUG = false;
52
53     /**
54      * The color model data is generated in (always un premult).
55      */

56     protected ColorModel JavaDoc dataModel;
57     /**
58      * PaintContext's output ColorModel ARGB if colors are not all
59      * opaque, RGB otherwise. Linear and premult are matched to
60      * output ColorModel.
61      */

62     protected ColorModel JavaDoc model;
63
64     /** Color model used if gradient colors are all opaque */
65     private static ColorModel JavaDoc lrgbmodel_NA = new DirectColorModel JavaDoc
66         (ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB),
67          24, 0xff0000, 0xFF00, 0xFF, 0x0,
68          false, DataBuffer.TYPE_INT);
69
70     private static ColorModel JavaDoc srgbmodel_NA = new DirectColorModel JavaDoc
71         (ColorSpace.getInstance(ColorSpace.CS_sRGB),
72          24, 0xff0000, 0xFF00, 0xFF, 0x0,
73          false, DataBuffer.TYPE_INT);
74
75     /** Color model used if some gradient colors are transparent */
76     private static ColorModel JavaDoc lrgbmodel_A = new DirectColorModel JavaDoc
77         (ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB),
78          32, 0xff0000, 0xFF00, 0xFF, 0xFF000000,
79          false, DataBuffer.TYPE_INT);
80
81     private static ColorModel JavaDoc srgbmodel_A = new DirectColorModel JavaDoc
82         (ColorSpace.getInstance(ColorSpace.CS_sRGB),
83          32, 0xff0000, 0xFF00, 0xFF, 0xFF000000,
84          false, DataBuffer.TYPE_INT);
85
86      /** The cached colorModel */
87     protected static ColorModel JavaDoc cachedModel;
88
89     /** The cached raster, which is reusable among instances */
90     protected static WeakReference JavaDoc cached;
91
92     /** Raster is reused whenever possible */
93     protected WritableRaster JavaDoc saved;
94
95     /** The method to use when painting out of the gradient bounds. */
96     protected MultipleGradientPaint.CycleMethodEnum cycleMethod;
97
98     /** The colorSpace in which to perform the interpolation */
99     protected MultipleGradientPaint.ColorSpaceEnum colorSpace;
100
101     /** Elements of the inverse transform matrix. */
102     protected float a00, a01, a10, a11, a02, a12;
103
104     /** This boolean specifies wether we are in simple lookup mode, where an
105      * input value between 0 and 1 may be used to directly index into a single
106      * array of gradient colors. If this boolean value is false, then we have
107      * to use a 2-step process where we have to determine which gradient array
108      * we fall into, then determine the index into that array.
109      */

110     protected boolean isSimpleLookup = true;
111
112     /** This boolean indicates if the gradient appears to have sudden
113      * discontinuities in it, this may be because of multiple stops
114      * at the same location or use of the REPEATE mode.
115      */

116     protected boolean hasDiscontinuity = false;
117
118     /** Size of gradients array for scaling the 0-1 index when looking up
119      * colors the fast way. */

120     protected int fastGradientArraySize;
121
122     /**
123      * Array which contains the interpolated color values for each interval,
124      * used by calculateSingleArrayGradient(). It is protected for possible
125      * direct access by subclasses.
126      */

127     protected int[] gradient;
128
129     /** Array of gradient arrays, one array for each interval. Used by
130      * calculateMultipleArrayGradient().
131      */

132     protected int[][] gradients;
133
134     /** This holds the blend of all colors in the gradient.
135      * we use this at extreamly low resolutions to ensure we
136      * get a decent blend of the colors.
137      */

138     protected int gradientAverage;
139
140     /** This holds the color to use when we are off the bottom of the
141      * gradient */

142     protected int gradientUnderflow;
143
144     /** This holds the color to use when we are off the top of the
145      * gradient */

146     protected int gradientOverflow;
147
148     /** Length of the 2D slow lookup gradients array. */
149     protected int gradientsLength;
150
151     /** Normalized intervals array */
152     protected float[] normalizedIntervals;
153
154     /** fractions array */
155     protected float[] fractions;
156
157     /** Used to determine if gradient colors are all opaque */
158     private int transparencyTest;
159
160     /** Colorspace conversion lookup tables */
161     private static final int SRGBtoLinearRGB[] = new int[256];
162     private static final int LinearRGBtoSRGB[] = new int[256];
163
164     //build the tables
165
static{
166         for (int k = 0; k < 256; k++) {
167             SRGBtoLinearRGB[k] = convertSRGBtoLinearRGB(k);
168             LinearRGBtoSRGB[k] = convertLinearRGBtoSRGB(k);
169         }
170     }
171
172     /** Constant number of max colors between any 2 arbitrary colors.
173      * Used for creating and indexing gradients arrays.
174      */

175     protected static final int GRADIENT_SIZE = 256;
176     protected static final int GRADIENT_SIZE_INDEX = GRADIENT_SIZE -1;
177
178     /** Maximum length of the fast single-array. If the estimated array size
179      * is greater than this, switch over to the slow lookup method.
180      * No particular reason for choosing this number, but it seems to provide
181      * satisfactory performance for the common case (fast lookup).
182      */

183     private static final int MAX_GRADIENT_ARRAY_SIZE = 5000;
184
185    /** Constructor for superclass. Does some initialization, but leaves most
186     * of the heavy-duty math for calculateGradient(), so the subclass may do
187     * some other manipulation beforehand if necessary. This is not possible
188     * if this computation is done in the superclass constructor which always
189     * gets called first.
190     **/

191     public MultipleGradientPaintContext(ColorModel JavaDoc cm,
192                                         Rectangle JavaDoc deviceBounds,
193                                         Rectangle2D JavaDoc userBounds,
194                                         AffineTransform JavaDoc t,
195                                         RenderingHints JavaDoc hints,
196                                         float[] fractions,
197                                         Color JavaDoc[] colors,
198                                         MultipleGradientPaint.CycleMethodEnum
199                                         cycleMethod,
200                                         MultipleGradientPaint.ColorSpaceEnum
201                                         colorSpace)
202         throws NoninvertibleTransformException JavaDoc
203     {
204         //We have to deal with the cases where the 1st gradient stop is not
205
//equal to 0 and/or the last gradient stop is not equal to 1.
206
//In both cases, create a new point and replicate the previous
207
//extreme point's color.
208

209         boolean fixFirst = false;
210         boolean fixLast = false;
211         int len = fractions.length;
212
213         //if the first gradient stop is not equal to zero, fix this condition
214
if (fractions[0] != 0f) {
215             fixFirst = true;
216             len++;
217         }
218
219         //if the last gradient stop is not equal to one, fix this condition
220
if (fractions[fractions.length - 1] != 1f) {
221             fixLast = true;
222             len++;
223         }
224         
225         for (int i=0; i<fractions.length-1; i++)
226             if (fractions[i] == fractions[i+1])
227                 len--;
228
229         this.fractions = new float[len];
230         Color JavaDoc [] loColors = new Color JavaDoc[len-1];
231         Color JavaDoc [] hiColors = new Color JavaDoc[len-1];
232         normalizedIntervals = new float[len-1];
233
234         gradientUnderflow = colors[0].getRGB();
235         gradientOverflow = colors[colors.length-1].getRGB();
236
237         int idx = 0;
238         if (fixFirst) {
239             this.fractions[0] = 0;
240             loColors[0] = colors[0];
241             hiColors[0] = colors[0];
242             normalizedIntervals[0] = fractions[0];
243             idx++;
244         }
245
246         for (int i=0; i<fractions.length-1; i++) {
247             if (fractions[i] == fractions[i+1]) {
248                 // System.out.println("EQ Fracts");
249
if (!colors[i].equals(colors[i+1])) {
250                     hasDiscontinuity = true;
251                 }
252                 continue;
253             }
254             this.fractions[idx] = fractions[i];
255             loColors[idx] = colors[i];
256             hiColors[idx] = colors[i+1];
257             normalizedIntervals[idx] = fractions[i+1]-fractions[i];
258             idx++;
259         }
260             
261         this.fractions[idx] = fractions[fractions.length-1];
262
263         if (fixLast) {
264             loColors[idx] = hiColors[idx] = colors[colors.length-1];
265             normalizedIntervals[idx] = 1-fractions[fractions.length-1];
266             idx++;
267             this.fractions[idx] = 1;
268         }
269
270         // The inverse transform is needed to from device to user space.
271
// Get all the components of the inverse transform matrix.
272
AffineTransform JavaDoc tInv = t.createInverse();
273
274         double m[] = new double[6];
275         tInv.getMatrix(m);
276         a00 = (float)m[0];
277         a10 = (float)m[1];
278         a01 = (float)m[2];
279         a11 = (float)m[3];
280         a02 = (float)m[4];
281         a12 = (float)m[5];
282
283         //copy some flags
284
this.cycleMethod = cycleMethod;
285         this.colorSpace = colorSpace;
286
287         // Setup an example Model, we may refine it later.
288
if (cm.getColorSpace() == lrgbmodel_A.getColorSpace())
289             dataModel = lrgbmodel_A;
290         else if (cm.getColorSpace() == srgbmodel_A.getColorSpace())
291             dataModel = srgbmodel_A;
292         else
293             throw new IllegalArgumentException JavaDoc
294                 ("Unsupported ColorSpace for interpolation");
295
296         calculateGradientFractions(loColors, hiColors);
297
298         model = GraphicsUtil.coerceColorModel(dataModel,
299                                               cm.isAlphaPremultiplied());
300     }
301
302
303     /** This function is the meat of this class. It calculates an array of
304      * gradient colors based on an array of fractions and color values at those
305      * fractions.
306      */

307     protected final void calculateGradientFractions
308         (Color JavaDoc []loColors, Color JavaDoc []hiColors) {
309
310         //if interpolation should occur in Linear RGB space, convert the
311
//colors using the lookup table
312
if (colorSpace == LinearGradientPaint.LINEAR_RGB) {
313             for (int i = 0; i < loColors.length; i++) {
314                 loColors[i] =
315                     new Color JavaDoc(SRGBtoLinearRGB[loColors[i].getRed()],
316                               SRGBtoLinearRGB[loColors[i].getGreen()],
317                               SRGBtoLinearRGB[loColors[i].getBlue()],
318                               loColors[i].getAlpha());
319                 
320                 hiColors[i] =
321                     new Color JavaDoc(SRGBtoLinearRGB[hiColors[i].getRed()],
322                               SRGBtoLinearRGB[hiColors[i].getGreen()],
323                               SRGBtoLinearRGB[hiColors[i].getBlue()],
324                               hiColors[i].getAlpha());
325             }
326         }
327
328         //initialize to be fully opaque for ANDing with colors
329
transparencyTest = 0xff000000;
330         if (cycleMethod == MultipleGradientPaint.NO_CYCLE) {
331             // Include overflow and underflow colors in transparency
332
// test.
333
transparencyTest &= gradientUnderflow;
334             transparencyTest &= gradientOverflow;
335         }
336
337         //array of interpolation arrays
338
gradients = new int[fractions.length - 1][];
339         gradientsLength = gradients.length;
340
341         // Find smallest interval
342
int n = normalizedIntervals.length;
343
344         float Imin = 1;
345
346         for(int i = 0; i < n; i++) {
347             Imin = (Imin > normalizedIntervals[i]) ?
348                 normalizedIntervals[i] : Imin;
349         }
350
351         //estimate the size of the entire gradients array.
352
//This is to prevent a tiny interval from causing the size of array to
353
//explode. If the estimated size is too large, break to using
354
//seperate arrays for each interval, and using an indexing scheme at
355
//look-up time.
356
int estimatedSize = 0;
357
358         if (Imin == 0) {
359             estimatedSize = Integer.MAX_VALUE;
360             hasDiscontinuity = true;
361         } else {
362             for (int i = 0; i < normalizedIntervals.length; i++) {
363                 estimatedSize += (normalizedIntervals[i]/Imin) * GRADIENT_SIZE;
364             }
365         }
366
367
368         if (estimatedSize > MAX_GRADIENT_ARRAY_SIZE) {
369             //slow method
370
calculateMultipleArrayGradient(loColors, hiColors);
371             if ((cycleMethod == MultipleGradientPaint.REPEAT) &&
372                 (gradients[0][0] !=
373                  gradients[gradients.length-1][GRADIENT_SIZE_INDEX]))
374                 hasDiscontinuity = true;
375         } else {
376             //fast method
377
calculateSingleArrayGradient(loColors, hiColors, Imin);
378             if ((cycleMethod == MultipleGradientPaint.REPEAT) &&
379                 (gradient[0] != gradient[fastGradientArraySize]))
380                 hasDiscontinuity = true;
381         }
382
383         // Use the most 'economical' model (no alpha).
384
if((transparencyTest >>> 24) == 0xff) {
385             if (dataModel.getColorSpace() == lrgbmodel_NA.getColorSpace())
386                 dataModel = lrgbmodel_NA;
387             else if (dataModel.getColorSpace() == srgbmodel_NA.getColorSpace())
388                 dataModel = srgbmodel_NA;
389             model = dataModel;
390         }
391     }
392
393
394     /**
395      * FAST LOOKUP METHOD
396      *
397      * This method calculates the gradient color values and places them in a
398      * single int array, gradient[]. It does this by allocating space for
399      * each interval based on its size relative to the smallest interval in
400      * the array. The smallest interval is allocated 255 interpolated values
401      * (the maximum number of unique in-between colors in a 24 bit color
402      * system), and all other intervals are allocated
403      * size = (255 * the ratio of their size to the smallest interval).
404      *
405      * This scheme expedites a speedy retrieval because the colors are
406      * distributed along the array according to their user-specified
407      * distribution. All that is needed is a relative index from 0 to 1.
408      *
409      * The only problem with this method is that the possibility exists for
410      * the array size to balloon in the case where there is a
411      * disproportionately small gradient interval. In this case the other
412      * intervals will be allocated huge space, but much of that data is
413      * redundant. We thus need to use the space conserving scheme below.
414      *
415      * @param Imin the size of the smallest interval
416      *
417      */

418     private void calculateSingleArrayGradient
419         (Color JavaDoc [] loColors, Color JavaDoc [] hiColors, float Imin) {
420
421         //set the flag so we know later it is a non-simple lookup
422
isSimpleLookup = true;
423
424         int rgb1; //2 colors to interpolate
425
int rgb2;
426
427         int gradientsTot = 1; //the eventual size of the single array
428

429         // These are fixed point 8.16 (start with 0.5)
430
int aveA = 0x008000;
431         int aveR = 0x008000;
432         int aveG = 0x008000;
433         int aveB = 0x008000;
434
435         //for every interval (transition between 2 colors)
436
for(int i=0; i < gradients.length; i++){
437
438             //create an array whose size is based on the ratio to the
439
//smallest interval.
440
int nGradients = (int)((normalizedIntervals[i]/Imin)*255f);
441             gradientsTot += nGradients;
442             gradients[i] = new int[nGradients];
443
444             //the the 2 colors (keyframes) to interpolate between
445
rgb1 = loColors[i].getRGB();
446             rgb2 = hiColors[i].getRGB();
447
448             //fill this array with the colors in between rgb1 and rgb2
449
interpolate(rgb1, rgb2, gradients[i]);
450
451             // Calculate Average of two colors...
452
int argb = gradients[i][GRADIENT_SIZE/2];
453             float norm = normalizedIntervals[i];
454             aveA += (int)(((argb>> 8)&0xFF0000)*norm);
455             aveR += (int)(((argb )&0xFF0000)*norm);
456             aveG += (int)(((argb<< 8)&0xFF0000)*norm);
457             aveB += (int)(((argb<<16)&0xFF0000)*norm);
458
459             //if the colors are opaque, transparency should still be 0xff000000
460
transparencyTest &= rgb1;
461             transparencyTest &= rgb2;
462         }
463
464         gradientAverage = (((aveA & 0xFF0000)<< 8) |
465                            ((aveR & 0xFF0000) ) |
466                            ((aveG & 0xFF0000)>> 8) |
467                            ((aveB & 0xFF0000)>>16));
468
469         // Put all gradients in a single array
470
gradient = new int[gradientsTot];
471         int curOffset = 0;
472         for(int i = 0; i < gradients.length; i++){
473             System.arraycopy(gradients[i], 0, gradient,
474                              curOffset, gradients[i].length);
475             curOffset += gradients[i].length;
476         }
477         gradient[gradient.length-1] = hiColors[hiColors.length-1].getRGB();
478
479         //if interpolation occurred in Linear RGB space, convert the
480
//gradients back to SRGB using the lookup table
481
if (colorSpace == LinearGradientPaint.LINEAR_RGB) {
482             if (dataModel.getColorSpace() ==
483                 ColorSpace.getInstance(ColorSpace.CS_sRGB)) {
484                 for (int i = 0; i < gradient.length; i++) {
485                     gradient[i] =
486                         convertEntireColorLinearRGBtoSRGB(gradient[i]);
487                 }
488                 gradientAverage =
489                     convertEntireColorLinearRGBtoSRGB(gradientAverage);
490             }
491         } else {
492             if (dataModel.getColorSpace() ==
493                 ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB)) {
494                 for (int i = 0; i < gradient.length; i++) {
495                     gradient[i] =
496                         convertEntireColorSRGBtoLinearRGB(gradient[i]);
497                 }
498                 gradientAverage =
499                     convertEntireColorSRGBtoLinearRGB(gradientAverage);
500             }
501         }
502
503         fastGradientArraySize = gradient.length - 1;
504     }
505
506
507     /**
508      * SLOW LOOKUP METHOD
509      *
510      * This method calculates the gradient color values for each interval and
511      * places each into its own 255 size array. The arrays are stored in
512      * gradients[][]. (255 is used because this is the maximum number of
513      * unique colors between 2 arbitrary colors in a 24 bit color system)
514      *
515      * This method uses the minimum amount of space (only 255 * number of
516      * intervals), but it aggravates the lookup procedure, because now we
517      * have to find out which interval to select, then calculate the index
518      * within that interval. This causes a significant performance hit,
519      * because it requires this calculation be done for every point in
520      * the rendering loop.
521      *
522      * For those of you who are interested, this is a classic example of the
523      * time-space tradeoff.
524      *
525      */

526     private void calculateMultipleArrayGradient
527         (Color JavaDoc [] loColors, Color JavaDoc [] hiColors) {
528
529         //set the flag so we know later it is a non-simple lookup
530
isSimpleLookup = false;
531
532         int rgb1; //2 colors to interpolate
533
int rgb2;
534
535         // These are fixed point 8.16 (start with 0.5)
536
int aveA = 0x008000;
537         int aveR = 0x008000;
538         int aveG = 0x008000;
539         int aveB = 0x008000;
540
541         //for every interval (transition between 2 colors)
542
for(int i=0; i < gradients.length; i++){
543
544             // This interval will never actually be used (zero size)
545
if (normalizedIntervals[i] == 0)
546                 continue;
547
548             //create an array of the maximum theoretical size for each interval
549
gradients[i] = new int[GRADIENT_SIZE];
550
551             //get the the 2 colors
552
rgb1 = loColors[i].getRGB();
553             rgb2 = hiColors[i].getRGB();
554
555             //fill this array with the colors in between rgb1 and rgb2
556
interpolate(rgb1, rgb2, gradients[i]);
557
558             // Calculate Average of two colors...
559
int argb = gradients[i][GRADIENT_SIZE/2];
560             float norm = normalizedIntervals[i];
561             aveA += (int)(((argb>> 8)&0xFF0000)*norm);
562             aveR += (int)(((argb )&0xFF0000)*norm);
563             aveG += (int)(((argb<< 8)&0xFF0000)*norm);
564             aveB += (int)(((argb<<16)&0xFF0000)*norm);
565
566             //if the colors are opaque, transparency should still be 0xff000000
567
transparencyTest &= rgb1;
568             transparencyTest &= rgb2;
569         }
570
571         gradientAverage = (((aveA & 0xFF0000)<< 8) |
572                            ((aveR & 0xFF0000) ) |
573                            ((aveG & 0xFF0000)>> 8) |
574                            ((aveB & 0xFF0000)>>16));
575
576         //if interpolation occurred in Linear RGB space, convert the
577
//gradients back to SRGB using the lookup table
578
if (colorSpace == LinearGradientPaint.LINEAR_RGB) {
579             if (dataModel.getColorSpace() ==
580                 ColorSpace.getInstance(ColorSpace.CS_sRGB)) {
581                 for (int j = 0; j < gradients.length; j++) {
582                     for (int i = 0; i < gradients[j].length; i++) {
583                         gradients[j][i] =
584                             convertEntireColorLinearRGBtoSRGB(gradients[j][i]);
585                     }
586                 }
587                 gradientAverage =
588                     convertEntireColorLinearRGBtoSRGB(gradientAverage);
589             }
590         } else {
591             if (dataModel.getColorSpace() ==
592                 ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB)) {
593                 for (int j = 0; j < gradients.length; j++) {
594                     for (int i = 0; i < gradients[j].length; i++) {
595                         gradients[j][i] =
596                             convertEntireColorSRGBtoLinearRGB(gradients[j][i]);
597                     }
598                 }
599                 gradientAverage =
600                     convertEntireColorSRGBtoLinearRGB(gradientAverage);
601             }
602         }
603     }
604
605     /** Yet another helper function. This one linearly interpolates between
606      * 2 colors, filling up the output array.
607      *
608      * @param rgb1 the start color
609      * @param rgb2 the end color
610      * @param output the output array of colors... assuming this is not null.
611      *
612      */

613     private void interpolate(int rgb1, int rgb2, int[] output) {
614
615         int a1, r1, g1, b1, da, dr, dg, db; //color components
616

617         //step between interpolated values.
618
float stepSize = 1/(float)output.length;
619
620         //extract color components from packed integer
621
a1 = (rgb1 >> 24) & 0xff;
622         r1 = (rgb1 >> 16) & 0xff;
623         g1 = (rgb1 >> 8) & 0xff;
624         b1 = (rgb1 ) & 0xff;
625         //calculate the total change in alpha, red, green, blue
626
da = ((rgb2 >> 24) & 0xff) - a1;
627         dr = ((rgb2 >> 16) & 0xff) - r1;
628         dg = ((rgb2 >> 8) & 0xff) - g1;
629         db = ((rgb2 ) & 0xff) - b1;
630
631         //for each step in the interval calculate the in-between color by
632
//multiplying the normalized current position by the total color change
633
//(.5 is added to prevent truncation round-off error)
634
for (int i = 0; i < output.length; i++) {
635             output[i] =
636                 (((int) ((a1 + i * da * stepSize) + .5) << 24)) |
637                 (((int) ((r1 + i * dr * stepSize) + .5) << 16)) |
638                 (((int) ((g1 + i * dg * stepSize) + .5) << 8)) |
639                 (((int) ((b1 + i * db * stepSize) + .5) ));
640         }
641     }
642
643
644     /** Yet another helper function. This one extracts the color components
645      * of an integer RGB triple, converts them from LinearRGB to SRGB, then
646      * recompacts them into an int.
647      */

648     private int convertEntireColorLinearRGBtoSRGB(int rgb) {
649
650         int a1, r1, g1, b1; //color components
651

652         //extract red, green, blue components
653
a1 = (rgb >> 24) & 0xff;
654         r1 = (rgb >> 16) & 0xff;
655         g1 = (rgb >> 8) & 0xff;
656         b1 = rgb & 0xff;
657
658         //use the lookup table
659
r1 = LinearRGBtoSRGB[r1];
660         g1 = LinearRGBtoSRGB[g1];
661         b1 = LinearRGBtoSRGB[b1];
662
663         //re-compact the components
664
return ((a1 << 24) |
665                 (r1 << 16) |
666                 (g1 << 8) |
667                 b1);
668     }
669
670     /** Yet another helper function. This one extracts the color components
671      * of an integer RGB triple, converts them from LinearRGB to SRGB, then
672      * recompacts them into an int.
673      */

674     private int convertEntireColorSRGBtoLinearRGB(int rgb) {
675
676         int a1, r1, g1, b1; //color components
677

678         //extract red, green, blue components
679
a1 = (rgb >> 24) & 0xff;
680         r1 = (rgb >> 16) & 0xff;
681         g1 = (rgb >> 8) & 0xff;
682         b1 = rgb & 0xff;
683
684         //use the lookup table
685
r1 = SRGBtoLinearRGB[r1];
686         g1 = SRGBtoLinearRGB[g1];
687         b1 = SRGBtoLinearRGB[b1];
688
689         //re-compact the components
690
return ((a1 << 24) |
691                 (r1 << 16) |
692                 (g1 << 8) |
693                 b1);
694     }
695
696
697     /** Helper function to index into the gradients array. This is necessary
698      * because each interval has an array of colors with uniform size 255.
699      * However, the color intervals are not necessarily of uniform length, so
700      * a conversion is required.
701      *
702      * @param position the unmanipulated position. want to map this into the
703      * range 0 to 1
704      *
705      * @returns integer color to display
706      *
707      */

708     protected final int indexIntoGradientsArrays(float position) {
709
710         //first, manipulate position value depending on the cycle method.
711

712         if (cycleMethod == MultipleGradientPaint.NO_CYCLE) {
713
714             if (position >= 1) { //upper bound is 1
715
return gradientOverflow;
716             }
717
718             else if (position <= 0) { //lower bound is 0
719
return gradientUnderflow;
720             }
721         }
722
723         else if (cycleMethod == MultipleGradientPaint.REPEAT) {
724             //get the fractional part
725
//(modulo behavior discards integer component)
726
position = position - (int)position;
727
728             //position now be between -1 and 1
729

730             if (position < 0) {
731                 position = position + 1; //force it to be in the range 0-1
732
}
733
734             int w=0, c1=0, c2=0;
735             if (isSimpleLookup) {
736               position *= gradient.length;
737               int idx1 = (int)(position);
738               if (idx1+1 < gradient.length)
739                 return gradient[idx1];
740
741               w = (int)((position-idx1)*(1<<16));
742               c1 = gradient[idx1];
743               c2 = gradient[0];
744             } else {
745               //for all the gradient interval arrays
746
for (int i = 0; i < gradientsLength; i++) {
747
748                 if (position < fractions[i+1]) { //this is the array we want
749

750                   float delta = position - fractions[i];
751                   
752                   delta = ((delta / normalizedIntervals[i]) * GRADIENT_SIZE);
753                   //this is the interval we want.
754
int index = (int)delta;
755                   if ((index+1<gradients[i].length) ||
756                       (i+1 < gradientsLength))
757                     return gradients[i][index];
758
759                   w = (int)((delta-index)*(1<<16));
760                   c1 = gradients[i][index];
761                   c2 = gradients[0][0];
762                   break;
763                 }
764               }
765             }
766
767             return
768               (((( ( (c1>> 8) &0xFF0000)+
769                     ((((c2>>>24) )-((c1>>>24) ))*w))&0xFF0000)<< 8) |
770                
771                ((( ( (c1 ) &0xFF0000)+
772                     ((((c2>> 16)&0xFF)-((c1>> 16)&0xFF))*w))&0xFF0000) ) |
773                     
774                ((( ( (c1<< 8) &0xFF0000)+
775                     ((((c2>> 8)&0xFF)-((c1>> 8)&0xFF))*w))&0xFF0000)>> 8) |
776                
777                ((( ( (c1<< 16) &0xFF0000)+
778                     ((((c2 )&0xFF)-((c1 )&0xFF))*w))&0xFF0000)>>16));
779
780             // return c1 +
781
// ((( ((((c2>>>24) )-((c1>>>24) ))*w)&0xFF0000)<< 8) |
782
// (( ((((c2>> 16)&0xFF)-((c1>> 16)&0xFF))*w)&0xFF0000) ) |
783
// (( ((((c2>> 8)&0xFF)-((c1>> 8)&0xFF))*w)&0xFF0000)>> 8) |
784
// (( ((((c2 )&0xFF)-((c1 )&0xFF))*w)&0xFF0000)>>16));
785
}
786
787         else { //cycleMethod == MultipleGradientPaint.REFLECT
788

789             if (position < 0) {
790                 position = -position; //take absolute value
791
}
792
793             int part = (int)position; //take the integer part
794

795             position = position - part; //get the fractional part
796

797             if ((part & 0x00000001) == 1) { //if integer part is odd
798
position = 1 - position; //want the reflected color instead
799
}
800         }
801
802         //now, get the color based on this 0-1 position:
803

804         if (isSimpleLookup) { //easy to compute: just scale index by array size
805
return gradient[(int)(position * fastGradientArraySize)];
806         }
807
808         else { //more complicated computation, to save space
809

810             //for all the gradient interval arrays
811
for (int i = 0; i < gradientsLength; i++) {
812
813                 if (position < fractions[i+1]) { //this is the array we want
814

815         &n