deweese 01/08/01 13:23:20 Modified: sources/org/apache/batik/ext/awt/image/rendered TurbulencePatternRed8Bit.java Log: Updated javadocs and added my deduction of how the Perlin Noise function works, in the hope that someone might be able to come up with a more efficent means to implement all or part of it... Revision Changes Path 1.2 +82 -118 xml-batik/sources/org/apache/batik/ext/awt/image/rendered/TurbulencePatternRed8Bit.java Index: TurbulencePatternRed8Bit.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/ext/awt/image/rendered/TurbulencePatternRed8Bit.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- TurbulencePatternRed8Bit.java 2001/08/01 19:46:20 1.1 +++ TurbulencePatternRed8Bit.java 2001/08/01 20:23:20 1.2 @@ -23,14 +23,37 @@ import java.awt.image.DirectColorModel; import java.awt.image.ColorModel; /** - * This class creates a noise pattern conform to the one defined for - * the feTurbulence filter of the SVG specification. It can be used by - * classes implementing specific interfaces, such as the TurbulenceOp - * and TurbulencePaintContext classes. + * This class creates a RenderedImage in conformance to the one + * defined for the feTurbulence filter of the SVG specification. What + * follows is my high-level description of how the noise is generated. + * This is not contained in the SVG spec, just the algorithm for + * doing it. This is provided in the hope that someone will figure + * out a clever way to accelerate parts of the function. * + * gradient contains a long list of random unit vectors. For each + * point we are to generate noise for we do two things. first we use + * the latticeSelector to 'co-mingle' the integer portions of x and y + * (this allows us to have a one-dimensional array of gradients that + * appears 2 dimensional, by using the co-mingled index). + * + * We do this for [x,y], [x+1,y], [x,y+1], and [x+1, y+1], this gives + * us the four gradient vectors that surround the point (b00, b10, ...) + * + * Next we construct the four vectors from the grid points (where the + * gradient vectors are defined) [these are rx0, rx1, ry0, ry1]. + * + * We then take the dot product between the gradient vectors and the + * grid point vectors (this gives the portion of the grid point vector + * that projects along the gradient vector for each grid point). + * These four dot projects are then combined with linear interpolation. + * The weight factor for the linear combination is the result of applying + * the 's' curve function to the fractional part of x and y (rx0, ry0). + * The S curve function get's it's name because it looks a bit like as + * 'S' from 0->1. + * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: TurbulencePatternRed8Bit.java,v 1.1 2001/08/01 19:46:20 deweese Exp $ - */ + * @author <a href="mailto:[EMAIL PROTECTED]">Thomas DeWeese</a> + * @version $Id: TurbulencePatternRed8Bit.java,v 1.2 2001/08/01 20:23:20 deweese Exp $ */ public final class TurbulencePatternRed8Bit extends AbstractRed { /** * Inner class to store tile stitching info. @@ -268,74 +291,15 @@ private static final double lerp(double t, double a, double b) { return ( a + t * (b - a) ); } - - private final void noise2_4(final double noise[], - double vec0, - double vec1){ - int b0, b1; - final int i, j, b00, b10, b01, b11; - final double rx0, rx1, ry0, ry1, sx, sy; - vec0 += PerlinN; - b0 = (int)vec0; - - i = latticeSelector[b0 & BM]; - j = latticeSelector[(b0+1) & BM]; - rx0 = vec0 - (int)vec0; - rx1 = rx0 - 1.0; - sx = s_curve(rx0); - - vec1 += PerlinN; - b0 = ((int)vec1) & BM; - b1 = (b0+1) & BM; - - b00 = latticeSelector[i + b0]<<3; - b10 = latticeSelector[j + b0]<<3; - b01 = latticeSelector[i + b1]<<3; - b11 = latticeSelector[j + b1]<<3; - - ry0 = vec1 - (int)vec1; - ry1 = ry0 - 1.0; - sy = s_curve(ry0); - - noise[0] = - lerp(sy, - lerp(sx, - rx0*gradient[b00+0] + ry0*gradient[b00+1], - rx1*gradient[b10+0] + ry0*gradient[b10+1]), - lerp(sx, - rx0*gradient[b01+0] + ry1*gradient[b01+1], - rx1*gradient[b11+0] + ry1*gradient[b11+1])); - - noise[1] = - lerp(sy, - lerp(sx, - rx0*gradient[b00+2] + ry0*gradient[b00+3], - rx1*gradient[b10+2] + ry0*gradient[b10+3]), - lerp(sx, - rx0*gradient[b01+2] + ry1*gradient[b01+3], - rx1*gradient[b11+2] + ry1*gradient[b11+3])); - - noise[2] = - lerp(sy, - lerp(sx, - rx0*gradient[b00+4] + ry0*gradient[b00+5], - rx1*gradient[b10+4] + ry0*gradient[b10+5]), - lerp(sx, - rx0*gradient[b01+4] + ry1*gradient[b01+5], - rx1*gradient[b11+4] + ry1*gradient[b11+5])); - - noise[3] = - lerp(sy, - lerp(sx, - rx0*gradient[b00+6] + ry0*gradient[b00+7], - rx1*gradient[b10+6] + ry0*gradient[b10+7]), - lerp(sx, - rx0*gradient[b01+6] + ry1*gradient[b01+7], - rx1*gradient[b11+6] + ry1*gradient[b11+7])); - - } - + /** + * Generate a pixel of noise corresponding to the point vec0,vec1. + * See class description for a high level discussion of method. + * This handles cases where channels <= 4. + * @param noise The place to put the generated noise. + * @param vec0 The X coordiate to generate noise for + * @param vec1 The Y coordiate to generate noise for + */ private final void noise2(final double noise[], double vec0, double vec1) { int b0, b1; final int i, j, b00, b10, b01, b11; @@ -410,6 +374,10 @@ * If any of the lattice is on the right or bottom edge, the * function uses the the latice on the other side of the * tile, i.e., the left or right edge. + * @param noise The place to put the generated noise. + * @param vec0 The X coordiate to generate noise for + * @param vec1 The Y coordiate to generate noise for + * @param stitchInfo The stitching information for the noise function. */ private final void noise2Stitch(final double noise[], final double vec0, final double vec1, @@ -506,12 +474,12 @@ /** * This is the heart of the turbulence calculation. It returns - * 'turbFunctionResult', as defined in the spec. - * @param rgb array for the four color components + * 'turbFunctionResult', as defined in the spec. This is + * special case for 4 bands of output. + * * @param point x and y coordinates of the point to process. * @param fSum array used to avoid reallocating double array for each pixel - * @param noise array used to avoid reallocating double array for - * each pixel + * @return The ARGB pixel value. */ private final int turbulence_4(double pointX, double pointY, @@ -637,7 +605,7 @@ switch (channels.length) { case 4: for(int nOctave = 0; nOctave < numOctaves; nOctave++){ - noise2_4(noise, pointX, pointY); + noise2(noise, pointX, pointY); if (noise[0]<0) fSum[0] -= (noise[0] * ratio); else fSum[0] += (noise[0] * ratio); @@ -728,13 +696,14 @@ } /** - * This is the heart of the turbulence calculation. It returns 'turbFunctionResult', as - * defined in the spec. + * This is the heart of the turbulence calculation. It returns + * 'turbFunctionResult', as defined in the spec. * @param rgb array for the four color components * @param point x and y coordinates of the point to process. * @param fSum array used to avoid reallocating double array for each pixel - * @param noise array used to avoid reallocating double array for each pixel - * @param channels channels for which values should be computed + * @param noise array used to avoid reallocating double array for + * each pixel + * @param stitchInfo The stitching information for the noise function */ private final void turbulenceStitch(final int rgb[], double pointX, double pointY, @@ -749,6 +718,7 @@ case 4: for(int nOctave = 0; nOctave < numOctaves; nOctave++){ noise2Stitch(noise, pointX, pointY, stitchInfo); + if (noise[3]<0) fSum[3] -= (noise[3] * ratio); else fSum[3] += (noise[3] * ratio); if (noise[2]<0) fSum[2] -= (noise[2] * ratio); @@ -840,15 +810,12 @@ } /** - * This is the heart of the turbulence calculation. It returns 'turbFunctionResult', as - * defined in the spec. - * @param rgb array for the four color components + * This is the heart of the turbulence calculation. It returns + * 'turbFunctionResult', as defined in the spec. This handles the + * case where we are generating 4 channels of noise. * @param point x and y coordinates of the point to process. * @param fSum array used to avoid reallocating double array for each pixel - * @param noise array used to avoid reallocating double array for each pixel - * @param numOctaves number of octaves (may be limited so that spatial frequency below - * half a pixel are not processed). - * @param channels channels for which values should be computed + * @return The ARGB pixel */ private final int turbulenceFractal_4( double pointX, double pointY, @@ -940,15 +907,13 @@ } /** - * This is the heart of the turbulence calculation. It returns 'turbFunctionResult', as - * defined in the spec. + * This is the heart of the turbulence calculation. It returns + * 'turbFunctionResult', as defined in the spec. * @param rgb array for the four color components * @param point x and y coordinates of the point to process. * @param fSum array used to avoid reallocating double array for each pixel - * @param noise array used to avoid reallocating double array for each pixel - * @param numOctaves number of octaves (may be limited so that spatial frequency below - * half a pixel are not processed). - * @param channels channels for which values should be computed + * @param noise array used to avoid reallocating double array for + * each pixel */ private final void turbulenceFractal(final int rgb[], double pointX, @@ -1000,15 +965,14 @@ } /** - * This is the heart of the turbulence calculation. It returns 'turbFunctionResult', as - * defined in the spec. + * This is the heart of the turbulence calculation. It returns + * 'turbFunctionResult', as defined in the spec. * @param rgb array for the four color components * @param point x and y coordinates of the point to process. * @param fSum array used to avoid reallocating double array for each pixel - * @param noise array used to avoid reallocating double array for each pixel - * @param numOctaves number of octaves (may be limited so that spatial frequency below - * half a pixel are not processed). - * @param channels channels for which values should be computed + * @param noise array used to avoid reallocating double array for + * each pixel + * @param stitchInfo The stitching information for the noise function */ private final void turbulenceFractalStitch(final int rgb[], double pointX, @@ -1063,10 +1027,7 @@ /** * Generates a Perlin noise pattern into dest Raster. - * - * @param txf image space to noise space transform. The 'noise space' is the - * space where the spatial characteristics of the noise are defined. - * @param des Raster where the pattern should be generated. + * @param dest Raster to fill with the pattern. */ public WritableRaster copyData(WritableRaster dest) { // @@ -1076,18 +1037,6 @@ throw new IllegalArgumentException ("Cannot generate a noise pattern into a null raster"); - // - // Now, limit the number of octaves so that we do not get frequencies - // below half a pixel. - // - // If d is the distance between to pixels in user space, then, - // numOctavesMax = -(log2(d) + log2(bf)) - // along one axis. - // - // The maximum distance along each axis is processed by computing the - // inverse transform of 'maximum' vectors from device space to the filter space - // and determining the maximum component along each axis. - // int w = dest.getWidth(); int h = dest.getHeight(); @@ -1289,6 +1238,19 @@ txf.deltaTransform(vecX, 0, vecX, 0, 1); txf.deltaTransform(vecY, 0, vecY, 0, 1); + // + // Now, limit the number of octaves so that we do not get frequencies + // below half a pixel. + // + // If d is the distance between to pixels in user space, then, + // numOctavesMax = -(log2(d) + log2(bf)) + // along one axis. + // + // The maximum distance along each axis is processed by + // computing the inverse transform of 'maximum' vectors from + // device space to the filter space and determining the + // maximum component along each axis. + double dx = Math.max(Math.abs(vecX[0]), Math.abs(vecY[0])); int maxX = -(int)Math.round((Math.log(dx) + Math.log(baseFrequencyX))/ Math.log(2)); @@ -1305,7 +1267,9 @@ if (this.numOctaves > 8) // beyond 8 octaves there is no significant contribution - // to the output pixel. + // to the output pixel (contribution is halved for each + // octave so after 8 we are contributing less than half a + // code value _at_best_). this.numOctaves = 8; if (tile != null) { --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]