You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by jo...@apache.org on 2004/03/09 00:34:12 UTC

cvs commit: cocoon-2.2/src/webapp/samples/imagereader samples.xml sitemap.xmap

joerg       2004/03/08 15:34:12

  Modified:    .        status.xml
               src/java/org/apache/cocoon/reading ImageReader.java
               src/webapp/samples/imagereader samples.xml sitemap.xmap
  Log:
  fixed bug 27020, thanks to Peter Horsfield: Added grayscaling and color transformation to the ImageReader.,
  ported the latest changes on ImageReader in 2.1 to 2.2 (handleJVMBug and so on)
  
  Revision  Changes    Path
  1.187     +4 -1      cocoon-2.2/status.xml
  
  Index: status.xml
  ===================================================================
  RCS file: /home/cvs/cocoon-2.2/status.xml,v
  retrieving revision 1.186
  retrieving revision 1.187
  diff -u -r1.186 -r1.187
  --- status.xml	8 Mar 2004 21:21:10 -0000	1.186
  +++ status.xml	8 Mar 2004 23:34:12 -0000	1.187
  @@ -189,6 +189,9 @@
   
     <changes>
    <release version="@version@" date="@date@">
  +   <action dev="JH" type="add" fixes-bug="27020" due-to="Peter Horsfield" due-to-email="peter@xml.grumpykitty.biz">
  +     Added grayscaling and color transformation to the ImageReader.
  +   </action>
      <action dev="JH" type="fix" fixes-bug="26851" due-to="Marco Rolappe" due-to-email="m_rolappe@web.de">
        LinkStatusGenerator now handles also links with a specified charset.
      </action>
  
  
  
  1.6       +164 -41   cocoon-2.2/src/java/org/apache/cocoon/reading/ImageReader.java
  
  Index: ImageReader.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.2/src/java/org/apache/cocoon/reading/ImageReader.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- ImageReader.java	8 Mar 2004 14:03:32 -0000	1.5
  +++ ImageReader.java	8 Mar 2004 23:34:12 -0000	1.6
  @@ -15,26 +15,28 @@
    */
   package org.apache.cocoon.reading;
   
  -import org.apache.avalon.framework.parameters.Parameters;
  +import java.awt.color.ColorSpace;
  +import java.awt.geom.AffineTransform;
  +import java.awt.image.AffineTransformOp;
  +import java.awt.image.BufferedImage;
  +import java.awt.image.ColorConvertOp;
  +import java.awt.image.RescaleOp;
  +import java.awt.image.WritableRaster;
  +import java.io.ByteArrayOutputStream;
  +import java.io.IOException;
  +import java.io.Serializable;
  +import java.util.Map;
   
  +import org.apache.avalon.framework.parameters.Parameters;
   import org.apache.cocoon.ProcessingException;
   import org.apache.cocoon.environment.SourceResolver;
  +import org.xml.sax.SAXException;
   
   import com.sun.image.codec.jpeg.ImageFormatException;
   import com.sun.image.codec.jpeg.JPEGCodec;
   import com.sun.image.codec.jpeg.JPEGDecodeParam;
   import com.sun.image.codec.jpeg.JPEGImageDecoder;
   import com.sun.image.codec.jpeg.JPEGImageEncoder;
  -import org.xml.sax.SAXException;
  -
  -import java.awt.geom.AffineTransform;
  -import java.awt.image.AffineTransformOp;
  -import java.awt.image.Raster;
  -import java.awt.image.WritableRaster;
  -import java.io.ByteArrayOutputStream;
  -import java.io.IOException;
  -import java.io.Serializable;
  -import java.util.Map;
   
   /**
    * The <code>ImageReader</code> component is used to serve binary image data
  @@ -52,13 +54,25 @@
    *     <dd>This parameter is optional. When specified it determines the height
    *         of the image that should be served.
    *     </dd>
  + *     <dt>&lt;scale(Red|Green|Blue)&gt;</dt>
  + *     <dd>This parameter is optional. When specified it will cause the 
  + *  specified color component in the image to be multiplied by the 
  + *  specified floating point value.
  + *     </dd>
  + *     <dt>&lt;offset(Red|Green|Blue)&gt;</dt>
  + *     <dd>This parameter is optional. When specified it will cause the 
  + *  specified color component in the image to be incremented by the 
  + *  specified floating point value.
  + *     </dd>
  + *     <dt>&lt;grayscale&gt;</dt>
  + *     <dd>This parameter is optional. When specified and set to true it
  + *  will cause each image pixel to be normalized. 
  + *     </dd>
    *   </dl>
    *
  - * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
  - * @author <a href="mailto:stephan@apache.org">Stephan Michels</a>
  - * @author <a href="mailto:tcurdt@apache.org">Torsten Curdt</a>
  + * @author <a href="mailto:dev@cocoon.apache.org">The Apache Cocoon Team</a>
    * @version CVS $Id$
  - * 
  + *
    * @avalon.component
    * @avalon.service type=Reader
    * @x-avalon.lifestyle type=pooled
  @@ -67,9 +81,17 @@
   
       private int width;
       private int height;
  +
  +    private float[] scaleColor = new float[3];
  +    private float[] offsetColor = new float[3];
  +    private RescaleOp colorFilter = null;
  +
       private boolean enlarge;
       private final static String ENLARGE_DEFAULT = "true";
   
  +    private ColorConvertOp grayscaleFilter = null;
  +    private final static String GRAYSCALE_DEFAULT = "false";
  +
       public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par)
               throws ProcessingException, SAXException, IOException {
   
  @@ -78,6 +100,39 @@
           width = par.getParameterAsInteger("width", 0);
           height = par.getParameterAsInteger("height", 0);
   
  +        scaleColor[0] = par.getParameterAsFloat("scaleRed", -1.0f);
  +        scaleColor[1] = par.getParameterAsFloat("scaleGreen", -1.0f);
  +        scaleColor[2] = par.getParameterAsFloat("scaleBlue", -1.0f);
  +        offsetColor[0] = par.getParameterAsFloat("offsetRed", 0.0f);
  +        offsetColor[1] = par.getParameterAsFloat("offsetGreen", 0.0f);
  +        offsetColor[2] = par.getParameterAsFloat("offsetBlue", 0.0f);   
  +
  +        boolean filterColor = false;
  +
  +        for (int i = 0; i < 3; ++i) {
  +            if (scaleColor[i] != -1.0f) {
  +                filterColor = true;
  +            } else {
  +                scaleColor[i] = 1.0f;
  +            }
  +            if (offsetColor[i] != 0.0f) {
  +                filterColor = true;
  +            }
  +        }
  +        
  +        if (filterColor) {
  +            colorFilter = new RescaleOp(scaleColor, offsetColor, null);
  +        } else {
  +            colorFilter = null;
  +        }
  +
  +        String grayscalePar = par.getParameter("grayscale", GRAYSCALE_DEFAULT);
  +        if ("true".equalsIgnoreCase(grayscalePar) || "yes".equalsIgnoreCase(grayscalePar)){            
  +            grayscaleFilter = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
  +        } else {
  +            grayscaleFilter = null;
  +        }   
  +    
           String enlargePar = par.getParameter("allow-enlarging", ENLARGE_DEFAULT);
           if ("true".equalsIgnoreCase(enlargePar) || "yes".equalsIgnoreCase(enlargePar)){
               enlarge = true;
  @@ -116,7 +171,7 @@
           }
   
           if (!enlarge) {
  -            if ((nw > ow && nh <= 0) || (oh > nh && nw <=0)) {
  +            if ((nw > ow && nh <= 0) || (nh > oh && nw <=0)) {
                   wm = 1.0d;
                   hm = 1.0d;
               } else if (nw > ow) {
  @@ -129,17 +184,17 @@
       }
   
       protected void processStream() throws IOException, ProcessingException {
  -        if (width > 0 || height > 0) {
  +        if (width > 0 || height > 0 || null != colorFilter || null != grayscaleFilter) {
               if (getLogger().isDebugEnabled()) {
  -                getLogger().debug("image " + ((width==0)?"?":Integer.toString(width))
  -                                  + "x" + ((height==0)?"?":Integer.toString(height))
  +                getLogger().debug("image " + ((width == 0) ? "?" : Integer.toString(width))
  +                                  + "x"    + ((height == 0) ? "?" : Integer.toString(height))
                                     + " expires: " + expires);
               }
   
               // since we create the image on the fly
               response.setHeader("Accept-Ranges", "none");
   
  -            /**
  +            /*
                * NOTE (SM):
                * Due to Bug Id 4502892 (which is found in *all* JVM implementations from
                * 1.2.x and 1.3.x on all OS!), we must buffer the JPEG generation to avoid
  @@ -163,27 +218,55 @@
   
               try {
                   JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(inputStream);
  -                Raster original = decoder.decodeAsRaster();
  -                JPEGDecodeParam decodeParam = decoder.getJPEGDecodeParam();
  -                double ow = decodeParam.getWidth();
  -                double oh = decodeParam.getHeight();
  -                AffineTransformOp filter = new AffineTransformOp(getTransform(ow, oh, width, height), AffineTransformOp.TYPE_BILINEAR);
  -                WritableRaster scaled = filter.createCompatibleDestRaster(original);
  -                filter.filter(original, scaled);
  -
  -                // JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
  -
  -                ByteArrayOutputStream bstream = new ByteArrayOutputStream();
  -                JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bstream);
  -                encoder.encode(scaled);
  -                out.write(bstream.toByteArray());
  +                BufferedImage original = decoder.decodeAsBufferedImage();
  +                BufferedImage currentImage = original;
  +
  +                if (width > 0 || height > 0) {
  +                    JPEGDecodeParam decodeParam = decoder.getJPEGDecodeParam();
  +                    double ow = decodeParam.getWidth();
  +                    double oh = decodeParam.getHeight();
  +
  +                    AffineTransformOp filter = new AffineTransformOp(getTransform(ow, oh, width, height), AffineTransformOp.TYPE_BILINEAR);
  +                    WritableRaster scaledRaster = filter.createCompatibleDestRaster(currentImage.getRaster());
  +
  +                    filter.filter(currentImage.getRaster(), scaledRaster);
  +
  +                    currentImage = new BufferedImage(original.getColorModel(), scaledRaster, true, null);
  +                }
  +
  +                if (null != grayscaleFilter) {
  +                    grayscaleFilter.filter(currentImage, currentImage);
  +                }
  +
  +                if (null != colorFilter) {
  +                    colorFilter.filter(currentImage, currentImage);
  +                }
  +
  +                if (!handleJVMBug()) {
  +                    if (getLogger().isDebugEnabled()) {
  +                        getLogger().debug( "No need to handle JVM bug" );
  +                    }
  +                    JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
  +                    encoder.encode(currentImage);
  +                } else {
  +                    if (getLogger().isDebugEnabled()) {
  +                        getLogger().debug( "Need to handle JVM bug" );
  +                    }
  +                    ByteArrayOutputStream bstream = new ByteArrayOutputStream();
  +                    JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bstream);
  +                    encoder.encode(currentImage);
  +                    out.write(bstream.toByteArray());
  +                }
   
                   out.flush();
               } catch (ImageFormatException e) {
                   throw new ProcessingException("Error reading the image. Note that only JPEG images are currently supported.");
  +            } finally {
  +              // Bugzilla Bug 25069, close inputStream in finally block
  +              // this will close inputStream even if processStream throws
  +              // an exception
  +              inputStream.close();
               }
  -
  -            inputStream.close();
           } else {
               // only read the resource - no modifications requested
               if (getLogger().isDebugEnabled()) {
  @@ -197,14 +280,54 @@
        * Generate the unique key.
        * This key must be unique inside the space of this component.
        *
  -     * @return The generated key consists from src and width and height
  +     * @return The generated key consists of the src and width and height, and the color transform
        * parameters
       */
       public Serializable getKey() {
  -        if (width > 0 || height > 0) {
  -            return this.inputSource.getURI() + ':' + this.width + ':' + this.height;
  +        return this.inputSource.getURI() 
  +                + ':' + this.width 
  +                + ':' + this.height
  +                + ":" + this.scaleColor[0]
  +                + ":" + this.scaleColor[1]
  +                + ":" + this.scaleColor[2]
  +                + ":" + this.offsetColor[0]
  +                + ":" + this.offsetColor[1]
  +                + ":" + this.offsetColor[2]
  +                + ":" + ((null == this.grayscaleFilter) ? "color" : "grayscale")
  +                + ":" + super.getKey();
  +    }
  +
  +    /**
  +     * Determine if workaround for Bug Id 4502892 is neccessary.
  +     * This method assumes that Bug is present if 
  +     * java.version is undeterminable, and for java.version
  +     * 1.1, 1.2, 1.3, all other java.version do not need the Bug handling
  +     *
  +     * @return true if we should handle the JVM bug, else false
  +     */
  +    protected boolean handleJVMBug() {
  +        // java.version=1.4.0
  +        String java_version = System.getProperty( "java.version", "0.0.0" );
  +        boolean handleJVMBug = true;
  +
  +        char major = java_version.charAt(0);
  +        char minor = java_version.charAt(2);
  +
  +        // make 0.0, 1.1, 1.2, 1.3 handleJVMBug = true
  +        if (major == '0' || major == '1') {
  +            if (minor == '0' || minor == '1' || minor == '2' || minor == '3') {
  +                handleJVMBug = true;
  +            } else {
  +                handleJVMBug = false;
  +            }
           } else {
  -            return super.getKey();
  +            handleJVMBug = true;
           }
  +        if (getLogger().isDebugEnabled()) {
  +            getLogger().debug( "Running java.version " + String.valueOf(java_version) + 
  +              " need to handle JVM bug " + String.valueOf(handleJVMBug) );
  +        }
  +
  +        return handleJVMBug;
       }
   }
  
  
  
  1.2       +61 -13    cocoon-2.2/src/webapp/samples/imagereader/samples.xml
  
  Index: samples.xml
  ===================================================================
  RCS file: /home/cvs/cocoon-2.2/src/webapp/samples/imagereader/samples.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- samples.xml	10 Aug 2003 11:42:47 -0000	1.1
  +++ samples.xml	8 Mar 2004 23:34:12 -0000	1.2
  @@ -1,19 +1,67 @@
  +<?xml version="1.0"?>
  +<!--
  +  Copyright 1999-2004 The Apache Software Foundation
  +
  +  Licensed under the Apache License, Version 2.0 (the "License");
  +  you may not use this file except in compliance with the License.
  +  You may obtain a copy of the License at
  +
  +      http://www.apache.org/licenses/LICENSE-2.0
  +
  +  Unless required by applicable law or agreed to in writing, software
  +  distributed under the License is distributed on an "AS IS" BASIS,
  +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  +  See the License for the specific language governing permissions and
  +  limitations under the License.
  +-->
   <samples xmlns:xlink="http://www.w3.org/1999/xlink">
  - 
  - <group name="Main examples page.">
  -  <sample name="Back" href="..">to Cocoon examples main page</sample>
  - </group>
  -
  -  <group name="ImageReader">
  -    <sample href="image-0" name="Image">
  -      Image of original size
  -    </sample>
  -    <sample href="image-250" name="Image 250x250">
  -      Image scaled up to the size 250 x 250
  +
  +  <group name="Main examples page.">
  +    <sample name="Back" href="..">to Cocoon examples main page</sample>
  +  </group>
  +
  +  <group name="The Original">
  +    <sample href="image" name="Image">
  +      Image of original size and color.
       </sample>
  -    <sample href="image-50" name="Image 50x50">
  -      Image scaled down to the size 50 x 50
  +  </group>
  +
  +  <group name="ImageReader - Size Scaling">
  +    <sample href="image-size-50" name="Image 50x50">
  +      Image scaled down to the size 50 x 50.
       </sample> 
  +    <sample href="image-size-250" name="Image 250x250">
  +      Image scaled up to the size 250 x 250.
  +    </sample>
  +  </group>
  +
  +  <group name="ImageReader - Grayscaling">
  +    <sample href="image-grayscale" name="Image grayscaled.">
  +      The image in gray.
  +    </sample>
     </group>
   
  +  <group name="ImageReader - Color Scaling">
  +    <sample href="image-color-scale-2-1-1" name="Image tinted red.">
  +      The values for the color red are duplicated.
  +    </sample>
  +    <sample href="image-color-scale-1-2-1" name="Image tinted green.">
  +      The values for the color green are duplicated.
  +    </sample>
  +    <sample href="image-color-scale-1-1-2" name="Image tinted blue.">
  +      The values for the color blue are duplicated.
  +    </sample>
  +  </group>
  +
  +  <group name="ImageReader - Color Offset">
  +    <sample href="image-color-offset-50-0-0" name="Image tinted red.">
  +      The values for the color red are incremented by 50.
  +    </sample>
  +    <sample href="image-color-offset-0-50-0" name="Image tinted green.">
  +      The values for the color green are incremented by 50.
  +    </sample>
  +    <sample href="image-color-offset-0-0-50" name="Image tinted blue.">
  +      The values for the color blue are incremented by 50.
  +    </sample>
  +  </group>
   </samples>
  
  
  
  1.3       +43 -2     cocoon-2.2/src/webapp/samples/imagereader/sitemap.xmap
  
  Index: sitemap.xmap
  ===================================================================
  RCS file: /home/cvs/cocoon-2.2/src/webapp/samples/imagereader/sitemap.xmap,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- sitemap.xmap	10 Aug 2003 11:42:47 -0000	1.2
  +++ sitemap.xmap	8 Mar 2004 23:34:12 -0000	1.3
  @@ -1,4 +1,19 @@
   <?xml version="1.0"?>
  +<!--
  +  Copyright 1999-2004 The Apache Software Foundation
  +
  +  Licensed under the Apache License, Version 2.0 (the "License");
  +  you may not use this file except in compliance with the License.
  +  You may obtain a copy of the License at
  +
  +      http://www.apache.org/licenses/LICENSE-2.0
  +
  +  Unless required by applicable law or agreed to in writing, software
  +  distributed under the License is distributed on an "AS IS" BASIS,
  +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  +  See the License for the specific language governing permissions and
  +  limitations under the License.
  +-->
   
   <!--
     CVS $Id$
  @@ -28,10 +43,36 @@
           <map:serialize/>
         </map:match>
    
  -      <map:match pattern="image-*">
  +      <map:match pattern="image">
  +        <map:read src="logo.jpg"/>
  +      </map:match>
  +
  +      <map:match pattern="image-size-*">
           <map:read type="image" src="logo.jpg">
             <map:parameter name="width" value="{1}"/>
             <map:parameter name="height" value="{1}"/>
  +        </map:read>
  +      </map:match>
  +
  +      <map:match pattern="image-grayscale">
  +        <map:read type="image" src="logo.jpg">
  +          <map:parameter name="grayscale" value="true"/>
  +        </map:read>
  +      </map:match>
  +
  +      <map:match pattern="image-color-scale-*-*-*">
  +        <map:read type="image" src="logo.jpg">
  +          <map:parameter name="scaleRed" value="{1}"/>
  +          <map:parameter name="scaleGreen" value="{2}"/>
  +          <map:parameter name="scaleBlue" value="{3}"/>
  +        </map:read>
  +      </map:match>
  +
  +      <map:match pattern="image-color-offset-*-*-*">
  +        <map:read type="image" src="logo.jpg">
  +          <map:parameter name="offsetRed" value="{1}"/>
  +          <map:parameter name="offsetGreen" value="{2}"/>
  +          <map:parameter name="offsetBlue" value="{3}"/>
           </map:read>
         </map:match>
       </map:pipeline>