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.util.*;
020import java.awt.*;
021import java.awt.image.*;
022import com.jhlabs.math.*;
023
024public class PlasmaFilter extends WholeImageFilter {
025        
026        public float turbulence = 1.0f;
027        private float scaling = 0.0f;
028        private Colormap colormap = new LinearColormap();
029        private Random randomGenerator;
030        private long seed = 567;
031        private boolean useColormap = false;
032        private boolean useImageColors = false;
033
034        public PlasmaFilter() {
035                randomGenerator = new Random();
036        }
037
038        /**
039     * Specifies the turbulence of the texture.
040     * @param turbulence the turbulence of the texture.
041     * @min-value 0
042     * @max-value 10
043     * @see #getTurbulence
044     */
045        public void setTurbulence(float turbulence) {
046                this.turbulence = turbulence;
047        }
048
049        /**
050     * Returns the turbulence of the effect.
051     * @return the turbulence of the effect.
052     * @see #setTurbulence
053     */
054        public float getTurbulence() {
055                return turbulence;
056        }
057
058        public void setScaling(float scaling) {
059                this.scaling = scaling;
060        }
061
062        public float getScaling() {
063                return scaling;
064        }
065
066    /**
067     * Set the colormap to be used for the filter.
068     * @param colormap the colormap
069     * @see #getColormap
070     */
071        public void setColormap(Colormap colormap) {
072                this.colormap = colormap;
073        }
074        
075    /**
076     * Get the colormap to be used for the filter.
077     * @return the colormap
078     * @see #setColormap
079     */
080        public Colormap getColormap() {
081                return colormap;
082        }
083        
084        public void setUseColormap(boolean useColormap) {
085                this.useColormap = useColormap;
086        }
087
088        public boolean getUseColormap() {
089                return useColormap;
090        }
091
092        public void setUseImageColors(boolean useImageColors) {
093                this.useImageColors = useImageColors;
094        }
095
096        public boolean getUseImageColors() {
097                return useImageColors;
098        }
099
100        public void setSeed(int seed) {
101                this.seed = seed;
102        }
103
104        public int getSeed() {
105                return (int)seed;
106        }
107
108        public void randomize() {
109                seed = new Date().getTime();
110        }
111        
112        private int randomRGB(int[] inPixels, int x, int y) {
113                if (useImageColors) {
114                        return inPixels[y*originalSpace.width+x];
115                } else {
116                        int r = (int)(255 * randomGenerator.nextFloat());
117                        int g = (int)(255 * randomGenerator.nextFloat());
118                        int b = (int)(255 * randomGenerator.nextFloat());
119                        return 0xff000000 | (r << 16) | (g << 8) | b;
120                }
121        }
122
123        private int displace(int rgb, float amount) {
124                int r = (rgb >> 16) & 0xff;
125                int g = (rgb >> 8) & 0xff;
126                int b = rgb & 0xff;
127                r = PixelUtils.clamp(r + (int)(amount * (randomGenerator.nextFloat()-0.5)));
128                g = PixelUtils.clamp(g + (int)(amount * (randomGenerator.nextFloat()-0.5)));
129                b = PixelUtils.clamp(b + (int)(amount * (randomGenerator.nextFloat()-0.5)));
130                return 0xff000000 | (r << 16) | (g << 8) | b;
131        }
132
133        private int average(int rgb1, int rgb2) {
134                return PixelUtils.combinePixels(rgb1, rgb2, PixelUtils.AVERAGE);
135        }
136
137        private int getPixel(int x, int y, int[] pixels, int stride) {
138                return pixels[y*stride+x];
139        }
140        
141        private void putPixel(int x, int y, int rgb, int[] pixels, int stride) {
142                pixels[y*stride+x] = rgb;
143        }
144        
145        private boolean doPixel(int x1, int y1, int x2, int y2, int[] pixels, int stride, int depth, int scale) {
146                int mx, my;
147
148                if (depth == 0) {
149                        int ml, mr, mt, mb, mm, t;
150
151                        int tl = getPixel(x1, y1, pixels, stride);
152                        int bl = getPixel(x1, y2, pixels, stride);
153                        int tr = getPixel(x2, y1, pixels, stride);
154                        int br = getPixel(x2, y2, pixels, stride);
155
156                        float amount = (256.0f / (2.0f * scale)) * turbulence;
157
158                        mx = (x1 + x2) / 2;
159                        my = (y1 + y2) / 2;
160
161                        if (mx == x1 && mx == x2 && my == y1 && my == y2)
162                                return true;
163
164                        if (mx != x1 || mx != x2) {
165                                ml = average(tl, bl);
166                                ml = displace(ml, amount);
167                                putPixel(x1, my, ml, pixels, stride);
168
169                                if (x1 != x2){
170                                        mr = average(tr, br);
171                                        mr = displace(mr, amount);
172                                        putPixel(x2, my, mr, pixels, stride);
173                                }
174                        }
175
176                        if (my != y1 || my != y2){
177                                if (x1 != mx || my != y2){
178                                        mb = average(bl, br);
179                                        mb = displace(mb, amount);
180                                        putPixel(mx, y2, mb, pixels, stride);
181                                }
182
183                                if (y1 != y2){
184                                        mt = average(tl, tr);
185                                        mt = displace(mt, amount);
186                                        putPixel(mx, y1, mt, pixels, stride);
187                                }
188                        }
189
190                        if (y1 != y2 || x1 != x2) {
191                                mm = average(tl, br);
192                                t = average(bl, tr);
193                                mm = average(mm, t);
194                                mm = displace(mm, amount);
195                                putPixel(mx, my, mm, pixels, stride);
196                        }
197
198                        if (x2-x1 < 3 && y2-y1 < 3)
199                                return false;
200                        return true;
201                }
202
203                mx = (x1 + x2) / 2;
204                my = (y1 + y2) / 2;
205
206                doPixel(x1, y1, mx, my, pixels, stride, depth-1, scale+1);
207                doPixel(x1, my, mx ,y2, pixels, stride, depth-1, scale+1);
208                doPixel(mx, y1, x2 , my, pixels, stride, depth-1, scale+1);
209                return doPixel(mx, my, x2, y2, pixels, stride, depth-1, scale+1);
210        }
211        
212        protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
213                int[] outPixels = new int[width * height];
214
215                randomGenerator.setSeed(seed);
216
217                int w1 = width-1;
218                int h1 = height-1;
219                putPixel(0, 0, randomRGB(inPixels, 0, 0), outPixels, width);
220                putPixel(w1, 0, randomRGB(inPixels, w1, 0), outPixels, width);
221                putPixel(0, h1, randomRGB(inPixels, 0, h1), outPixels, width);
222                putPixel(w1, h1, randomRGB(inPixels, w1, h1), outPixels, width);
223                putPixel(w1/2, h1/2, randomRGB(inPixels, w1/2, h1/2), outPixels, width);
224                putPixel(0, h1/2, randomRGB(inPixels, 0, h1/2), outPixels, width);
225                putPixel(w1, h1/2, randomRGB(inPixels, w1, h1/2), outPixels, width);
226                putPixel(w1/2, 0, randomRGB(inPixels, w1/2, 0), outPixels, width);
227                putPixel(w1/2, h1, randomRGB(inPixels, w1/2, h1), outPixels, width);
228
229                int depth = 1;
230                while (doPixel(0, 0, width-1, height-1, outPixels, width, depth, 0))
231                        depth++;
232
233                if (useColormap && colormap != null) {
234                        int index = 0;
235                        for (int y = 0; y < height; y++) {
236                                for (int x = 0; x < width; x++) {
237                                        outPixels[index] = colormap.getColor((outPixels[index] & 0xff)/255.0f);
238                                        index++;
239                                }
240                        }
241                }
242                return outPixels;
243        }
244
245        public String toString() {
246                return "Texture/Plasma...";
247        }
248        
249}