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/**
023 * A filter which draws contours on an image at given brightness levels.
024 */
025public class ContourFilter extends WholeImageFilter {
026
027        private float levels = 5;
028        private float scale = 1;
029        private float offset = 0;
030        private int contourColor = 0xff000000;
031        
032        public ContourFilter() {
033        }
034
035        public void setLevels( float levels ) {
036                this.levels = levels;
037        }
038        
039        public float getLevels() {
040                return levels;
041        }
042        
043        /**
044     * Specifies the scale of the contours.
045     * @param scale the scale of the contours.
046     * @min-value 0
047     * @max-value 1
048     * @see #getScale
049     */
050        public void setScale( float scale ) {
051                this.scale = scale;
052        }
053        
054        /**
055     * Returns the scale of the contours.
056     * @return the scale of the contours.
057     * @see #setScale
058     */
059        public float getScale() {
060                return scale;
061        }
062        
063        public void setOffset( float offset ) {
064                this.offset = offset;
065        }
066        
067        public float getOffset() {
068                return offset;
069        }
070        
071        public void setContourColor( int contourColor ) {
072                this.contourColor = contourColor;
073        }
074        
075        public int getContourColor() {
076                return contourColor;
077        }
078        
079        protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
080                int index = 0;
081                short[][] r = new short[3][width];
082                int[] outPixels = new int[width * height];
083
084                short[] table = new short[256];
085                int offsetl = (int)(offset * 256 / levels);
086                for ( int i = 0; i < 256; i++ )
087                        table[i] = (short)PixelUtils.clamp( (int)(255 * Math.floor(levels*(i+offsetl) / 256) / (levels-1) - offsetl) );
088
089                for (int x = 0; x < width; x++) {
090                        int rgb = inPixels[x];
091                        r[1][x] = (short)PixelUtils.brightness( rgb );
092                }
093                for (int y = 0; y < height; y++) {
094                        boolean yIn = y > 0 && y < height-1;
095                        int nextRowIndex = index+width;
096                        if ( y < height-1) {
097                                for (int x = 0; x < width; x++) {
098                                        int rgb = inPixels[nextRowIndex++];
099                                        r[2][x] = (short)PixelUtils.brightness( rgb );
100                                }
101                        }
102                        for (int x = 0; x < width; x++) {
103                                boolean xIn = x > 0 && x < width-1;
104                                int w = x-1;
105                                int e = x+1;
106                                int v = 0;
107                                
108                                if ( yIn && xIn ) {
109                                        short nwb = r[0][w];
110                                        short neb = r[0][x];
111                                        short swb = r[1][w];
112                                        short seb = r[1][x];
113                                        short nw = table[nwb];
114                                        short ne = table[neb];
115                                        short sw = table[swb];
116                                        short se = table[seb];
117
118                                        if (nw != ne || nw != sw || ne != se || sw != se) {
119                                                v = (int)(scale * (Math.abs(nwb - neb) + Math.abs(nwb - swb) + Math.abs(neb - seb) + Math.abs(swb - seb)));
120//                                              v /= 255;
121                                                if (v > 255)
122                                                        v = 255;
123                                        }
124                                }
125
126                                if ( v != 0 )
127                                        outPixels[index] = PixelUtils.combinePixels( inPixels[index], contourColor, PixelUtils.NORMAL, v );
128//                                      outPixels[index] = PixelUtils.combinePixels( (contourColor & 0xff)|(v << 24), inPixels[index], PixelUtils.NORMAL );
129                                else
130                                        outPixels[index] = inPixels[index];
131                                index++;
132                        }
133                        short[] t;
134                        t = r[0];
135                        r[0] = r[1];
136                        r[1] = r[2];
137                        r[2] = t;
138                }
139        
140                return outPixels;
141        }
142
143        public String toString() {
144                return "Stylize/Contour...";
145        }
146
147}
148