001/*
002Copyright 2006 Jerry Huxtable
003
004Licensed under the Apache License, Version 2.0 (the "License");
005you may not use this file except in compliance with the License.
006You may obtain a copy of the License at
007
008   http://www.apache.org/licenses/LICENSE-2.0
009
010Unless required by applicable law or agreed to in writing, software
011distributed under the License is distributed on an "AS IS" BASIS,
012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013See the License for the specific language governing permissions and
014limitations under the License.
015*/
016
017package com.jhlabs.image;
018
019import java.awt.*;
020import java.awt.image.*;
021import com.jhlabs.math.*;
022
023/**
024 * This filter applies a marbling effect to an image, displacing pixels by random amounts.
025 */
026public class MarbleFilter extends TransformFilter {
027
028        private float[] sinTable, cosTable;
029        private float xScale = 4;
030        private float yScale = 4;
031        private float amount = 1;
032        private float turbulence = 1;
033        
034        public MarbleFilter() {
035                setEdgeAction(CLAMP);
036        }
037        
038        /**
039     * Set the X scale of the effect.
040     * @param xScale the scale.
041     * @see #getXScale
042     */
043        public void setXScale(float xScale) {
044                this.xScale = xScale;
045        }
046
047        /**
048     * Get the X scale of the effect.
049     * @return the scale.
050     * @see #setXScale
051     */
052        public float getXScale() {
053                return xScale;
054        }
055
056        /**
057     * Set the Y scale of the effect.
058     * @param yScale the scale.
059     * @see #getYScale
060     */
061        public void setYScale(float yScale) {
062                this.yScale = yScale;
063        }
064
065        /**
066     * Get the Y scale of the effect.
067     * @return the scale.
068     * @see #setYScale
069     */
070        public float getYScale() {
071                return yScale;
072        }
073
074        /**
075         * Set the amount of effect.
076         * @param amount the amount
077     * @min-value 0
078     * @max-value 1
079     * @see #getAmount
080         */
081        public void setAmount(float amount) {
082                this.amount = amount;
083        }
084
085        /**
086         * Get the amount of effect.
087         * @return the amount
088     * @see #setAmount
089         */
090        public float getAmount() {
091                return amount;
092        }
093
094        /**
095     * Specifies the turbulence of the effect.
096     * @param turbulence the turbulence of the effect.
097     * @min-value 0
098     * @max-value 1
099     * @see #getTurbulence
100     */
101        public void setTurbulence(float turbulence) {
102                this.turbulence = turbulence;
103        }
104
105        /**
106     * Returns the turbulence of the effect.
107     * @return the turbulence of the effect.
108     * @see #setTurbulence
109     */
110        public float getTurbulence() {
111                return turbulence;
112        }
113
114        private void initialize() {
115                sinTable = new float[256];
116                cosTable = new float[256];
117                for (int i = 0; i < 256; i++) {
118                        float angle = ImageMath.TWO_PI*i/256f*turbulence;
119                        sinTable[i] = (float)(-yScale*Math.sin(angle));
120                        cosTable[i] = (float)(yScale*Math.cos(angle));
121                }
122        }
123
124        private int displacementMap(int x, int y) {
125                return PixelUtils.clamp((int)(127 * (1+Noise.noise2(x / xScale, y / xScale))));
126        }
127        
128        protected void transformInverse(int x, int y, float[] out) {
129                int displacement = displacementMap(x, y);
130                out[0] = x + sinTable[displacement];
131                out[1] = y + cosTable[displacement];
132        }
133
134    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
135                initialize();
136                return super.filter( src, dst );
137        }
138
139        public String toString() {
140                return "Distort/Marble...";
141        }
142}