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