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