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.Struct;
032    import railo.runtime.type.util.CollectionUtil;
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 = 
215                            tile.shape.getBounds();
216                            AffineTransform t = g.getTransform();
217    x = tile.x + transition * tile.vx;
218    y = tile.y + transition * tile.vy;
219                            g.translate( x, y );
220    //                      g.translate( tile.w*0.5f, tile.h*0.5f );
221                            g.rotate( transition * rz[i] );
222    //                      g.scale( (float)Math.cos( transition * rx[i] ), (float)Math.cos( transition * ry[i] ) );
223    //                      g.translate( -tile.w*0.5f, -tile.h*0.5f );
224                            g.setColor( Color.getHSBColor( h, 1, 1 ) );
225                            Shape clip = g.getClip();
226                            g.clip( tile.shape );
227                            g.drawImage( src, 0, 0, null );
228                            g.setClip( clip );
229                            g.setTransform( t );
230                    }
231    
232                    g.dispose();
233            return dst;
234        }
235        
236            public String toString() {
237                    return "Transition/Shatter...";
238            }
239            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
240                    Object o;
241                    if((o=parameters.removeEL(KeyImpl.init("Iterations")))!=null)setIterations(ImageFilterUtil.toIntValue(o,"Iterations"));
242                    if((o=parameters.removeEL(KeyImpl.init("CentreX")))!=null)setCentreX(ImageFilterUtil.toFloatValue(o,"CentreX"));
243                    if((o=parameters.removeEL(KeyImpl.init("CentreY")))!=null)setCentreY(ImageFilterUtil.toFloatValue(o,"CentreY"));
244                    //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre"));
245                    if((o=parameters.removeEL(KeyImpl.init("Transition")))!=null)setTransition(ImageFilterUtil.toFloatValue(o,"Transition"));
246                    if((o=parameters.removeEL(KeyImpl.init("Distance")))!=null)setDistance(ImageFilterUtil.toFloatValue(o,"Distance"));
247                    if((o=parameters.removeEL(KeyImpl.init("Rotation")))!=null)setRotation(ImageFilterUtil.toFloatValue(o,"Rotation"));
248                    if((o=parameters.removeEL(KeyImpl.init("Zoom")))!=null)setZoom(ImageFilterUtil.toFloatValue(o,"Zoom"));
249                    if((o=parameters.removeEL(KeyImpl.init("StartAlpha")))!=null)setStartAlpha(ImageFilterUtil.toFloatValue(o,"StartAlpha"));
250                    if((o=parameters.removeEL(KeyImpl.init("EndAlpha")))!=null)setEndAlpha(ImageFilterUtil.toFloatValue(o,"EndAlpha"));
251                    if((o=parameters.removeEL(KeyImpl.init("Tile")))!=null)setTile(ImageFilterUtil.toIntValue(o,"Tile"));
252    
253                    // check for arguments not supported
254                    if(parameters.size()>0) {
255                            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]");
256                    }
257    
258                    return filter(src, dst);
259            }
260    }