001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019/*
020*
021
022Licensed under the Apache License, Version 2.0 (the "License");
023you may not use this file except in compliance with the License.
024You may obtain a copy of the License at
025
026   http://www.apache.org/licenses/LICENSE-2.0
027
028Unless required by applicable law or agreed to in writing, software
029distributed under the License is distributed on an "AS IS" BASIS,
030WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
031See the License for the specific language governing permissions and
032limitations under the License.
033*/
034
035package lucee.runtime.img.filter;import java.awt.image.BufferedImage;
036import java.awt.image.Raster;
037import java.awt.image.WritableRaster;
038
039import lucee.runtime.engine.ThreadLocalPageContext;
040import lucee.runtime.exp.FunctionException;
041import lucee.runtime.exp.PageException;
042import lucee.runtime.type.KeyImpl;
043import lucee.runtime.type.Struct;
044import lucee.runtime.type.util.CollectionUtil;
045
046/**
047 * A filter which uses the alpha channel of a "mask" image to interpolate between a source and destination image.
048 */
049public class ApplyMaskFilter extends AbstractBufferedImageOp  implements DynFiltering {
050        
051        private BufferedImage destination;
052        private BufferedImage maskImage;
053
054    /**
055     * Construct an ApplyMaskFIlter.
056     */
057        public ApplyMaskFilter() {
058        }
059
060    /**
061     * Construct an ApplyMaskFIlter.
062     * @param maskImage the mask image
063     * @param destination the destination image
064     */
065        public ApplyMaskFilter( BufferedImage maskImage, BufferedImage destination ) {
066                this.maskImage = maskImage;
067                this.destination = destination;
068        }
069
070    /**
071     * Set the destination image.
072     * @param destination the destination image
073     * @see #getDestination
074     */
075        public void setDestination( BufferedImage destination ) {
076                this.destination = destination;
077        }
078        
079    /**
080     * Get the destination image.
081     * @return the destination image
082     * @see #setDestination
083     */
084        public BufferedImage getDestination() {
085                return destination;
086        }
087        
088    /**
089     * Set the mask image.
090     * @param maskImage the mask image
091     * @see #getMaskImage
092     */
093        public void setMaskImage( BufferedImage maskImage ) {
094                this.maskImage = maskImage;
095        }
096        
097    /**
098     * Get the mask image.
099     * @return the mask image
100     * @see #setMaskImage
101     */
102        public BufferedImage getMaskImage() {
103                return maskImage;
104        }
105                
106    /**
107     * Interpolates between two rasters according to the alpha level of a mask raster.
108     * @param src the source raster
109     * @param dst the destination raster
110     * @param sel the mask raster
111     */
112        public static void composeThroughMask(Raster src, WritableRaster dst, Raster sel) {
113                int x = src.getMinX();
114                int y = src.getMinY();
115                int w = src.getWidth();
116                int h = src.getHeight();
117
118                int srcRGB[] = null;
119                int selRGB[] = null;
120                int dstRGB[] = null;
121
122                for ( int i = 0; i < h; i++ ) {
123                        srcRGB = src.getPixels(x, y, w, 1, srcRGB);
124                        selRGB = sel.getPixels(x, y, w, 1, selRGB);
125                        dstRGB = dst.getPixels(x, y, w, 1, dstRGB);
126
127                        int k = x;
128                        for ( int j = 0; j < w; j++ ) {
129                                int sr = srcRGB[k];
130                                int dir = dstRGB[k];
131                                int sg = srcRGB[k+1];
132                                int dig = dstRGB[k+1];
133                                int sb = srcRGB[k+2];
134                                int dib = dstRGB[k+2];
135                                int sa = srcRGB[k+3];
136                                int dia = dstRGB[k+3];
137
138                                float a = selRGB[k+3]/255f;
139                                float ac = 1-a;
140
141                                dstRGB[k] = (int)(a*sr + ac*dir); 
142                                dstRGB[k+1] = (int)(a*sg + ac*dig); 
143                                dstRGB[k+2] = (int)(a*sb + ac*dib); 
144                                dstRGB[k+3] = (int)(a*sa + ac*dia);
145                                k += 4;
146                        }
147
148                        dst.setPixels(x, y, w, 1, dstRGB);
149                        y++;
150                }
151        }
152
153    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
154        //int width = src.getWidth();
155        //int height = src.getHeight();
156                //int type = src.getType();
157                //WritableRaster srcRaster = 
158        src.getRaster();
159
160        if ( dst == null )
161            dst = createCompatibleDestImage( src, null );
162                //WritableRaster dstRaster = 
163        dst.getRaster();
164
165        if ( destination != null && maskImage != null )
166                        composeThroughMask( src.getRaster(), dst.getRaster(), maskImage.getRaster() );
167
168        return dst;
169    }
170
171        public String toString() {
172                return "Keying/Key...";
173        }
174        public BufferedImage filter(BufferedImage src ,Struct parameters) throws PageException {
175                BufferedImage dst=src;
176                Object o;
177                if((o=parameters.removeEL(KeyImpl.init("MaskImage")))!=null)setMaskImage(ImageFilterUtil.toBufferedImage(o,"MaskImage"));
178                if((o=parameters.removeEL(KeyImpl.init("destination")))!=null)setDestination(ImageFilterUtil.toBufferedImage(o,"destination"));
179
180                // check for arguments not supported
181                if(parameters.size()>0) {
182                        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]");
183                }
184
185                return filter(src, dst);
186        }
187}