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.Color; 036import java.awt.Graphics2D; 037import java.awt.Rectangle; 038import java.awt.Shape; 039import java.awt.geom.AffineTransform; 040import java.awt.geom.Point2D; 041import java.awt.image.BufferedImage; 042import java.util.Random; 043 044import lucee.runtime.engine.ThreadLocalPageContext; 045import lucee.runtime.exp.FunctionException; 046import lucee.runtime.exp.PageException; 047import lucee.runtime.img.ImageUtil; 048import lucee.runtime.type.KeyImpl; 049import lucee.runtime.type.Struct; 050import lucee.runtime.type.util.CollectionUtil; 051 052public class ShatterFilter extends AbstractBufferedImageOp implements DynFiltering { 053 private float centreX = 0.5f, centreY = 0.5f; 054 private float distance; 055 private float transition; 056 private float rotation; 057 private float zoom; 058 private float startAlpha = 1; 059 private float endAlpha = 1; 060 private int iterations = 5; 061 private int tile; 062 063 public ShatterFilter() { 064 } 065 066 public void setTransition( float transition ) { 067 this.transition = transition; 068 } 069 070 public float getTransition() { 071 return transition; 072 } 073 074 public void setDistance( float distance ) { 075 this.distance = distance; 076 } 077 078 public float getDistance() { 079 return distance; 080 } 081 082 public void setRotation( float rotation ) { 083 this.rotation = rotation; 084 } 085 086 public float getRotation() { 087 return rotation; 088 } 089 090 public void setZoom( float zoom ) { 091 this.zoom = zoom; 092 } 093 094 public float getZoom() { 095 return zoom; 096 } 097 098 public void setStartAlpha( float startAlpha ) { 099 this.startAlpha = startAlpha; 100 } 101 102 public float getStartAlpha() { 103 return startAlpha; 104 } 105 106 public void setEndAlpha( float endAlpha ) { 107 this.endAlpha = endAlpha; 108 } 109 110 public float getEndAlpha() { 111 return endAlpha; 112 } 113 114 public void setCentreX( float centreX ) { 115 this.centreX = centreX; 116 } 117 118 public float getCentreX() { 119 return centreX; 120 } 121 122 public void setCentreY( float centreY ) { 123 this.centreY = centreY; 124 } 125 126 public float getCentreY() { 127 return centreY; 128 } 129 130 public Point2D getCentre() { 131 return new Point2D.Float( centreX, centreY ); 132 } 133 134 public void setIterations( int iterations ) { 135 this.iterations = iterations; 136 } 137 138 public int getIterations() { 139 return iterations; 140 } 141 142 public void setTile( int tile ) { 143 this.tile = tile; 144 } 145 146 public int getTile() { 147 return tile; 148 } 149 150 static class Tile { 151 float x, y, vx, vy, w, h; 152 float rotation; 153 Shape shape; 154 } 155 156 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 157 if ( dst == null ) 158 dst = createCompatibleDestImage( src, null ); 159 float width = src.getWidth(); 160 float height = src.getHeight(); 161 float cx = src.getWidth() * centreX; 162 float cy = src.getHeight() * centreY; 163 //float imageRadius = (float)Math.sqrt( cx*cx + cy*cy ); 164 165// BufferedImage[] tiles = new BufferedImage[iterations]; 166 int numTiles = iterations*iterations; 167 Tile[] shapes = new Tile[numTiles]; 168 float[] rx = new float[numTiles]; 169 float[] ry = new float[numTiles]; 170 float[] rz = new float[numTiles]; 171 172 Graphics2D g = dst.createGraphics(); 173// g.drawImage( src, null, null ); 174 175 Random random = new Random( 0 ); 176 //float lastx = 0, lasty = 0; 177/* 178 for ( int i = 0; i <= numTiles; i++ ) { 179 double angle = (double)i * 2*Math.PI / numTiles; 180 float x = cx + width*(float)Math.cos(angle); 181 float y = cy + height*(float)Math.sin(angle); 182 g.setColor( Color.black ); 183 g.setColor( Color.getHSBColor( (float)angle, 1, 1 ) ); 184 if ( i != 0 ) { 185 rz[i-1] = tile*(2*random.nextFloat()-1); 186 ry[i-1] = tile*random.nextFloat(); 187 rz[i-1] = tile*random.nextFloat(); 188 GeneralPath p = new GeneralPath(); 189 p.moveTo( cx, cy ); 190 p.lineTo( lastx, lasty ); 191 p.lineTo( x, y ); 192 p.closePath(); 193 shapes[i-1] = p; 194// Rectangle r = p.getBounds(); 195// r.intersect( r, new Rectangle( (int)width, (int)height ), r ); 196 } 197 lastx = x; 198 lasty = y; 199 } 200*/ 201 for ( int y = 0; y < iterations; y++ ) { 202 int y1 = (int)height*y/iterations; 203 int y2 = (int)height*(y+1)/iterations; 204 for ( int x = 0; x < iterations; x++ ) { 205 int i = y*iterations+x; 206 int x1 = (int)width*x/iterations; 207 int x2 = (int)width*(x+1)/iterations; 208 rx[i] = tile*random.nextFloat(); 209 ry[i] = tile*random.nextFloat(); 210 rx[i] = 0; 211 ry[i] = 0; 212 rz[i] = tile*(2*random.nextFloat()-1); 213 Shape p = new Rectangle( x1, y1, x2-x1, y2-y1 ); 214 shapes[i] = new Tile(); 215 shapes[i].shape = p; 216 shapes[i].x = (x1+x2)*0.5f; 217 shapes[i].y = (y1+y2)*0.5f; 218 shapes[i].vx = width-(cx-x); 219 shapes[i].vy = height-(cy-y); 220 shapes[i].w = x2-x1; 221 shapes[i].h = y2-y1; 222 } 223 } 224 225 for ( int i = 0; i < numTiles; i++ ) { 226 float h = (float)i / numTiles; 227 double angle = h * 2*Math.PI; 228 float x = transition * width*(float)Math.cos(angle); 229 float y = transition * height*(float)Math.sin(angle); 230 231 Tile tile = shapes[i]; 232 //Rectangle r = 233 tile.shape.getBounds(); 234 AffineTransform t = g.getTransform(); 235x = tile.x + transition * tile.vx; 236y = tile.y + transition * tile.vy; 237 g.translate( x, y ); 238// g.translate( tile.w*0.5f, tile.h*0.5f ); 239 g.rotate( transition * rz[i] ); 240// g.scale( (float)Math.cos( transition * rx[i] ), (float)Math.cos( transition * ry[i] ) ); 241// g.translate( -tile.w*0.5f, -tile.h*0.5f ); 242 g.setColor( Color.getHSBColor( h, 1, 1 ) ); 243 Shape clip = g.getClip(); 244 g.clip( tile.shape ); 245 g.drawImage( src, 0, 0, null ); 246 g.setClip( clip ); 247 g.setTransform( t ); 248 } 249 250 g.dispose(); 251 return dst; 252 } 253 254 public String toString() { 255 return "Transition/Shatter..."; 256 } 257 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 258 Object o; 259 if((o=parameters.removeEL(KeyImpl.init("Iterations")))!=null)setIterations(ImageFilterUtil.toIntValue(o,"Iterations")); 260 if((o=parameters.removeEL(KeyImpl.init("CentreX")))!=null)setCentreX(ImageFilterUtil.toFloatValue(o,"CentreX")); 261 if((o=parameters.removeEL(KeyImpl.init("CentreY")))!=null)setCentreY(ImageFilterUtil.toFloatValue(o,"CentreY")); 262 //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre")); 263 if((o=parameters.removeEL(KeyImpl.init("Transition")))!=null)setTransition(ImageFilterUtil.toFloatValue(o,"Transition")); 264 if((o=parameters.removeEL(KeyImpl.init("Distance")))!=null)setDistance(ImageFilterUtil.toFloatValue(o,"Distance")); 265 if((o=parameters.removeEL(KeyImpl.init("Rotation")))!=null)setRotation(ImageFilterUtil.toFloatValue(o,"Rotation")); 266 if((o=parameters.removeEL(KeyImpl.init("Zoom")))!=null)setZoom(ImageFilterUtil.toFloatValue(o,"Zoom")); 267 if((o=parameters.removeEL(KeyImpl.init("StartAlpha")))!=null)setStartAlpha(ImageFilterUtil.toFloatValue(o,"StartAlpha")); 268 if((o=parameters.removeEL(KeyImpl.init("EndAlpha")))!=null)setEndAlpha(ImageFilterUtil.toFloatValue(o,"EndAlpha")); 269 if((o=parameters.removeEL(KeyImpl.init("Tile")))!=null)setTile(ImageFilterUtil.toIntValue(o,"Tile")); 270 271 // check for arguments not supported 272 if(parameters.size()>0) { 273 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 [Iterations, CentreX, CentreY, Centre, Transition, Distance, Rotation, Zoom, StartAlpha, EndAlpha, Tile]"); 274 } 275 276 return filter(src, dst); 277 } 278}