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.Point; 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 class which warps an image using a field Warp algorithm. 030 */ 031 public class FieldWarpFilter extends TransformFilter implements DynFiltering { 032 033 public static class Line { 034 public int x1, y1, x2, y2; 035 public int dx, dy; 036 public float length, lengthSquared; 037 038 public Line(int x1, int y1, int x2, int y2) { 039 this.x1 = x1; 040 this.y1 = y1; 041 this.x2 = x2; 042 this.y2 = y2; 043 } 044 045 public void setup() { 046 dx = x2-x1; 047 dy = y2-y1; 048 lengthSquared = dx*dx + dy*dy; 049 length = (float)Math.sqrt(lengthSquared); 050 } 051 } 052 053 private float amount = 1.0f; 054 private float power = 1.0f; 055 private float strength = 2.0f; 056 private Line[] inLines; 057 private Line[] outLines; 058 private Line[] intermediateLines; 059 private float width, height; 060 061 public FieldWarpFilter() { 062 } 063 064 /** 065 * Set the amount of warp. 066 * @param amount the amount 067 * @min-value 0 068 * @max-value 1 069 * @see #getAmount 070 */ 071 public void setAmount(float amount) { 072 this.amount = amount; 073 } 074 075 /** 076 * Get the amount of warp. 077 * @return the amount 078 * @see #setAmount 079 */ 080 public float getAmount() { 081 return amount; 082 } 083 084 public void setPower(float power) { 085 this.power = power; 086 } 087 088 public float getPower() { 089 return power; 090 } 091 092 public void setStrength(float strength) { 093 this.strength = strength; 094 } 095 096 public float getStrength() { 097 return strength; 098 } 099 100 public void setInLines( Line[] inLines ) { 101 this.inLines = inLines; 102 } 103 104 public Line[] getInLines() { 105 return inLines; 106 } 107 108 public void setOutLines( Line[] outLines ) { 109 this.outLines = outLines; 110 } 111 112 public Line[] getOutLines() { 113 return outLines; 114 } 115 116 protected void transform(int x, int y, Point out) { 117 } 118 119 protected void transformInverse(int x, int y, float[] out) { 120 float u = 0, v = 0; 121 float fraction = 0; 122 float distance; 123 float fdist; 124 float weight; 125 float a = 0.001f; 126 float b = 1.5f*strength + 0.5f; 127 float p = power; 128 129 float totalWeight = 0.0f; 130 float sumX = 0.0f; 131 float sumY = 0.0f; 132 133 for (int line = 0; line < inLines.length; line++) { 134 Line l1 = inLines[line]; 135 Line l = intermediateLines[line]; 136 float dx = x - l.x1; 137 float dy = y - l.y1; 138 139 fraction = (dx * l.dx + dy * l.dy) / l.lengthSquared; 140 fdist = (dy * l.dx - dx * l.dy) / l.length; 141 if (fraction <= 0) 142 distance = (float)Math.sqrt(dx*dx + dy*dy); 143 else if (fraction >= 1) { 144 dx = x - l.x2; 145 dy = y - l.y2; 146 distance = (float)Math.sqrt(dx*dx + dy*dy); 147 } else if (fdist >= 0) 148 distance = fdist; 149 else 150 distance = -fdist; 151 u = l1.x1 + fraction * l1.dx - fdist * l1.dy / l1.length; 152 v = l1.y1 + fraction * l1.dy + fdist * l1.dx / l1.length; 153 154 weight = (float)Math.pow(Math.pow(l.length, p) / (a + distance), b); 155 156 sumX += (u - x) * weight; 157 sumY += (v - y) * weight; 158 //if (x % 10 == 0&&y == 20)System.out.println("distance="+distance+" weight="+weight+" sumX="+sumX+" sumY="+sumY+" u="+u+" v="+v); 159 totalWeight += weight; 160 } 161 162 // out[0] = ImageMath.clamp(x + sumX / totalWeight + 0.5f, 0, width-1); 163 // out[1] = ImageMath.clamp(y + sumY / totalWeight + 0.5f, 0, height-1); 164 out[0] = x + sumX / totalWeight + 0.5f; 165 out[1] = y + sumY / totalWeight + 0.5f; 166 } 167 168 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 169 this.width = width; 170 this.height = height; 171 if ( inLines != null && outLines != null ) { 172 intermediateLines = new Line[inLines.length]; 173 for (int line = 0; line < inLines.length; line++) { 174 Line l = intermediateLines[line] = new Line( 175 ImageMath.lerp(amount, inLines[line].x1, outLines[line].x1), 176 ImageMath.lerp(amount, inLines[line].y1, outLines[line].y1), 177 ImageMath.lerp(amount, inLines[line].x2, outLines[line].x2), 178 ImageMath.lerp(amount, inLines[line].y2, outLines[line].y2) 179 ); 180 l.setup(); 181 inLines[line].setup(); 182 } 183 dst = super.filter( src, dst ); 184 intermediateLines = null; 185 return dst; 186 } 187 return src; 188 } 189 190 public String toString() { 191 return "Distort/Field Warp..."; 192 } 193 194 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 195 Object o; 196 if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount")); 197 if((o=parameters.removeEL(KeyImpl.init("Power")))!=null)setPower(ImageFilterUtil.toFloatValue(o,"Power")); 198 if((o=parameters.removeEL(KeyImpl.init("Strength")))!=null)setStrength(ImageFilterUtil.toFloatValue(o,"Strength")); 199 if((o=parameters.removeEL(KeyImpl.init("InLines")))!=null)setInLines(ImageFilterUtil.toAFieldWarpFilter$Line(o,"InLines")); 200 if((o=parameters.removeEL(KeyImpl.init("OutLines")))!=null)setOutLines(ImageFilterUtil.toAFieldWarpFilter$Line(o,"OutLines")); 201 if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction")); 202 if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation")); 203 204 // check for arguments not supported 205 if(parameters.size()>0) { 206 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 [Amount, Power, Strength, InLines, OutLines, EdgeAction, Interpolation]"); 207 } 208 209 return filter(src, dst); 210 } 211 }