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