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