Source code: org/apache/batik/ext/awt/image/rendered/TurbulencePatternRed.java
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;
22 import java.awt.color.ColorSpace;
23 import java.awt.geom.AffineTransform;
24 import java.awt.geom.Rectangle2D;
25 import java.awt.image.ColorModel;
26 import java.awt.image.DataBuffer;
27 import java.awt.image.DataBufferInt;
28 import java.awt.image.DirectColorModel;
29 import java.awt.image.SinglePixelPackedSampleModel;
30 import java.awt.image.WritableRaster;
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 IDENTITY = new AffineTransform();
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 tile;
175
176 /**
177 * Defines the tile for the turbulence function
178 */
179 private AffineTransform 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 getTile(){
235 return (Rectangle2D)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 if ((rgb[2] & 0xFFFFFF00) != 0)
753 rgb[2] = ((rgb[2] & 0x80000000) != 0)?0:255;
754 rgb[1] = (int)(fSum[1] * 255);
755 if ((rgb[1] & 0xFFFFFF00) != 0)
756 rgb[1] = ((rgb[1] & 0x80000000) != 0)?0:255;
757 rgb[0] = (int)(fSum[0] * 255);
758 if ((rgb[0] & 0xFFFFFF00) != 0)
759 rgb[0] = ((rgb[0] & 0x80000000) != 0)?0:255;
760 break;
761 case 3:
762 for(int nOctave = 0; nOctave < numOctaves; nOctave++){
763 noise2Stitch(noise, pointX, pointY, stitchInfo);
764 if (noise[2]<0) fSum[2] -= (noise[2] * ratio);
765 else fSum[2] += (noise[2] * ratio);
766 if (noise[1]<0) fSum[1] -= (noise[1] * ratio);
767 else fSum[1] += (noise[1] * ratio);
768 if (noise[0]<0) fSum[0] -= (noise[0] * ratio);
769 else fSum[0] += (noise[0] * ratio);
770 ratio *= .5;
771 pointX *= 2;
772 pointY *= 2;
773
774 stitchInfo.doubleFrequency();
775 }
776 rgb[2] = (int)(fSum[2] * 255);
777 if ((rgb[2] & 0xFFFFFF00) != 0)
778 rgb[2] = ((rgb[2] & 0x80000000) != 0)?0:255;
779 rgb[1] = (int)(fSum[1] * 255);
780 if ((rgb[1] & 0xFFFFFF00) != 0)
781 rgb[1] = ((rgb[1] & 0x80000000) != 0)?0:255;
782 rgb[0] = (int)(fSum[0] * 255);
783 if ((rgb[0] & 0xFFFFFF00) != 0)
784 rgb[0] = ((rgb[0] & 0x80000000) != 0)?0:255;
785 break;
786 case 2:
787 for(int nOctave = 0; nOctave < numOctaves; nOctave++){
788 noise2Stitch(noise, pointX, pointY, stitchInfo);
789 if (noise[1]<0) fSum[1] -= (noise[1] * ratio);
790 else fSum[1] += (noise[1] * ratio);
791 if (noise[0]<0) fSum[0] -= (noise[0] * ratio);
792 else fSum[0] += (noise[0] * ratio);
793 ratio *= .5;
794 pointX *= 2;
795 pointY *= 2;
796
797 stitchInfo.doubleFrequency();
798 }
799 rgb[1] = (int)(fSum[1] * 255);
800 if ((rgb[1] & 0xFFFFFF00) != 0)
801 rgb[1] = ((rgb[1] & 0x80000000) != 0)?0:255;
802 rgb[0] = (int)(fSum[0] * 255);
803 if ((rgb[0] & 0xFFFFFF00) != 0)
804 rgb[0] = ((rgb[0] & 0x80000000) != 0)?0:255;
805 break;
806 case 1:
807 for(int nOctave = 0; nOctave < numOctaves; nOctave++){
808 noise2Stitch(noise, pointX, pointY, stitchInfo);
809 if (noise[0]<0) fSum[0] -= (noise[0] * ratio);
810 else fSum[0] += (noise[0] * ratio);
811 ratio *= .5;
812 pointX *= 2;
813 pointY *= 2;
814
815 stitchInfo.doubleFrequency();
816 }
817 rgb[0] = (int)(fSum[0] * 255);
818 if ((rgb[0] & 0xFFFFFF00) != 0)
819 rgb[0] = ((rgb[0] & 0x80000000) != 0)?0:255;
820 break;
821 }
822 }
823
824 /**
825 * This is the heart of the turbulence calculation. It returns
826 * 'turbFunctionResult', as defined in the spec. This handles the
827 * case where we are generating 4 channels of noise.
828 * @param point x and y coordinates of the point to process.
829 * @param fSum array used to avoid reallocating double array for each pixel
830 * @return The ARGB pixel
831 */
832 private final int turbulenceFractal_4( double pointX,
833 double pointY,
834 final double fSum[]) {
835 int b0, b1, nOctave, i, j;
836 double px, py, rx0, rx1, ry0, ry1, sx, sy, ratio = 127.5;
837
838 pointX *= baseFrequencyX;
839 pointY *= baseFrequencyY;
840 fSum[0] = fSum[1] = fSum[2] = fSum[3] = 127.5;
841
842 for (nOctave = numOctaves; nOctave > 0; nOctave--){
843 px = pointX+PerlinN;
844
845 b0 = ((int)px)&BM;
846 i = latticeSelector[b0 ];
847 j = latticeSelector[b0+1];
848
849 rx0 = px - (int)px;
850 rx1 = rx0 - 1.0;
851 sx = s_curve(rx0);
852
853 py = pointY+PerlinN;
854 b0 = ((int)py) & BM;
855 b1 = (b0+1) & BM;
856
857 b1 = ((j + b0)&BM)<<3;
858 b0 = ((i + b0)&BM)<<3;
859
860 ry0 = py - (int)py;
861 ry1 = ry0 - 1.0;
862 sy = s_curve(ry0);
863
864 fSum[0] += lerp(sy,
865 lerp(sx,
866 rx0*gradient[b0+0] + ry0*gradient[b0+1],
867 rx1*gradient[b1+0] + ry0*gradient[b1+1]),
868 lerp(sx,
869 rx0*gradient[b0+8+0] + ry1*gradient[b0+8+1],
870 rx1*gradient[b1+8+0] + ry1*gradient[b1+8+1]))*ratio;
871
872 fSum[1] += lerp(sy,
873 lerp(sx,
874 rx0*gradient[b0+2] + ry0*gradient[b0+3],
875 rx1*gradient[b1+2] + ry0*gradient[b1+3]),
876 lerp(sx,
877 rx0*gradient[b0+8+2] + ry1*gradient[b0+8+3],
878 rx1*gradient[b1+8+2] + ry1*gradient[b1+8+3]))*ratio;
879
880 fSum[2] += lerp(sy,
881 lerp(sx,
882 rx0*gradient[b0+4] + ry0*gradient[b0+5],
883 rx1*gradient[b1+4] + ry0*gradient[b1+5]),
884 lerp(sx,
885 rx0*gradient[b0+8+4] + ry1*gradient[b0+8+5],
886 rx1*gradient[b1+8+4] + ry1*gradient[b1+8+5]))*ratio;
887
888 fSum[3] += lerp(sy,
889 lerp(sx,
890 rx0*gradient[b0+6] + ry0*gradient[b0+7],
891 rx1*gradient[b1+6] + ry0*gradient[b1+7]),
892 lerp(sx,
893 rx0*gradient[b0+8+6] + ry1*gradient[b0+8+7],
894 rx1*gradient[b1+8+6] + ry1*gradient[b1+8+7]))*ratio;
895
896 ratio *= .5;
897 pointX *= 2;
898 pointY *= 2;
899 }
900
901 i = (int)fSum[0];
902 if ((i & 0xFFFFFF00) == 0) j = i<<16;
903 else j = ((i & 0x80000000) != 0)?0:0xFF0000;
904
905 i = (int)fSum[1];
906 if ((i & 0xFFFFFF00) == 0) j |= i<<8;
907 else j |= ((i & 0x80000000) != 0)?0:0xFF00;
908
909 i = (int)fSum[2];
910 if ((i & 0xFFFFFF00) == 0) j |= i;
911 else j |= ((i & 0x80000000) != 0)?0:0xFF;
912
913 i = (int)fSum[3];
914 if ((i & 0xFFFFFF00) == 0) j |= i<<24;
915 else j |= ((i & 0x80000000) != 0)?0:0xFF000000;
916 return j;
917 }
918
919 /**
920 * This is the heart of the turbulence calculation. It returns
921 * 'turbFunctionResult', as defined in the spec.
922 * @param rgb array for the four color components
923 * @param point x and y coordinates of the point to process.
924 * @param fSum array used to avoid reallocating double array for each pixel
925 * @param noise array used to avoid reallocating double array for
926 * each pixel
927 */
928 private final void turbulenceFractal(final int rgb[],
929 double pointX,
930 double pointY,
931 final double fSum[],
932 final double noise[]){
933 double ratio = 127.5;
934 int nOctave;
935 fSum[0] = fSum[1] = fSum[2] = fSum[3] = 127.5;
936 pointX *= baseFrequencyX;
937 pointY *= baseFrequencyY;
938 for(nOctave = numOctaves; nOctave > 0; nOctave--){
939 noise2(noise, pointX, pointY);
940
941 switch (channels.length) {
942 case 4:
943 fSum[3] += (noise[3] * ratio);
944 case 3:
945 fSum[2] += (noise[2] * ratio);
946 case 2:
947 fSum[1] += (noise[1] * ratio);
948 case 1:
949 fSum[0] += (noise[0] * ratio);
950 }
951
952 ratio *= .5;
953 pointX *= 2;
954 pointY *= 2;
955 }
956
957 switch (channels.length) {
958 case 4:
959 rgb[3] = (int)fSum[3];
960 if ((rgb[3] & 0xFFFFFF00) != 0)
961 rgb[3] = ((rgb[3] & 0x80000000) != 0)?0:255;
962 case 3:
963 rgb[2] = (int)fSum[2];
964 if ((rgb[2] & 0xFFFFFF00) != 0)
965 rgb[2] = ((rgb[2] & 0x80000000) != 0)?0:255;
966 case 2:
967 rgb[1] = (int)fSum[1];
968 if ((rgb[1] & 0xFFFFFF00) != 0)
969 rgb[1] = ((rgb[1] & 0x80000000) != 0)?0:255;
970 case 1:
971 rgb[0] = (int)fSum[0];
972 if ((rgb[0] & 0xFFFFFF00) != 0)
973 rgb[0] = ((rgb[0] & 0x80000000) != 0)?0:255;
974 }
975 }
976
977 /**
978 * This is the heart of the turbulence calculation. It returns
979 * 'turbFunctionResult', as defined in the spec.
980 * @param rgb array for the four color components
981 * @param point x and y coordinates of the point to process.
982 * @param fSum array used to avoid reallocating double array for each pixel
983 * @param noise array used to avoid reallocating double array for
984 * each pixel
985 * @param stitchInfo The stitching information for the noise function
986 */
987 private final void turbulenceFractalStitch(final int rgb[],
988 double pointX,
989 double pointY,
990 final double fSum[],
991 final double noise[],
992 StitchInfo stitchInfo){
993 double ratio = 127.5;
994 int nOctave;
995 fSum[0] = fSum[1] = fSum[2] = fSum[3] = 127.5;
996 pointX *= baseFrequencyX;
997 pointY *= baseFrequencyY;
998 for(nOctave = numOctaves; nOctave > 0; nOctave--){
999 noise2Stitch(noise, pointX, pointY, stitchInfo);
1000
1001 switch (channels.length) {
1002 case 4:
1003 fSum[3] += (noise[3] * ratio);
1004 case 3:
1005 fSum[2] += (noise[2] * ratio);
1006 case 2:
1007 fSum[1] += (noise[1] * ratio);
1008 case 1:
1009 fSum[0] += (noise[0] * ratio);
1010 }
1011
1012 ratio *= .5;
1013 pointX *= 2;
1014 pointY *= 2;
1015 stitchInfo.doubleFrequency();
1016 }
1017
1018 switch (channels.length) {
1019 case 4:
1020 rgb[3] = (int)fSum[3];
1021 if ((rgb[3] & 0xFFFFFF00) != 0)
1022 rgb[3] = ((rgb[3] & 0x80000000) != 0)?0:255;
1023 case 3:
1024 rgb[2] = (int)fSum[2];
1025 if ((rgb[2] & 0xFFFFFF00) != 0)
1026 rgb[2] = ((rgb[2] & 0x80000000) != 0)?0:255;
1027 case 2:
1028 rgb[1] = (int)fSum[1];
1029 if ((rgb[1] & 0xFFFFFF00) != 0)
1030 rgb[1] = ((rgb[1] & 0x80000000) != 0)?0:255;
1031 case 1:
1032 rgb[0] = (int)fSum[0];
1033 if ((rgb[0] & 0xFFFFFF00) != 0)
1034 rgb[0] = ((rgb[0] & 0x80000000) != 0)?0:255;
1035 }
1036 }
1037
1038 /**
1039 * Generates a Perlin noise pattern into dest Raster.
1040 * @param dest Raster to fill with the pattern.
1041 */
1042 public WritableRaster copyData(WritableRaster dest) {
1043 //
1044 // First, check input arguments
1045 //
1046 if(dest==null)
1047 throw new IllegalArgumentException
1048 ("Cannot generate a noise pattern into a null raster");
1049
1050
1051 int w = dest.getWidth();
1052 int h = dest.getHeight();
1053
1054 // Access the integer buffer for the destination Raster
1055 DataBufferInt dstDB = (DataBufferInt)dest.getDataBuffer();
1056 SinglePixelPackedSampleModel sppsm;
1057 int minX = dest.getMinX();
1058 int minY = dest.getMinY();
1059 sppsm = (SinglePixelPackedSampleModel)dest.getSampleModel();
1060 int dstOff = dstDB.getOffset() +
1061 sppsm.getOffset(minX - dest.getSampleModelTranslateX(),
1062 minY - dest.getSampleModelTranslateY());
1063
1064 final int destPixels[] = dstDB.getBankData()[0];
1065 int dstAdjust = sppsm.getScanlineStride() - w;
1066
1067 // Generate pixel pattern now
1068 int i, end, dp=dstOff;
1069 final int rgb[] = new int[4];
1070 final double fSum[] = {0, 0, 0, 0};
1071 final double noise[] = {0, 0, 0, 0};
1072
1073 final double tx0, tx1, ty0, ty1;
1074 tx0 = tx[0];
1075 tx1 = tx[1];
1076 // Update for y step, (note we substract all the stuff we
1077 // added while going across the scan line).
1078 ty0 = ty[0]-(w*tx0);
1079 ty1 = ty[1]-(w*tx1);
1080
1081 double p[] = {minX, minY};
1082 txf.transform(p, 0, p, 0, 1);
1083 double point_0 = p[0];
1084 double point_1 = p[1];
1085
1086 if(isFractalNoise){
1087 if(stitchInfo == null){
1088 if (channels.length == 4) {
1089 for(i=0; i<h; i++){
1090 for(end=dp+w; dp<end; dp++) {
1091 destPixels[dp] = turbulenceFractal_4
1092 (point_0, point_1, fSum);
1093 point_0 += tx0;
1094 point_1 += tx1;
1095 }
1096 point_0 += ty0;
1097 point_1 += ty1;
1098 dp += dstAdjust;
1099 }
1100 } else {
1101 for(i=0; i<h; i++){
1102 for(end=dp+w; dp<end; dp++){
1103 turbulenceFractal(rgb, point_0, point_1, fSum, noise);
1104
1105 // Write RGB value.
1106 destPixels[dp] = ((rgb[3]<<24) |
1107 (rgb[0]<<16) |
1108 (rgb[1]<<8) |
1109 (rgb[2] ));
1110 point_0 += tx0;
1111 point_1 += tx1;
1112 }
1113 point_0 += ty0;
1114 point_1 += ty1;
1115 dp += dstAdjust;
1116 }
1117 }
1118 }
1119 else{
1120 StitchInfo si = new StitchInfo();
1121 for(i=0; i<h; i++){
1122 for(end=dp+w; dp<end; dp++){
1123 si.assign(this.stitchInfo);
1124 turbulenceFractalStitch(rgb, point_0, point_1,
1125 fSum, noise, si);
1126
1127 // Write RGB value.
1128 destPixels[dp] = ((rgb[3]<<24) |
1129 (rgb[0]<<16) |
1130 (rgb[1]<<8) |
1131 (rgb[2] ));
1132 point_0 += tx0;
1133 point_1 += tx1;
1134 }
1135 point_0 += ty0;
1136 point_1 += ty1;
1137 dp += dstAdjust;
1138 }
1139 }
1140 }
1141 else{ // Loop for turbulence noise
1142 if(stitchInfo == null){
1143 if (channels.length == 4) {
1144 for(i=0; i<h; i++){
1145 for(end=dp+w; dp<end; dp++){
1146 destPixels[dp] = turbulence_4
1147 (point_0, point_1, fSum);
1148
1149 point_0 += tx0;
1150 point_1 += tx1;
1151 }
1152 point_0 += ty0;
1153 point_1 += ty1;
1154 dp += dstAdjust;
1155 }
1156 } else {
1157 for(i=0; i<h; i++){
1158 for(end=dp+w; dp<end; dp++){
1159 turbulence(rgb, point_0, point_1, fSum, noise);
1160
1161 // Write RGB value.
1162 destPixels[dp] = ((rgb[3]<<24) |
1163 (rgb[0]<<16) |
1164 (rgb[1]<<8) |
1165 (rgb[2] ));
1166 point_0 += tx0;
1167 point_1 += tx1;
1168 }
1169 point_0 += ty0;
1170 point_1 += ty1;
1171 dp += dstAdjust;
1172 }
1173 }
1174 }
1175 else{
1176 StitchInfo si = new StitchInfo();
1177 for(i=0; i<h; i++){
1178 for(end=dp+w; dp<end; dp++){
1179 si.assign(this.stitchInfo);
1180 turbulenceStitch(rgb, point_0, point_1,
1181 fSum, noise, si);
1182
1183 // Write RGB value.
1184 destPixels[dp] = ((rgb[3]<<24) |
1185 (rgb[0]<<16) |
1186 (rgb[1]<<8) |
1187 (rgb[2] ));
1188 point_0 += tx0;
1189 point_1 += tx1;
1190 }
1191 point_0 += ty0;
1192 point_1 += ty1;
1193 dp += dstAdjust;
1194 }
1195 }
1196 }
1197
1198 return dest;
1199 }
1200
1201 /**
1202 * @param baseFrequencyX x-axis base frequency for the noise
1203 * function along the x-axis
1204 * @param baseFrequencyY y-axis base frequency for the noise
1205 * function along the x-axis
1206 * @param numOctaves number of octaves in the noise
1207 * function. Positive integral value.
1208 * @param seed starting number for the pseudo random number generator
1209 * @param isFractalNoise defines whether the filter performs a
1210 * fractal noise or a turbulence function.
1211 * @param tile defines the tile size. May be null if stitchTiles
1212 * is false. Otherwise, should not be null.
1213 * @param txf The affine transform from device to user space.
1214 * @param cs The Colorspace to output.
1215 * @param alpha True if the data should have an alpha channel.
1216 */
1217 public TurbulencePatternRed(double baseFrequencyX,
1218 double baseFrequencyY,
1219 int numOctaves,
1220 int seed,
1221 boolean isFractalNoise,
1222 Rectangle2D tile,
1223 AffineTransform txf,
1224 Rectangle devRect,
1225 ColorSpace cs,
1226 boolean alpha) {
1227 this.baseFrequencyX = baseFrequencyX;
1228 this.baseFrequencyY = baseFrequencyY;
1229 this.seed = seed;
1230 this.isFractalNoise = isFractalNoise;
1231 this.tile = tile;
1232 this.txf = txf;
1233
1234 if(this.txf == null)
1235 this.txf = IDENTITY;
1236
1237 int nChannels = cs.getNumComponents();
1238 if (alpha) nChannels++;
1239 channels = new int[nChannels];
1240 for(int i=0; i<channels.length; i++)
1241 channels[i] = i;
1242
1243 txf.deltaTransform(tx, 0, tx, 0, 1);
1244 txf.deltaTransform(ty, 0, ty, 0, 1);
1245
1246 double vecX[] = {.5, 0};
1247 double vecY[] = {0, .5};
1248 txf.deltaTransform(vecX, 0, vecX, 0, 1);
1249 txf.deltaTransform(vecY, 0, vecY, 0, 1);
1250
1251 //
1252 // Now, limit the number of octaves so that we do not get frequencies
1253 // below half a pixel.
1254 //
1255 // If d is the distance between to pixels in user space, then,
1256 // numOctavesMax = -(log2(d) + log2(bf))
1257 // along one axis.
1258 //
1259 // The maximum distance along each axis is processed by
1260 // computing the inverse transform of 'maximum' vectors from
1261 // device space to the filter space and determining the
1262 // maximum component along each axis.
1263
1264 double dx = Math.max(Math.abs(vecX[0]), Math.abs(vecY[0]));
1265 int maxX = -(int)Math.round((Math.log(dx) + Math.log(baseFrequencyX))/
1266 Math.log(2));
1267
1268 double dy = Math.max(Math.abs(vecX[1]), Math.abs(vecY[1]));
1269 int maxY = -(int)Math.round((Math.log(dy) + Math.log(baseFrequencyY))/
1270 Math.log(2));
1271
1272 this.numOctaves = numOctaves > maxX? maxX : numOctaves;
1273 this.numOctaves = this.numOctaves > maxY? maxY : this.numOctaves;
1274
1275 if(this.numOctaves < 1 && numOctaves > 1)
1276 this.numOctaves = 1;
1277
1278 if (this.numOctaves > 8)
1279 // beyond 8 octaves there is no significant contribution
1280 // to the output pixel (contribution is halved for each
1281 // octave so after 8 we are contributing less than half a
1282 // code value _at_best_).
1283 this.numOctaves = 8;
1284
1285 if (tile != null) {
1286 //
1287 // Adjust frequencies to the tile size
1288 //
1289 double lowFreq = Math.floor(tile.getWidth()*baseFrequencyX)/tile.getWidth();
1290 double highFreq = Math.ceil(tile.getWidth()*baseFrequencyX)/tile.getWidth();
1291 if(baseFrequencyX/lowFreq < highFreq/baseFrequencyX)
1292 this.baseFrequencyX = lowFreq;
1293 else
1294 this.baseFrequencyX = highFreq;
1295
1296 lowFreq = Math.floor(tile.getHeight()*baseFrequencyY)/tile.getHeight();
1297 highFreq = Math.ceil(tile.getHeight()*baseFrequencyY)/tile.getHeight();
1298 if(baseFrequencyY/lowFreq < highFreq/baseFrequencyY)
1299 this.baseFrequencyY = lowFreq;
1300 else
1301 this.baseFrequencyY = highFreq;
1302
1303 //
1304 // Now, process the initial latice grid size to compute the minimum
1305 // and maximum latice values on each axis.
1306 //
1307 stitchInfo = new StitchInfo();
1308 stitchInfo.width = ((int)(tile.getWidth()*this.baseFrequencyX));
1309 stitchInfo.height = ((int)(tile.getHeight()*this.baseFrequencyY));
1310 stitchInfo.wrapX = ((int)(tile.getX()*this.baseFrequencyX +
1311 PerlinN + stitchInfo.width));
1312 stitchInfo.wrapY = ((int)(tile.getY()*this.baseFrequencyY +
1313 PerlinN + stitchInfo.height));
1314
1315 // Protect agains zero frequencies. Setting values to 1
1316 // will not affect the result of the computations.
1317 if(stitchInfo.width == 0) stitchInfo.width = 1;
1318 if(stitchInfo.height == 0) stitchInfo.height = 1;
1319
1320 // System.out.println( "minLatticeX = " + minLatticeX +
1321 // " minLatticeY = " + minLatticeY +
1322 // " maxLatticeX = " + maxLatticeX +
1323 // " maxLatticeY = " + maxLatticeY);
1324 }
1325
1326 initLattice(seed);
1327
1328 ColorModel cm;
1329 if (alpha)
1330 cm = new DirectColorModel
1331 (cs, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000,
1332 false, DataBuffer.TYPE_INT);
1333 else
1334 cm = new DirectColorModel
1335 (cs, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x0,
1336 false, DataBuffer.TYPE_INT);
1337
1338 int tileSize = AbstractTiledRed.getDefaultTileSize();
1339 init((CachableRed)null, devRect, cm,
1340 cm.createCompatibleSampleModel(tileSize, tileSize),
1341 0, 0, null);
1342 }
1343
1344}