001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.ext.awt;
020:
021: import java.awt.Color;
022: import java.awt.Rectangle;
023: import java.awt.RenderingHints;
024: import java.awt.geom.AffineTransform;
025: import java.awt.geom.NoninvertibleTransformException;
026: import java.awt.geom.Point2D;
027: import java.awt.geom.Rectangle2D;
028: import java.awt.image.ColorModel;
029:
030: /**
031: * Provides the actual implementation for the LinearGradientPaint
032: * This is where the pixel processing is done.
033: *
034: * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
035: * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
036: * @version $Id: LinearGradientPaintContext.java 475685 2006-11-16 11:16:05Z cam $
037: * @see java.awt.PaintContext
038: * @see java.awt.Paint
039: * @see java.awt.GradientPaint
040: */
041: final class LinearGradientPaintContext extends
042: MultipleGradientPaintContext {
043:
044: /**
045: * The following invariants are used to process the gradient value from
046: * a device space coordinate, (X, Y):
047: * g(X, Y) = dgdX*X + dgdY*Y + gc
048: */
049: private float dgdX, dgdY, gc, pixSz;
050:
051: private static final int DEFAULT_IMPL = 1;
052: private static final int ANTI_ALIAS_IMPL = 3;
053:
054: private int fillMethod;
055:
056: /**
057: * Constructor for LinearGradientPaintContext.
058: *
059: * @param cm {@link ColorModel} that receives
060: * the <code>Paint</code> data. This is used only as a hint.
061: *
062: * @param deviceBounds the device space bounding box of the
063: * graphics primitive being rendered
064: *
065: * @param userBounds the user space bounding box of the
066: * graphics primitive being rendered
067: *
068: * @param t the {@link AffineTransform} from user
069: * space into device space (gradientTransform should be
070: * concatenated with this)
071: *
072: * @param hints the hints that the context object uses to choose
073: * between rendering alternatives
074: *
075: * @param dStart gradient start point, in user space
076: *
077: * @param dEnd gradient end point, in user space
078: *
079: * @param fractions the fractions specifying the gradient distribution
080: *
081: * @param colors the gradient colors
082: *
083: * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
084: *
085: * @param colorSpace which colorspace to use for interpolation,
086: * either SRGB or LINEAR_RGB
087: *
088: */
089: public LinearGradientPaintContext(ColorModel cm,
090: Rectangle deviceBounds, Rectangle2D userBounds,
091: AffineTransform t, RenderingHints hints, Point2D dStart,
092: Point2D dEnd, float[] fractions, Color[] colors,
093: MultipleGradientPaint.CycleMethodEnum cycleMethod,
094: MultipleGradientPaint.ColorSpaceEnum colorSpace)
095: throws NoninvertibleTransformException {
096: super (cm, deviceBounds, userBounds, t, hints, fractions,
097: colors, cycleMethod, colorSpace);
098:
099: // Use single precision floating points
100: Point2D.Float start = new Point2D.Float((float) dStart.getX(),
101: (float) dStart.getY());
102: Point2D.Float end = new Point2D.Float((float) dEnd.getX(),
103: (float) dEnd.getY());
104:
105: // A given point in the raster should take on the same color as its
106: // projection onto the gradient vector.
107: // Thus, we want the projection of the current position vector
108: // onto the gradient vector, then normalized with respect to the
109: // length of the gradient vector, giving a value which can be mapped into
110: // the range 0-1.
111: // projection = currentVector dot gradientVector / length(gradientVector)
112: // normalized = projection / length(gradientVector)
113:
114: float dx = end.x - start.x; // change in x from start to end
115: float dy = end.y - start.y; // change in y from start to end
116: float dSq = dx * dx + dy * dy; // total distance squared
117:
118: //avoid repeated calculations by doing these divides once.
119: float constX = dx / dSq;
120: float constY = dy / dSq;
121:
122: //incremental change along gradient for +x
123: dgdX = a00 * constX + a10 * constY;
124: //incremental change along gradient for +y
125: dgdY = a01 * constX + a11 * constY;
126:
127: float dgdXAbs = Math.abs(dgdX);
128: float dgdYAbs = Math.abs(dgdY);
129: if (dgdXAbs > dgdYAbs)
130: pixSz = dgdXAbs;
131: else
132: pixSz = dgdYAbs;
133:
134: //constant, incorporates the translation components from the matrix
135: gc = (a02 - start.x) * constX + (a12 - start.y) * constY;
136:
137: Object colorRend = hints
138: .get(RenderingHints.KEY_COLOR_RENDERING);
139: Object rend = hints.get(RenderingHints.KEY_RENDERING);
140:
141: fillMethod = DEFAULT_IMPL;
142:
143: if ((cycleMethod == MultipleGradientPaint.REPEAT)
144: || hasDiscontinuity) {
145: if (rend == RenderingHints.VALUE_RENDER_QUALITY)
146: fillMethod = ANTI_ALIAS_IMPL;
147: // ColorRend overrides rend.
148: if (colorRend == RenderingHints.VALUE_COLOR_RENDER_SPEED)
149: fillMethod = DEFAULT_IMPL;
150: else if (colorRend == RenderingHints.VALUE_COLOR_RENDER_QUALITY)
151: fillMethod = ANTI_ALIAS_IMPL;
152: }
153: }
154:
155: protected void fillHardNoCycle(int[] pixels, int off, int adjust,
156: int x, int y, int w, int h) {
157:
158: //constant which can be pulled out of the inner loop
159: final float initConst = (dgdX * x) + gc;
160:
161: for (int i = 0; i < h; i++) { //for every row
162: //initialize current value to be start.
163: float g = initConst + dgdY * (y + i);
164: final int rowLimit = off + w; // end of row iteration
165:
166: if (dgdX == 0) {
167: // System.out.println("In fillHard: " + g);
168: final int val;
169: if (g <= 0)
170: val = gradientUnderflow;
171: else if (g >= 1)
172: val = gradientOverflow;
173: else {
174: // Could be a binary search...
175: int gradIdx = 0;
176: while (gradIdx < gradientsLength - 1) {
177: if (g < fractions[gradIdx + 1])
178: break;
179: gradIdx++;
180: }
181: float delta = (g - fractions[gradIdx]);
182: float idx = ((delta * GRADIENT_SIZE_INDEX) / normalizedIntervals[gradIdx]) + 0.5f;
183: val = gradients[gradIdx][(int) idx];
184: }
185:
186: while (off < rowLimit) {
187: pixels[off++] = val;
188: }
189: } else {
190: // System.out.println("In fillHard2: " + g);
191: int gradSteps;
192: int preGradSteps;
193: final int preVal, postVal;
194:
195: float gradStepsF;
196: float preGradStepsF;
197: if (dgdX >= 0) {
198: gradStepsF = ((1 - g) / dgdX);
199: preGradStepsF = (float) Math.ceil((0 - g) / dgdX);
200: preVal = gradientUnderflow;
201: postVal = gradientOverflow;
202: } else { // dgdX < 0
203: gradStepsF = ((0 - g) / dgdX);
204: preGradStepsF = (float) Math.ceil((1 - g) / dgdX);
205: preVal = gradientOverflow;
206: postVal = gradientUnderflow;
207: }
208:
209: if (gradStepsF > w)
210: gradSteps = w;
211: else
212: gradSteps = (int) gradStepsF;
213: if (preGradStepsF > w)
214: preGradSteps = w;
215: else
216: 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
242: .ceil((fractions[gradIdx + 1] - g)
243: / dgdX);
244: int steps;
245: if (stepsD > w)
246: steps = w;
247: else
248: steps = (int) stepsD;
249: int subGradLimit = off + steps;
250: if (subGradLimit > gradLimit)
251: subGradLimit = gradLimit;
252:
253: int idx = (int) (((delta * GRADIENT_SIZE_INDEX) / normalizedIntervals[gradIdx]) * (1 << 16))
254: + (1 << 15);
255: int step = (int) (((dgdX * GRADIENT_SIZE_INDEX) / normalizedIntervals[gradIdx]) * (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)
279: steps = w;
280: else
281: steps = (int) stepsD;
282: int subGradLimit = off + steps;
283: if (subGradLimit > gradLimit)
284: subGradLimit = gradLimit;
285:
286: int idx = (int) (((delta * GRADIENT_SIZE_INDEX) / normalizedIntervals[gradIdx]) * (1 << 16))
287: + (1 << 15);
288: int step = (int) (((dgdX * GRADIENT_SIZE_INDEX) / normalizedIntervals[gradIdx]) * (1 << 16));
289: while (off < subGradLimit) {
290: pixels[off++] = grad[idx >> 16];
291: idx += step;
292: }
293: g += dgdX * stepsD;
294: gradIdx--;
295: }
296: }
297:
298: while (off < rowLimit) {
299: pixels[off++] = postVal;
300: }
301: }
302: off += adjust; //change in off from row to row
303: }
304: }
305:
306: protected void fillSimpleNoCycle(int[] pixels, int off, int adjust,
307: int x, int y, int w, int h) {
308: //constant which can be pulled out of the inner loop
309: final float initConst = (dgdX * x) + gc;
310: final float step = dgdX * fastGradientArraySize;
311: final int fpStep = (int) (step * (1 << 16)); // fix point step
312:
313: final int[] grad = gradient;
314:
315: for (int i = 0; i < h; i++) { //for every row
316: //initialize current value to be start.
317: float g = initConst + dgdY * (y + i);
318: g *= fastGradientArraySize;
319: g += 0.5; // rounding factor...
320:
321: final int rowLimit = off + w; // end of row iteration
322:
323: float check = dgdX * fastGradientArraySize * w;
324: if (check < 0)
325: 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 = (int) Math
352: .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: protected void fillSimpleReflect(int[] pixels, int off, int adjust,
434: int x, int y, int w, int h) {
435: final float initConst = (dgdX * x) + gc;
436:
437: final int[] grad = gradient;
438:
439: for (int i = 0; i < h; i++) { //for every row
440: //initialize current value to be start.
441: float g = initConst + dgdY * (y + i);
442:
443: // now limited g to -2<->2
444: g = g - 2 * ((int) (g / 2.0f));
445:
446: float step = dgdX;
447: // Pull it into the positive half
448: if (g < 0) {
449: g = -g; //take absolute value
450: step = -step; // Change direction..
451: }
452:
453: // Now do the same for dgdX. This is safe because
454: // any step that is a multiple of 2.0 has no
455: // affect, hence we can remove it which the first
456: // part does. The second part simply adds 2.0
457: // (which has no affect due to the cylcle) to move
458: // all negative step values into the positive
459: // side.
460: step = step - 2 * ((int) step / 2.0f);
461: if (step < 0)
462: step += 2.0;
463: final int reflectMax = 2 * fastGradientArraySize;
464:
465: // Scale for gradient array.
466: g *= fastGradientArraySize;
467: g += 0.5;
468: step *= fastGradientArraySize;
469: final int rowLimit = off + w; // end of row iteration
470: while (off < rowLimit) {
471: int idx = (int) g;
472: if (idx >= reflectMax) {
473: g -= reflectMax;
474: idx -= reflectMax;
475: }
476:
477: if (idx <= fastGradientArraySize)
478: pixels[off++] = grad[idx];
479: else
480: pixels[off++] = grad[reflectMax - idx];
481: g += step;
482: }
483:
484: off += adjust; //change in off from row to row
485: }
486: }
487:
488: /**
489: * Return a Raster containing the colors generated for the graphics
490: * operation. This is where the area is filled with colors distributed
491: * linearly.
492: *
493: * @param x The x coordinate of the area in device space for which colors
494: * are generated.
495: * @param y The y coordinate of the area in device space for which colors
496: * are generated.
497: * @param w The width of the area in device space for which colors
498: * are generated.
499: * @param h The height of the area in device space for which colors
500: * are generated.
501: */
502: protected void fillRaster(int[] pixels, int off, int adjust, int x,
503: int y, int w, int h) {
504:
505: //constant which can be pulled out of the inner loop
506: final float initConst = (dgdX * x) + gc;
507:
508: if (fillMethod == ANTI_ALIAS_IMPL) {
509: //initialize current value to be start.
510: for (int i = 0; i < h; i++) { //for every row
511: float g = initConst + dgdY * (y + i);
512:
513: final int rowLimit = off + w; // end of row iteration
514: while (off < rowLimit) { //for every pixel in this row.
515: //get the color
516: pixels[off++] = indexGradientAntiAlias(g, pixSz);
517: g += dgdX; //incremental change in g
518: }
519: off += adjust; //change in off from row to row
520: }
521: } else if (!isSimpleLookup) {
522: if (cycleMethod == MultipleGradientPaint.NO_CYCLE) {
523: fillHardNoCycle(pixels, off, adjust, x, y, w, h);
524: } else {
525: //initialize current value to be start.
526: for (int i = 0; i < h; i++) { //for every row
527: float g = initConst + dgdY * (y + i);
528:
529: final int rowLimit = off + w; // end of row iteration
530: while (off < rowLimit) { //for every pixel in this row.
531: //get the color
532: pixels[off++] = indexIntoGradientsArrays(g);
533: g += dgdX; //incremental change in g
534: }
535: off += adjust; //change in off from row to row
536: }
537: }
538: } else {
539: // Simple implementations: just scale index by array size
540:
541: if (cycleMethod == MultipleGradientPaint.NO_CYCLE)
542: fillSimpleNoCycle(pixels, off, adjust, x, y, w, h);
543: else if (cycleMethod == MultipleGradientPaint.REPEAT)
544: fillSimpleRepeat(pixels, off, adjust, x, y, w, h);
545: else
546: //cycleMethod == MultipleGradientPaint.REFLECT
547: fillSimpleReflect(pixels, off, adjust, x, y, w, h);
548: }
549: }
550: }
|