001    
002    package railo.runtime.img;
003    import java.awt.image.BufferedImage;
004    import java.awt.image.ColorModel;
005    
006    import railo.runtime.exp.ExpressionException;
007    import railo.runtime.img.interpolation.Bessel;
008    import railo.runtime.img.interpolation.Blackman;
009    import railo.runtime.img.interpolation.Hamming;
010    import railo.runtime.img.interpolation.Hanning;
011    import railo.runtime.img.interpolation.Hermite;
012    import railo.runtime.img.interpolation.Interpolation;
013    import railo.runtime.img.interpolation.Lanczos;
014    import railo.runtime.img.interpolation.Mitchell;
015    import railo.runtime.img.interpolation.Quadratic;
016    import railo.runtime.img.interpolation.Triangle;
017    
018    
019    public class ImageResizer       {
020        
021        private static class ContributionInfo {
022            int pixel;
023            double weight;
024            
025            
026            public void setPixel(int pixel) {
027                this.pixel = pixel;
028            }
029            
030            public int getPixel() {
031                return pixel;
032            }
033            
034            public void setWeight(double weight) {
035                this.weight = weight;
036            }
037            
038            public double getWeight() {
039                return weight;
040            }
041        }
042        
043        private static int horizontal(BufferedImage source, BufferedImage destination, double xFactor,Interpolation ip, double blur, ContributionInfo[] contribution) {
044            if (source.getWidth() == destination.getWidth()
045                && source.getHeight() == destination.getHeight()) {
046                destination.setData(source.getData());
047                return destination.getWidth();
048            }
049            double scale = blur * Math.max(1.0 / xFactor, 1.0);
050            double support = Math.max(scale * ip.getSupport(), 0.5);
051            if (support <= 0.5) {
052                support = 0.500000000001;
053                scale = 1.0;
054            }
055            ColorModel cm = ColorModel.getRGBdefault();
056            for (int x = 0; x < destination.getWidth(); x++) {
057                double center = x / xFactor;
058                int start = (int) Math.max(center - support + 0.5, 0.0);
059                int end = (int) Math.min(center + support + 0.5,
060                                         source.getWidth());
061                int n = setContributionWeight(start, end, contribution, ip,
062                                              center, scale);
063                int sourceWidth = (contribution[n - 1].getPixel()
064                                   - contribution[0].getPixel() + 1);
065                int[] sourcePixels = new int[sourceWidth * source.getHeight()];
066                int[] destPixels = new int[destination.getHeight()];
067                sourcePixels = source.getRGB(contribution[0].getPixel(), 0,
068                                             sourceWidth, source.getHeight(),
069                                             sourcePixels, 0, sourceWidth);
070                int destIndex = 0;
071                for (int y = 0; y < destination.getHeight(); y++) {
072                    double blue = 0.0;
073                    double green = 0.0;
074                    double red = 0.0;
075                    double opacity = 0.0;
076                    int[] c = new int[4];
077                    for (int i = 0; i < n; i++) {
078                        int j = (y * (contribution[n - 1].getPixel()
079                                      - contribution[0].getPixel() + 1)
080                                 + (contribution[i].getPixel()
081                                    - contribution[0].getPixel()));
082                        c = cm.getComponents(sourcePixels[j], c, 0);
083                        red += contribution[i].getWeight() * c[0];
084                        green += contribution[i].getWeight() * c[1];
085                        blue += contribution[i].getWeight() * c[2];
086                        opacity += contribution[i].getWeight() * c[3];
087                    }
088                    red = red < 0.0 ? 0.0 : red > 255.0 ? 255.0 : red + 0.5;
089                    green
090                        = green < 0.0 ? 0.0 : green > 255.0 ? 255.0 : green + 0.5;
091                    blue = blue < 0.0 ? 0.0 : blue > 255.0 ? 255.0 : blue + 0.5;
092                    opacity = (opacity < 0.0 ? 0.0 : opacity > 255.0 ? 255.0
093                               : opacity + 0.5);
094                    destPixels[destIndex]
095                        = cm.getDataElement(new int[] { (int) red, (int) green,
096                                                        (int) blue,
097                                                        (int) opacity },
098                                            0);
099                    destIndex++;
100                }
101                destination.setRGB(x, 0, 1, destination.getHeight(), destPixels, 0,
102                                   1);
103            }
104            return destination.getWidth();
105        }
106        
107        private static int setContributionWeight(int start, int end, ContributionInfo[] contribution, Interpolation interpolation, double center, double scale) {
108                    int n = 0;
109                    double density = 0.0;
110                    for (int i = start; i < end; i++) {
111                        contribution[n].setPixel(i);
112                        contribution[n].setWeight(interpolation.f((i - center + 0.5D)
113                                                           / scale) / scale);
114                        density += contribution[n].getWeight();
115                        n++;
116                    }
117                    density = density == 0.0 ? 1.0 : 1.0 / density;
118                    for (int i = 0; i < n; i++)
119                        contribution[i].setWeight(contribution[i].getWeight() * density);
120                    return n;
121        }
122        
123        private static int vertical(BufferedImage source, BufferedImage destination, double y_factor, Interpolation ip, double blur, ContributionInfo[] contribution) {
124            /*if (source.getWidth() == destination.getWidth()
125                && source.getHeight() == destination.getHeight()) {
126                destination.setData(source.getData());
127                return destination.getWidth();
128            }*/
129            double scale = blur * Math.max(1.0 / y_factor, 1.0);
130            double support = Math.max(scale * ip.getSupport(), 0.5);
131            if (support <= 0.5) {
132                support = 0.500000000001;
133                scale = 1.0;
134            }
135            ColorModel cm = ColorModel.getRGBdefault();
136            for (int y = 0; y < destination.getHeight(); y++) {
137                double center =  y / y_factor;
138                int start = (int) Math.max(center - support + 0.5, 0.0);
139                int end = (int) Math.min(center + support + 0.5,
140                                         source.getHeight());
141                int n = setContributionWeight(start, end, contribution, ip,
142                                              center, scale);
143                int sourceHeight = (contribution[n - 1].getPixel()
144                                    - contribution[0].getPixel() + 1);
145                int[] sourcePixels = new int[source.getWidth() * sourceHeight];
146                int[] destPixels = new int[destination.getWidth()];
147                sourcePixels = source.getRGB(0, contribution[0].getPixel(),
148                                             source.getWidth(), sourceHeight,
149                                             sourcePixels, 0, source.getWidth());
150                int destIndex = 0;
151                for (int x = 0; x < destination.getWidth(); x++) {
152                    double blue = 0.0;
153                    double green = 0.0;
154                    double red = 0.0;
155                    double opacity = 0.0;
156                    int[] c = new int[4];
157                    for (int i = 0; i < n; i++) {
158                        int j = ((contribution[i].getPixel()
159                                  - contribution[0].getPixel()) * source.getWidth()
160                                 + x);
161                        c = cm.getComponents(sourcePixels[j], c, 0);
162                        red += contribution[i].getWeight() * c[0];
163                        green += contribution[i].getWeight() * c[1];
164                        blue += contribution[i].getWeight() * c[2];
165                        opacity += contribution[i].getWeight() * c[3];
166                    }
167                    red = red < 0.0 ? 0.0 : red > 255.0 ? 255.0 : red + 0.5;
168                    green
169                        = green < 0.0 ? 0.0 : green > 255.0 ? 255.0 : green + 0.5;
170                    blue = blue < 0.0 ? 0.0 : blue > 255.0 ? 255.0 : blue + 0.5;
171                    opacity = (opacity < 0.0 ? 0.0 : opacity > 255.0 ? 255.0
172                               : opacity + 0.5);
173                    destPixels[destIndex]
174                        = cm.getDataElement(new int[] { (int) red, (int) green,
175                                                        (int) blue,
176                                                        (int) opacity },
177                                            0);
178                    destIndex++;
179                }
180                destination.setRGB(0, y, destination.getWidth(), 1, destPixels, 0,
181                                   destination.getWidth());
182            }
183            return destination.getHeight();
184        }
185        
186    
187        
188        public static BufferedImage resize(BufferedImage image, int columns, int rows, int interpolation, double blur) throws ExpressionException {
189            if (columns == 0 || rows == 0)
190                    throw new ExpressionException("invalid size for image");
191                    
192            BufferedImage resizeImage = ImageUtil.createBufferedImage(image, columns, rows);
193                    
194                    Interpolation inter = getInterpolation(interpolation);
195                    double xFactor = (double) columns / (double) image.getWidth();
196                    double scale = blur * Math.max(1.0 / xFactor, 1.0);
197                    double xSupport = Math.max(scale * inter.getSupport(), 0.5);
198                    double yFactor = (double) rows / (double) image.getHeight();
199                    scale = blur * Math.max(1.0 / yFactor, 1.0);
200                    double ySupport = Math.max(scale * inter.getSupport(), 0.5);
201                    double support = Math.max(xSupport, ySupport);
202                    if (support < inter.getSupport())
203                        support = inter.getSupport();
204                    ContributionInfo[] contribution = new ContributionInfo[(int) support * 2 + 3];
205                    
206                    for (int cloop = 0; cloop < (int) support * 2 + 3; cloop++)
207                        contribution[cloop] = new ContributionInfo();
208                    
209                    int status;
210                    if (columns * (image.getHeight() + rows) < rows * (image.getWidth() + columns)) {
211                        BufferedImage sourceImage = ImageUtil.createBufferedImage(image, columns, image.getHeight());
212                        status = horizontal(image, sourceImage, xFactor, inter, blur, contribution);
213                        status |= vertical(sourceImage, resizeImage, yFactor,inter, blur, contribution);
214                    } 
215                    else {
216                        BufferedImage sourceImage = ImageUtil.createBufferedImage(image, image.getWidth(), rows);
217                        status = vertical(image, sourceImage, yFactor, inter, blur, contribution);
218                        status |= horizontal(sourceImage, resizeImage, xFactor, inter, blur, contribution);
219                    }
220                    
221                    if (status == 0) throw new ExpressionException("can't resize image");
222                    return resizeImage;
223        }
224        
225       
226        
227       
228        
229        private static Interpolation getInterpolation(int interpolation) throws ExpressionException {
230                    switch (interpolation) {
231                    
232                            case 0: return new Triangle();
233                            case Image.IP_HERMITE:  return new Hermite();
234                            case Image.IP_HANNING:  return new Hanning();
235                            case Image.IP_MEDIUMQUALITY:
236                            case Image.IP_HIGHPERFORMANCE:
237                            case Image.IP_HAMMING:  return new Hamming();
238                            case Image.IP_BLACKMAN: return new Blackman();
239                            case Image.IP_QUADRATIC:return new Quadratic();
240                            case Image.IP_HIGHQUALITY:
241                            case Image.IP_MEDIUMPERFORMANCE:
242                            case Image.IP_MITCHELL: return new Mitchell();
243                            case Image.IP_HIGHESTQUALITY:
244                            case Image.IP_LANCZOS:  return new Lanczos();
245                            case Image.IP_BESSEL:   return new Bessel();
246                            default:        throw new ExpressionException("invalid interpolation definition");
247                    }
248        }
249        
250        
251    }