001: /*
002:
003: Copyright 2001-2004 The Apache Software Foundation
004:
005: Licensed under the Apache License, Version 2.0 (the "License");
006: you may not use this file except in compliance with the License.
007: You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016:
017: */
018: package com.xoetrope.batik.ext.awt;
019:
020: import java.awt.Color;
021: import java.awt.Rectangle;
022: import java.awt.RenderingHints;
023: import java.awt.geom.AffineTransform;
024: import java.awt.geom.NoninvertibleTransformException;
025: import java.awt.geom.Point2D;
026: import java.awt.geom.Rectangle2D;
027: import java.awt.image.ColorModel;
028:
029: /**
030: * Provides the actual implementation for the LinearGradientPaint
031: * This is where the pixel processing is done.
032: *
033: * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
034: * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
035: * @version $Id: LinearGradientPaintContext.java,v 1.2 2006/08/31 09:28:43 val Exp $
036: * @see java.awt.PaintContext
037: * @see java.awt.Paint
038: * @see java.awt.GradientPaint
039: */
040: final class LinearGradientPaintContext extends
041: MultipleGradientPaintContext {
042:
043: /**
044: * The following invariants are used to process the gradient value from
045: * a device space coordinate, (X, Y):
046: * g(X, Y) = dgdX*X + dgdY*Y + gc
047: */
048: private float dgdX, dgdY, gc, pixSz;
049:
050: private static final int DEFAULT_IMPL = 1;
051: private static final int ANTI_ALIAS_IMPL = 3;
052:
053: private int fillMethod;
054:
055: /**
056: * Constructor for LinearGradientPaintContext.
057: *
058: * @param cm {@link ColorModel} that receives
059: * the <code>Paint</code> data. This is used only as a hint.
060: *
061: * @param deviceBounds the device space bounding box of the
062: * graphics primitive being rendered
063: *
064: * @param userBounds the user space bounding box of the
065: * graphics primitive being rendered
066: *
067: * @param t the {@link AffineTransform} from user
068: * space into device space (gradientTransform should be
069: * concatenated with this)
070: *
071: * @param hints the hints that the context object uses to choose
072: * between rendering alternatives
073: *
074: * @param dStart gradient start point, in user space
075: *
076: * @param dEnd gradient end point, in user space
077: *
078: * @param fractions the fractions specifying the gradient distribution
079: *
080: * @param colors the gradient colors
081: *
082: * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
083: *
084: * @param colorSpace which colorspace to use for interpolation,
085: * either SRGB or LINEAR_RGB
086: *
087: */
088: public LinearGradientPaintContext(ColorModel cm,
089: Rectangle deviceBounds, Rectangle2D userBounds,
090: AffineTransform t, RenderingHints hints, Point2D dStart,
091: Point2D dEnd, float[] fractions, Color[] colors,
092: MultipleGradientPaint.CycleMethodEnum cycleMethod,
093: MultipleGradientPaint.ColorSpaceEnum colorSpace)
094: throws NoninvertibleTransformException {
095: super (cm, deviceBounds, userBounds, t, hints, fractions,
096: colors, cycleMethod, colorSpace);
097:
098: // Use single precision floating points
099: Point2D.Float start = new Point2D.Float((float) dStart.getX(),
100: (float) dStart.getY());
101: Point2D.Float end = new Point2D.Float((float) dEnd.getX(),
102: (float) dEnd.getY());
103:
104: // A given point in the raster should take on the same color as its
105: // projection onto the gradient vector.
106: // Thus, we want the projection of the current position vector
107: // onto the gradient vector, then normalized with respect to the
108: // length of the gradient vector, giving a value which can be mapped into
109: // the range 0-1.
110: // projection = currentVector dot gradientVector / length(gradientVector)
111: // normalized = projection / length(gradientVector)
112:
113: float dx = end.x - start.x; // change in x from start to end
114: float dy = end.y - start.y; // change in y from start to end
115: float dSq = dx * dx + dy * dy; // total distance squared
116:
117: //avoid repeated calculations by doing these divides once.
118: float constX = dx / dSq;
119: float constY = dy / dSq;
120:
121: //incremental change along gradient for +x
122: dgdX = a00 * constX + a10 * constY;
123: //incremental change along gradient for +y
124: dgdY = a01 * constX + a11 * constY;
125:
126: float dgdXAbs = Math.abs(dgdX);
127: float dgdYAbs = Math.abs(dgdY);
128: if (dgdXAbs > dgdYAbs)
129: pixSz = dgdXAbs;
130: else
131: pixSz = dgdYAbs;
132:
133: //constant, incorporates the translation components from the matrix
134: gc = (a02 - start.x) * constX + (a12 - start.y) * constY;
135:
136: Object colorRend = hints
137: .get(RenderingHints.KEY_COLOR_RENDERING);
138: Object rend = hints.get(RenderingHints.KEY_RENDERING);
139:
140: fillMethod = DEFAULT_IMPL;
141:
142: if ((cycleMethod == MultipleGradientPaint.REPEAT)
143: || hasDiscontinuity) {
144: if (rend == RenderingHints.VALUE_RENDER_QUALITY)
145: fillMethod = ANTI_ALIAS_IMPL;
146: // ColorRend overrides rend.
147: if (colorRend == RenderingHints.VALUE_COLOR_RENDER_SPEED)
148: fillMethod = DEFAULT_IMPL;
149: else if (colorRend == RenderingHints.VALUE_COLOR_RENDER_QUALITY)
150: fillMethod = ANTI_ALIAS_IMPL;
151: }
152: }
153:
154: protected void fillHardNoCycle(int[] pixels, int off, int adjust,
155: int x, int y, int w, int h) {
156:
157: //constant which can be pulled out of the inner loop
158: final float initConst = (dgdX * x) + gc;
159:
160: for (int i = 0; i < h; i++) { //for every row
161: //initialize current value to be start.
162: float g = initConst + dgdY * (y + i);
163: final int rowLimit = off + w; // end of row iteration
164:
165: if (dgdX == 0) {
166: // System.out.println("In fillHard: " + g);
167: final int val;
168: if (g <= 0)
169: val = gradientUnderflow;
170: else if (g >= 1)
171: val = gradientOverflow;
172: else {
173: // Could be a binary search...
174: int gradIdx = 0;
175: while (gradIdx < gradientsLength - 1) {
176: if (g < fractions[gradIdx + 1])
177: break;
178: gradIdx++;
179: }
180: float delta = (g - fractions[gradIdx]);
181: float idx = ((delta * GRADIENT_SIZE_INDEX) / normalizedIntervals[gradIdx]) + 0.5f;
182: val = gradients[gradIdx][(int) idx];
183: }
184:
185: while (off < rowLimit) {
186: pixels[off++] = val;
187: }
188: } else {
189: // System.out.println("In fillHard2: " + g);
190: int gradSteps;
191: int preGradSteps;
192: final int preVal, postVal;
193:
194: float gradStepsF;
195: float preGradStepsF;
196: if (dgdX >= 0) {
197: gradStepsF = ((1 - g) / dgdX);
198: preGradStepsF = (float) Math.ceil((0 - g) / dgdX);
199: preVal = gradientUnderflow;
200: postVal = gradientOverflow;
201: } else { // dgdX < 0
202: gradStepsF = ((0 - g) / dgdX);
203: preGradStepsF = (float) Math.ceil((1 - g) / dgdX);
204: preVal = gradientOverflow;
205: postVal = gradientUnderflow;
206: }
207:
208: if (gradStepsF > w)
209: gradSteps = w;
210: else
211: gradSteps = (int) gradStepsF;
212: if (preGradStepsF > w)
213: preGradSteps = w;
214: else
215: preGradSteps = (int) preGradStepsF;
216:
217: final int gradLimit = off + gradSteps;
218: if (preGradSteps > 0) {
219: final int preGradLimit = off + preGradSteps;
220:
221: while (off < preGradLimit) {
222: pixels[off++] = preVal;
223: }
224: g += dgdX * preGradSteps;
225: }
226:
227: if (dgdX > 0) {
228: // Could be a binary search...
229: int gradIdx = 0;
230: while (gradIdx < gradientsLength - 1) {
231: if (g < fractions[gradIdx + 1])
232: break;
233: gradIdx++;
234: }
235:
236: while (off < gradLimit) {
237: float delta = (g - fractions[gradIdx]);
238: final int[] grad = gradients[gradIdx];
239:
240: double stepsD = Math
241: .ceil((fractions[gradIdx + 1] - g)
242: / dgdX);
243: int steps;
244: if (stepsD > w)
245: steps = w;
246: else
247: steps = (int) stepsD;
248: int subGradLimit = off + steps;
249: if (subGradLimit > gradLimit)
250: subGradLimit = gradLimit;
251:
252: int idx = (int) (((delta * GRADIENT_SIZE_INDEX) / normalizedIntervals[gradIdx]) * (1 << 16))
253: + (1 << 15);
254: int step = (int) (((dgdX * GRADIENT_SIZE_INDEX) / normalizedIntervals[gradIdx]) * (1 << 16));
255: while (off < subGradLimit) {
256: pixels[off++] = grad[idx >> 16];
257: idx += step;
258: }
259: g += dgdX * stepsD;
260: gradIdx++;
261: }
262: } else {
263: // Could be a binary search...
264: int gradIdx = gradientsLength - 1;
265: while (gradIdx > 0) {
266: if (g > fractions[gradIdx])
267: break;
268: gradIdx--;
269: }
270:
271: while (off < gradLimit) {
272: float delta = (g - fractions[gradIdx]);
273: final int[] grad = gradients[gradIdx];
274:
275: double stepsD = Math.ceil(delta / -dgdX);
276: int steps;
277: if (stepsD > w)
278: steps = w;
279: else
280: steps = (int) stepsD;
281: int subGradLimit = off + steps;
282: if (subGradLimit > gradLimit)
283: subGradLimit = gradLimit;
284:
285: int idx = (int) (((delta * GRADIENT_SIZE_INDEX) / normalizedIntervals[gradIdx]) * (1 << 16))
286: + (1 << 15);
287: int step = (int) (((dgdX * GRADIENT_SIZE_INDEX) / normalizedIntervals[gradIdx]) * (1 << 16));
288: while (off < subGradLimit) {
289: pixels[off++] = grad[idx >> 16];
290: idx += step;
291: }
292: g += dgdX * stepsD;
293: gradIdx--;
294: }
295: }
296:
297: while (off < rowLimit) {
298: pixels[off++] = postVal;
299: }
300: }
301: off += adjust; //change in off from row to row
302: }
303: }
304:
305: protected void fillSimpleNoCycle(int[] pixels, int off, int adjust,
306: int x, int y, int w, int h) {
307: //constant which can be pulled out of the inner loop
308: final float initConst = (dgdX * x) + gc;
309: final float step = dgdX * fastGradientArraySize;
310: final int fpStep = (int) (step * (1 << 16)); // fix point step
311:
312: final int[] grad = gradient;
313:
314: for (int i = 0; i < h; i++) { //for every row
315: //initialize current value to be start.
316: float g = initConst + dgdY * (y + i);
317: g *= fastGradientArraySize;
318: g += 0.5; // rounding factor...
319:
320: final int rowLimit = off + w; // end of row iteration
321:
322: float check = dgdX * fastGradientArraySize * w;
323: if (check < 0)
324: check = -check;
325: if (check < .3) {
326: // System.out.println("In fillSimpleNC: " + g);
327: final int val;
328: if (g <= 0)
329: val = gradientUnderflow;
330: else if (g >= fastGradientArraySize)
331: val = gradientOverflow;
332: else
333: val = grad[(int) g];
334: while (off < rowLimit) {
335: pixels[off++] = val;
336: }
337: } else {
338: // System.out.println("In fillSimpleNC2: " + g);
339: int gradSteps;
340: int preGradSteps;
341: final int preVal, postVal;
342: if (dgdX > 0) {
343: gradSteps = (int) ((fastGradientArraySize - g) / step);
344: preGradSteps = (int) Math.ceil(0 - g / step);
345: preVal = gradientUnderflow;
346: postVal = gradientOverflow;
347:
348: } else { // dgdX < 0
349: gradSteps = (int) ((0 - g) / step);
350: preGradSteps = (int) Math
351: .ceil((fastGradientArraySize - g) / step);
352: preVal = gradientOverflow;
353: postVal = gradientUnderflow;
354: }
355:
356: if (gradSteps > w)
357: gradSteps = w;
358: final int gradLimit = off + gradSteps;
359:
360: if (preGradSteps > 0) {
361: if (preGradSteps > w)
362: preGradSteps = w;
363: final int preGradLimit = off + preGradSteps;
364:
365: while (off < preGradLimit) {
366: pixels[off++] = preVal;
367: }
368: g += step * preGradSteps;
369: }
370:
371: int fpG = (int) (g * (1 << 16));
372: while (off < gradLimit) {
373: pixels[off++] = grad[fpG >> 16];
374: fpG += fpStep;
375: }
376:
377: while (off < rowLimit) {
378: pixels[off++] = postVal;
379: }
380: }
381: off += adjust; //change in off from row to row
382: }
383: }
384:
385: protected void fillSimpleRepeat(int[] pixels, int off, int adjust,
386: int x, int y, int w, int h) {
387:
388: final float initConst = (dgdX * x) + gc;
389:
390: // Limit step to fractional part of
391: // fastGradientArraySize (the non fractional part has
392: // no affect anyways, and would mess up lots of stuff
393: // below).
394: float step = (dgdX - (int) dgdX) * fastGradientArraySize;
395:
396: // Make it a Positive step (a small negative step is
397: // the same as a positive step slightly less than
398: // fastGradientArraySize.
399: if (step < 0)
400: step += fastGradientArraySize;
401:
402: final int[] grad = gradient;
403:
404: for (int i = 0; i < h; i++) { //for every row
405: //initialize current value to be start.
406: float g = initConst + dgdY * (y + i);
407:
408: // now Limited between -1 and 1.
409: g = g - (int) g;
410: // put in the positive side.
411: if (g < 0)
412: g += 1;
413:
414: // scale for gradient array...
415: g *= fastGradientArraySize;
416: g += 0.5; // rounding factor
417: final int rowLimit = off + w; // end of row iteration
418: while (off < rowLimit) {
419: int idx = (int) g;
420: if (idx >= fastGradientArraySize) {
421: g -= fastGradientArraySize;
422: idx -= fastGradientArraySize;
423: }
424: pixels[off++] = grad[idx];
425: g += step;
426: }
427:
428: off += adjust; //change in off from row to row
429: }
430: }
431:
432: protected void fillSimpleReflect(int[] pixels, int off, int adjust,
433: int x, int y, int w, int h) {
434: final float initConst = (dgdX * x) + gc;
435:
436: final int[] grad = gradient;
437:
438: for (int i = 0; i < h; i++) { //for every row
439: //initialize current value to be start.
440: float g = initConst + dgdY * (y + i);
441:
442: // now limited g to -2<->2
443: g = g - 2 * ((int) (g / 2.0f));
444:
445: float step = dgdX;
446: // Pull it into the positive half
447: if (g < 0) {
448: g = -g; //take absolute value
449: step = -step; // Change direction..
450: }
451:
452: // Now do the same for dgdX. This is safe because
453: // any step that is a multiple of 2.0 has no
454: // affect, hence we can remove it which the first
455: // part does. The second part simply adds 2.0
456: // (which has no affect due to the cylcle) to move
457: // all negative step values into the positive
458: // side.
459: step = step - 2 * ((int) step / 2.0f);
460: if (step < 0)
461: step += 2.0;
462: final int reflectMax = 2 * fastGradientArraySize;
463:
464: // Scale for gradient array.
465: g *= fastGradientArraySize;
466: g += 0.5;
467: step *= fastGradientArraySize;
468: final int rowLimit = off + w; // end of row iteration
469: while (off < rowLimit) {
470: int idx = (int) g;
471: if (idx >= reflectMax) {
472: g -= reflectMax;
473: idx -= reflectMax;
474: }
475:
476: if (idx <= fastGradientArraySize)
477: pixels[off++] = grad[idx];
478: else
479: pixels[off++] = grad[reflectMax - idx];
480: g += step;
481: }
482:
483: off += adjust; //change in off from row to row
484: }
485: }
486:
487: /**
488: * Return a Raster containing the colors generated for the graphics
489: * operation. This is where the area is filled with colors distributed
490: * linearly.
491: *
492: * @param x,y,w,h The area in device space for which colors are
493: * generated.
494: *
495: */
496: protected void fillRaster(int[] pixels, int off, int adjust, int x,
497: int y, int w, int h) {
498:
499: //constant which can be pulled out of the inner loop
500: final float initConst = (dgdX * x) + gc;
501:
502: if (fillMethod == ANTI_ALIAS_IMPL) {
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++] = indexGradientAntiAlias(g, pixSz);
511: g += dgdX; //incremental change in g
512: }
513: off += adjust; //change in off from row to row
514: }
515: } else if (!isSimpleLookup) {
516: if (cycleMethod == MultipleGradientPaint.NO_CYCLE) {
517: fillHardNoCycle(pixels, off, adjust, x, y, w, h);
518: } else {
519: //initialize current value to be start.
520: for (int i = 0; i < h; i++) { //for every row
521: float g = initConst + dgdY * (y + i);
522:
523: final int rowLimit = off + w; // end of row iteration
524: while (off < rowLimit) { //for every pixel in this row.
525: //get the color
526: pixels[off++] = indexIntoGradientsArrays(g);
527: g += dgdX; //incremental change in g
528: }
529: off += adjust; //change in off from row to row
530: }
531: }
532: } else {
533: // Simple implementations: just scale index by array size
534:
535: if (cycleMethod == MultipleGradientPaint.NO_CYCLE)
536: fillSimpleNoCycle(pixels, off, adjust, x, y, w, h);
537: else if (cycleMethod == MultipleGradientPaint.REPEAT)
538: fillSimpleRepeat(pixels, off, adjust, x, y, w, h);
539: else
540: //cycleMethod == MultipleGradientPaint.REFLECT
541: fillSimpleReflect(pixels, off, adjust, x, y, w, h);
542: }
543: }
544:
545: }
|