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.image.BufferedImage;
018    import java.awt.image.Raster;
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.type.KeyImpl;
025    import railo.runtime.type.Struct;
026    import railo.runtime.type.util.CollectionUtil;
027    
028    /**
029     * A filter which uses the alpha channel of a "mask" image to interpolate between a source and destination image.
030     */
031    public class ApplyMaskFilter extends AbstractBufferedImageOp  implements DynFiltering {
032            
033            private BufferedImage destination;
034            private BufferedImage maskImage;
035    
036        /**
037         * Construct an ApplyMaskFIlter.
038         */
039            public ApplyMaskFilter() {
040            }
041    
042        /**
043         * Construct an ApplyMaskFIlter.
044         * @param maskImage the mask image
045         * @param destination the destination image
046         */
047            public ApplyMaskFilter( BufferedImage maskImage, BufferedImage destination ) {
048                    this.maskImage = maskImage;
049                    this.destination = destination;
050            }
051    
052        /**
053         * Set the destination image.
054         * @param destination the destination image
055         * @see #getDestination
056         */
057            public void setDestination( BufferedImage destination ) {
058                    this.destination = destination;
059            }
060            
061        /**
062         * Get the destination image.
063         * @return the destination image
064         * @see #setDestination
065         */
066            public BufferedImage getDestination() {
067                    return destination;
068            }
069            
070        /**
071         * Set the mask image.
072         * @param maskImage the mask image
073         * @see #getMaskImage
074         */
075            public void setMaskImage( BufferedImage maskImage ) {
076                    this.maskImage = maskImage;
077            }
078            
079        /**
080         * Get the mask image.
081         * @return the mask image
082         * @see #setMaskImage
083         */
084            public BufferedImage getMaskImage() {
085                    return maskImage;
086            }
087                    
088        /**
089         * Interpolates between two rasters according to the alpha level of a mask raster.
090         * @param src the source raster
091         * @param dst the destination raster
092         * @param sel the mask raster
093         */
094            public static void composeThroughMask(Raster src, WritableRaster dst, Raster sel) {
095                    int x = src.getMinX();
096                    int y = src.getMinY();
097                    int w = src.getWidth();
098                    int h = src.getHeight();
099    
100                    int srcRGB[] = null;
101                    int selRGB[] = null;
102                    int dstRGB[] = null;
103    
104                    for ( int i = 0; i < h; i++ ) {
105                            srcRGB = src.getPixels(x, y, w, 1, srcRGB);
106                            selRGB = sel.getPixels(x, y, w, 1, selRGB);
107                            dstRGB = dst.getPixels(x, y, w, 1, dstRGB);
108    
109                            int k = x;
110                            for ( int j = 0; j < w; j++ ) {
111                                    int sr = srcRGB[k];
112                                    int dir = dstRGB[k];
113                                    int sg = srcRGB[k+1];
114                                    int dig = dstRGB[k+1];
115                                    int sb = srcRGB[k+2];
116                                    int dib = dstRGB[k+2];
117                                    int sa = srcRGB[k+3];
118                                    int dia = dstRGB[k+3];
119    
120                                    float a = selRGB[k+3]/255f;
121                                    float ac = 1-a;
122    
123                                    dstRGB[k] = (int)(a*sr + ac*dir); 
124                                    dstRGB[k+1] = (int)(a*sg + ac*dig); 
125                                    dstRGB[k+2] = (int)(a*sb + ac*dib); 
126                                    dstRGB[k+3] = (int)(a*sa + ac*dia);
127                                    k += 4;
128                            }
129    
130                            dst.setPixels(x, y, w, 1, dstRGB);
131                            y++;
132                    }
133            }
134    
135        public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
136            //int width = src.getWidth();
137            //int height = src.getHeight();
138                    //int type = src.getType();
139                    //WritableRaster srcRaster = 
140            src.getRaster();
141    
142            if ( dst == null )
143                dst = createCompatibleDestImage( src, null );
144                    //WritableRaster dstRaster = 
145            dst.getRaster();
146    
147            if ( destination != null && maskImage != null )
148                            composeThroughMask( src.getRaster(), dst.getRaster(), maskImage.getRaster() );
149    
150            return dst;
151        }
152    
153            public String toString() {
154                    return "Keying/Key...";
155            }
156            public BufferedImage filter(BufferedImage src ,Struct parameters) throws PageException {
157                    BufferedImage dst=src;
158                    Object o;
159                    if((o=parameters.removeEL(KeyImpl.init("MaskImage")))!=null)setMaskImage(ImageFilterUtil.toBufferedImage(o,"MaskImage"));
160                    if((o=parameters.removeEL(KeyImpl.init("destination")))!=null)setDestination(ImageFilterUtil.toBufferedImage(o,"destination"));
161    
162                    // check for arguments not supported
163                    if(parameters.size()>0) {
164                            throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+CollectionUtil.getKeyList(parameters,", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported [MaskImage]");
165                    }
166    
167                    return filter(src, dst);
168            }
169    }