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.Rectangle; 018 import java.awt.image.BufferedImage; 019 020 import railo.runtime.engine.ThreadLocalPageContext; 021 import railo.runtime.exp.FunctionException; 022 import railo.runtime.exp.PageException; 023 import railo.runtime.img.ImageUtil; 024 import railo.runtime.type.KeyImpl; 025 import railo.runtime.type.Struct; 026 import railo.runtime.type.util.CollectionUtil; 027 028 /** 029 * A filter for warping images using the gridwarp algorithm. 030 * You need to supply two warp grids, one for the source image and 031 * one for the destination image. The image will be warped so that 032 * a point in the source grid moves to its counterpart in the destination 033 * grid. 034 */ 035 public class WarpFilter extends WholeImageFilter implements DynFiltering { 036 037 private WarpGrid sourceGrid; 038 private WarpGrid destGrid; 039 private int frames = 1; 040 041 private BufferedImage morphImage; 042 private float time; 043 044 /** 045 * Create a WarpFilter. 046 */ 047 public WarpFilter() { 048 //this(new WarpGrid(),new WarpGrid()); 049 } 050 051 /** 052 * Create a WarpFilter with two warp grids. 053 * @param sourceGrid the source grid 054 * @param destGrid the destination grid 055 */ 056 public WarpFilter(WarpGrid sourceGrid, WarpGrid destGrid) { 057 this.sourceGrid = sourceGrid; 058 this.destGrid = destGrid; 059 } 060 061 /** 062 * Set the source warp grid. 063 * @param sourceGrid the source grid 064 * @see #getSourceGrid 065 */ 066 public void setSourceGrid(WarpGrid sourceGrid) { 067 this.sourceGrid = sourceGrid; 068 } 069 070 /** 071 * Get the source warp grid. 072 * @return the source grid 073 * @see #setSourceGrid 074 */ 075 public WarpGrid getSourceGrid() { 076 return sourceGrid; 077 } 078 079 /** 080 * Set the destination warp grid. 081 * @param destGrid the destination grid 082 * @see #getDestGrid 083 */ 084 public void setDestGrid(WarpGrid destGrid) { 085 this.destGrid = destGrid; 086 } 087 088 /** 089 * Get the destination warp grid. 090 * @return the destination grid 091 * @see #setDestGrid 092 */ 093 public WarpGrid getDestGrid() { 094 return destGrid; 095 } 096 097 public void setFrames(int frames) { 098 this.frames = frames; 099 } 100 101 public int getFrames() { 102 return frames; 103 } 104 105 /** 106 * For morphing, sets the image we're morphing to. If not, set then we're just warping. 107 */ 108 public void setMorphImage(BufferedImage morphImage) { 109 this.morphImage = morphImage; 110 } 111 112 public BufferedImage getMorphImage() { 113 return morphImage; 114 } 115 116 public void setTime(float time) { 117 this.time = time; 118 } 119 120 public float getTime() { 121 return time; 122 } 123 124 protected void transformSpace(Rectangle r) { 125 r.width *= frames; 126 } 127 128 protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) { 129 int[] outPixels = new int[width * height]; 130 131 if ( morphImage != null ) { 132 int[] morphPixels = getRGB( morphImage, 0, 0, width, height, null ); 133 morph( inPixels, morphPixels, outPixels, sourceGrid, destGrid, width, height, time ); 134 } else if (frames <= 1) { 135 sourceGrid.warp(inPixels, width, height, sourceGrid, destGrid, outPixels); 136 } else { 137 WarpGrid newGrid = new WarpGrid(sourceGrid.rows, sourceGrid.cols, width, height); 138 for (int i = 0; i < frames; i++) { 139 float t = (float)i/(frames-1); 140 sourceGrid.lerp(t, destGrid, newGrid); 141 sourceGrid.warp(inPixels, width, height, sourceGrid, newGrid, outPixels); 142 } 143 } 144 return outPixels; 145 } 146 147 public void morph(int[] srcPixels, int[] destPixels, int[] outPixels, WarpGrid srcGrid, WarpGrid destGrid, int width, int height, float t) { 148 WarpGrid newGrid = new WarpGrid(srcGrid.rows, srcGrid.cols, width, height); 149 srcGrid.lerp(t, destGrid, newGrid); 150 srcGrid.warp(srcPixels, width, height, srcGrid, newGrid, outPixels); 151 int[] destPixels2 = new int[width * height]; 152 destGrid.warp(destPixels, width, height, destGrid, newGrid, destPixels2); 153 crossDissolve(outPixels, destPixels2, width, height, t); 154 } 155 156 public void crossDissolve(int[] pixels1, int[] pixels2, int width, int height, float t) { 157 int index = 0; 158 for (int y = 0; y < height; y++) { 159 for (int x = 0; x < width; x++) { 160 pixels1[index] = ImageMath.mixColors(t, pixels1[index], pixels2[index]); 161 index++; 162 } 163 } 164 } 165 166 public String toString() { 167 return "Distort/Mesh Warp..."; 168 } 169 170 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 171 Object o; 172 if((o=parameters.removeEL(KeyImpl.init("SourceGrid")))!=null)setSourceGrid(ImageFilterUtil.toWarpGrid(o,"SourceGrid")); 173 if((o=parameters.removeEL(KeyImpl.init("DestGrid")))!=null)setDestGrid(ImageFilterUtil.toWarpGrid(o,"DestGrid")); 174 if((o=parameters.removeEL(KeyImpl.init("Frames")))!=null)setFrames(ImageFilterUtil.toIntValue(o,"Frames")); 175 if((o=parameters.removeEL(KeyImpl.init("MorphImage")))!=null)setMorphImage(ImageFilterUtil.toBufferedImage(o,"MorphImage")); 176 if((o=parameters.removeEL(KeyImpl.init("Time")))!=null)setTime(ImageFilterUtil.toFloatValue(o,"Time")); 177 178 // check for arguments not supported 179 if(parameters.size()>0) { 180 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 [SourceGrid, DestGrid, Frames, MorphImage, Time]"); 181 } 182 183 return filter(src, dst); 184 } 185 } 186