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; 018 019 import java.awt.image.BufferedImage; 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 simulates the appearance of looking through glass. A separate grayscale displacement image is provided and 030 * pixels in the source image are displaced according to the gradient of the displacement map. 031 */ 032 public class DisplaceFilter extends TransformFilter implements DynFiltering { 033 034 private float amount = 1; 035 private BufferedImage displacementMap = null; 036 private int[] xmap, ymap; 037 private int dw, dh; 038 039 public DisplaceFilter() { 040 } 041 042 /** 043 * Set the displacement map. 044 * @param displacementMap an image representing the displacment at each point 045 * @see #getDisplacementMap 046 */ 047 public void setDisplacementMap(BufferedImage displacementMap) { 048 this.displacementMap = displacementMap; 049 } 050 051 /** 052 * Get the displacement map. 053 * @return an image representing the displacment at each point 054 * @see #setDisplacementMap 055 */ 056 public BufferedImage getDisplacementMap() { 057 return displacementMap; 058 } 059 060 /** 061 * Set the amount of distortion. 062 * @param amount the amount 063 * @min-value 0 064 * @max-value 1 065 * @see #getAmount 066 */ 067 public void setAmount(float amount) { 068 this.amount = amount; 069 } 070 071 /** 072 * Get the amount of distortion. 073 * @return the amount 074 * @see #setAmount 075 */ 076 public float getAmount() { 077 return amount; 078 } 079 080 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 081 int w = src.getWidth(); 082 int h = src.getHeight(); 083 084 BufferedImage dm = displacementMap != null ? displacementMap : src; 085 086 dw = dm.getWidth(); 087 dh = dm.getHeight(); 088 089 int[] mapPixels = new int[dw*dh]; 090 getRGB( dm, 0, 0, dw, dh, mapPixels ); 091 xmap = new int[dw*dh]; 092 ymap = new int[dw*dh]; 093 094 int i = 0; 095 for ( int y = 0; y < dh; y++ ) { 096 for ( int x = 0; x < dw; x++ ) { 097 int rgb = mapPixels[i]; 098 int r = (rgb >> 16) & 0xff; 099 int g = (rgb >> 8) & 0xff; 100 int b = rgb & 0xff; 101 mapPixels[i] = (r+g+b) / 8; // An arbitrary scaling factor which gives a good range for "amount" 102 i++; 103 } 104 } 105 106 i = 0; 107 for ( int y = 0; y < dh; y++ ) { 108 int j1 = ((y+dh-1) % dh) * dw; 109 int j2 = y*dw; 110 int j3 = ((y+1) % dh) * dw; 111 for ( int x = 0; x < dw; x++ ) { 112 int k1 = (x+dw-1) % dw; 113 int k2 = x; 114 int k3 = (x+1) % dw; 115 xmap[i] = mapPixels[k1+j1] + mapPixels[k1+j2] + mapPixels[k1+j3] - mapPixels[k3+j1] - mapPixels[k3+j2] - mapPixels[k3+j3]; 116 ymap[i] = mapPixels[k1+j3] + mapPixels[k2+j3] + mapPixels[k3+j3] - mapPixels[k1+j1] - mapPixels[k2+j1] - mapPixels[k3+j1]; 117 i++; 118 } 119 } 120 mapPixels = null; 121 dst = super.filter( src, dst ); 122 xmap = ymap = null; 123 return dst; 124 } 125 126 protected void transformInverse(int x, int y, float[] out) { 127 float xDisplacement, yDisplacement; 128 float nx = x; 129 float ny = y; 130 int i = (y % dh)*dw + x % dw; 131 out[0] = x + amount * xmap[i]; 132 out[1] = y + amount * ymap[i]; 133 } 134 135 public String toString() { 136 return "Distort/Displace..."; 137 } 138 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src); 139 Object o; 140 if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount")); 141 if((o=parameters.removeEL(KeyImpl.init("DisplacementMap")))!=null)setDisplacementMap(ImageFilterUtil.toBufferedImage(o,"DisplacementMap")); 142 if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction")); 143 if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation")); 144 145 // check for arguments not supported 146 if(parameters.size()>0) { 147 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 [Amount, DisplacementMap, EdgeAction, Interpolation]"); 148 } 149 150 return filter(src, dst); 151 } 152 }