KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > ext > awt > image > rendered > TurbulencePatternRed


1 /*
2
3    Copyright 1999-2003 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
19 package org.apache.batik.ext.awt.image.rendered;
20
21 import java.awt.Rectangle JavaDoc;
22 import java.awt.color.ColorSpace JavaDoc;
23 import java.awt.geom.AffineTransform JavaDoc;
24 import java.awt.geom.Rectangle2D JavaDoc;
25 import java.awt.image.ColorModel JavaDoc;
26 import java.awt.image.DataBuffer JavaDoc;
27 import java.awt.image.DataBufferInt JavaDoc;
28 import java.awt.image.DirectColorModel JavaDoc;
29 import java.awt.image.SinglePixelPackedSampleModel JavaDoc;
30 import java.awt.image.WritableRaster JavaDoc;
31 /**
32  * This class creates a RenderedImage in conformance to the one
33  * defined for the feTurbulence filter of the SVG specification. What
34  * follows is my high-level description of how the noise is generated.
35  * This is not contained in the SVG spec, just the algorithm for
36  * doing it. This is provided in the hope that someone will figure
37  * out a clever way to accelerate parts of the function.
38  *
39  * gradient contains a long list of random unit vectors. For each
40  * point we are to generate noise for we do two things. first we use
41  * the latticeSelector to 'co-mingle' the integer portions of x and y
42  * (this allows us to have a one-dimensional array of gradients that
43  * appears 2 dimensional, by using the co-mingled index).
44  *
45  * We do this for [x,y], [x+1,y], [x,y+1], and [x+1, y+1], this gives
46  * us the four gradient vectors that surround the point (b00, b10, ...)
47  *
48  * Next we construct the four vectors from the grid points (where the
49  * gradient vectors are defined) [these are rx0, rx1, ry0, ry1].
50  *
51  * We then take the dot product between the gradient vectors and the
52  * grid point vectors (this gives the portion of the grid point vector
53  * that projects along the gradient vector for each grid point).
54  * These four dot projects are then combined with linear interpolation.
55  * The weight factor for the linear combination is the result of applying
56  * the 's' curve function to the fractional part of x and y (rx0, ry0).
57  * The S curve function get's it's name because it looks a bit like as
58  * 'S' from 0->1.
59  *
60  * @author <a HREF="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
61  * @author <a HREF="mailto:DeWeese@apache.org">Thomas DeWeese</a>
62  * @version $Id: TurbulencePatternRed.java,v 1.4 2004/10/30 18:38:05 deweese Exp $ */

63 public final class TurbulencePatternRed extends AbstractRed {
64     /**
65      * Inner class to store tile stitching info.
66      * #see
67      */

68     static final class StitchInfo {
69         /**
70          * Width of the integer lattice tile
71          */

72         int width;
73
74         /**
75          * Height of the integer lattice tile
76          */

77         int height;
78
79         /**
80          * Value beyond which values are wrapped on
81          * the x-axis.
82          * @see #noise2Stitch
83          */

84         int wrapX;
85
86         /**
87          * Value beyond which values are wrapped on
88          * the y-axis.
89          * @see #noise2Stitch
90          */

91         int wrapY;
92
93         /**
94          * Default constructor
95          */

96         StitchInfo(){
97         }
98
99         /**
100          * Copy constructor
101          */

102         StitchInfo(StitchInfo stitchInfo){
103             this.width = stitchInfo.width;
104             this.height = stitchInfo.height;
105             this.wrapX = stitchInfo.wrapX;
106             this.wrapY = stitchInfo.wrapY;
107         }
108
109         final void assign(StitchInfo stitchInfo) {
110             this.width = stitchInfo.width;
111             this.height = stitchInfo.height;
112             this.wrapX = stitchInfo.wrapX;
113             this.wrapY = stitchInfo.wrapY;
114         }
115
116         /*
117          * Adjustst the StitchInfo for when the frequency has been
118          * doubled.
119          *
120          * width = tileWidth*baseFrequencyX
121          * height = tileHeight*baseFrequencyY
122          * minY = tileY*baseFrequencyY + PerlinN
123          * wrapX = tileX*baseFrequencyX + PerlinN + width
124          * wrapY = tileY*baseFrequencyY + PerlinN + height
125          *
126          */

127         final void doubleFrequency(){
128             width *= 2;
129             height *= 2;
130             wrapX *= 2;
131             wrapY *= 2;
132             wrapX -= PerlinN;
133             wrapY -= PerlinN;
134         }
135     }
136
137     /**
138      * Used when stitching is on
139      */

140     private StitchInfo stitchInfo = null;
141
142     /**
143      * Identity transform, default used when null input in generatePattern
144      * @see #generatePattern
145      */

146     private static final AffineTransform JavaDoc IDENTITY = new AffineTransform JavaDoc();
147
148     /**
149      * x-axis base frequency for the noise function along the x-axis
150      */

151     private double baseFrequencyX;
152
153     /**
154      * y-axis base frequency for the noise function along the y-axis
155      */

156     private double baseFrequencyY;
157
158     /**
159      * Number of octaves in the noise function
160      */

161     private int numOctaves;
162
163     /**
164      * Starting number for the pseudo random number generator
165      */

166     private int seed;
167
168     /**
169      * Defines the tile for the turbulence function, if non-null turns
170      * on stitching, so frequencies are adjusted to avoid
171      * discontinuities in case frequencies do not match tile
172      * boundaries.
173      */

174     private Rectangle2D JavaDoc tile;
175
176     /**
177      * Defines the tile for the turbulence function
178      */

179     private AffineTransform JavaDoc txf;
180
181     /**
182      * Defines whether the filter performs a fractal noise or a turbulence function
183      */

184     private boolean isFractalNoise;
185
186     /**
187      * List of channels that the generator produces.
188      */

189     private int channels[];
190
191     // To avoid doing an inverse transform on each pixel, transform
192
// the image space unit vectors and process how much of a delta
193
// this is in filter space.
194
double tx[] = {1, 0};
195     double ty[] = {0, 1};
196
197     /**
198      * Produces results in the range [1, 2**31 - 2].
199      * Algorithm is: r = (a * r) mod m
200      * where a = 16807 and m = 2**31 - 1 = 2147483647
201      * See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988
202      * To test: the algorithm should produce the result 1043618065
203      * as the 10,000th generated number if the original seed is 1.
204      */

205     private static final int RAND_m = 2147483647; /* 2**31 - 1 */
206     private static final int RAND_a = 16807; /* 7**5; primitive root of m */
207     private static final int RAND_q = 127773; /* m / a */
208     private static final int RAND_r = 2836; /* m % a */
209
210     private static final int BSize = 0x100;
211     private static final int BM = 0xff;
212     private static final double PerlinN = 0x1000;
213     private static final int NP = 12 /* 2^PerlinN */;
214     private static final int NM = 0xfff;
215     private final int latticeSelector[] = new int[BSize + 1];
216     private final double gradient[] = new double[(BSize+1)*8];
217
218     public double getBaseFrequencyX(){
219         return baseFrequencyX;
220     }
221
222     public double getBaseFrequencyY(){
223         return baseFrequencyY;
224     }
225
226     public int getNumOctaves(){
227         return numOctaves;
228     }
229
230     public int getSeed(){
231         return seed;
232     }
233
234     public Rectangle2D JavaDoc getTile(){
235         return (Rectangle2D JavaDoc)tile.clone();
236     }
237
238     public boolean isFractalNoise(){
239         return isFractalNoise;
240     }
241
242     public boolean[] getChannels(){
243         boolean channels[] = new boolean[4];
244         for(int i=0; i<this.channels.length; i++)
245             channels[this.channels[i]] = true;
246
247         return channels;
248     }
249
250     public final int setupSeed(int seed) {
251         if (seed <= 0) seed = -(seed % (RAND_m - 1)) + 1;
252         if (seed > RAND_m - 1) seed = RAND_m - 1;
253         return seed;
254     }
255
256     public final int random(int seed) {
257       int result = RAND_a * (seed % RAND_q) - RAND_r * (seed / RAND_q);
258         if (result <= 0) result += RAND_m;
259         return result;
260     }
261
262     private void initLattice(int seed) {
263         double u, v, s;
264         int i, j, k, s1, s2;
265         seed = setupSeed(seed);
266
267         for(k = 0; k < 4; k++){
268             for(i = 0; i < BSize; i++){
269                 u = (((seed = random(seed)) % (BSize + BSize)) - BSize);
270                 v = (((seed = random(seed)) % (BSize + BSize)) - BSize);
271                 
272                 s = 1/Math.sqrt(u*u + v*v);
273                 gradient[i*8 + k*2 ] = u*s;
274                 gradient[i*8 + k*2 + 1] = v*s;
275             }
276         }
277
278         for(i = 0; i < BSize; i++)
279             latticeSelector[i] = i;
280
281         while(--i > 0){
282             k = latticeSelector[i];
283             j = (seed = random(seed)) % BSize;
284             latticeSelector[i] = latticeSelector[j];
285             latticeSelector[j] = k;
286             
287             // Now we apply the lattice to the gradient array, this
288
// lets us avoid one of the lattice lookups.
289
s1 = i<<3;
290             s2 = j<<3;
291             for (j=0; j<8; j++) {
292                 s = gradient[s1+j];
293                 gradient[s1+j] = gradient[s2+j];
294                 gradient[s2+j] = s;
295             }
296         }
297         latticeSelector[BSize] = latticeSelector[0];
298         for (j=0; j<8; j++)
299             gradient[(BSize*8)+j] = gradient[j];
300     }
301
302
303     private static final double s_curve(final double t) {
304         return (t * t * (3 - 2 * t) );
305     }
306
307     private static final double lerp(double t, double a, double b) {
308         return ( a + t * (b - a) );
309     }
310
311     /**
312      * Generate a pixel of noise corresponding to the point vec0,vec1.
313      * See class description for a high level discussion of method.
314      * This handles cases where channels <= 4.
315      * @param noise The place to put the generated noise.
316      * @param vec0 The X coordiate to generate noise for
317      * @param vec1 The Y coordiate to generate noise for
318      */

319     private final void noise2(final double noise[], double vec0, double vec1) {
320         int b0, b1;
321         final int i, j;
322         final double rx0, rx1, ry0, ry1, sx, sy;
323
324         vec0 += PerlinN;
325         b0 = ((int)vec0)&BM;
326
327         i = latticeSelector[b0];
328         j = latticeSelector[b0+1];
329
330         rx0 = vec0 - (int)vec0;
331         rx1 = rx0 - 1.0;
332         sx = s_curve(rx0);
333
334         vec1 += PerlinN;
335         b0 = (int)vec1;
336
337         // The gradient array already has the latticeSelector applied
338
// to it, So we can avoid doing the last lookup.
339
b1 = ((j + b0)&BM)<<3;
340         b0 = ((i + b0)&BM)<<3;
341
342         ry0 = vec1 - (int)vec1;
343         ry1 = ry0 - 1.0;
344         sy = s_curve(ry0);
345
346         switch (channels.length) {
347             // Intentionally use 'fall through' in switch statement.
348
case 4:
349             noise[3] =
350                 lerp(sy,
351                      lerp(sx,
352                           rx0*gradient[b0+6] + ry0*gradient[b0+7],
353                           rx1*gradient[b1+6] + ry0*gradient[b1+7]),
354                      lerp(sx,
355                           rx0*gradient[b0+8+6] + ry1*gradient[b0+8+7],
356                           rx1*gradient[b1+8+6] + ry1*gradient[b1+8+7]));
357         case 3:
358             noise[2] =
359                 lerp(sy,
360                      lerp(sx,
361                           rx0*gradient[b0+4] + ry0*gradient[b0+5],
362                           rx1*gradient[b1+4] + ry0*gradient[b1+5]),
363                      lerp(sx,
364                           rx0*gradient[b0+8+4] + ry1*gradient[b0+8+5],
365                           rx1*gradient[b1+8+4] + ry1*gradient[b1+8+5]));
366         case 2:
367             noise[1] =
368                 lerp(sy,
369                      lerp(sx,
370                           rx0*gradient[b0+2] + ry0*gradient[b0+3],
371                           rx1*gradient[b1+2] + ry0*gradient[b1+3]),
372                      lerp(sx,
373                           rx0*gradient[b0+8+2] + ry1*gradient[b0+8+3],
374                           rx1*gradient[b1+8+2] + ry1*gradient[b1+8+3]));
375         case 1:
376             noise[0] =
377                 lerp(sy,
378                      lerp(sx,
379                           rx0*gradient[b0+0] + ry0*gradient[b0+1],
380                           rx1*gradient[b1+0] + ry0*gradient[b1+1]),
381                      lerp(sx,
382                           rx0*gradient[b0+8+0] + ry1*gradient[b0+8+1],
383                           rx1*gradient[b1+8+0] + ry1*gradient[b1+8+1]));
384         }
385     }
386
387     /**
388      * This version of the noise function implements stitching.
389      * If any of the lattice is on the right or bottom edge, the
390      * function uses the the latice on the other side of the
391      * tile, i.e., the left or right edge.
392      * @param noise The place to put the generated noise.
393      * @param vec0 The X coordiate to generate noise for
394      * @param vec1 The Y coordiate to generate noise for
395      * @param stitchInfo The stitching information for the noise function.
396      */

397     private final void noise2Stitch(final double noise[],
398                                     final double vec0, final double vec1,
399                                     final StitchInfo stitchInfo){
400         int b0, b1;
401         final int i, j, b00, b10, b01, b11;
402         double t;
403         final double rx0, rx1, ry0, ry1, sx, sy;
404
405         t = vec0 + PerlinN;
406         b0 = ((int)t);
407         b1 = b0+1;
408         // Stitch lattice tile x coordinates
409
if (b1 >= stitchInfo.wrapX) {
410             if (b0 >= stitchInfo.wrapX) {
411                 b0 -= stitchInfo.width;
412                 b1 -= stitchInfo.width;
413             } else {
414                 b1 -= stitchInfo.width;
415             }
416         }
417         i = latticeSelector[b0&BM];
418         j = latticeSelector[b1&BM];
419
420         rx0 = t - (int)t;
421         rx1 = rx0 - 1.0;
422         sx = s_curve(rx0);
423
424         t = vec1 + PerlinN;
425         b0 = ((int)t);
426         b1 = b0+1;
427         // Stitch lattice tile y coordinates
428
if (b1 >= stitchInfo.wrapY) {
429             if (b0 >= stitchInfo.wrapY) {
430                 b0 -= stitchInfo.height;
431                 b1 -= stitchInfo.height;
432             } else {
433                 b1 -= stitchInfo.height;
434             }
435         }
436         // In this case we still need to keep all four indexes since
437
// we may have split y across the stitch boundry, in which
438
// case b0 and b1 do not have a fixed offset from one another.
439
// We still avoid a latticeSelector lookup for each index though...
440
b00 = ((i + b0)&BM)<<3;
441         b10 = ((j + b0)&BM)<<3;
442         b01 = ((i + b1)&BM)<<3;
443         b11 = ((j + b1)&BM)<<3;
444
445         ry0 = t - (int)t;
446         ry1 = ry0 - 1.0;
447         sy = s_curve(ry0);
448
449         switch (channels.length) {
450             // Intentionally use 'fall through' in switch statement.
451
case 4:
452             noise[3] =
453                 lerp(sy,
454                      lerp(sx,
455                           rx0*gradient[b00+6] + ry0*gradient[b00+7],
456                           rx1*gradient[b10+6] + ry0*gradient[b10+7]),
457                      lerp(sx,
458                           rx0*gradient[b01+6] + ry1*gradient[b01+7],
459                           rx1*gradient[b11+6] + ry1*gradient[b11+7]));
460         case 3:
461             noise[2] =
462                 lerp(sy,
463                      lerp(sx,
464                           rx0*gradient[b00+4] + ry0*gradient[b00+5],
465                           rx1*gradient[b10+4] + ry0*gradient[b10+5]),
466                      lerp(sx,
467                           rx0*gradient[b01+4] + ry1*gradient[b01+5],
468                           rx1*gradient[b11+4] + ry1*gradient[b11+5]));
469         case 2:
470             noise[1] =
471                 lerp(sy,
472                      lerp(sx,
473                           rx0*gradient[b00+2] + ry0*gradient[b00+3],
474                           rx1*gradient[b10+2] + ry0*gradient[b10+3]),
475                      lerp(sx,
476                           rx0*gradient[b01+2] + ry1*gradient[b01+3],
477                           rx1*gradient[b11+2] + ry1*gradient[b11+3]));
478         case 1:
479             noise[0] =
480                 lerp(sy,
481                      lerp(sx,
482                           rx0*gradient[b00+0] + ry0*gradient[b00+1],
483                           rx1*gradient[b10+0] + ry0*gradient[b10+1]),
484                      lerp(sx,
485                           rx0*gradient[b01+0] + ry1*gradient[b01+1],
486                           rx1*gradient[b11+0] + ry1*gradient[b11+1]));
487         }
488     }
489
490     /**
491      * This is the heart of the turbulence calculation. It returns
492      * 'turbFunctionResult', as defined in the spec. This is
493      * special case for 4 bands of output.
494      *
495      * @param point x and y coordinates of the point to process.
496      * @param fSum array used to avoid reallocating double array for each pixel
497      * @return The ARGB pixel value.
498      */

499     private final int turbulence_4(double pointX,
500                                    double pointY,
501                                    final double fSum[]) {
502         double n, ratio = 255;
503         int i, j, b0, b1, nOctave;
504         double px, py, rx0, rx1, ry0, ry1, sx, sy;
505
506         pointX *= baseFrequencyX;
507         pointY *= baseFrequencyY;
508         fSum[0] = fSum[1] = fSum[2] = fSum[3] = 0;
509
510         for (nOctave = numOctaves; nOctave > 0; nOctave--){
511             px = pointX+PerlinN;
512
513             b0 = ((int)px)&BM;
514             i = latticeSelector[b0 ];
515             j = latticeSelector[b0+1];
516
517             rx0 = px - (int)px;
518             rx1 = rx0 - 1.0;
519             sx = s_curve(rx0);
520
521             py = pointY+PerlinN;
522             b0 = ((int)py) & BM;
523             b1 = (b0+1) & BM;
524
525             b1 = ((j + b0)&BM)<<3;
526             b0 = ((i + b0)&BM)<<3;
527
528             ry0 = py - (int)py;
529             ry1 = ry0 - 1.0;
530             sy = s_curve(ry0);
531
532             n = lerp(sy,
533                      lerp(sx,
534                           rx0*gradient[b0+0] + ry0*gradient[b0+1],
535                           rx1*gradient[b1+0] + ry0*gradient[b1+1]),
536                      lerp(sx,
537                           rx0*gradient[b0+8+0] + ry1*gradient[b0+8+1],
538                           rx1*gradient[b1+8+0] + ry1*gradient[b1+8+1]));
539
540             if (n<0) fSum[0] -= (n * ratio);
541             else fSum[0] += (n * ratio);
542
543             n = lerp(sy,
544                      lerp(sx,
545                           rx0*gradient[b0+2] + ry0*gradient[b0+3],
546                           rx1*gradient[b1+2] + ry0*gradient[b1+3]),
547                      lerp(sx,
548                           rx0*gradient[b0+8+2] + ry1*gradient[b0+8+3],
549                           rx1*gradient[b1+8+2] + ry1*gradient[b1+8+3]));
550
551             if (n<0) fSum[1] -= (n * ratio);
552             else fSum[1] += (n * ratio);
553
554             n = lerp(sy,
555                      lerp(sx,
556                           rx0*gradient[b0+4] + ry0*gradient[b0+5],
557                           rx1*gradient[b1+4] + ry0*gradient[b1+5]),
558                      lerp(sx,
559                           rx0*gradient[b0+8+4] + ry1*gradient[b0+8+5],
560                           rx1*gradient[b1+8+4] + ry1*gradient[b1+8+5]));
561
562             if (n<0) fSum[2] -= (n * ratio);
563             else fSum[2] += (n * ratio);
564
565             n = lerp(sy,
566                      lerp(sx,
567                           rx0*gradient[b0+6] + ry0*gradient[b0+7],
568                           rx1*gradient[b1+6] + ry0*gradient[b1+7]),
569                      lerp(sx,
570                           rx0*gradient[b0+8+6] + ry1*gradient[b0+8+7],
571                           rx1*gradient[b1+8+6] + ry1*gradient[b1+8+7]));
572             if (n<0) fSum[3] -= (n * ratio);
573             else fSum[3] += (n * ratio);
574
575             ratio *= .5;
576             pointX *= 2;
577             pointY *= 2;
578         }
579
580         i = (int)fSum[0];
581         if ((i & 0xFFFFFF00) == 0) j = i<<16;
582         else j = ((i & 0x80000000) != 0)?0:0xFF0000;
583
584         i = (int)fSum[1];
585         if ((i & 0xFFFFFF00) == 0) j |= i<<8;
586         else j |= ((i & 0x80000000) != 0)?0:0xFF00;
587
588         i = (int)fSum[2];
589         if ((i & 0xFFFFFF00) == 0) j |= i;
590         else j |= ((i & 0x80000000) != 0)?0:0xFF;
591
592         i = (int)fSum[3];
593         if ((i & 0xFFFFFF00) == 0) j |= i<<24;
594         else j |= ((i & 0x80000000) != 0)?0:0xFF000000;
595         return j;
596     }
597
598
599     /**
600      * This is the heart of the turbulence calculation. It returns
601      * 'turbFunctionResult', as defined in the spec.
602      * @param rgb array for the four color components
603      * @param point x and y coordinates of the point to process.
604      * @param fSum array used to avoid reallocating double array for each pixel
605      * @param noise array used to avoid reallocating double array for
606      * each pixel
607      */

608     private final void turbulence(final int rgb[],
609                                   double pointX,
610                                   double pointY,
611                                   final double fSum[],
612                                   final double noise[]) {
613         fSum[0] = fSum[1] = fSum[2] = fSum[3] = 0;
614         double ratio = 255;
615         pointX *= baseFrequencyX;
616         pointY *= baseFrequencyY;
617         switch (channels.length) {
618         case 4:
619             for(int nOctave = 0; nOctave < numOctaves; nOctave++){
620                 noise2(noise, pointX, pointY);
621
622                 if (noise[0]<0) fSum[0] -= (noise[0] * ratio);
623                 else fSum[0] += (noise[0] * ratio);
624                 if (noise[1]<0) fSum[1] -= (noise[1] * ratio);
625                 else fSum[1] += (noise[1] * ratio);
626                 if (noise[2]<0) fSum[2] -= (noise[2] * ratio);
627                 else fSum[2] += (noise[2] * ratio);
628                 if (noise[3]<0) fSum[3] -= (noise[3] * ratio);
629                 else fSum[3] += (noise[3] * ratio);
630                 ratio *= .5;
631                 pointX *= 2;
632                 pointY *= 2;
633             }
634
635             rgb[0] = (int)fSum[0];
636             if ((rgb[0] & 0xFFFFFF00) != 0)
637                 rgb[0] = ((rgb[0] & 0x80000000) != 0)?0:255;
638             rgb[1] = (int)fSum[1];
639             if ((rgb[1] & 0xFFFFFF00) != 0)
640                 rgb[1] = ((rgb[1] & 0x80000000) != 0)?0:255;
641             rgb[2] = (int)fSum[2];
642             if ((rgb[2] & 0xFFFFFF00) != 0)
643                 rgb[2] = ((rgb[2] & 0x80000000) != 0)?0:255;
644             rgb[3] = (int)fSum[3];
645             if ((rgb[3] & 0xFFFFFF00) != 0)
646                 rgb[3] = ((rgb[3] & 0x80000000) != 0)?0:255;
647             break;
648         case 3:
649             for(int nOctave = 0; nOctave < numOctaves; nOctave++){
650                 noise2(noise, pointX, pointY);
651
652                 if (noise[2]<0) fSum[2] -= (noise[2] * ratio);
653                 else fSum[2] += (noise[2] * ratio);
654                 if (noise[1]<0) fSum[1] -= (noise[1] * ratio);
655                 else fSum[1] += (noise[1] * ratio);
656                 if (noise[0]<0) fSum[0] -= (noise[0] * ratio);
657                 else fSum[0] += (noise[0] * ratio);
658                 ratio *= .5;
659                 pointX *= 2;
660                 pointY *= 2;
661             }
662             rgb[2] = (int)fSum[2];
663             if ((rgb[2] & 0xFFFFFF00) != 0)
664                 rgb[2] = ((rgb[2] & 0x80000000) != 0)?0:255;
665             rgb[1] = (int)fSum[1];
666             if ((rgb[1] & 0xFFFFFF00) != 0)
667                 rgb[1] = ((rgb[1] & 0x80000000) != 0)?0:255;
668             rgb[0] = (int)fSum[0];
669             if ((rgb[0] & 0xFFFFFF00) != 0)
670                 rgb[0] = ((rgb[0] & 0x80000000) != 0)?0:255;
671             break;
672         case 2:
673             for(int nOctave = 0; nOctave < numOctaves; nOctave++){
674                 noise2(noise, pointX, pointY);
675
676                 if (noise[1]<0) fSum[1] -= (noise[1] * ratio);
677                 else fSum[1] += (noise[1] * ratio);
678                 if (noise[0]<0) fSum[0] -= (noise[0] * ratio);
679                 else fSum[0] += (noise[0] * ratio);
680                 ratio *= .5;
681                 pointX *= 2;
682                 pointY *= 2;
683             }
684
685             rgb[1] = (int)fSum[1];
686             if ((rgb[1] & 0xFFFFFF00) != 0)
687                 rgb[1] = ((rgb[1] & 0x80000000) != 0)?0:255;
688             rgb[0] = (int)fSum[0];
689             if ((rgb[0] & 0xFFFFFF00) != 0)
690                 rgb[0] = ((rgb[0] & 0x80000000) != 0)?0:255;
691             break;
692         case 1:
693             for(int nOctave = 0; nOctave < numOctaves; nOctave++){
694                 noise2(noise, pointX, pointY);
695
696                 if (noise[0]<0) fSum[0] -= (noise[0] * ratio);
697                 else fSum[0] += (noise[0] * ratio);
698                 ratio *= .5;
699                 pointX *= 2;
700                 pointY *= 2;
701             }
702
703             rgb[0] = (int)fSum[0];
704             if ((rgb[0] & 0xFFFFFF00) != 0)
705                 rgb[0] = ((rgb[0] & 0x80000000) != 0)?0:255;
706             break;
707         }
708     }
709
710     /**
711      * This is the heart of the turbulence calculation. It returns
712      * 'turbFunctionResult', as defined in the spec.
713      * @param rgb array for the four color components
714      * @param point x and y coordinates of the point to process.
715      * @param fSum array used to avoid reallocating double array for each pixel
716      * @param noise array used to avoid reallocating double array for
717      * each pixel
718      * @param stitchInfo The stitching information for the noise function
719      */

720     private final void turbulenceStitch(final int rgb[],
721                                         double pointX, double pointY,
722                                         final double fSum[],
723                                         final double noise[],
724                                         StitchInfo stitchInfo){
725         double ratio = 1;
726         pointX *= baseFrequencyX;
727         pointY *= baseFrequencyY;
728         fSum[0] = fSum[1] = fSum[2] = fSum[3] = 0;
729         switch (channels.length) {
730         case 4:
731             for(int nOctave = 0; nOctave < numOctaves; nOctave++){
732                 noise2Stitch(noise, pointX, pointY, stitchInfo);
733
734                 if (noise[3]<0) fSum[3] -= (noise[3] * ratio);
735                 else fSum[3] += (noise[3] * ratio);
736                 if (noise[2]<0) fSum[2] -= (noise[2] * ratio);
737                 else fSum[2] += (noise[2] * ratio);
738                 if (noise[1]<0) fSum[1] -= (noise[1] * ratio);
739                 else fSum[1] += (noise[1] * ratio);
740                 if (noise[0]<0) fSum[0] -= (noise[0] * ratio);
741                 else fSum[0] += (noise[0] * ratio);
742                 ratio *= .5;
743                 pointX *= 2;
744                 pointY *= 2;
745
746                 stitchInfo.doubleFrequency();
747             }
748             rgb[3] = (int)(fSum[3] * 255);
749             if ((rgb[3] & 0xFFFFFF00) != 0)
750                 rgb[3] = ((rgb[3] & 0x80000000) != 0)?0:255;
751             rgb[2] = (int)(fSum[2] * 255);
752