001    /*
002    *
003    
004    Licensed under the Apache License, Version 2.0 (the "License");
005    you may not use this file except in compliance with the License.
006    You may obtain a copy of the License at
007    
008       http://www.apache.org/licenses/LICENSE-2.0
009    
010    Unless required by applicable law or agreed to in writing, software
011    distributed under the License is distributed on an "AS IS" BASIS,
012    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013    See the License for the specific language governing permissions and
014    limitations under the License.
015    */
016    
017    package railo.runtime.img.filter;import java.awt.Color;
018    import java.awt.image.BufferedImage;
019    import java.awt.image.WritableRaster;
020    
021    import railo.runtime.engine.ThreadLocalPageContext;
022    import railo.runtime.exp.FunctionException;
023    import railo.runtime.exp.PageException;
024    import railo.runtime.img.ImageUtil;
025    import railo.runtime.type.KeyImpl;
026    import railo.runtime.type.List;
027    import railo.runtime.type.Struct;
028    
029    /**
030     * An experimental filter which can be used for keying against a clean shot. Given a source image, a clean image and a destination image, 
031     * the filter replaces all pixels in the source which nearly equal the equivalent clean pixel by destination pixels.
032     */
033    public class KeyFilter extends AbstractBufferedImageOp  implements DynFiltering {
034            
035            private float hTolerance = 0;
036            private float sTolerance = 0;
037            private float bTolerance = 0;
038            private BufferedImage destination;
039            private BufferedImage cleanImage;
040    
041            public KeyFilter() {
042            }
043    
044            /**
045             * Set the hue tolerance of the image in the range 0..1.
046             * @param hTolerance the tolerance
047         * @see #getHTolerance
048             */
049            public void setHTolerance( float hTolerance ) {
050                    this.hTolerance = hTolerance;
051            }
052            
053            /**
054             * Get the hue tolerance.
055             * @return the tolerance
056         * @see #setHTolerance
057             */
058            public float getHTolerance() {
059                    return hTolerance;
060            }
061            
062            /**
063             * Set the saturation tolerance of the image in the range 0..1.
064             * @param sTolerance the tolerance
065         * @see #getSTolerance
066             */
067            public void setSTolerance( float sTolerance ) {
068                    this.sTolerance = sTolerance;
069            }
070            
071            /**
072             * Get the saturation tolerance.
073             * @return the tolerance
074         * @see #setSTolerance
075             */
076            public float getSTolerance() {
077                    return sTolerance;
078            }
079            
080            /**
081             * Set the brightness tolerance of the image in the range 0..1.
082             * @param bTolerance the tolerance
083         * @see #getBTolerance
084             */
085            public void setBTolerance( float bTolerance ) {
086                    this.bTolerance = bTolerance;
087            }
088            
089            /**
090             * Get the brightness tolerance.
091             * @return the tolerance
092         * @see #setBTolerance
093             */
094            public float getBTolerance() {
095                    return bTolerance;
096            }
097            
098        /**
099         * Set the destination image.
100         * @param destination the destination image
101         * @see #getDestination
102         */
103            public void setDestination( BufferedImage destination ) {
104                    this.destination = destination;
105            }
106            
107        /**
108         * Get the destination image.
109         * @return the destination image
110         * @see #setDestination
111         */
112            public BufferedImage getDestination() {
113                    return destination;
114            }
115            
116        /**
117         * Get the clean image.
118         * @param cleanImage the clean image
119         * @see #getCleanImage
120         */
121            public void setCleanImage( BufferedImage cleanImage ) {
122                    this.cleanImage = cleanImage;
123            }
124            
125        /**
126         * Get the clean image.
127         * @return the clean image
128         * @see #setCleanImage
129         */
130            public BufferedImage getCleanImage() {
131                    return cleanImage;
132            }
133                    
134        public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
135            int width = src.getWidth();
136            int height = src.getHeight();
137                    int type = src.getType();
138                    WritableRaster srcRaster = src.getRaster();
139    
140            if ( dst == null )
141                dst = createCompatibleDestImage( src, null );
142                    WritableRaster dstRaster = dst.getRaster();
143    
144            if ( destination != null && cleanImage != null ) {
145                float[] hsb1 = null;
146                float[] hsb2 = null;
147                int[] inPixels = null;
148                int[] outPixels = null;
149                int[] cleanPixels = null;
150                for ( int y = 0; y < height; y++ ) {
151                    inPixels = getRGB( src, 0, y, width, 1, inPixels );
152                    outPixels = getRGB( destination, 0, y, width, 1, outPixels );
153                    cleanPixels = getRGB( cleanImage, 0, y, width, 1, cleanPixels );
154                    for ( int x = 0; x < width; x++ ) {
155                        int rgb1 = inPixels[x];
156                        int out = outPixels[x];
157                        int rgb2 = cleanPixels[x];
158    
159                        int r1 = (rgb1 >> 16) & 0xff;
160                        int g1 = (rgb1 >> 8) & 0xff;
161                        int b1 = rgb1 & 0xff;
162                        int r2 = (rgb2 >> 16) & 0xff;
163                        int g2 = (rgb2 >> 8) & 0xff;
164                        int b2 = rgb2 & 0xff;
165                        hsb1 = Color.RGBtoHSB( r1, b1, g1, hsb1 );
166                        hsb2 = Color.RGBtoHSB( r2, b2, g2, hsb2 );
167    //                    int tolerance = (int)(255*tolerance);
168    //                    return Math.abs(r1-r2) <= tolerance && Math.abs(g1-g2) <= tolerance && Math.abs(b1-b2) <= tolerance;
169    
170     //                   if ( PixelUtils.nearColors( in, clean, (int)(255*tolerance) ) )
171                        if ( Math.abs( hsb1[0] - hsb2[0] ) < hTolerance && Math.abs( hsb1[1] - hsb2[1] ) < sTolerance && Math.abs( hsb1[2] - hsb2[2] ) < bTolerance )
172                            inPixels[x] = out;
173                        else
174                            inPixels[x] = rgb1;
175                    }
176                    setRGB( dst, 0, y, width, 1, inPixels );
177                }
178            }
179    
180            return dst;
181        }
182    
183            public String toString() {
184                    return "Keying/Key...";
185            }
186            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
187                    Object o;
188                    if((o=parameters.removeEL(KeyImpl.init("HTolerance")))!=null)setHTolerance(ImageFilterUtil.toFloatValue(o,"HTolerance"));
189                    if((o=parameters.removeEL(KeyImpl.init("STolerance")))!=null)setSTolerance(ImageFilterUtil.toFloatValue(o,"STolerance"));
190                    if((o=parameters.removeEL(KeyImpl.init("BTolerance")))!=null)setBTolerance(ImageFilterUtil.toFloatValue(o,"BTolerance"));
191                    if((o=parameters.removeEL(KeyImpl.init("CleanImage")))!=null)setCleanImage(ImageFilterUtil.toBufferedImage(o,"CleanImage"));
192                    if((o=parameters.removeEL(KeyImpl.init("destination")))!=null)setDestination(ImageFilterUtil.toBufferedImage(o,"destination"));
193    
194                    // check for arguments not supported
195                    if(parameters.size()>0) {
196                            throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+List.arrayToList(parameters.keysAsString(),", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported [HTolerance, STolerance, BTolerance, CleanImage]");
197                    }
198    
199                    return filter(src, dst);
200            }
201    }