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