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 }