1 16 package org.apache.cocoon.reading; 17 18 import java.awt.color.ColorSpace ; 19 import java.awt.geom.AffineTransform ; 20 import java.awt.image.AffineTransformOp ; 21 import java.awt.image.BufferedImage ; 22 import java.awt.image.ColorConvertOp ; 23 import java.awt.image.RescaleOp ; 24 import java.awt.image.WritableRaster ; 25 import java.io.ByteArrayOutputStream ; 26 import java.io.IOException ; 27 import java.io.InputStream ; 28 import java.io.Serializable ; 29 import java.util.Map ; 30 31 import org.apache.avalon.framework.parameters.Parameters; 32 import org.apache.cocoon.ProcessingException; 33 import org.apache.cocoon.environment.SourceResolver; 34 import org.apache.cocoon.reading.ResourceReader; 35 import org.apache.commons.lang.SystemUtils; 36 import org.xml.sax.SAXException ; 37 38 import com.sun.image.codec.jpeg.ImageFormatException; 39 import com.sun.image.codec.jpeg.JPEGCodec; 40 import com.sun.image.codec.jpeg.JPEGDecodeParam; 41 import com.sun.image.codec.jpeg.JPEGEncodeParam; 42 import com.sun.image.codec.jpeg.JPEGImageDecoder; 43 import com.sun.image.codec.jpeg.JPEGImageEncoder; 44 45 100 final public class ImageReader extends ResourceReader { 101 private static final boolean GRAYSCALE_DEFAULT = false; 102 private static final boolean ENLARGE_DEFAULT = true; 103 private static final boolean FIT_DEFAULT = false; 104 105 106 private static final boolean JVMBugFixed = SystemUtils.isJavaVersionAtLeast(1.4f); 107 108 private int width; 109 private int height; 110 private float[] scaleColor = new float[3]; 111 private float[] offsetColor = new float[3]; 112 private float[] quality = new float[1]; 113 114 private boolean enlarge; 115 private boolean fitUniform; 116 private boolean usePercent; 117 private RescaleOp colorFilter; 118 private ColorConvertOp grayscaleFilter; 119 120 121 public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) 122 throws ProcessingException, SAXException , IOException { 123 124 char lastChar; 125 String tmpWidth = par.getParameter("width", "0"); 126 String tmpHeight = par.getParameter("height", "0"); 127 128 this.scaleColor[0] = par.getParameterAsFloat("scaleRed", -1.0f); 129 this.scaleColor[1] = par.getParameterAsFloat("scaleGreen", -1.0f); 130 this.scaleColor[2] = par.getParameterAsFloat("scaleBlue", -1.0f); 131 this.offsetColor[0] = par.getParameterAsFloat("offsetRed", 0.0f); 132 this.offsetColor[1] = par.getParameterAsFloat("offsetGreen", 0.0f); 133 this.offsetColor[2] = par.getParameterAsFloat("offsetBlue", 0.0f); 134 this.quality[0] = par.getParameterAsFloat("quality", 0.9f); 135 136 boolean filterColor = false; 137 for (int i = 0; i < 3; ++i) { 138 if (this.scaleColor[i] != -1.0f) { 139 filterColor = true; 140 } else { 141 this.scaleColor[i] = 1.0f; 142 } 143 if (this.offsetColor[i] != 0.0f) { 144 filterColor = true; 145 } 146 } 147 148 if (filterColor) { 149 this.colorFilter = new RescaleOp (scaleColor, offsetColor, null); 150 } 151 152 usePercent = false; 153 lastChar = tmpWidth.charAt(tmpWidth.length() - 1); 154 if (lastChar == '%') { 155 usePercent = true; 156 width = Integer.parseInt(tmpWidth.substring(0, tmpWidth.length() - 1)); 157 } else { 158 width = Integer.parseInt(tmpWidth); 159 } 160 161 lastChar = tmpHeight.charAt(tmpHeight.length() - 1); 162 if(lastChar == '%') { 163 usePercent = true; 164 height = Integer.parseInt(tmpHeight.substring(0, tmpHeight.length() - 1)); 165 } else { 166 height = Integer.parseInt(tmpHeight); 167 } 168 169 if (par.getParameterAsBoolean("grayscale", GRAYSCALE_DEFAULT)) { 170 this.grayscaleFilter = new ColorConvertOp (ColorSpace.getInstance(ColorSpace.CS_GRAY), null); 171 } 172 173 this.enlarge = par.getParameterAsBoolean("allow-enlarging", ENLARGE_DEFAULT); 174 this.fitUniform = par.getParameterAsBoolean("fit-uniform", FIT_DEFAULT); 175 176 super.setup(resolver, objectModel, src, par); 177 } 178 179 protected void setupHeaders() { 180 if (byteRanges && hasTransform()) { 182 byteRanges = false; 183 } 184 185 super.setupHeaders(); 186 } 187 188 191 private boolean hasTransform() { 192 return width > 0 || height > 0 || null != colorFilter || null != grayscaleFilter || (this.quality[0] != 0.9f); 193 } 194 195 206 private AffineTransform getTransform(double ow, double oh, double nw, double nh) { 207 double wm = 1.0d; 208 double hm = 1.0d; 209 210 if (fitUniform) { 211 if (ow/oh > nw/nh) { 216 nh = 0; } else { 219 nw = 0; } 221 } 222 223 if (nw > 0) { 224 wm = nw / ow; 225 if (nh > 0) { 226 hm = nh / oh; 227 } else { 228 hm = wm; 229 } 230 } else { 231 if (nh > 0) { 232 hm = nh / oh; 233 wm = hm; 234 } 235 } 236 237 if (!enlarge) { 238 if ((nw > ow && nh <= 0) || (nh > oh && nw <=0)) { 239 wm = 1.0d; 240 hm = 1.0d; 241 } else if (nw > ow) { 242 wm = 1.0d; 243 } else if (nh > oh) { 244 hm = 1.0d; 245 } 246 } 247 return new AffineTransform (wm, 0.0d, 0.0d, hm, 0.0d, 0.0d); 248 } 249 250 protected void processStream(InputStream inputStream) throws IOException , ProcessingException { 251 if (hasTransform()) { 252 if (getLogger().isDebugEnabled()) { 253 getLogger().debug("image " + ((width == 0) ? "?" : Integer.toString(width)) 254 + "x" + ((height == 0) ? "?" : Integer.toString(height)) 255 + " expires: " + expires); 256 } 257 258 279 280 try { 281 JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(inputStream); 282 BufferedImage original = decoder.decodeAsBufferedImage(); 283 BufferedImage currentImage = original; 284 285 if (width > 0 || height > 0) { 286 JPEGDecodeParam decodeParam = decoder.getJPEGDecodeParam(); 287 double ow = decodeParam.getWidth(); 288 double oh = decodeParam.getHeight(); 289 290 if (usePercent == true) { 291 if (width > 0) { 292 width = Math.round((int)(ow * width) / 100); 293 } 294 if (height > 0) { 295 height = Math.round((int)(oh * height) / 100); 296 } 297 } 298 299 AffineTransformOp filter = new AffineTransformOp (getTransform(ow, oh, width, height), AffineTransformOp.TYPE_BILINEAR); 300 WritableRaster scaledRaster = filter.createCompatibleDestRaster(currentImage.getRaster()); 301 302 filter.filter(currentImage.getRaster(), scaledRaster); 303 304 currentImage = new BufferedImage (original.getColorModel(), scaledRaster, true, null); 305 } 306 307 if (null != grayscaleFilter) { 308 grayscaleFilter.filter(currentImage, currentImage); 309 } 310 311 if (null != colorFilter) { 312 colorFilter.filter(currentImage, currentImage); 313 } 314 315 if (JVMBugFixed) { 317 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); 318 JPEGEncodeParam p = encoder.getDefaultJPEGEncodeParam(currentImage); 319 p.setQuality(this.quality[0], true); 320 encoder.setJPEGEncodeParam(p); 321 encoder.encode(currentImage); 322 } else { 323 ByteArrayOutputStream bstream = new ByteArrayOutputStream (); 324 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bstream); 325 JPEGEncodeParam p = encoder.getDefaultJPEGEncodeParam(currentImage); 326 p.setQuality(this.quality[0], true); 327 encoder.setJPEGEncodeParam(p); 328 encoder.encode(currentImage); 329 out.write(bstream.toByteArray()); 330 } 331 332 out.flush(); 333 } catch (ImageFormatException e) { 334 throw new ProcessingException("Error reading the image. " + 335 "Note that only JPEG images are currently supported."); 336 } finally { 337 inputStream.close(); 341 } 342 } else { 343 if (getLogger().isDebugEnabled()) { 345 getLogger().debug("passing original resource"); 346 } 347 super.processStream(inputStream); 348 } 349 } 350 351 358 public Serializable getKey() { 359 return this.inputSource.getURI() 360 + ':' + this.width 361 + ':' + this.height 362 + ":" + this.scaleColor[0] 363 + ":" + this.scaleColor[1] 364 + ":" + this.scaleColor[2] 365 + ":" + this.offsetColor[0] 366 + ":" + this.offsetColor[1] 367 + ":" + this.offsetColor[2] 368 + ":" + this.quality[0] 369 + ":" + ((null == this.grayscaleFilter) ? "color" : "grayscale") 370 + ":" + super.getKey(); 371 } 372 373 public void recycle(){ 374 super.recycle(); 375 this.colorFilter = null; 376 this.grayscaleFilter = null; 377 } 378 } 379 | Popular Tags |