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.math;
018    
019    import java.awt.Image;
020    import java.awt.image.BufferedImage;
021    import java.awt.image.ImageObserver;
022    import java.awt.image.PixelGrabber;
023    
024    import railo.runtime.img.filter.ImageMath;
025    import railo.runtime.img.filter.PixelUtils;
026    
027    public class ImageFunction2D implements Function2D {
028    
029            public final static int ZERO = 0;
030            public final static int CLAMP = 1;
031            public final static int WRAP = 2;
032            
033            protected int[] pixels;
034            protected int width;
035            protected int height;
036            protected int edgeAction = ZERO;
037            protected boolean alpha = false;
038            
039            public ImageFunction2D(BufferedImage image) {
040                    this(image, false);
041            }
042            
043            public ImageFunction2D(BufferedImage image, boolean alpha) {
044                    this(image, ZERO, alpha);
045            }
046            
047            public ImageFunction2D(BufferedImage image, int edgeAction, boolean alpha) {
048                    init( getRGB( image, 0, 0, image.getWidth(), image.getHeight(), null), image.getWidth(), image.getHeight(), edgeAction, alpha);
049            }
050            
051            public ImageFunction2D(int[] pixels, int width, int height, int edgeAction, boolean alpha) {
052                    init(pixels, width, height, edgeAction, alpha);
053            }
054            
055            public ImageFunction2D(Image image) {
056                    this( image, ZERO, false );
057            }
058            
059            public ImageFunction2D(Image image, int edgeAction, boolean alpha) {
060                    PixelGrabber pg = new PixelGrabber(image, 0, 0, -1, -1, null, 0, -1);
061                    try {
062                            pg.grabPixels();
063                    } catch (InterruptedException e) {
064                            throw new RuntimeException("interrupted waiting for pixels!");
065                    }
066                    if ((pg.status() & ImageObserver.ABORT) != 0) {
067                            throw new RuntimeException("image fetch aborted");
068                    }
069                    init((int[])pg.getPixels(), pg.getWidth(), pg.getHeight(), edgeAction, alpha);
070            }
071    
072            /**
073             * A convenience method for getting ARGB pixels from an image. This tries to avoid the performance
074             * penalty of BufferedImage.getRGB unmanaging the image.
075             */
076            public int[] getRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) {
077                    int type = image.getType();
078                    if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB )
079                            return (int [])image.getRaster().getDataElements( x, y, width, height, pixels );
080                    return image.getRGB( x, y, width, height, pixels, 0, width );
081        }
082    
083            public void init(int[] pixels, int width, int height, int edgeAction, boolean alpha) {
084                    this.pixels = pixels;
085                    this.width = width;
086                    this.height = height;
087                    this.edgeAction = edgeAction;
088                    this.alpha = alpha;
089            }
090            
091            public float evaluate(float x, float y) {
092                    int ix = (int)x;
093                    int iy = (int)y;
094                    if (edgeAction == WRAP) {
095                            ix = ImageMath.mod(ix, width);
096                            iy = ImageMath.mod(iy, height);
097                    } else if (ix < 0 || iy < 0 || ix >= width || iy >= height) {
098                            if (edgeAction == ZERO)
099                                    return 0;
100                            if (ix < 0)
101                                    ix = 0;
102                            else if (ix >= width)
103                                    ix = width-1;
104                            if (iy < 0)
105                                    iy = 0;
106                            else if (iy >= height)
107                                    iy = height-1;
108                    }
109                    return alpha ? ((pixels[iy*width+ix] >> 24) & 0xff) / 255.0f : PixelUtils.brightness(pixels[iy*width+ix]) / 255.0f;
110            }
111            
112            public void setEdgeAction(int edgeAction) {
113                    this.edgeAction = edgeAction;
114            }
115    
116            public int getEdgeAction() {
117                    return edgeAction;
118            }
119    
120            public int getWidth() {
121                    return width;
122            }
123            
124            public int getHeight() {
125                    return height;
126            }
127            
128            public int[] getPixels() {
129                    return pixels;
130            }
131    }
132