You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xmlgraphics.apache.org by je...@apache.org on 2007/12/11 14:52:53 UTC

svn commit: r603245 - in /xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps: FormGenerator.java ImageEncodingHelper.java ImageFormGenerator.java PSImageUtils.java

Author: jeremias
Date: Tue Dec 11 05:52:51 2007
New Revision: 603245

URL: http://svn.apache.org/viewvc?rev=603245&view=rev
Log:
Another round of refactoring in PSImageUtils to become more versatile and faster.

Added:
    xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/FormGenerator.java   (with props)
    xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java   (with props)
    xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageFormGenerator.java   (with props)
Modified:
    xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/PSImageUtils.java

Added: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/FormGenerator.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/FormGenerator.java?rev=603245&view=auto
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/FormGenerator.java (added)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/FormGenerator.java Tue Dec 11 05:52:51 2007
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+/* $Id$ */
+ 
+package org.apache.xmlgraphics.ps;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+
+/**
+ * Abstract helper class for generating PostScript forms.
+ */
+public abstract class FormGenerator {
+
+    private String formName;
+    private String title;
+    private Dimension2D dimensions;
+    
+    /**
+     * Main constructor.
+     * @param formName the form's name
+     * @param title the form's title or null
+     * @param dimensions the form's dimensions
+     */
+    public FormGenerator(String formName, String title, Dimension2D dimensions) {
+        this.formName = formName;
+        this.title = title;
+        this.dimensions = dimensions;
+    }
+    
+    /**
+     * Returns the form's name.
+     * @return the form's name
+     */
+    public String getFormName() {
+        return this.formName;
+    }
+    
+    /**
+     * Returns the form's title.
+     * @return the form's title or null if there's no title
+     */
+    public String getTitle() {
+        return this.title;
+    }
+    
+    /**
+     * returns the form's dimensions.
+     * @return the form's dimensions
+     */
+    public Dimension2D getDimensions() {
+        return this.dimensions;
+    }
+    
+    /**
+     * Generates the PostScript code for the PaintProc of the form.
+     * @param gen the PostScript generator
+     * @throws IOException if an I/O error occurs
+     */
+    protected abstract void generatePaintProc(PSGenerator gen) throws IOException;
+
+    /**
+     * Generates some PostScript code right after the form definition (used primarily for
+     * bitmap data).
+     * @param gen the PostScript generator
+     * @throws IOException if an I/O error occurs
+     */
+    protected void generateAdditionalDataStream(PSGenerator gen) throws IOException {
+        //nop
+    }
+    
+    /**
+     * Returns the matrix for use in the form.
+     * @return the matrix
+     */
+    protected AffineTransform getMatrix() {
+        return new AffineTransform();
+    }
+    
+    /**
+     * Returns the form's bounding box.
+     * @return the form's bounding box
+     */
+    protected Rectangle2D getBBox() {
+        return new Rectangle2D.Double(0, 0, dimensions.getWidth(), dimensions.getHeight());
+    }
+    
+    /**
+     * Generates the PostScript form.
+     * @param gen the PostScript generator
+     * @return a PSResource instance representing the form
+     * @throws IOException if an I/O error occurs
+     */
+    public PSResource generate(PSGenerator gen) throws IOException {
+        if (gen.getPSLevel() < 2) {
+            throw new UnsupportedOperationException(
+                    "Forms require at least Level 2 PostScript");
+        }
+        gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE,
+                new Object[] {PSResource.TYPE_FORM, getFormName()});
+        if (title != null) {
+            gen.writeDSCComment(DSCConstants.TITLE, title);
+        }
+        gen.writeln("/" + formName);
+        gen.writeln("<< /FormType 1");
+        gen.writeln("  /BBox " + gen.formatRectangleToArray(getBBox())); 
+        gen.writeln("  /Matrix " + gen.formatMatrix(getMatrix()));
+        gen.writeln("  /PaintProc {");
+        gen.writeln("    pop");
+        gen.writeln("    gsave");
+        generatePaintProc(gen);
+        gen.writeln("    grestore");
+        gen.writeln("  } bind");
+        gen.writeln(">> def");
+        generateAdditionalDataStream(gen);
+        gen.writeDSCComment(DSCConstants.END_RESOURCE);
+        PSResource res = new PSResource(PSResource.TYPE_FORM, formName); 
+        gen.getResourceTracker().registerSuppliedResource(res);
+        return res;
+    }
+}

Propchange: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/FormGenerator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/FormGenerator.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java?rev=603245&view=auto
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java (added)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java Tue Dec 11 05:52:51 2007
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+/* $Id$ */
+ 
+package org.apache.xmlgraphics.ps;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.IndexColorModel;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.xmlgraphics.image.GraphicsUtil;
+
+/**
+ * Helper class for encoding bitmap images.
+ */
+public class ImageEncodingHelper {
+
+    private static final ColorModel DEFAULT_RGB_COLOR_MODEL = new ComponentColorModel(
+            ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB),
+            false, false, ColorModel.OPAQUE, DataBuffer.TYPE_BYTE);
+    
+    private RenderedImage image;
+    private ColorModel encodedColorModel;
+    private boolean firstTileDump;
+    
+    /**
+     * Main constructor
+     * @param image the image
+     */
+    public ImageEncodingHelper(RenderedImage image) {
+        this.image = image;
+        determineEncodedColorModel();
+    }
+    
+    /**
+     * Returns the associated image.
+     * @return the image
+     */
+    public RenderedImage getImage() {
+        return this.image;
+    }
+    
+    /**
+     * Returns the native {@link ColorModel} used by the image.
+     * @return the native color model
+     */
+    public ColorModel getNativeColorModel() {
+        return getImage().getColorModel();
+    }
+    
+    /**
+     * Returns the effective {@link ColorModel} used to encode the image. If this is different
+     * from the value returned by {@link #getNativeColorModel()} this means that the image
+     * is converted in order to encode it because no native encoding is currently possible. 
+     * @return the effective color model
+     */
+    public ColorModel getEncodedColorModel() {
+        return this.encodedColorModel;
+    }
+    
+    /**
+     * Indicates whether the image has an alpha channel.
+     * @return true if the image has an alpha channel
+     */
+    public boolean hasAlpha() {
+        return image.getColorModel().hasAlpha();
+    }
+    
+    /**
+     * Indicates whether the image is converted during encodation.
+     * @return true if the image cannot be encoded in its native format
+     */
+    public boolean isConverted() {
+        return getNativeColorModel() != getEncodedColorModel();
+    }
+    
+    private void writeRGBTo(OutputStream out) throws IOException {
+        Raster raster = image.getData();
+        Object data;
+        int nbands = raster.getNumBands();
+        int dataType = raster.getDataBuffer().getDataType();
+        switch (dataType) {
+        case DataBuffer.TYPE_BYTE:
+            data = new byte[nbands];
+            break;
+        case DataBuffer.TYPE_USHORT:
+            data = new short[nbands];
+            break;
+        case DataBuffer.TYPE_INT:
+            data = new int[nbands];
+            break;
+        case DataBuffer.TYPE_FLOAT:
+            data = new float[nbands];
+            break;
+        case DataBuffer.TYPE_DOUBLE:
+            data = new double[nbands];
+            break;
+        default:
+            throw new IllegalArgumentException("Unknown data buffer type: "+
+                                               dataType);
+        }
+        
+        ColorModel colorModel = image.getColorModel();
+        int w = image.getWidth();
+        int h = image.getHeight();
+        for (int y = 0; y < h; y++) {
+            for (int x = 0; x < w; x++) {
+                int rgb = colorModel.getRGB(raster.getDataElements(x, y, data));
+                int r = (rgb >> 16) & 0xFF;
+                int g = (rgb >> 8) & 0xFF;
+                int b = (rgb) & 0xFF;
+                out.write(r);
+                out.write(g);
+                out.write(b);
+            }
+        }
+    }
+    
+    private boolean optimizedWriteTo(OutputStream out)
+            throws IOException {
+        if (this.firstTileDump) {
+            Raster raster = image.getTile(0, 0);
+            DataBuffer buffer = raster.getDataBuffer();
+            if (buffer instanceof DataBufferByte) {
+                out.write(((DataBufferByte)buffer).getData());
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Indicates whether the image consists of multiple tiles.
+     * @return true if there are multiple tiles
+     */
+    protected boolean isMultiTile() {
+        int tilesX = image.getNumXTiles();
+        int tilesY = image.getNumYTiles();
+        return (tilesX != 1 || tilesY != 1); 
+    }
+    
+    /**
+     * Determines the color model used for encoding the image.
+     */
+    protected void determineEncodedColorModel() {
+        this.firstTileDump = false;
+        this.encodedColorModel = DEFAULT_RGB_COLOR_MODEL;
+
+        ColorModel cm = image.getColorModel();
+        ColorSpace cs = cm.getColorSpace();
+
+        int numComponents = cm.getNumComponents();
+
+        if (!isMultiTile()) {
+            if (numComponents == 1 && cs.getType() == ColorSpace.TYPE_GRAY) {
+                if (cm.getTransferType() == DataBuffer.TYPE_BYTE) {
+                    this.firstTileDump = true;
+                    this.encodedColorModel = cm;
+                }
+            } else if (cm instanceof IndexColorModel) {
+                if (cm.getTransferType() == DataBuffer.TYPE_BYTE) {
+                    this.firstTileDump = true;
+                    this.encodedColorModel = cm;
+                }
+            } else if (cm instanceof ComponentColorModel
+                    && numComponents == 3
+                    && !cm.hasAlpha()) {
+                Raster raster = image.getTile(0, 0);
+                DataBuffer buffer = raster.getDataBuffer();
+                if (cm.getTransferType() == DataBuffer.TYPE_BYTE
+                        && buffer.getOffset() == 0
+                        && buffer.getNumBanks() == 1) {
+                    this.firstTileDump = true;
+                    this.encodedColorModel = cm;
+                }
+            }
+        }
+
+    }
+    
+    /**
+     * Encodes the image and writes everything to the given OutputStream.
+     * @param out the OutputStream
+     * @throws IOException if an I/O error occurs
+     */
+    public void encode(OutputStream out) throws IOException {
+        if (!isConverted()) {
+            optimizedWriteTo(out);
+        } else {
+            writeRGBTo(out);
+        }
+    }
+    
+    /**
+     * Encodes the image's alpha channel. If it doesn't have an alpha channel, an
+     * {@link IllegalStateException} is thrown.
+     * @param out the OutputStream
+     * @throws IOException if an I/O error occurs
+     */
+    public void encodeAlpha(OutputStream out) throws IOException {
+        if (!hasAlpha()) {
+            throw new IllegalStateException("Image doesn't have an alpha channel");
+        }
+        Raster alpha = GraphicsUtil.getAlphaRaster(image);
+        DataBuffer buffer = alpha.getDataBuffer();
+        if (buffer instanceof DataBufferByte) {
+            out.write(((DataBufferByte)buffer).getData());
+        } else {
+            throw new UnsupportedOperationException(
+                    "Alpha raster not supported: " + buffer.getClass().getName());
+        }
+    }
+
+    /**
+     * Writes all pixels (color components only) of a RenderedImage to an OutputStream.
+     * @param image the image to be encoded 
+     * @param out the OutputStream to write to
+     * @throws IOException if an I/O error occurs
+     */
+    public static void encodePackedColorComponents(RenderedImage image, OutputStream out)
+                throws IOException {
+        ImageEncodingHelper helper = new ImageEncodingHelper(image);
+        helper.encode(out);
+    }
+    
+    /**
+     * Create an ImageEncoder for the given RenderImage instance.
+     * @param img the image
+     * @return the requested ImageEncoder
+     */
+    public static ImageEncoder createRenderedImageEncoder(RenderedImage img) {
+        return new RenderedImageEncoder(img);
+    }
+    
+    /**
+     * ImageEncoder implementation for RenderedImage instances.
+     */
+    private static class RenderedImageEncoder implements ImageEncoder {
+
+        private RenderedImage img;
+        
+        public RenderedImageEncoder(RenderedImage img) {
+            this.img = img;
+        }
+        
+        public void writeTo(OutputStream out) throws IOException {
+            ImageEncodingHelper.encodePackedColorComponents(img, out);
+        }
+
+        public String getImplicitFilter() {
+            return null; //No implicit filters with RenderedImage instances
+        }
+        
+    }
+    
+}

Propchange: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageFormGenerator.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageFormGenerator.java?rev=603245&view=auto
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageFormGenerator.java (added)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageFormGenerator.java Tue Dec 11 05:52:51 2007
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+/* $Id$ */
+ 
+package org.apache.xmlgraphics.ps;
+
+import java.awt.Dimension;
+import java.awt.color.ColorSpace;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Dimension2D;
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+
+/**
+ * Abstract helper class for generating PostScript forms.
+ */
+public class ImageFormGenerator extends FormGenerator {
+
+    //Mode 1 (RenderedImage)
+    private RenderedImage image;
+    
+    //Mode 2 (ImageEncoder)
+    private ImageEncoder encoder;
+    private ColorSpace colorSpace;
+    
+    private boolean invertImage;
+    private Dimension pixelDimensions;
+    
+    /**
+     * Main constructor.
+     * @param formName the form's name
+     * @param title the form's title or null
+     * @param dimensions the form's dimensions in units (usually points)
+     * @param image the image
+     * @param invertImage true if the image shall be inverted
+     */
+    public ImageFormGenerator(String formName, String title,
+            Dimension2D dimensions,
+            RenderedImage image, boolean invertImage) {
+        super(formName, title, dimensions);
+        this.image = image;
+        this.encoder = ImageEncodingHelper.createRenderedImageEncoder(image);
+        this.invertImage = invertImage;
+        this.pixelDimensions = new Dimension(image.getWidth(), image.getHeight());
+    }
+    
+    /**
+     * Main constructor.
+     * @param formName the form's name
+     * @param title the form's title or null
+     * @param dimensions the form's dimensions in units (usually points)
+     * @param dimensionsPx the form's dimensions in pixels
+     * @param encoder the image encoder
+     * @param colorSpace the target color space
+     * @param invertImage true if the image shall be inverted
+     */
+    public ImageFormGenerator(String formName, String title,
+            Dimension2D dimensions, Dimension dimensionsPx,
+            ImageEncoder encoder, ColorSpace colorSpace, boolean invertImage) {
+        super(formName, title, dimensions);
+        this.encoder = encoder;
+        this.colorSpace = colorSpace;
+        this.invertImage = invertImage;
+        this.pixelDimensions = dimensionsPx;
+    }
+    
+    /**
+     * Returns the name of the data segment associated with this image form.
+     * @return the data segment name
+     */
+    protected String getDataName() {
+        return getFormName() + ":Data";
+    }
+    
+    private String getAdditionalFilters(PSGenerator gen) {
+        String implicitFilter = encoder.getImplicitFilter(); 
+        if (implicitFilter != null) {
+            return "/ASCII85Decode filter " + implicitFilter + " filter";
+        } else {
+            if (gen.getPSLevel() >= 3) {
+                return "/ASCII85Decode filter /FlateDecode filter";
+            } else {
+                return "/ASCII85Decode filter /RunLengthDecode filter";
+            }
+        }
+    }
+    
+    /** {@inheritDoc} */
+    protected void generatePaintProc(PSGenerator gen) throws IOException {
+        if (gen.getPSLevel() == 2) {
+            gen.writeln("    userdict /i 0 put"); //rewind image data
+        } else {
+            gen.writeln("    " + getDataName() + " 0 setfileposition"); //rewind image data
+        }
+        String dataSource;
+        if (gen.getPSLevel() == 2) {
+            dataSource = "{ " + getDataName() + " i get /i i 1 add store } bind";
+        } else {
+            dataSource = getDataName();
+        }
+        AffineTransform at = new AffineTransform();
+        at.scale(getDimensions().getWidth(), getDimensions().getHeight());
+        gen.concatMatrix(at);
+        PSDictionary imageDict = new PSDictionary();
+        imageDict.put("/DataSource", dataSource);
+        if (this.image != null) {
+            PSImageUtils.writeImageCommand(this.image, imageDict, gen);
+        } else {
+            imageDict.put("/BitsPerComponent", Integer.toString(8));
+            PSImageUtils.writeImageCommand(imageDict,
+                    this.pixelDimensions, this.colorSpace, this.invertImage,
+                    gen);
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void generateAdditionalDataStream(PSGenerator gen) throws IOException {
+        gen.writeln("/" + getDataName() + " currentfile");
+        gen.writeln(getAdditionalFilters(gen));
+        if (gen.getPSLevel() == 2) {
+            //Creates a data array from the inline file
+            gen.writeln("{ /temp exch def ["
+                    + " { temp 16384 string readstring not {exit } if } loop ] } exec");
+        } else {
+            gen.writeln("/ReusableStreamDecode filter");
+        }
+        PSImageUtils.compressAndWriteBitmap(encoder, gen);
+        gen.writeln("def");
+    }
+    
+}

Propchange: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageFormGenerator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageFormGenerator.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/PSImageUtils.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/PSImageUtils.java?rev=603245&r1=603244&r2=603245&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/PSImageUtils.java (original)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/PSImageUtils.java Tue Dec 11 05:52:51 2007
@@ -21,11 +21,10 @@
 
 import java.awt.Dimension;
 import java.awt.color.ColorSpace;
+import java.awt.geom.Dimension2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.ColorModel;
-import java.awt.image.ComponentColorModel;
 import java.awt.image.DataBuffer;
-import java.awt.image.DataBufferByte;
 import java.awt.image.IndexColorModel;
 import java.awt.image.Raster;
 import java.awt.image.RenderedImage;
@@ -34,7 +33,6 @@
 import java.io.OutputStream;
 
 import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.output.CountingOutputStream;
 import org.apache.xmlgraphics.util.io.ASCII85OutputStream;
 import org.apache.xmlgraphics.util.io.Finalizable;
 import org.apache.xmlgraphics.util.io.FlateEncodeOutputStream;
@@ -81,7 +79,7 @@
     
     /**
      * Writes a bitmap image to the PostScript stream.
-     * @param img the bitmap image as a byte array
+     * @param encoder the image encoder
      * @param imgDim the dimensions of the image
      * @param imgDescription the name of the image
      * @param targetRect the target rectangle to place the image in
@@ -96,7 +94,7 @@
             ColorSpace colorSpace, boolean invertImage,
             PSGenerator gen) throws IOException {
         gen.saveGraphicsState();
-        translateAndScale(gen, targetRect);
+        translateAndScale(gen, null, targetRect);
 
         gen.commentln("%AXGBeginBitmap: " + imgDescription);
 
@@ -117,7 +115,10 @@
                 gen.writeln("/Data RawData /RunLengthDecode filter def");
             }
         }
-        writeImageCommand(imgDim, colorSpace, invertImage, gen, "Data");
+        PSDictionary imageDict = new PSDictionary();
+        imageDict.put("/DataSource", "Data");
+        imageDict.put("/BitsPerComponent", Integer.toString(8));
+        writeImageCommand(imageDict, imgDim, colorSpace, invertImage, gen);
         /* the following two lines could be enabled if something still goes wrong
          * gen.write("Data closefile");
          * gen.write("RawData flushfile");
@@ -128,7 +129,7 @@
 
         compressAndWriteBitmap(encoder, gen);
 
-        gen.writeln("");
+        gen.newLine();
         gen.commentln("%AXGEndBitmap");
         gen.restoreGraphicsState();
     }
@@ -142,11 +143,11 @@
      */
     private static void writeImage(RenderedImage img,
             Rectangle2D targetRect, PSGenerator gen) throws IOException {
-        ImageEncoder encoder = new RenderedImageEncoder(img);
+        ImageEncoder encoder = ImageEncodingHelper.createRenderedImageEncoder(img);
         String imgDescription = img.getClass().getName();
         
         gen.saveGraphicsState();
-        translateAndScale(gen, targetRect);
+        translateAndScale(gen, null, targetRect);
 
         gen.commentln("%AXGBeginBitmap: " + imgDescription);
 
@@ -186,79 +187,123 @@
         gen.restoreGraphicsState();
     }
 
-    private static void writeImageCommand(RenderedImage img,
-            PSDictionary imageDict, PSGenerator gen) throws IOException {
+    private static ColorModel populateImageDictionary(
+                ImageEncodingHelper helper, PSDictionary imageDict) {
+        RenderedImage img = helper.getImage();
         String w = Integer.toString(img.getWidth());
         String h = Integer.toString(img.getHeight());
         imageDict.put("/ImageType", "1");
         imageDict.put("/Width", w);
         imageDict.put("/Height", h);
         
-        ColorModel cm = img.getColorModel();
-        ColorSpace cs = cm.getColorSpace();
-        int tilesX = img.getNumXTiles();
-        int tilesY = img.getNumYTiles();
-        boolean multiTile = (tilesX != 1 || tilesY != 1);
+        ColorModel cm = helper.getEncodedColorModel();
         
         boolean invertColors = false;
+        String decodeArray = getDecodeArray(cm.getNumComponents(), invertColors);
+        int bitsPerComp = cm.getComponentSize(0);
+        
+        // Setup scanning for left-to-right and top-to-bottom
+        imageDict.put("/ImageMatrix", "[" + w + " 0 0 " + h + " 0 0]");
+        
+        if ((cm instanceof IndexColorModel)) {
+            IndexColorModel im = (IndexColorModel)cm;
+            int c = im.getMapSize();
+            int hival = c - 1;
+            if (hival > 4095) {
+                throw new UnsupportedOperationException("hival must not go beyond 4095");
+            }
+            decodeArray = "[0 " + Integer.toString(hival) + "]";
+            bitsPerComp = im.getPixelSize();
+        }
+        imageDict.put("/BitsPerComponent", Integer.toString(bitsPerComp));
+        imageDict.put("/Decode", decodeArray);
+        return cm;
+    }
+
+    private static String getDecodeArray(int numComponents, boolean invertColors) {
         String decodeArray;
-        boolean monochrome1Bit = (cm.getPixelSize() == 1 && !multiTile);
-        if (cs.getType() == ColorSpace.TYPE_CMYK) {
+        StringBuffer sb = new StringBuffer("[");
+        for (int i = 0; i < numComponents; i++) {
+            if (i > 0) {
+                sb.append(" ");
+            }
             if (invertColors) {
-                decodeArray = "[1 0 1 0 1 0 1 0]";
+                sb.append("1 0");
             } else {
-                decodeArray = "[0 1 0 1 0 1 0 1]";
+                sb.append("0 1");
             }
-        } else if (cs.getType() == ColorSpace.TYPE_GRAY || monochrome1Bit) {
-            decodeArray = "[0 1]";
-        } else {
-            decodeArray = "[0 1 0 1 0 1]";
         }
-        
-        int bitsPerComp = 8;
-        
-        // Setup scanning for left-to-right and top-to-bottom
-        imageDict.put("/ImageMatrix", "[" + w + " 0 0 " + h + " 0 0]");
-        
+        sb.append("]");
+        decodeArray = sb.toString();
+        return decodeArray;
+    }
+
+    private static void prepareColorspace(PSGenerator gen, ColorSpace colorSpace)
+            throws IOException {
+        gen.writeln(getColorSpaceName(colorSpace) + " setcolorspace");
+    }
+
+    private static void prepareColorSpace(PSGenerator gen, ColorModel cm) throws IOException {
         //Prepare color space
-        boolean indexedColorSpace = (cm instanceof IndexColorModel);
-        if (indexedColorSpace) {
-            if (!multiTile) {
-                IndexColorModel im = (IndexColorModel)cm;
-                gen.write("[/Indexed " + getColorSpaceName(cs));
-                int c = im.getMapSize();
-                int hival = c - 1;
-                if (hival > 4095) {
-                    throw new UnsupportedOperationException("hival must not go beyond 4095");
-                }
-                gen.writeln(" " + Integer.toString(hival));
-                gen.write("  <");
-                int[] palette = new int[c];
-                im.getRGBs(palette);
-                for (int i = 0; i < c; i++) {
-                    if (i > 0) {
-                        if ((i % 8) == 0) {
-                            gen.newLine();
-                            gen.write("   ");
-                        } else {
-                            gen.write(" ");
-                        }
+        if ((cm instanceof IndexColorModel)) {
+            ColorSpace cs = cm.getColorSpace();
+            IndexColorModel im = (IndexColorModel)cm;
+            gen.write("[/Indexed " + getColorSpaceName(cs));
+            int c = im.getMapSize();
+            int hival = c - 1;
+            if (hival > 4095) {
+                throw new UnsupportedOperationException("hival must not go beyond 4095");
+            }
+            gen.writeln(" " + Integer.toString(hival));
+            gen.write("  <");
+            int[] palette = new int[c];
+            im.getRGBs(palette);
+            for (int i = 0; i < c; i++) {
+                if (i > 0) {
+                    if ((i % 8) == 0) {
+                        gen.newLine();
+                        gen.write("   ");
+                    } else {
+                        gen.write(" ");
                     }
-                    gen.write(rgb2Hex(palette[i]));
                 }
-                gen.writeln(">");
-                gen.writeln("] setcolorspace");
-                decodeArray = "[0 " + Integer.toString(hival) + "]";
-                bitsPerComp = im.getPixelSize();
-            } else {
-                cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
-                gen.writeln(getColorSpaceName(cs) + " setcolorspace");
+                gen.write(rgb2Hex(palette[i]));
             }
+            gen.writeln(">");
+            gen.writeln("] setcolorspace");
         } else {
-            gen.writeln(getColorSpaceName(cs) + " setcolorspace");
+            gen.writeln(getColorSpaceName(cm.getColorSpace()) + " setcolorspace");
         }
-        imageDict.put("/BitsPerComponent", Integer.toString(bitsPerComp));
+    }
+
+    static void writeImageCommand(RenderedImage img,
+            PSDictionary imageDict, PSGenerator gen) throws IOException {
+        ImageEncodingHelper helper = new ImageEncodingHelper(img);
+        ColorModel cm = helper.getEncodedColorModel();
+        populateImageDictionary(helper, imageDict);
+        
+        writeImageCommand(imageDict, cm, gen);
+    }
+
+    static void writeImageCommand(PSDictionary imageDict, ColorModel cm, PSGenerator gen)
+                throws IOException {
+        prepareColorSpace(gen, cm);
+        gen.write(imageDict.toString());
+        gen.writeln(" image");
+    }
+
+    static void writeImageCommand(PSDictionary imageDict,
+            Dimension imgDim, ColorSpace colorSpace, boolean invertImage,
+            PSGenerator gen) throws IOException {
+        imageDict.put("/ImageType", "1");
+        imageDict.put("/Width", Integer.toString(imgDim.width));
+        imageDict.put("/Height", Integer.toString(imgDim.height));
+        String decodeArray = getDecodeArray(colorSpace.getNumComponents(), invertImage);
         imageDict.put("/Decode", decodeArray);
+        // Setup scanning for left-to-right and top-to-bottom
+        imageDict.put("/ImageMatrix", "[" + imgDim.width + " 0 0 " + imgDim.height + " 0 0]");
+
+        prepareColorspace(gen, colorSpace);
         gen.write(imageDict.toString());
         gen.writeln(" image");
     }
@@ -295,28 +340,6 @@
         writeImage(img, targetRect, gen);
     }
 
-    private static void writeImageCommand(Dimension imgDim, ColorSpace colorSpace, 
-            boolean invertImage, PSGenerator gen, String dataSource) throws IOException {
-        prepareColorspace(colorSpace, gen);
-        gen.writeln("<< /ImageType 1");
-        gen.writeln("  /Width " + imgDim.width);
-        gen.writeln("  /Height " + imgDim.height);
-        gen.writeln("  /BitsPerComponent 8");
-        StringBuffer sb = new StringBuffer();
-        sb.append("  /Decode [");
-        for (int i = 0; i < colorSpace.getNumComponents(); i++) {
-            sb.append(invertImage ? "1 0 " : "0 1 ");
-        }
-        sb.append("]");
-        gen.writeln(sb.toString());
-        // Setup scanning for left-to-right and top-to-bottom
-        gen.writeln("  /ImageMatrix [" + imgDim.width + " 0 0 "
-              + imgDim.height + " 0 0]");
-
-        gen.writeln("  /DataSource " + dataSource);
-        gen.writeln(">> image");
-    }
-
     /**
      * Writes a bitmap image as a PostScript form enclosed by DSC resource wrappers to the
      * PostScript file.
@@ -330,7 +353,7 @@
      * @param gen the PostScript generator
      * @return a PSResource representing the form for resource tracking
      * @throws IOException In case of an I/O exception
-     * @deprecated Please use the variant with the more versatile ImageEncoder as parameter
+     * @deprecated Please use {@link FormGenerator}
      */
     public static PSResource writeReusableImage(final byte[] img,
             Dimension imgDim, String formName, String imageDescription,
@@ -364,9 +387,11 @@
      * @param gen the PostScript generator
      * @return a PSResource representing the form for resource tracking
      * @throws IOException In case of an I/O exception
+     * @deprecated Please use {@link FormGenerator}
      */
-    public static PSResource writeReusableImage(ImageEncoder encoder,
-            Dimension imgDim, String formName, String imageDescription,
+    protected static PSResource writeReusableImage(ImageEncoder encoder,
+            Dimension imgDim,
+            String formName, String imageDescription,
             ColorSpace colorSpace, boolean invertImage,
             PSGenerator gen) throws IOException {
         if (gen.getPSLevel() < 2) {
@@ -409,7 +434,10 @@
         } else {
             dataSource = dataName;
         }
-        writeImageCommand(imgDim, colorSpace, invertImage, gen, dataSource); 
+        PSDictionary imageDict = new PSDictionary();
+        imageDict.put("/DataSource", dataSource);
+        imageDict.put("/BitsPerComponent", Integer.toString(8));
+        writeImageCommand(imageDict, imgDim, colorSpace, invertImage, gen); 
         gen.writeln("    grestore");
         gen.writeln("  } bind");
         gen.writeln(">> def");
@@ -436,39 +464,54 @@
      * @param targetRect the target rectangle to place the image in
      * @param gen the PostScript generator
      * @throws IOException In case of an I/O exception
+     * @deprecated Please use {@link #paintForm(PSResource, Dimension2D, Rectangle2D, PSGenerator)}
+     *          instead.
      */
     public static void paintReusableImage(
             String formName,
             Rectangle2D targetRect, 
             PSGenerator gen) throws IOException {
         PSResource form = new PSResource(PSResource.TYPE_FORM, formName);
-        paintForm(form, targetRect, gen);
+        paintForm(form, null, targetRect, gen);
+    }
+    
+    /**
+     * Paints a reusable image (previously added as a PostScript form).
+     * @param form the PostScript form resource implementing the image
+     * @param targetRect the target rectangle to place the image in
+     * @param gen the PostScript generator
+     * @throws IOException In case of an I/O exception
+     * @deprecated Please use {@link #paintForm(PSResource, Dimension2D, Rectangle2D, PSGenerator)}
+     *          instead.
+     */
+    public static void paintForm(
+            PSResource form,
+            Rectangle2D targetRect, 
+            PSGenerator gen) throws IOException {
+        paintForm(form, null, targetRect, gen);
     }
     
     /**
      * Paints a reusable image (previously added as a PostScript form).
      * @param form the PostScript form resource implementing the image
+     * @param formDimensions the original dimensions of the form
      * @param targetRect the target rectangle to place the image in
      * @param gen the PostScript generator
      * @throws IOException In case of an I/O exception
      */
     public static void paintForm(
             PSResource form,
+            Dimension2D formDimensions,
             Rectangle2D targetRect, 
             PSGenerator gen) throws IOException {
         gen.saveGraphicsState();
-        translateAndScale(gen, targetRect);
+        translateAndScale(gen, formDimensions, targetRect);
         gen.writeln(form.getName() + " execform");
         
         gen.getResourceTracker().notifyResourceUsageOnPage(form);
         gen.restoreGraphicsState();
     }
     
-    private static void prepareColorspace(ColorSpace colorSpace, PSGenerator gen)
-                throws IOException {
-        gen.writeln(getColorSpaceName(colorSpace) + " setcolorspace");
-    }
-
     private static String getColorSpaceName(ColorSpace colorSpace) {
         if (colorSpace.getType() == ColorSpace.TYPE_CMYK) {
             return("/DeviceCMYK");
@@ -479,7 +522,7 @@
         }
     }
 
-    private static void compressAndWriteBitmap(ImageEncoder encoder, PSGenerator gen)
+    static void compressAndWriteBitmap(ImageEncoder encoder, PSGenerator gen)
                 throws IOException {
         OutputStream out = gen.getOutputStream();
         out = new ASCII85OutputStream(out);
@@ -493,131 +536,31 @@
                 out = new RunLengthEncodeOutputStream(out);
             }
         }
-        CountingOutputStream cout = new CountingOutputStream(out);
-        encoder.writeTo(cout);
+        encoder.writeTo(out);
         if (out instanceof Finalizable) {
             ((Finalizable)out).finalizeStream();
         } else {
             out.flush();
         }
-        gen.newLine();
+        gen.newLine(); //Just to be sure
     }
 
-    private static void translateAndScale(PSGenerator gen, Rectangle2D targetRect)
-            throws IOException {
+    private static void translateAndScale(PSGenerator gen,
+            Dimension2D formDimensions, Rectangle2D targetRect)
+                throws IOException {
         gen.writeln(gen.formatDouble(targetRect.getX()) + " " 
                 + gen.formatDouble(targetRect.getY()) + " translate");
-        gen.writeln(gen.formatDouble(targetRect.getWidth()) + " " 
-                + gen.formatDouble(targetRect.getHeight()) + " scale");
-    }
-
-    private static class RenderedImageEncoder implements ImageEncoder {
-
-        private RenderedImage img;
-        
-        public RenderedImageEncoder(RenderedImage img) {
-            this.img = img;
-        }
-        
-        private void writeRGBTo(OutputStream out) throws IOException {
-            Raster raster = img.getData();
-            Object data;
-            int nbands = raster.getNumBands();
-            int dataType = raster.getDataBuffer().getDataType();
-            switch (dataType) {
-            case DataBuffer.TYPE_BYTE:
-                data = new byte[nbands];
-                break;
-            case DataBuffer.TYPE_USHORT:
-                data = new short[nbands];
-                break;
-            case DataBuffer.TYPE_INT:
-                data = new int[nbands];
-                break;
-            case DataBuffer.TYPE_FLOAT:
-                data = new float[nbands];
-                break;
-            case DataBuffer.TYPE_DOUBLE:
-                data = new double[nbands];
-                break;
-            default:
-                throw new IllegalArgumentException("Unknown data buffer type: "+
-                                                   dataType);
-            }
-            
-            ColorModel colorModel = img.getColorModel();
-            int w = img.getWidth();
-            int h = img.getHeight();
-            for (int y = 0; y < h; y++) {
-                for (int x = 0; x < w; x++) {
-                    int rgb = colorModel.getRGB(raster.getDataElements(x, y, data));
-                    int r = (rgb >> 16) & 0xFF;
-                    int g = (rgb >> 8) & 0xFF;
-                    int b = (rgb) & 0xFF;
-                    out.write(r);
-                    out.write(g);
-                    out.write(b);
-                }
-            }
+        if (formDimensions == null) {
+            formDimensions = new Dimension(1, 1);
         }
-        
-        private boolean optimizedWriteTo(OutputStream out) throws IOException {
-            ColorModel cm = img.getColorModel();
-            ColorSpace cs = cm.getColorSpace();
-            int tilesX = img.getNumXTiles();
-            int tilesY = img.getNumYTiles();
-            int colorComponents = cm.getNumColorComponents();
-            int components = cm.getNumComponents();
-            boolean multiTile = (tilesX != 1 || tilesY != 1); 
-            if (components == 1 && cs.getType() == ColorSpace.TYPE_GRAY && !multiTile) {
-                Raster raster = img.getTile(0, 0);
-                DataBuffer buffer = raster.getDataBuffer();
-                if (buffer instanceof DataBufferByte) {
-                    out.write(((DataBufferByte)buffer).getData());
-                    return true;
-                }
-            }
-            if (!multiTile && cm instanceof IndexColorModel) {
-                IndexColorModel im = (IndexColorModel)cm;
-                //int pixelSize = im.getPixelSize(); 
-                Raster raster = img.getTile(0, 0);
-                DataBuffer buffer = raster.getDataBuffer();
-                if (buffer instanceof DataBufferByte) {
-                    DataBufferByte byteBuffer = (DataBufferByte)buffer;
-                    out.write(byteBuffer.getData());
-                    return true;
-                }
-            }
-            if (!multiTile && cm instanceof ComponentColorModel
-                    && components == 3 && colorComponents == 3) {
-                ComponentColorModel ccm = (ComponentColorModel)cm;
-                Raster raster = img.getTile(0, 0);
-                DataBuffer buffer = raster.getDataBuffer();
-                if (buffer instanceof DataBufferByte
-                        && buffer.getOffset() == 0
-                        && buffer.getNumBanks() == 1) {
-                    DataBufferByte byteBuffer = (DataBufferByte)buffer;
-                    out.write(byteBuffer.getData());
-                    return true;
-                }
-            }
-            return false;
-        }
-        
-        public void writeTo(OutputStream out) throws IOException {
-            boolean optimized = optimizedWriteTo(out);
-            if (!optimized) {
-                //fallback
-                writeRGBTo(out);
-            }
-        }
-
-        public String getImplicitFilter() {
-            return null; //No implicit filters with RenderedImage instances
+        double sx = targetRect.getWidth() / formDimensions.getWidth();
+        double sy = targetRect.getHeight() / formDimensions.getHeight();
+        if (sx != 1 || sy != 1) {
+            gen.writeln(gen.formatDouble(sx) + " " 
+                    + gen.formatDouble(sy) + " scale");
         }
-        
     }
-    
+
     /**
      * Extracts a packed RGB integer array of a RenderedImage.
      * @param img the image
@@ -697,8 +640,8 @@
                     float bboxx, float bboxy, float bboxw, float bboxh,
                     PSGenerator gen) throws IOException {
        renderEPS(new java.io.ByteArrayInputStream(rawEPS), name,
-               x, y, w, h,
-               bboxx, bboxy, bboxw, bboxh,
+               new Rectangle2D.Float(x, y, w, h),
+               new Rectangle2D.Float(bboxx, bboxy, bboxw, bboxh),
                gen);
     }
     
@@ -706,38 +649,35 @@
      * Places an EPS file in the PostScript stream.
      * @param in the InputStream that contains the EPS stream
      * @param name name for the EPS document
-     * @param x x-coordinate of viewport in points
-     * @param y y-coordinate of viewport in points
-     * @param w width of viewport in points
-     * @param h height of viewport in points
-     * @param bboxx x-coordinate of EPS bounding box in points
-     * @param bboxy y-coordinate of EPS bounding box in points
-     * @param bboxw width of EPS bounding box in points
-     * @param bboxh height of EPS bounding box in points
+     * @param viewport the viewport in points in which to place the EPS
+     * @param bbox the EPS bounding box in points
      * @param gen the PS generator
      * @throws IOException in case an I/O error happens during output
      */
     public static void renderEPS(InputStream in, String name,
-                    float x, float y, float w, float h,
-                    float bboxx, float bboxy, float bboxw, float bboxh,
+            Rectangle2D viewport, Rectangle2D bbox,
                     PSGenerator gen) throws IOException {
         gen.getResourceTracker().notifyResourceUsageOnPage(PSProcSets.EPS_PROCSET);
         gen.writeln("%AXGBeginEPS: " + name);
         gen.writeln("BeginEPSF");
 
-        gen.writeln(gen.formatDouble(x) + " " + gen.formatDouble(y) + " translate");
-        gen.writeln("0 " + gen.formatDouble(h) + " translate");
+        gen.writeln(gen.formatDouble(viewport.getX()) 
+                + " " + gen.formatDouble(viewport.getY()) + " translate");
+        gen.writeln("0 " + gen.formatDouble(viewport.getHeight()) + " translate");
         gen.writeln("1 -1 scale");
-        float sx = w / bboxw;
-        float sy = h / bboxh;
+        double sx = viewport.getWidth() / bbox.getWidth();
+        double sy = viewport.getHeight() / bbox.getHeight();
         if (sx != 1 || sy != 1) {
             gen.writeln(gen.formatDouble(sx) + " " + gen.formatDouble(sy) + " scale");
         }
-        if (bboxx != 0 || bboxy != 0) {
-            gen.writeln(gen.formatDouble(-bboxx) + " " + gen.formatDouble(-bboxy) + " translate");
-        }
-        gen.writeln(gen.formatDouble(bboxx) + " " + gen.formatDouble(bboxy) 
-                + " " + gen.formatDouble(bboxw) + " " + gen.formatDouble(bboxh) + " re clip");
+        if (bbox.getX() != 0 || bbox.getY() != 0) {
+            gen.writeln(gen.formatDouble(-bbox.getX())
+                    + " " + gen.formatDouble(-bbox.getY()) + " translate");
+        }
+        gen.writeln(gen.formatDouble(bbox.getX())
+                + " " + gen.formatDouble(bbox.getY())
+                + " " + gen.formatDouble(bbox.getWidth())
+                + " " + gen.formatDouble(bbox.getHeight()) + " re clip");
         gen.writeln("newpath");
         
         PSResource res = new PSResource(PSResource.TYPE_FILE, name);



---------------------------------------------------------------------
Apache XML Graphics Project URL: http://xmlgraphics.apache.org/
To unsubscribe, e-mail: commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: commits-help@xmlgraphics.apache.org