org.apache.fop.render.pdf.PDFImageHandlerGraphics2D
org.apache.fop.render.pdf.PDFImageHandlerRenderedImage
org.apache.fop.render.pdf.PDFImageHandlerRawJPEG
+org.apache.fop.render.pdf.PDFImageHandlerRawPNG
org.apache.fop.render.pdf.PDFImageHandlerRawCCITTFax
org.apache.fop.render.pdf.PDFImageHandlerSVG
org.apache.fop.render.java2d.Java2DImageHandlerRenderedImage
org.apache.fop.render.ps.PSImageHandlerEPS
org.apache.fop.render.ps.PSImageHandlerRawCCITTFax
org.apache.fop.render.ps.PSImageHandlerRawJPEG
+org.apache.fop.render.ps.PSImageHandlerRawPNG
org.apache.fop.render.ps.PSImageHandlerGraphics2D
org.apache.fop.render.ps.PSImageHandlerSVG
org.apache.fop.render.afp.AFPImageHandlerRenderedImage
private PDFColor transparent = null;
private String key;
private PDFDocument pdfDoc;
+ private PDFFilter pdfFilter;
/**
* Create a bitmap image.
* {@inheritDoc}
*/
public PDFFilter getPDFFilter() {
- return null;
+ return pdfFilter;
}
+ public void setPDFFilter(PDFFilter pdfFilter) {
+ this.pdfFilter = pdfFilter;
+ }
}
package org.apache.fop.render.pdf;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
+import java.awt.image.IndexColorModel;
+import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.java2d.color.profile.ColorProfileUtil;
+import org.apache.fop.pdf.PDFArray;
import org.apache.fop.pdf.PDFColor;
import org.apache.fop.pdf.PDFConformanceException;
import org.apache.fop.pdf.PDFDeviceColorSpace;
/** the image */
protected Image image;
- private PDFICCStream pdfICCStream = null;
+ private PDFICCStream pdfICCStream;
+
+ private static final int MAX_HIVAL = 255;
/**
* Creates a new PDFImage from an Image instance.
//nop
}
+ /**
+ * This is to be used by populateXObjectDictionary() when the image is palette based.
+ * @param dict the dictionary to fill in
+ * @param icm the image color model
+ */
+ protected void populateXObjectDictionaryForIndexColorModel(PDFDictionary dict, IndexColorModel icm) {
+ PDFArray indexed = new PDFArray(dict);
+ indexed.add(new PDFName("Indexed"));
+ if (icm.getColorSpace().getType() != ColorSpace.TYPE_RGB) {
+ log.warn("Indexed color space is not using RGB as base color space."
+ + " The image may not be handled correctly." + " Base color space: "
+ + icm.getColorSpace() + " Image: " + image.getInfo());
+ }
+ indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName()));
+ int c = icm.getMapSize();
+ int hival = c - 1;
+ if (hival > MAX_HIVAL) {
+ throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL);
+ }
+ indexed.add(new Integer(hival));
+ int[] palette = new int[c];
+ icm.getRGBs(palette);
+ ByteArrayOutputStream baout = new ByteArrayOutputStream();
+ for (int i = 0; i < c; i++) {
+ // TODO Probably doesn't work for non RGB based color spaces
+ // See log warning above
+ int entry = palette[i];
+ baout.write((entry & 0xFF0000) >> 16);
+ baout.write((entry & 0xFF00) >> 8);
+ baout.write(entry & 0xFF);
+ }
+ indexed.add(baout.toByteArray());
+
+ dict.put("ColorSpace", indexed);
+ dict.put("BitsPerComponent", icm.getPixelSize());
+
+ Integer index = getIndexOfFirstTransparentColorInPalette(icm);
+ if (index != null) {
+ PDFArray mask = new PDFArray(dict);
+ mask.add(index);
+ mask.add(index);
+ dict.put("Mask", mask);
+ }
+ }
+
+ private static Integer getIndexOfFirstTransparentColorInPalette(IndexColorModel icm) {
+ byte[] alphas = new byte[icm.getMapSize()];
+ byte[] reds = new byte[icm.getMapSize()];
+ byte[] greens = new byte[icm.getMapSize()];
+ byte[] blues = new byte[icm.getMapSize()];
+ icm.getAlphas(alphas);
+ icm.getReds(reds);
+ icm.getGreens(greens);
+ icm.getBlues(blues);
+ for (int i = 0; i < icm.getMapSize(); i++) {
+ if ((alphas[i] & 0xFF) == 0) {
+ return new Integer(i);
+ }
+ }
+ return null;
+ }
+
/**
* Converts a ColorSpace object to a PDFColorSpace object.
* @param cs ColorSpace instance
--- /dev/null
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+// Original author: Matthias Reichenbacher\r
+\r
+package org.apache.fop.render.pdf;\r
+\r
+import java.awt.image.ColorModel;\r
+import java.awt.image.IndexColorModel;\r
+import java.io.DataInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.util.zip.Deflater;\r
+import java.util.zip.DeflaterOutputStream;\r
+import java.util.zip.Inflater;\r
+import java.util.zip.InflaterInputStream;\r
+\r
+import org.apache.commons.io.IOUtils;\r
+import org.apache.commons.io.output.ByteArrayOutputStream;\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+\r
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;\r
+import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;\r
+\r
+import org.apache.fop.pdf.BitmapImage;\r
+import org.apache.fop.pdf.FlateFilter;\r
+import org.apache.fop.pdf.PDFColor;\r
+import org.apache.fop.pdf.PDFDeviceColorSpace;\r
+import org.apache.fop.pdf.PDFDictionary;\r
+import org.apache.fop.pdf.PDFDocument;\r
+import org.apache.fop.pdf.PDFFilter;\r
+import org.apache.fop.pdf.PDFFilterException;\r
+import org.apache.fop.pdf.PDFFilterList;\r
+import org.apache.fop.pdf.PDFICCStream;\r
+import org.apache.fop.pdf.PDFReference;\r
+\r
+public class ImageRawPNGAdapter extends AbstractImageAdapter {\r
+\r
+ /** logging instance */\r
+ private static Log log = LogFactory.getLog(ImageRawPNGAdapter.class);\r
+\r
+ private PDFICCStream pdfICCStream;\r
+ private PDFFilter pdfFilter;\r
+ private String maskRef;\r
+ private PDFReference softMask;\r
+ private int numberOfInterleavedComponents;\r
+\r
+ /**\r
+ * Creates a new PDFImage from an Image instance.\r
+ * @param image the image\r
+ * @param key XObject key\r
+ */\r
+ public ImageRawPNGAdapter(ImageRawPNG image, String key) {\r
+ super(image, key);\r
+ }\r
+\r
+ /** {@inheritDoc} */\r
+ public void setup(PDFDocument doc) {\r
+ super.setup(doc);\r
+ ColorModel cm = ((ImageRawPNG) this.image).getColorModel();\r
+ if (cm instanceof IndexColorModel) {\r
+ numberOfInterleavedComponents = 1;\r
+ } else {\r
+ // this can be 1 (gray), 2 (gray + alpha), 3 (rgb) or 4 (rgb + alpha)\r
+ // numberOfInterleavedComponents = (cm.hasAlpha() ? 1 : 0) + cm.getNumColorComponents();\r
+ numberOfInterleavedComponents = cm.getNumComponents();\r
+ }\r
+\r
+ // set up image compression for non-alpha channel\r
+ FlateFilter flate;\r
+ try {\r
+ flate = new FlateFilter();\r
+ flate.setApplied(true);\r
+ flate.setPredictor(FlateFilter.PREDICTION_PNG_OPT);\r
+ if (numberOfInterleavedComponents < 3) {\r
+ // means palette (1) or gray (1) or gray + alpha (2)\r
+ flate.setColors(1);\r
+ } else {\r
+ // means rgb (3) or rgb + alpha (4)\r
+ flate.setColors(3);\r
+ }\r
+ flate.setColumns(image.getSize().getWidthPx());\r
+ flate.setBitsPerComponent(this.getBitsPerComponent());\r
+ } catch (PDFFilterException e) {\r
+ throw new RuntimeException("FlateFilter configuration error", e);\r
+ }\r
+ this.pdfFilter = flate;\r
+\r
+ // Handle transparency channel if applicable; note that for palette images the transparency is\r
+ // not TRANSLUCENT\r
+ if (cm.hasAlpha() && cm.getTransparency() == ColorModel.TRANSLUCENT) {\r
+ doc.getProfile().verifyTransparencyAllowed(image.getInfo().getOriginalURI());\r
+ // TODO: Implement code to combine image with background color if transparency is not allowed\r
+ // here we need to inflate the PNG pixel data, which includes alpha, separate the alpha channel\r
+ // and then deflate it back again\r
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+ DeflaterOutputStream dos = new DeflaterOutputStream(baos, new Deflater());\r
+ InputStream in = ((ImageRawStream) image).createInputStream();\r
+ try {\r
+ InflaterInputStream infStream = new InflaterInputStream(in, new Inflater());\r
+ DataInputStream dataStream = new DataInputStream(infStream);\r
+ // offset is the byte offset of the alpha component\r
+ int offset = numberOfInterleavedComponents - 1; // 1 for GA, 3 for RGBA\r
+ int numColumns = image.getSize().getWidthPx();\r
+ int bytesPerRow = numberOfInterleavedComponents * numColumns;\r
+ int filter;\r
+ // read line by line; the first byte holds the filter\r
+ while ((filter = dataStream.read()) != -1) {\r
+ byte[] bytes = new byte[bytesPerRow];\r
+ dataStream.readFully(bytes, 0, bytesPerRow);\r
+ dos.write((byte) filter);\r
+ for (int j = 0; j < numColumns; j++) {\r
+ dos.write(bytes, offset, 1);\r
+ offset += numberOfInterleavedComponents;\r
+ }\r
+ offset = numberOfInterleavedComponents - 1;\r
+ }\r
+ dos.close();\r
+ } catch (IOException e) {\r
+ throw new RuntimeException("Error processing transparency channel:", e);\r
+ } finally {\r
+ IOUtils.closeQuietly(in);\r
+ }\r
+ // set up alpha channel compression\r
+ FlateFilter transFlate;\r
+ try {\r
+ transFlate = new FlateFilter();\r
+ transFlate.setApplied(true);\r
+ transFlate.setPredictor(FlateFilter.PREDICTION_PNG_OPT);\r
+ transFlate.setColors(1);\r
+ transFlate.setColumns(image.getSize().getWidthPx());\r
+ transFlate.setBitsPerComponent(this.getBitsPerComponent());\r
+ } catch (PDFFilterException e) {\r
+ throw new RuntimeException("FlateFilter configuration error", e);\r
+ }\r
+ BitmapImage alphaMask = new BitmapImage("Mask:" + this.getKey(), image.getSize().getWidthPx(),\r
+ image.getSize().getHeightPx(), baos.toByteArray(), null);\r
+ alphaMask.setPDFFilter(transFlate);\r
+ alphaMask.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));\r
+ softMask = doc.addImage(null, alphaMask).makeReference();\r
+ }\r
+ }\r
+\r
+ /** {@inheritDoc} */\r
+ public PDFDeviceColorSpace getColorSpace() {\r
+ // DeviceGray, DeviceRGB, or DeviceCMYK\r
+ return toPDFColorSpace(image.getColorSpace());\r
+ }\r
+\r
+ /** {@inheritDoc} */\r
+ public int getBitsPerComponent() {\r
+ return ((ImageRawPNG) this.image).getBitDepth();\r
+ }\r
+\r
+ /** {@inheritDoc} */\r
+ public boolean isTransparent() {\r
+ return ((ImageRawPNG) this.image).isTransparent();\r
+ }\r
+\r
+ /** {@inheritDoc} */\r
+ public PDFColor getTransparentColor() {\r
+ return new PDFColor(((ImageRawPNG) this.image).getTransparentColor());\r
+ }\r
+\r
+ /** {@inheritDoc} */\r
+ public String getMask() {\r
+ return maskRef;\r
+ }\r
+\r
+ /** {@inheritDoc} */\r
+ public String getSoftMask() {\r
+ return softMask.toString();\r
+ }\r
+\r
+ /** {@inheritDoc} */\r
+ public PDFReference getSoftMaskReference() {\r
+ return softMask;\r
+ }\r
+\r
+ /** {@inheritDoc} */\r
+ public PDFFilter getPDFFilter() {\r
+ return pdfFilter;\r
+ }\r
+\r
+ /** {@inheritDoc} */\r
+ public void outputContents(OutputStream out) throws IOException {\r
+ InputStream in = ((ImageRawStream) image).createInputStream();\r
+\r
+ try {\r
+ if (numberOfInterleavedComponents == 1 || numberOfInterleavedComponents == 3) {\r
+ // means we have Gray, RGB, or Palette\r
+ IOUtils.copy(in, out);\r
+ } else {\r
+ // means we have Gray + alpha or RGB + alpha\r
+ // TODO: since we have alpha here do this when the alpha channel is extracted\r
+ int numBytes = numberOfInterleavedComponents - 1; // 1 for Gray, 3 for RGB\r
+ int numColumns = image.getSize().getWidthPx();\r
+ InflaterInputStream infStream = new InflaterInputStream(in, new Inflater());\r
+ DataInputStream dataStream = new DataInputStream(infStream);\r
+ int offset = 0;\r
+ int bytesPerRow = numberOfInterleavedComponents * numColumns;\r
+ int filter;\r
+ // here we need to inflate the PNG pixel data, which includes alpha, separate the alpha\r
+ // channel and then deflate the RGB channels back again\r
+ DeflaterOutputStream dos = new DeflaterOutputStream(out, new Deflater());\r
+ while ((filter = dataStream.read()) != -1) {\r
+ byte[] bytes = new byte[bytesPerRow];\r
+ dataStream.readFully(bytes, 0, bytesPerRow);\r
+ dos.write((byte) filter);\r
+ for (int j = 0; j < numColumns; j++) {\r
+ dos.write(bytes, offset, numBytes);\r
+ offset += numberOfInterleavedComponents;\r
+ }\r
+ offset = 0;\r
+ }\r
+ dos.close();\r
+ }\r
+ } finally {\r
+ IOUtils.closeQuietly(in);\r
+ }\r
+ }\r
+\r
+ /** {@inheritDoc} */\r
+ public PDFICCStream getICCStream() {\r
+ return pdfICCStream;\r
+ }\r
+\r
+ /** {@inheritDoc} */\r
+ public String getFilterHint() {\r
+ return PDFFilterList.PRECOMPRESSED_FILTER;\r
+ }\r
+\r
+ public void populateXObjectDictionary(PDFDictionary dict) {\r
+ ColorModel cm = ((ImageRawPNG) image).getColorModel();\r
+ if (cm instanceof IndexColorModel) {\r
+ IndexColorModel icm = (IndexColorModel) cm;\r
+ super.populateXObjectDictionaryForIndexColorModel(dict, icm);\r
+ }\r
+ }\r
+}\r
import java.io.IOException;
import java.io.OutputStream;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.ps.ImageEncodingHelper;
import org.apache.fop.pdf.AlphaRasterImage;
-import org.apache.fop.pdf.PDFArray;
import org.apache.fop.pdf.PDFColor;
import org.apache.fop.pdf.PDFDeviceColorSpace;
import org.apache.fop.pdf.PDFDictionary;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFFilter;
import org.apache.fop.pdf.PDFFilterList;
-import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFReference;
/**
return (getImage().getTransparentColor() != null);
}
- private static Integer getIndexOfFirstTransparentColorInPalette(RenderedImage image) {
- ColorModel cm = image.getColorModel();
- if (cm instanceof IndexColorModel) {
- IndexColorModel icm = (IndexColorModel)cm;
- //Identify the transparent color in the palette
- byte[] alphas = new byte[icm.getMapSize()];
- byte[] reds = new byte[icm.getMapSize()];
- byte[] greens = new byte[icm.getMapSize()];
- byte[] blues = new byte[icm.getMapSize()];
- icm.getAlphas(alphas);
- icm.getReds(reds);
- icm.getGreens(greens);
- icm.getBlues(blues);
- for (int i = 0;
- i < ((IndexColorModel) cm).getMapSize();
- i++) {
- if ((alphas[i] & 0xFF) == 0) {
- return Integer.valueOf(i);
- }
- }
- }
- return null;
- }
-
/** {@inheritDoc} */
@Override
public PDFColor getTransparentColor() {
}
}
- private static final int MAX_HIVAL = 255;
-
/** {@inheritDoc} */
@Override
public void populateXObjectDictionary(PDFDictionary dict) {
ColorModel cm = getEffectiveColorModel();
if (cm instanceof IndexColorModel) {
- IndexColorModel icm = (IndexColorModel)cm;
- PDFArray indexed = new PDFArray(dict);
- indexed.add(new PDFName("Indexed"));
-
- if (icm.getColorSpace().getType() != ColorSpace.TYPE_RGB) {
- log.warn("Indexed color space is not using RGB as base color space."
- + " The image may not be handled correctly."
- + " Base color space: " + icm.getColorSpace()
- + " Image: " + image.getInfo());
- }
- indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName()));
- int c = icm.getMapSize();
- int hival = c - 1;
- if (hival > MAX_HIVAL) {
- throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL);
- }
- indexed.add(Integer.valueOf(hival));
- int[] palette = new int[c];
- icm.getRGBs(palette);
- ByteArrayOutputStream baout = new ByteArrayOutputStream();
- for (int i = 0; i < c; i++) {
- //TODO Probably doesn't work for non RGB based color spaces
- //See log warning above
- int entry = palette[i];
- baout.write((entry & 0xFF0000) >> 16);
- baout.write((entry & 0xFF00) >> 8);
- baout.write(entry & 0xFF);
- }
- indexed.add(baout.toByteArray());
- IOUtils.closeQuietly(baout);
-
- dict.put("ColorSpace", indexed);
- dict.put("BitsPerComponent", icm.getPixelSize());
-
- Integer index = getIndexOfFirstTransparentColorInPalette(getImage().getRenderedImage());
- if (index != null) {
- PDFArray mask = new PDFArray(dict);
- mask.add(index);
- mask.add(index);
- dict.put("Mask", mask);
- }
+ IndexColorModel icm = (IndexColorModel) cm;
+ super.populateXObjectDictionaryForIndexColorModel(dict, icm);
}
}
--- /dev/null
+/*
+ * 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$ */
+
+// Original author: Matthias Reichenbacher
+
+package org.apache.fop.render.pdf;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
+
+import org.apache.fop.pdf.PDFImage;
+import org.apache.fop.render.RenderingContext;
+
+/**
+ * Image handler implementation which handles CCITT encoded images (CCITT fax group 3/4)
+ * for PDF output.
+ */
+public class PDFImageHandlerRawPNG extends AbstractPDFImageHandler {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {ImageFlavor.RAW_PNG};
+
+ @Override
+ PDFImage createPDFImage(Image image, String xobjectKey) {
+ return new ImageRawPNGAdapter((ImageRawPNG) image, xobjectKey);
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 100;
+ }
+
+ /** {@inheritDoc} */
+ public Class<ImageRawPNG> getSupportedImageClass() {
+ return ImageRawPNG.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return FLAVORS;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCompatible(RenderingContext targetContext, Image image) {
+ return (image == null || image instanceof ImageRawPNG)
+ && targetContext instanceof PDFRenderingContext;
+ }
+
+}
--- /dev/null
+/*
+ * 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.fop.render.ps;
+
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+
+import org.apache.commons.io.IOUtils;
+
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
+import org.apache.xmlgraphics.ps.ImageEncoder;
+
+/**
+ * ImageEncoder implementation for PNG images.
+ */
+public class ImageEncoderPNG implements ImageEncoder {
+ private final ImageRawPNG image;
+ private int numberOfInterleavedComponents;
+
+ /**
+ * Main constructor
+ * @param image the PNG image
+ */
+ public ImageEncoderPNG(ImageRawPNG image) {
+ this.image = image;
+ ColorModel cm = ((ImageRawPNG) this.image).getColorModel();
+ if (cm instanceof IndexColorModel) {
+ numberOfInterleavedComponents = 1;
+ } else {
+ // this can be 1 (gray), 2 (gray + alpha), 3 (rgb) or 4 (rgb + alpha)
+ // numberOfInterleavedComponents = (cm.hasAlpha() ? 1 : 0) + cm.getNumColorComponents();
+ numberOfInterleavedComponents = cm.getNumComponents();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void writeTo(OutputStream out) throws IOException {
+ // TODO: refactor this code with equivalent PDF code
+ InputStream in = ((ImageRawStream) image).createInputStream();
+ try {
+ if (numberOfInterleavedComponents == 1 || numberOfInterleavedComponents == 3) {
+ // means we have Gray, RGB, or Palette
+ IOUtils.copy(in, out);
+ } else {
+ // means we have Gray + alpha or RGB + alpha
+ int numBytes = numberOfInterleavedComponents - 1; // 1 for Gray, 3 for RGB
+ int numColumns = image.getSize().getWidthPx();
+ InflaterInputStream infStream = new InflaterInputStream(in, new Inflater());
+ DataInputStream dataStream = new DataInputStream(infStream);
+ int offset = 0;
+ int bytesPerRow = numberOfInterleavedComponents * numColumns;
+ int filter;
+ // here we need to inflate the PNG pixel data, which includes alpha, separate the alpha
+ // channel and then deflate the RGB channels back again
+ // TODO: not using the baos below and using the original out instead (as happens in PDF)
+ // would be preferable but that does not work with the rest of the postscript code; this
+ // needs to be revisited
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DeflaterOutputStream dos = new DeflaterOutputStream(/* out */baos, new Deflater());
+ while ((filter = dataStream.read()) != -1) {
+ byte[] bytes = new byte[bytesPerRow];
+ dataStream.readFully(bytes, 0, bytesPerRow);
+ dos.write((byte) filter);
+ for (int j = 0; j < numColumns; j++) {
+ dos.write(bytes, offset, numBytes);
+ offset += numberOfInterleavedComponents;
+ }
+ offset = 0;
+ }
+ dos.close();
+ IOUtils.copy(new ByteArrayInputStream(baos.toByteArray()), out);
+ }
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public String getImplicitFilter() {
+ String filter = "<< /Predictor 15 /Columns " + image.getSize().getWidthPx();
+ filter += " /Colors " + (numberOfInterleavedComponents > 2 ? 3 : 1);
+ filter += " /BitsPerComponent " + image.getBitDepth() + " >> /FlateDecode";
+ return filter;
+ }
+}
--- /dev/null
+/*
+ * 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.fop.render.ps;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.ColorModel;
+import java.io.IOException;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
+import org.apache.xmlgraphics.ps.FormGenerator;
+import org.apache.xmlgraphics.ps.ImageEncoder;
+import org.apache.xmlgraphics.ps.ImageFormGenerator;
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.ps.PSImageUtils;
+
+import org.apache.fop.render.RenderingContext;
+
+/**
+ * Image handler implementation which handles raw (not decoded) PNG images for PostScript output.
+ */
+public class PSImageHandlerRawPNG implements PSImageHandler {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {ImageFlavor.RAW_PNG};
+
+ /** {@inheritDoc} */
+ public void handleImage(RenderingContext context, Image image, Rectangle pos) throws IOException {
+ PSRenderingContext psContext = (PSRenderingContext) context;
+ PSGenerator gen = psContext.getGenerator();
+ ImageRawPNG png = (ImageRawPNG) image;
+
+ float x = (float) pos.getX() / 1000f;
+ float y = (float) pos.getY() / 1000f;
+ float w = (float) pos.getWidth() / 1000f;
+ float h = (float) pos.getHeight() / 1000f;
+ Rectangle2D targetRect = new Rectangle2D.Float(x, y, w, h);
+
+ ImageEncoder encoder = new ImageEncoderPNG(png);
+ ImageInfo info = image.getInfo();
+ Dimension imgDim = info.getSize().getDimensionPx();
+ String imgDescription = image.getClass().getName();
+ ColorModel cm = png.getColorModel();
+
+ PSImageUtils.writeImage(encoder, imgDim, imgDescription, targetRect, cm, gen);
+ }
+
+ /** {@inheritDoc} */
+ public void generateForm(RenderingContext context, Image image, PSImageFormResource form)
+ throws IOException {
+ PSRenderingContext psContext = (PSRenderingContext) context;
+ PSGenerator gen = psContext.getGenerator();
+ ImageRawPNG png = (ImageRawPNG) image;
+ ImageInfo info = image.getInfo();
+ String imageDescription = info.getMimeType() + " " + info.getOriginalURI();
+
+ ImageEncoder encoder = new ImageEncoderPNG(png);
+ FormGenerator formGen = new ImageFormGenerator(form.getName(), imageDescription, info.getSize()
+ .getDimensionPt(), info.getSize().getDimensionPx(), encoder, png.getColorSpace(),
+ false);
+ formGen.generate(gen);
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 200;
+ }
+
+ /** {@inheritDoc} */
+ public Class<ImageRawPNG> getSupportedImageClass() {
+ return ImageRawPNG.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return FLAVORS;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCompatible(RenderingContext targetContext, Image image) {
+ if (targetContext instanceof PSRenderingContext) {
+ PSRenderingContext psContext = (PSRenderingContext) targetContext;
+ // The filters required for this implementation need PS level 2 or higher
+ if (psContext.getGenerator().getPSLevel() >= 2) {
+ return (image == null || image instanceof ImageRawPNG);
+ }
+ }
+ return false;
+ }
+
+}
package org.apache.fop.render.ps;
+import java.awt.Dimension;
import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.ColorModel;
import java.awt.image.RenderedImage;
import java.io.IOException;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
import org.apache.xmlgraphics.ps.FormGenerator;
+import org.apache.xmlgraphics.ps.ImageEncoder;
+import org.apache.xmlgraphics.ps.ImageEncodingHelper;
import org.apache.xmlgraphics.ps.ImageFormGenerator;
import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.xmlgraphics.ps.PSImageUtils;
/** {@inheritDoc} */
public void handleImage(RenderingContext context, Image image, Rectangle pos)
throws IOException {
- PSRenderingContext psContext = (PSRenderingContext)context;
+ PSRenderingContext psContext = (PSRenderingContext) context;
PSGenerator gen = psContext.getGenerator();
- ImageRendered imageRend = (ImageRendered)image;
+ ImageRendered imageRend = (ImageRendered) image;
- float x = (float)pos.getX() / 1000f;
- float y = (float)pos.getY() / 1000f;
- float w = (float)pos.getWidth() / 1000f;
- float h = (float)pos.getHeight() / 1000f;
+ float x = (float) pos.getX() / 1000f;
+ float y = (float) pos.getY() / 1000f;
+ float w = (float) pos.getWidth() / 1000f;
+ float h = (float) pos.getHeight() / 1000f;
+ Rectangle2D targetRect = new Rectangle2D.Double(x, y, w, h);
RenderedImage ri = imageRend.getRenderedImage();
- PSImageUtils.renderBitmapImage(ri, x, y, w, h, gen);
+ ImageEncoder encoder = ImageEncodingHelper.createRenderedImageEncoder(ri);
+ Dimension imgDim = new Dimension(ri.getWidth(), ri.getHeight());
+ String imgDescription = ri.getClass().getName();
+ ImageEncodingHelper helper = new ImageEncodingHelper(ri);
+ ColorModel cm = helper.getEncodedColorModel();
+
+ PSImageUtils.writeImage(encoder, imgDim, imgDescription, targetRect, cm, gen);
}
/** {@inheritDoc} */
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
+ <action context="Images" dev="GA" type="fix" fixes-bug="40676" due-to="Luis Bernardo, Matthias Reischenbacher">
+ Support use of ImageLoaderRawPNG decoder in order to prevent re-encoding of PNG images (and unnecssary output file bloat).
+ </action>
<action context="Code" dev="GA" type="fix" fixes-bug="53412" due-to="Alexios Giotis">
Eliminate incorrect use of object identity which produces excessive property cache collisions.
</action>
--- /dev/null
+/*
+ * 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.fop.render;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+import org.apache.xmlgraphics.image.loader.ImageSize;
+
+public final class RawPNGTestUtil {
+
+ private static int NUM_ROWS = 32;
+ private static int NUM_COLUMNS = 32;
+ private static int DPI = 72;
+
+ private RawPNGTestUtil() {
+
+ }
+
+ /**
+ * Builds a PNG IDAT section for a square of a given color and alpha; the filter is fixed.
+ * @param gray the gray color; set to -1 if using RGB
+ * @param red the red color; ignored if gray > -1
+ * @param green the green color; ignored if gray > -1
+ * @param blue the blue color; ignored if gray > -1
+ * @param alpha the alpha color; set to -1 if not present
+ * @return the PNG IDAT byte array
+ * @throws IOException
+ */
+ public static byte[] buildGRGBAData(int gray, int red, int green, int blue, int alpha) throws IOException {
+ // build an image, 32x32, Gray or RGB, with or without alpha, and with filter
+ int filter = 0;
+ int numRows = NUM_ROWS;
+ int numColumns = NUM_COLUMNS;
+ int numComponents = (gray > -1 ? 1 : 3) + (alpha > -1 ? 1 : 0);
+ int numBytesPerRow = numColumns * numComponents + 1; // 1 for filter
+ int numBytes = numRows * numBytesPerRow;
+ byte[] data = new byte[numBytes];
+ for (int r = 0; r < numRows; r++) {
+ data[r * numBytesPerRow] = (byte) filter;
+ for (int c = 0; c < numColumns; c++) {
+ if (numComponents == 1) {
+ data[r * numBytesPerRow + numComponents * c + 1] = (byte) gray;
+ } else if (numComponents == 2) {
+ data[r * numBytesPerRow + numComponents * c + 1] = (byte) gray;
+ data[r * numBytesPerRow + numComponents * c + 2] = (byte) alpha;
+ } else if (numComponents == 3) {
+ data[r * numBytesPerRow + numComponents * c + 1] = (byte) red;
+ data[r * numBytesPerRow + numComponents * c + 2] = (byte) green;
+ data[r * numBytesPerRow + numComponents * c + 3] = (byte) blue;
+ } else if (numComponents == 4) {
+ data[r * numBytesPerRow + numComponents * c + 1] = (byte) red;
+ data[r * numBytesPerRow + numComponents * c + 2] = (byte) green;
+ data[r * numBytesPerRow + numComponents * c + 3] = (byte) blue;
+ data[r * numBytesPerRow + numComponents * c + 4] = (byte) alpha;
+ }
+ }
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DeflaterOutputStream dos = new DeflaterOutputStream(baos, new Deflater());
+ dos.write(data);
+ dos.close();
+ return baos.toByteArray();
+ }
+
+ /**
+ *
+ * @return a default ImageSize
+ */
+ public static ImageSize getImageSize() {
+ return new ImageSize(NUM_ROWS, NUM_COLUMNS, DPI);
+ }
+}
--- /dev/null
+/*
+ * 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.fop.render.pdf;
+
+import java.awt.image.ComponentColorModel;
+import java.awt.image.IndexColorModel;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+import org.junit.Test;
+
+import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
+
+import org.apache.fop.pdf.FlateFilter;
+import org.apache.fop.pdf.PDFAMode;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFProfile;
+import org.apache.fop.render.RawPNGTestUtil;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ImageRawPNGAdapterTestCase {
+
+ @Test
+ public void testSetupWithIndexColorModel() {
+ IndexColorModel cm = mock(IndexColorModel.class);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ PDFDocument doc = mock(PDFDocument.class);
+ PDFProfile profile = mock(PDFProfile.class);
+ ImageRawPNGAdapter irpnga = new ImageRawPNGAdapter(irpng, "mock");
+ ImageSize is = RawPNGTestUtil.getImageSize();
+
+ when(irpng.getColorModel()).thenReturn(cm);
+ // when(cm.hasAlpha()).thenReturn(false);
+ when(doc.getProfile()).thenReturn(profile);
+ when(profile.getPDFAMode()).thenReturn(PDFAMode.PDFA_1A);
+ when(irpng.getSize()).thenReturn(is);
+ irpnga.setup(doc);
+ FlateFilter filter = (FlateFilter) irpnga.getPDFFilter();
+ assertEquals(1, filter.getColors());
+ }
+
+ @Test
+ public void testSetupWithComponentColorModel() throws IOException {
+ ComponentColorModel cm = mock(ComponentColorModel.class);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ PDFDocument doc = mock(PDFDocument.class);
+ PDFProfile profile = mock(PDFProfile.class);
+ ImageRawPNGAdapter irpnga = new ImageRawPNGAdapter(irpng, "mock");
+ ImageSize is = RawPNGTestUtil.getImageSize();
+
+ when(irpng.getColorModel()).thenReturn(cm);
+ when(cm.getNumComponents()).thenReturn(3);
+ // when(cm.hasAlpha()).thenReturn(false);
+ when(doc.getProfile()).thenReturn(profile);
+ when(profile.getPDFAMode()).thenReturn(PDFAMode.PDFA_1A);
+ when(irpng.getSize()).thenReturn(is);
+ irpnga.setup(doc);
+ FlateFilter filter = (FlateFilter) irpnga.getPDFFilter();
+ assertEquals(3, filter.getColors());
+ }
+
+ @Test
+ public void testOutputContentsWithRGBPNG() throws IOException {
+ testOutputContentsWithGRGBAPNG(-1, 128, 128, 128, -1);
+ }
+
+ @Test
+ public void testOutputContentsWithRGBAPNG() throws IOException {
+ testOutputContentsWithGRGBAPNG(-1, 128, 128, 128, 128);
+ }
+
+ @Test
+ public void testOutputContentsWithGPNG() throws IOException {
+ testOutputContentsWithGRGBAPNG(128, -1, -1, -1, -1);
+ }
+
+ @Test
+ public void testOutputContentsWithGAPNG() throws IOException {
+ testOutputContentsWithGRGBAPNG(128, -1, -1, -1, 128);
+ }
+
+ private void testOutputContentsWithGRGBAPNG(int gray, int red, int green, int blue, int alpha)
+ throws IOException {
+ int numColorComponents = gray > -1 ? 1 : 3;
+ int numComponents = numColorComponents + (alpha > -1 ? 1 : 0);
+ ComponentColorModel cm = mock(ComponentColorModel.class);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ PDFDocument doc = mock(PDFDocument.class);
+ PDFProfile profile = mock(PDFProfile.class);
+ ImageRawPNGAdapter irpnga = new ImageRawPNGAdapter(irpng, "mock");
+ ImageSize is = RawPNGTestUtil.getImageSize();
+
+ when(irpng.getColorModel()).thenReturn(cm);
+ when(cm.getNumComponents()).thenReturn(numComponents);
+ // when(cm.hasAlpha()).thenReturn(false);
+ when(doc.getProfile()).thenReturn(profile);
+ when(profile.getPDFAMode()).thenReturn(PDFAMode.PDFA_1A);
+ when(irpng.getSize()).thenReturn(is);
+ irpnga.setup(doc);
+ FlateFilter filter = (FlateFilter) irpnga.getPDFFilter();
+ assertEquals(numColorComponents, filter.getColors());
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] data = RawPNGTestUtil.buildGRGBAData(gray, red, green, blue, alpha);
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ when(irpng.createInputStream()).thenReturn(bais);
+ irpnga.outputContents(baos);
+ if (alpha > -1) {
+ byte[] expected = RawPNGTestUtil.buildGRGBAData(gray, red, green, blue, -1);
+ assertArrayEquals(expected, baos.toByteArray());
+ } else {
+ assertArrayEquals(data, baos.toByteArray());
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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.fop.render.ps;
+
+import java.awt.image.ComponentColorModel;
+import java.awt.image.IndexColorModel;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.junit.Test;
+
+import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
+
+import org.apache.fop.render.RawPNGTestUtil;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ImageEncoderPNGTestCase {
+
+ @Test
+ public void testWriteToWithRGBPNG() throws IOException {
+ testWriteToWithGRGBAPNG(-1, 128, 128, 128, -1);
+ }
+
+ @Test
+ public void testWriteToWithGPNG() throws IOException {
+ testWriteToWithGRGBAPNG(128, -1, -1, -1, -1);
+ }
+
+ @Test
+ public void testWriteToWithRGBAPNG() throws IOException {
+ testWriteToWithGRGBAPNG(-1, 128, 128, 128, 128);
+ }
+
+ @Test
+ public void testWriteToWithGAPNG() throws IOException {
+ testWriteToWithGRGBAPNG(128, -1, -1, -1, 128);
+ }
+
+ private void testWriteToWithGRGBAPNG(int gray, int red, int green, int blue, int alpha)
+ throws IOException {
+ int numComponents = (gray > -1 ? 1 : 3) + (alpha > -1 ? 1 : 0);
+ ImageSize is = RawPNGTestUtil.getImageSize();
+ ComponentColorModel cm = mock(ComponentColorModel.class);
+ when(cm.getNumComponents()).thenReturn(numComponents);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ when(irpng.getColorModel()).thenReturn(cm);
+ when(irpng.getSize()).thenReturn(is);
+ ImageEncoderPNG iepng = new ImageEncoderPNG(irpng);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] data = RawPNGTestUtil.buildGRGBAData(gray, red, green, blue, alpha);
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ when(irpng.createInputStream()).thenReturn(bais);
+ iepng.writeTo(baos);
+ if (alpha > -1) {
+ byte[] expected = RawPNGTestUtil.buildGRGBAData(gray, red, green, blue, -1);
+ assertArrayEquals(expected, baos.toByteArray());
+ } else {
+ assertArrayEquals(data, baos.toByteArray());
+ }
+ }
+
+ @Test
+ public void testWriteToWithPalettePNG() throws IOException {
+ ImageSize is = RawPNGTestUtil.getImageSize();
+ IndexColorModel cm = mock(IndexColorModel.class);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ when(irpng.getColorModel()).thenReturn(cm);
+ when(irpng.getSize()).thenReturn(is);
+ ImageEncoderPNG iepng = new ImageEncoderPNG(irpng);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] data = RawPNGTestUtil.buildGRGBAData(128, -1, -1, -1, -1);
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ when(irpng.createInputStream()).thenReturn(bais);
+ iepng.writeTo(baos);
+ assertArrayEquals(data, baos.toByteArray());
+ }
+
+ @Test
+ public void testGetImplicitFilterWithIndexColorModel() {
+ ImageSize is = RawPNGTestUtil.getImageSize();
+ IndexColorModel cm = mock(IndexColorModel.class);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ when(irpng.getColorModel()).thenReturn(cm);
+ when(irpng.getBitDepth()).thenReturn(8);
+ when(irpng.getSize()).thenReturn(is);
+ ImageEncoderPNG iepng = new ImageEncoderPNG(irpng);
+
+ String expectedFilter = "<< /Predictor 15 /Columns 32 /Colors 1 /BitsPerComponent 8 >> /FlateDecode";
+ assertEquals(expectedFilter, iepng.getImplicitFilter());
+ }
+
+ @Test
+ public void testGetImplicitFilterWithComponentColorModel() {
+ ImageSize is = RawPNGTestUtil.getImageSize();
+ ComponentColorModel cm = mock(ComponentColorModel.class);
+ when(cm.getNumComponents()).thenReturn(3);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ when(irpng.getColorModel()).thenReturn(cm);
+ when(irpng.getBitDepth()).thenReturn(8);
+ when(irpng.getSize()).thenReturn(is);
+ ImageEncoderPNG iepng = new ImageEncoderPNG(irpng);
+
+ String expectedFilter = "<< /Predictor 15 /Columns 32 /Colors 3 /BitsPerComponent 8 >> /FlateDecode";
+ assertEquals(expectedFilter, iepng.getImplicitFilter());
+ }
+
+}