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.List;
026    import railo.runtime.type.Struct;
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 = src.getRaster();
140    
141            if ( dst == null )
142                dst = createCompatibleDestImage( src, null );
143                    WritableRaster dstRaster = dst.getRaster();
144    
145            if ( destination != null && maskImage != null )
146                            composeThroughMask( src.getRaster(), dst.getRaster(), maskImage.getRaster() );
147    
148            return dst;
149        }
150    
151            public String toString() {
152                    return "Keying/Key...";
153            }
154            public BufferedImage filter(BufferedImage src ,Struct parameters) throws PageException {
155                    BufferedImage dst=src;
156                    Object o;
157                    if((o=parameters.removeEL(KeyImpl.init("MaskImage")))!=null)setMaskImage(ImageFilterUtil.toBufferedImage(o,"MaskImage"));
158                    if((o=parameters.removeEL(KeyImpl.init("destination")))!=null)setDestination(ImageFilterUtil.toBufferedImage(o,"destination"));
159    
160                    // check for arguments not supported
161                    if(parameters.size()>0) {
162                            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 [MaskImage]");
163                    }
164    
165                    return filter(src, dst);
166            }
167    }