source: josm/trunk/src/com/kitfox/svg/batik/LinearGradientPaintContext.java@ 6235

Last change on this file since 6235 was 4256, checked in by bastiK, 14 years ago

see #6560 - basic svg support, includes kitfox svgsalamander, r 98, patched

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