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.*;
021
022// original code Copyright (C) Jerry Huxtable 1998
023//
024// customizations (C) Michele Puccini 19/12/2001
025// - conversion from float to int math
026// - complete rewrite of applyMap()
027// - implemented merge to dest function
028
029public class ShapeFilter extends WholeImageFilter {
030
031        public final static int LINEAR = 0;
032        public final static int CIRCLE_UP = 1;
033        public final static int CIRCLE_DOWN = 2;
034        public final static int SMOOTH = 3;
035
036        private float factor = 1.0f;
037        protected Colormap colormap;
038        private boolean useAlpha = true;
039        private boolean invert = false;
040        private boolean merge = false;
041        private int type;
042
043        private final static int one   = 41;
044        private final static int sqrt2 = (int)(41*Math.sqrt(2));
045        private final static int sqrt5 = (int)(41*Math.sqrt(5));
046
047        public ShapeFilter() {
048                colormap = new LinearColormap();
049        }
050
051        public void setFactor(float factor) {
052                this.factor = factor;
053        }
054
055        public float getFactor() {
056                return factor;
057        }
058
059    /**
060     * Set the colormap to be used for the filter.
061     * @param colormap the colormap
062     * @see #getColormap
063     */
064        public void setColormap(Colormap colormap) {
065                this.colormap = colormap;
066        }
067
068    /**
069     * Get the colormap to be used for the filter.
070     * @return the colormap
071     * @see #setColormap
072     */
073        public Colormap getColormap() {
074                return colormap;
075        }
076
077        public void setUseAlpha(boolean useAlpha) {
078                this.useAlpha = useAlpha;
079        }
080
081        public boolean getUseAlpha() {
082                return useAlpha;
083        }
084
085        public void setType(int type) {
086                this.type = type;
087        }
088
089        public int getType() {
090                return type;
091        }
092
093        public void setInvert(boolean invert) {
094                this.invert = invert;
095        }
096
097        public boolean getInvert() {
098                return invert;
099        }
100
101        public void setMerge(boolean merge) {
102                this.merge = merge;
103        }
104
105        public boolean getMerge() {
106                return merge;
107        }
108
109        protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
110                int[] map = new int[width * height];
111                makeMap(inPixels, map, width, height);
112                int max = distanceMap(map, width, height);
113                applyMap(map, inPixels, width, height, max);
114
115                return inPixels;
116        }
117
118        public int distanceMap(int[] map, int width, int height) {
119                int xmax = width - 3;
120                int ymax = height - 3;
121                int max = 0;
122                int v;
123                
124                for (int y = 0; y < height; y++) {
125                        for (int x = 0; x < width; x++) {
126                                int offset = x + y * width;
127                                if (map[offset] > 0) {
128                                        if (x < 2 || x > xmax || y < 2 || y > ymax)
129                                                v = setEdgeValue(x, y, map, width, offset, xmax, ymax);
130                                        else
131                                                v = setValue(map, width, offset);
132                                        if (v > max)
133                                                max = v;
134                                }
135                        }
136                }
137                for (int y = height-1; y >= 0; y--) {
138                        for (int x = width-1; x >= 0; x--) {
139                                int offset = x + y * width;
140                                if (map[offset] > 0) {
141                                        if (x < 2 || x > xmax || y < 2 || y > ymax)
142                                                v = setEdgeValue(x, y, map, width, offset, xmax, ymax);
143                                        else
144                                                v = setValue(map, width, offset);
145                                        if (v > max)
146                                                max = v;
147                                }
148                        }
149                }
150                return max;
151        }
152
153        private void makeMap(int[] pixels, int[] map, int width, int height) {
154                for (int y = 0; y < height; y++) {
155                        for (int x = 0; x < width; x++) {
156                                int offset = x + y * width;
157                                int b = useAlpha ? (pixels[offset] >> 24) & 0xff : PixelUtils.brightness(pixels[offset]);
158//                              map[offset] = b * one;
159                                map[offset] = b * one / 10;
160                        }
161                }
162        }
163
164        private void applyMap(int[] map, int[] pixels, int width, int height, int max) {
165                if (max == 0)
166                        max = 1;
167                for (int y = 0; y < height; y++) {
168                        for (int x = 0; x < width; x++) {
169                                int offset = x + y * width;
170                                int m = map[offset];
171                                float v = 0;
172                                int sa = 0, sr = 0, sg = 0, sb = 0;
173
174                                if (m == 0) {
175                                        // default color
176                                        sa = sr = sg = sb = 0;
177                                        sa = (pixels[offset] >> 24) & 0xff;
178                                } else {
179                                        // get V from map
180                                        v = ImageMath.clamp(factor * m / max, 0, 1);
181                                        switch (type) {
182                                        case CIRCLE_UP :
183                                                v = (ImageMath.circleUp(v));
184                                                break;
185                                        case CIRCLE_DOWN :
186                                                v = (ImageMath.circleDown(v));
187                                                break;
188                                        case SMOOTH :
189                                                v = (ImageMath.smoothStep(0, 1, v));
190                                                break;
191                                        }
192
193                                        if (colormap == null) {
194                                                sr = sg = sb = (int)(v*255);
195                                        } else {
196                                                int c = (colormap.getColor(v));
197
198                                                sr = (c >> 16) & 0xFF;
199                                                sg = (c >> 8) & 0xFF;
200                                                sb = (c) & 0xFF;
201                                        }
202                                        
203                                        sa = useAlpha ? (pixels[offset] >> 24) & 0xff : PixelUtils.brightness(pixels[offset]);
204                                        
205                                        // invert v if necessary
206                                        if (invert) {
207                                                sr = 255-sr;
208                                                sg = 255-sg;
209                                                sb = 255-sb;
210                                        }
211                                }
212
213                                // write results
214                                if (merge) {
215                                        // merge with source
216                                        int transp = 255;
217                                        int col = pixels[offset];
218
219                                        int a = (col & 0xFF000000) >> 24;
220                                        int     r = (col & 0xFF0000) >> 16;
221                                        int     g = (col & 0xFF00) >> 8;
222                                        int     b = (col & 0xFF);
223
224                                        r = (int)((sr*r/transp));
225                                        g = (int)((sg*g/transp));
226                                        b = (int)((sb*b/transp));
227                                
228                                        // clip colors
229                                        if (r < 0)
230                                                r = 0;
231                                        if (r > 255)
232                                                r = 255; 
233                                        if (g < 0)
234                                                g = 0;
235                                        if (g > 255)
236                                                g = 255; 
237                                        if (b < 0)
238                                                b = 0;
239                                        if (b > 255)
240                                                b = 255;
241                                        
242                                        pixels[offset] = (a << 24) | (r << 16) | (g << 8) | b;
243                                } else {
244                                        // write gray shades
245                                        pixels[offset] = (sa << 24) | (sr << 16) | (sg << 8) | sb;
246                                }
247                        }
248                }
249        }
250
251        private int setEdgeValue(int x, int y, int[] map, int width, int offset, int xmax, int ymax) {
252                int min, v;
253                int r1, r2, r3, r4, r5;
254
255                r1 = offset - width - width - 2;
256                r2 = r1 + width;
257                r3 = r2 + width;
258                r4 = r3 + width;
259                r5 = r4 + width;
260
261                if (y == 0 || x == 0 || y == ymax+2 || x == xmax+2)
262                        return map[offset] = one;
263
264                v = map[r2 + 2] + one;
265                min = v;
266                
267                v = map[r3 + 1] + one;
268                if (v < min)
269                        min = v;
270                
271                v = map[r3 + 3] + one;
272                if (v < min)
273                        min = v;
274                
275                v = map[r4 + 2] + one;
276                if (v < min)
277                        min = v;
278                
279                v = map[r2 + 1] + sqrt2;
280                if (v < min)
281                        min = v;
282                        
283                v = map[r2 + 3] + sqrt2;
284                if (v < min)
285                        min = v;
286                        
287                v = map[r4 + 1] + sqrt2;
288                if (v < min)
289                        min = v;
290                        
291                v = map[r4 + 3] + sqrt2;
292                if (v < min)
293                        min = v;
294                
295                if (y == 1 || x == 1 || y == ymax+1 || x == xmax+1)
296                        return map[offset] = min;
297
298                v = map[r1 + 1] + sqrt5;
299                if (v < min)
300                        min = v;
301                        
302                v = map[r1 + 3] + sqrt5;
303                if (v < min)
304                        min = v;
305                        
306                v = map[r2 + 4] + sqrt5;
307                if (v < min)
308                        min = v;
309                        
310                v = map[r4 + 4] + sqrt5;
311                if (v < min)
312                        min = v;
313                        
314                v = map[r5 + 3] + sqrt5;
315                if (v < min)
316                        min = v;
317                        
318                v = map[r5 + 1] + sqrt5;
319                if (v < min)
320                        min = v;
321                        
322                v = map[r4] + sqrt5;
323                if (v < min)
324                        min = v;
325                        
326                v = map[r2] + sqrt5;
327                if (v < min)
328                        min = v;
329
330                return map[offset] = min;
331        }
332
333        private int setValue(int[] map, int width, int offset) {
334                int min, v;
335                int r1, r2, r3, r4, r5;
336
337                r1 = offset - width - width - 2;
338                r2 = r1 + width;
339                r3 = r2 + width;
340                r4 = r3 + width;
341                r5 = r4 + width;
342
343                v = map[r2 + 2] + one;
344                min = v;
345                v = map[r3 + 1] + one;
346                if (v < min)
347                        min = v;
348                v = map[r3 + 3] + one;
349                if (v < min)
350                        min = v;
351                v = map[r4 + 2] + one;
352                if (v < min)
353                        min = v;
354                
355                v = map[r2 + 1] + sqrt2;
356                if (v < min)
357                        min = v;
358                v = map[r2 + 3] + sqrt2;
359                if (v < min)
360                        min = v;
361                v = map[r4 + 1] + sqrt2;
362                if (v < min)
363                        min = v;
364                v = map[r4 + 3] + sqrt2;
365                if (v < min)
366                        min = v;
367                
368                v = map[r1 + 1] + sqrt5;
369                if (v < min)
370                        min = v;
371                v = map[r1 + 3] + sqrt5;
372                if (v < min)
373                        min = v;
374                v = map[r2 + 4] + sqrt5;
375                if (v < min)
376                        min = v;
377                v = map[r4 + 4] + sqrt5;
378                if (v < min)
379                        min = v;
380                v = map[r5 + 3] + sqrt5;
381                if (v < min)
382                        min = v;
383                v = map[r5 + 1] + sqrt5;
384                if (v < min)
385                        min = v;
386                v = map[r4] + sqrt5;
387                if (v < min)
388                        min = v;
389                v = map[r2] + sqrt5;
390                if (v < min)
391                        min = v;
392
393                return map[offset] = min;
394        }
395        
396        public String toString() {
397                return "Stylize/Shapeburst...";
398        }
399
400}