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 java.util.*;
022
023/**
024 * An experimental filter which can be used for keying against a clean shot. Given a source image, a clean image and a destination image, 
025 * the filter replaces all pixels in the source which nearly equal the equivalent clean pixel by destination pixels.
026 */
027public class KeyFilter extends AbstractBufferedImageOp {
028        
029        private float hTolerance = 0;
030        private float sTolerance = 0;
031        private float bTolerance = 0;
032        private BufferedImage destination;
033        private BufferedImage cleanImage;
034
035        public KeyFilter() {
036        }
037
038        /**
039         * Set the hue tolerance of the image in the range 0..1.
040         * @param hTolerance the tolerance
041     * @see #getHTolerance
042         */
043        public void setHTolerance( float hTolerance ) {
044                this.hTolerance = hTolerance;
045        }
046        
047        /**
048         * Get the hue tolerance.
049         * @return the tolerance
050     * @see #setHTolerance
051         */
052        public float getHTolerance() {
053                return hTolerance;
054        }
055        
056        /**
057         * Set the saturation tolerance of the image in the range 0..1.
058         * @param sTolerance the tolerance
059     * @see #getSTolerance
060         */
061        public void setSTolerance( float sTolerance ) {
062                this.sTolerance = sTolerance;
063        }
064        
065        /**
066         * Get the saturation tolerance.
067         * @return the tolerance
068     * @see #setSTolerance
069         */
070        public float getSTolerance() {
071                return sTolerance;
072        }
073        
074        /**
075         * Set the brightness tolerance of the image in the range 0..1.
076         * @param bTolerance the tolerance
077     * @see #getBTolerance
078         */
079        public void setBTolerance( float bTolerance ) {
080                this.bTolerance = bTolerance;
081        }
082        
083        /**
084         * Get the brightness tolerance.
085         * @return the tolerance
086     * @see #setBTolerance
087         */
088        public float getBTolerance() {
089                return bTolerance;
090        }
091        
092    /**
093     * Set the destination image.
094     * @param destination the destination image
095     * @see #getDestination
096     */
097        public void setDestination( BufferedImage destination ) {
098                this.destination = destination;
099        }
100        
101    /**
102     * Get the destination image.
103     * @return the destination image
104     * @see #setDestination
105     */
106        public BufferedImage getDestination() {
107                return destination;
108        }
109        
110    /**
111     * Get the clean image.
112     * @param cleanImage the clean image
113     * @see #getCleanImage
114     */
115        public void setCleanImage( BufferedImage cleanImage ) {
116                this.cleanImage = cleanImage;
117        }
118        
119    /**
120     * Get the clean image.
121     * @return the clean image
122     * @see #setCleanImage
123     */
124        public BufferedImage getCleanImage() {
125                return cleanImage;
126        }
127                
128    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
129        int width = src.getWidth();
130        int height = src.getHeight();
131                int type = src.getType();
132                WritableRaster srcRaster = src.getRaster();
133
134        if ( dst == null )
135            dst = createCompatibleDestImage( src, null );
136                WritableRaster dstRaster = dst.getRaster();
137
138        if ( destination != null && cleanImage != null ) {
139            float[] hsb1 = null;
140            float[] hsb2 = null;
141            int[] inPixels = null;
142            int[] outPixels = null;
143            int[] cleanPixels = null;
144            for ( int y = 0; y < height; y++ ) {
145                inPixels = getRGB( src, 0, y, width, 1, inPixels );
146                outPixels = getRGB( destination, 0, y, width, 1, outPixels );
147                cleanPixels = getRGB( cleanImage, 0, y, width, 1, cleanPixels );
148                for ( int x = 0; x < width; x++ ) {
149                    int rgb1 = inPixels[x];
150                    int out = outPixels[x];
151                    int rgb2 = cleanPixels[x];
152
153                    int r1 = (rgb1 >> 16) & 0xff;
154                    int g1 = (rgb1 >> 8) & 0xff;
155                    int b1 = rgb1 & 0xff;
156                    int r2 = (rgb2 >> 16) & 0xff;
157                    int g2 = (rgb2 >> 8) & 0xff;
158                    int b2 = rgb2 & 0xff;
159                    hsb1 = Color.RGBtoHSB( r1, b1, g1, hsb1 );
160                    hsb2 = Color.RGBtoHSB( r2, b2, g2, hsb2 );
161//                    int tolerance = (int)(255*tolerance);
162//                    return Math.abs(r1-r2) <= tolerance && Math.abs(g1-g2) <= tolerance && Math.abs(b1-b2) <= tolerance;
163
164 //                   if ( PixelUtils.nearColors( in, clean, (int)(255*tolerance) ) )
165                    if ( Math.abs( hsb1[0] - hsb2[0] ) < hTolerance && Math.abs( hsb1[1] - hsb2[1] ) < sTolerance && Math.abs( hsb1[2] - hsb2[2] ) < bTolerance )
166                        inPixels[x] = out;
167                    else
168                        inPixels[x] = rgb1;
169                }
170                setRGB( dst, 0, y, width, 1, inPixels );
171            }
172        }
173
174        return dst;
175    }
176
177        public String toString() {
178                return "Keying/Key...";
179        }
180}