Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/batik/ext/awt/LinearGradientPaintContext.java


1   /*
2   
3      Copyright 2001-2004  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;
21  import java.awt.Rectangle;
22  import java.awt.RenderingHints;
23  import java.awt.geom.AffineTransform;
24  import java.awt.geom.NoninvertibleTransformException;
25  import java.awt.geom.Point2D;
26  import java.awt.geom.Rectangle2D;
27  import java.awt.image.ColorModel;
28  
29  /**
30   * Provides the actual implementation for the LinearGradientPaint
31   * This is where the pixel processing is done.
32   * 
33   * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
34   * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
35   * @version $Id: LinearGradientPaintContext.java,v 1.13 2005/03/27 08:58:32 cam Exp $
36   * @see java.awt.PaintContext
37   * @see java.awt.Paint
38   * @see java.awt.GradientPaint
39   */
40  final class LinearGradientPaintContext extends MultipleGradientPaintContext {
41      
42      /**
43       * The following invariants are used to process the gradient value from 
44       * a device space coordinate, (X, Y):
45       * g(X, Y) = dgdX*X + dgdY*Y + gc
46       */
47      private float dgdX, dgdY, gc, pixSz;    
48             
49      private static final int DEFAULT_IMPL = 1;
50      private static final int ANTI_ALIAS_IMPL  = 3;
51  
52      private int fillMethod;
53  
54      /** 
55       * Constructor for LinearGradientPaintContext.
56       *
57       *  @param cm {@link ColorModel} that receives
58       *  the <code>Paint</code> data. This is used only as a hint.
59       *
60       *  @param deviceBounds the device space bounding box of the 
61       *  graphics primitive being rendered
62       *
63       *  @param userBounds the user space bounding box of the 
64       *  graphics primitive being rendered
65       * 
66       *  @param t the {@link AffineTransform} from user
67       *  space into device space (gradientTransform should be 
68       *  concatenated with this)
69       *
70       *  @param hints the hints that the context object uses to choose
71       *  between rendering alternatives
72       *
73       *  @param dStart gradient start point, in user space
74       *
75       *  @param dEnd gradient end point, in user space
76       *
77       *  @param fractions the fractions specifying the gradient distribution
78       *
79       *  @param colors the gradient colors
80       *
81       *  @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
82       *
83       *  @param colorSpace which colorspace to use for interpolation, 
84       *  either SRGB or LINEAR_RGB
85       *
86       */
87      public LinearGradientPaintContext(ColorModel cm,
88                                        Rectangle deviceBounds,
89                                        Rectangle2D userBounds,
90                                        AffineTransform t,
91                                        RenderingHints hints,
92                                        Point2D dStart,
93                                        Point2D dEnd,
94                                        float[] fractions,
95                                        Color[] colors, 
96                                        MultipleGradientPaint.CycleMethodEnum 
97                                        cycleMethod,
98                                        MultipleGradientPaint.ColorSpaceEnum 
99                                        colorSpace)
100         throws NoninvertibleTransformException
101     {  
102         super(cm, deviceBounds, userBounds, t, hints, fractions, 
103               colors, cycleMethod, colorSpace);
104         
105         // Use single precision floating points
106         Point2D.Float start = new Point2D.Float((float)dStart.getX(),
107                                                 (float)dStart.getY());
108         Point2D.Float end = new Point2D.Float((float)dEnd.getX(),
109                                               (float)dEnd.getY());
110         
111         // A given point in the raster should take on the same color as its
112         // projection onto the gradient vector.
113         // Thus, we want the projection of the current position vector
114         // onto the gradient vector, then normalized with respect to the
115         // length of the gradient vector, giving a value which can be mapped into
116         // the range 0-1.
117         // projection = currentVector dot gradientVector / length(gradientVector)
118         // normalized = projection / length(gradientVector)
119 
120         float dx = end.x - start.x; // change in x from start to end
121         float dy = end.y - start.y; // change in y from start to end
122         float dSq = dx*dx + dy*dy; // total distance squared
123   
124         //avoid repeated calculations by doing these divides once.
125         float constX = dx/dSq;
126         float constY = dy/dSq;
127   
128         //incremental change along gradient for +x
129         dgdX = a00*constX + a10*constY;
130         //incremental change along gradient for +y
131         dgdY = a01*constX + a11*constY;
132         
133         float dgdXAbs = Math.abs(dgdX);
134         float dgdYAbs = Math.abs(dgdY);
135         if (dgdXAbs > dgdYAbs)  pixSz = dgdXAbs;
136         else                    pixSz = dgdYAbs;
137 
138         //constant, incorporates the translation components from the matrix
139         gc = (a02-start.x)*constX + (a12-start.y)*constY;           
140 
141         Object colorRend = hints.get(RenderingHints.KEY_COLOR_RENDERING);
142         Object rend      = hints.get(RenderingHints.KEY_RENDERING);
143 
144         fillMethod = DEFAULT_IMPL;
145 
146         if ((cycleMethod == MultipleGradientPaint.REPEAT) ||
147             hasDiscontinuity) {
148             if (rend      == RenderingHints.VALUE_RENDER_QUALITY)
149                 fillMethod = ANTI_ALIAS_IMPL;
150             // ColorRend overrides rend.
151             if (colorRend == RenderingHints.VALUE_COLOR_RENDER_SPEED)
152                 fillMethod = DEFAULT_IMPL;
153             else if (colorRend == RenderingHints.VALUE_COLOR_RENDER_QUALITY)
154                 fillMethod = ANTI_ALIAS_IMPL;
155         } 
156     }
157 
158     protected void fillHardNoCycle(int[] pixels, int off, int adjust, 
159                               int x, int y, int w, int h) {
160 
161         //constant which can be pulled out of the inner loop
162         final float initConst = (dgdX*x) + gc;
163 
164         for(int i=0; i<h; i++) { //for every row
165             //initialize current value to be start.
166             float g = initConst + dgdY*(y+i); 
167             final int rowLimit = off+w;  // end of row iteration
168 
169             if (dgdX == 0) {
170                 // System.out.println("In fillHard: " + g);
171                 final int val;
172                 if (g <= 0) 
173                     val = gradientUnderflow;
174                 else if (g >= 1)
175                     val = gradientOverflow;
176                 else {
177                     // Could be a binary search...
178                     int gradIdx = 0;
179                     while (gradIdx < gradientsLength-1) {
180                         if (g < fractions[gradIdx+1])
181                             break;
182                         gradIdx++;
183                     }
184                     float delta = (g-fractions[gradIdx]);
185                     float idx  = ((delta*GRADIENT_SIZE_INDEX)
186                                   /normalizedIntervals[gradIdx])+0.5f;
187                     val = gradients[gradIdx][(int)idx];
188                 }
189 
190                 while (off < rowLimit) {
191                     pixels[off++] = val;
192                 }
193             } else {
194                 // System.out.println("In fillHard2: " + g);
195                 int gradSteps;
196                 int preGradSteps;
197                 final int preVal, postVal;
198 
199                 float gradStepsF;
200                 float preGradStepsF;
201                 if (dgdX >= 0) {
202                     gradStepsF    =          ((1-g)/dgdX);
203                     preGradStepsF = (float)Math.ceil((0-g)/dgdX);
204                     preVal  = gradientUnderflow;
205                     postVal = gradientOverflow;
206                 } else { // dgdX < 0
207                     gradStepsF    =          ((0-g)/dgdX);
208                     preGradStepsF = (float)Math.ceil((1-g)/dgdX);
209                     preVal  = gradientOverflow;
210                     postVal = gradientUnderflow;
211                 }
212 
213                 if (gradStepsF > w)    gradSteps = w;
214                 else                   gradSteps = (int)gradStepsF;
215                 if (preGradStepsF > w) preGradSteps = w;
216                 else                   preGradSteps = (int)preGradStepsF;
217 
218                 final int gradLimit    = off + gradSteps;
219                 if (preGradSteps > 0) {
220                     final int preGradLimit = off + preGradSteps;
221 
222                     while (off < preGradLimit) {
223                         pixels[off++] = preVal;
224                     }
225                     g += dgdX*preGradSteps;
226                 }
227                         
228                 if (dgdX > 0) {
229                     // Could be a binary search...
230                     int gradIdx = 0;
231                     while (gradIdx < gradientsLength-1) {
232                         if (g < fractions[gradIdx+1])
233                             break;
234                         gradIdx++;
235                     }
236                     
237                     while (off < gradLimit) {
238                         float delta = (g-fractions[gradIdx]);
239                         final int [] grad = gradients[gradIdx];
240 
241                         double stepsD = Math.ceil
242                             ((fractions[gradIdx+1]-g)/dgdX);
243                         int steps;
244                         if (stepsD > w) steps = w;
245                         else            steps = (int)stepsD;
246                         int subGradLimit = off + steps;
247                         if (subGradLimit > gradLimit)
248                             subGradLimit = gradLimit;
249 
250                         int idx  = (int)(((delta*GRADIENT_SIZE_INDEX)
251                                           /normalizedIntervals[gradIdx])
252                                          *(1<<16)) + (1<<15);
253                         int step = (int)(((dgdX*GRADIENT_SIZE_INDEX)
254                                           /normalizedIntervals[gradIdx])
255                                          *(1<<16));
256                         while (off < subGradLimit) {
257                             pixels[off++] = grad[idx>>16];
258                             idx += step;
259                         }
260                         g+=dgdX*stepsD;
261                         gradIdx++;
262                     }
263                 } else {
264                     // Could be a binary search...
265                     int gradIdx = gradientsLength-1;
266                     while (gradIdx > 0) {
267                         if (g > fractions[gradIdx])
268                             break;
269                         gradIdx--;
270                     }
271                     
272                     while (off < gradLimit) {
273                         float delta = (g-fractions[gradIdx]);
274                         final int [] grad = gradients[gradIdx];
275 
276                         double stepsD     = Math.ceil(delta/-dgdX);
277                         int    steps;
278                         if (stepsD > w) steps = w;
279                         else            steps = (int)stepsD;
280                         int subGradLimit = off + steps;
281                         if (subGradLimit > gradLimit)
282                             subGradLimit = gradLimit;
283 
284                         int idx  = (int)(((delta*GRADIENT_SIZE_INDEX)
285                                           /normalizedIntervals[gradIdx])
286                                          *(1<<16)) + (1<<15);
287                         int step = (int)(((dgdX*GRADIENT_SIZE_INDEX)
288                                           /normalizedIntervals[gradIdx])
289                                          *(1<<16));
290                         while (off < subGradLimit) {
291                             pixels[off++] = grad[idx>>16];
292                             idx += step;
293                         }
294                         g+=dgdX*stepsD;
295                         gradIdx--;
296                     }
297                 }
298 
299                 while (off < rowLimit) {
300                     pixels[off++] = postVal;
301                 }
302             }
303             off += adjust; //change in off from row to row
304         }
305     }
306 
307     protected void fillSimpleNoCycle(int[] pixels, int off, int adjust, 
308                                 int x, int y, int w, int h) {
309         //constant which can be pulled out of the inner loop
310         final float initConst = (dgdX*x) + gc;
311         final float      step = dgdX*fastGradientArraySize;
312         final int      fpStep = (int)(step*(1<<16));  // fix point step
313 
314         final int [] grad = gradient;
315 
316         for(int i=0; i<h; i++){ //for every row
317             //initialize current value to be start.
318             float g = initConst + dgdY*(y+i); 
319             g *= fastGradientArraySize;
320             g += 0.5; // rounding factor...
321 
322             final int rowLimit = off+w;  // end of row iteration
323 
324             float check = dgdX*fastGradientArraySize*w;
325             if (check < 0) check = -check;
326             if (check < .3) {
327                 // System.out.println("In fillSimpleNC: " + g);
328                 final int val;
329                 if (g<=0) 
330                     val = gradientUnderflow;
331                 else if (g>=fastGradientArraySize) 
332                     val = gradientOverflow;
333                 else 
334                     val = grad[(int)g];
335                 while (off < rowLimit) {
336                     pixels[off++] = val;
337                 }
338             } else {
339                 // System.out.println("In fillSimpleNC2: " + g);
340                 int gradSteps;
341                 int preGradSteps;
342                 final int preVal, postVal;
343                 if (dgdX > 0) {
344                     gradSteps = (int)((fastGradientArraySize-g)/step);
345                     preGradSteps = (int)Math.ceil(0-g/step);
346                     preVal  = gradientUnderflow;
347                     postVal = gradientOverflow;
348 
349                 } else { // dgdX < 0
350                     gradSteps    = (int)((0-g)/step);
351                     preGradSteps = 
352                         (int)Math.ceil((fastGradientArraySize-g)/step);
353                     preVal  = gradientOverflow;
354                     postVal = gradientUnderflow;
355                 }
356 
357                 if (gradSteps > w) 
358                     gradSteps = w;
359                 final int gradLimit    = off + gradSteps;
360 
361                 if (preGradSteps > 0) {
362                     if (preGradSteps > w)
363                         preGradSteps = w;
364                     final int preGradLimit = off + preGradSteps;
365 
366                     while (off < preGradLimit) {
367                         pixels[off++] = preVal;
368                     }
369                     g += step*preGradSteps;
370                 }
371                         
372                 int fpG = (int)(g*(1<<16));
373                 while (off < gradLimit) {
374                     pixels[off++] = grad[fpG>>16];
375                     fpG += fpStep;
376                 }
377                         
378                 while (off < rowLimit) {
379                     pixels[off++] = postVal;
380                 }
381             }
382             off += adjust; //change in off from row to row
383         }
384     }
385     
386     protected void fillSimpleRepeat(int[] pixels, int off, int adjust, 
387                                int x, int y, int w, int h) {
388 
389         final float initConst = (dgdX*x) + gc;
390 
391         // Limit step to fractional part of
392         // fastGradientArraySize (the non fractional part has
393         // no affect anyways, and would mess up lots of stuff
394         // below).
395         float step = (dgdX - (int)dgdX)*fastGradientArraySize;
396 
397                 // Make it a Positive step (a small negative step is
398                 // the same as a positive step slightly less than
399                 // fastGradientArraySize.
400         if (step < 0) 
401             step += fastGradientArraySize;
402 
403         final int [] grad = gradient;
404 
405         for(int i=0; i<h; i++) { //for every row
406             //initialize current value to be start.
407             float g = initConst + dgdY*(y+i); 
408 
409             // now Limited between -1 and 1.
410             g = g-(int)g;
411             // put in the positive side.
412             if (g < 0)
413                 g += 1;
414                         
415             // scale for gradient array... 
416             g *= fastGradientArraySize;
417             g += 0.5; // rounding factor
418             final int rowLimit = off+w;  // end of row iteration
419             while (off < rowLimit) {
420                 int idx = (int)g;
421                 if (idx >= fastGradientArraySize) {
422                     g   -= fastGradientArraySize;
423                     idx -= fastGradientArraySize; 
424                 }
425                 pixels[off++] = grad[idx];
426                 g += step;
427             }
428 
429             off += adjust; //change in off from row to row
430         }
431     }
432 
433 
434     protected void fillSimpleReflect(int[] pixels, int off, int adjust, 
435                                 int x, int y, int w, int h) {
436         final float initConst = (dgdX*x) + gc;
437 
438         final int [] grad = gradient;
439 
440         for (int i=0; i<h; i++) { //for every row
441             //initialize current value to be start.
442             float g = initConst + dgdY*(y+i); 
443 
444             // now limited g to -2<->2
445             g = g - 2*((int)(g/2.0f));
446 
447             float step = dgdX;
448             // Pull it into the positive half
449             if (g < 0) {
450                 g = -g; //take absolute value
451                 step = - step;  // Change direction..
452             }
453 
454             // Now do the same for dgdX. This is safe because
455             // any step that is a multiple of 2.0 has no
456             // affect, hence we can remove it which the first
457             // part does.  The second part simply adds 2.0
458             // (which has no affect due to the cylcle) to move
459             // all negative step values into the positive
460             // side.
461             step = step - 2*((int)step/2.0f);
462             if (step < 0) 
463                 step += 2.0;
464             final int reflectMax = 2*fastGradientArraySize;
465 
466             // Scale for gradient array.
467             g    *= fastGradientArraySize;
468             g    += 0.5;
469             step *= fastGradientArraySize;
470             final int rowLimit = off+w;  // end of row iteration
471             while (off < rowLimit) {
472                 int idx = (int)g;
473                 if (idx >= reflectMax) {
474                     g   -= reflectMax;
475                     idx -= reflectMax;
476                 }
477 
478                 if (idx <= fastGradientArraySize)
479                     pixels[off++] = grad[idx];
480                 else
481                     pixels[off++] = grad[reflectMax-idx];
482                 g+= step;
483             }
484 
485             off += adjust; //change in off from row to row
486         }
487     }
488         
489     /**
490      * Return a Raster containing the colors generated for the graphics
491      * operation.  This is where the area is filled with colors distributed
492      * linearly.
493      *
494      * @param x,y,w,h The area in device space for which colors are
495      * generated.
496      *
497      */
498     protected void fillRaster(int[] pixels, int off, int adjust, 
499                               int x, int y, int w, int h) {
500   
501         //constant which can be pulled out of the inner loop
502         final float initConst = (dgdX*x) + gc;
503 
504         if (fillMethod == ANTI_ALIAS_IMPL) {
505             //initialize current value to be start.
506             for(int i=0; i<h; i++){ //for every row
507                 float g = initConst + dgdY*(y+i);
508                 
509                 final int rowLimit = off+w;  // end of row iteration
510                 while(off < rowLimit){ //for every pixel in this row.
511                     //get the color
512                     pixels[off++] = indexGradientAntiAlias(g, pixSz); 
513                     g += dgdX; //incremental change in g
514                 }
515                 off += adjust; //change in off from row to row
516             }
517         }
518         else if (!isSimpleLookup) {
519             if (cycleMethod == MultipleGradientPaint.NO_CYCLE) {
520                 fillHardNoCycle(pixels, off, adjust, x, y, w, h);
521             }
522             else {
523                 //initialize current value to be start.
524                 for(int i=0; i<h; i++){ //for every row
525                     float g = initConst + dgdY*(y+i); 
526                 
527                     final int rowLimit = off+w;  // end of row iteration
528                     while(off < rowLimit){ //for every pixel in this row.
529                         //get the color
530                         pixels[off++] = indexIntoGradientsArrays(g); 
531                         g += dgdX; //incremental change in g
532                     }
533                     off += adjust; //change in off from row to row
534                 }
535             }
536         } else {
537             // Simple implementations: just scale index by array size
538             
539             if (cycleMethod == MultipleGradientPaint.NO_CYCLE)
540                 fillSimpleNoCycle(pixels, off, adjust, x, y, w, h);
541             else if (cycleMethod == MultipleGradientPaint.REPEAT)
542                 fillSimpleRepeat(pixels, off, adjust, x, y, w, h);
543             else //cycleMethod == MultipleGradientPaint.REFLECT
544                 fillSimpleReflect(pixels, off, adjust, x, y, w, h);
545         }
546     }
547     
548     
549 }