diff options
Diffstat (limited to 'src/java')
3 files changed, 253 insertions, 56 deletions
diff --git a/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java index 28c942a08..b956d8264 100644 --- a/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java +++ b/src/java/org/apache/fop/render/afp/AFPImageHandlerRenderedImage.java @@ -19,24 +19,33 @@ package org.apache.fop.render.afp; +import java.awt.image.ColorModel; import java.awt.image.RenderedImage; import java.io.IOException; import org.apache.commons.io.output.ByteArrayOutputStream; -import org.apache.fop.afp.AFPDataObjectInfo; -import org.apache.fop.afp.AFPImageObjectInfo; -import org.apache.fop.afp.AFPObjectAreaInfo; -import org.apache.fop.afp.AFPPaintingState; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.apache.xmlgraphics.image.loader.ImageFlavor; import org.apache.xmlgraphics.image.loader.impl.ImageRendered; import org.apache.xmlgraphics.ps.ImageEncodingHelper; import org.apache.xmlgraphics.util.MimeConstants; +import org.apache.fop.afp.AFPDataObjectInfo; +import org.apache.fop.afp.AFPImageObjectInfo; +import org.apache.fop.afp.AFPObjectAreaInfo; +import org.apache.fop.afp.AFPPaintingState; +import org.apache.fop.util.BitmapImageUtil; + /** * PDFImageHandler implementation which handles RenderedImage instances. */ public class AFPImageHandlerRenderedImage extends AFPImageHandler { + /** logging instance */ + private static Log log = LogFactory.getLog(AFPImageHandlerRenderedImage.class); + private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { ImageFlavor.BUFFERED_IMAGE, ImageFlavor.RENDERED_IMAGE @@ -67,22 +76,69 @@ public class AFPImageHandlerRenderedImage extends AFPImageHandler { int dataWidth = renderedImage.getWidth(); imageObjectInfo.setDataWidth(dataWidth); + int maxPixelSize = paintingState.getBitsPerPixel(); + if (paintingState.isColorImages()) { + maxPixelSize *= 3; //RGB only at the moment + } + + ColorModel cm = renderedImage.getColorModel(); + if (log.isTraceEnabled()) { + log.trace("ColorModel: " + cm); + } + int pixelSize = cm.getPixelSize(); + if (cm.hasAlpha()) { + pixelSize -= 8; + } + //TODO Add support for CMYK images + + byte[] imageData = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageEncodingHelper.encodeRenderedImageAsRGB(renderedImage, baos); - byte[] imageData = baos.toByteArray(); - - boolean colorImages = paintingState.isColorImages(); - imageObjectInfo.setColor(colorImages); - - // convert to grayscale - if (!colorImages) { - baos.reset(); - int bitsPerPixel = paintingState.getBitsPerPixel(); - imageObjectInfo.setBitsPerPixel(bitsPerPixel); - ImageEncodingHelper.encodeRGBAsGrayScale( - imageData, dataWidth, dataHeight, bitsPerPixel, baos); + boolean allowDirectEncoding = true; + if (allowDirectEncoding && pixelSize <= maxPixelSize) { + //Attempt to encode without resampling the image + ImageEncodingHelper helper = new ImageEncodingHelper(renderedImage); + ColorModel encodedColorModel = helper.getEncodedColorModel(); + boolean directEncode = true; + if (helper.getEncodedColorModel().getPixelSize() > maxPixelSize) { + directEncode = false; //pixel size needs to be reduced + } + if (BitmapImageUtil.getColorIndexSize(renderedImage) > 2) { + directEncode = false; //Lookup tables are not implemented, yet + } + if (BitmapImageUtil.isMonochromeImage(renderedImage) + && BitmapImageUtil.isZeroBlack(renderedImage)) { + directEncode = false; //Passing additive/subtractive info not implemented, yet + } + if (directEncode) { + log.debug("Encoding image directly..."); + imageObjectInfo.setBitsPerPixel(encodedColorModel.getPixelSize()); + helper.encode(baos); + imageData = baos.toByteArray(); + } + } + if (imageData == null) { + log.debug("Encoding image via RGB..."); + //Convert image to 24bit RGB + ImageEncodingHelper.encodeRenderedImageAsRGB(renderedImage, baos); imageData = baos.toByteArray(); + + boolean colorImages = paintingState.isColorImages(); + imageObjectInfo.setColor(colorImages); + + // convert to grayscale + if (!colorImages) { + log.debug("Converting RGB image to grayscale..."); + baos.reset(); + int bitsPerPixel = paintingState.getBitsPerPixel(); + imageObjectInfo.setBitsPerPixel(bitsPerPixel); + //TODO this should be done off the RenderedImage to avoid buffering the + //intermediate 24bit image + ImageEncodingHelper.encodeRGBAsGrayScale( + imageData, dataWidth, dataHeight, bitsPerPixel, baos); + imageData = baos.toByteArray(); + } } + imageObjectInfo.setData(imageData); // set object area info diff --git a/src/java/org/apache/fop/render/pcl/PCLGenerator.java b/src/java/org/apache/fop/render/pcl/PCLGenerator.java index fbb9c6ad0..50cf622c7 100644 --- a/src/java/org/apache/fop/render/pcl/PCLGenerator.java +++ b/src/java/org/apache/fop/render/pcl/PCLGenerator.java @@ -47,6 +47,8 @@ import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.xmlgraphics.image.GraphicsUtil; import org.apache.xmlgraphics.util.UnitConv; +import org.apache.fop.util.BitmapImageUtil; + /** * This class provides methods for generating PCL print files. */ @@ -522,7 +524,7 @@ public class PCLGenerator { * @return the gray value */ public final int convertToGray(int r, int g, int b) { - return (r * 30 + g * 59 + b * 11) / 100; + return BitmapImageUtil.convertToGray(r, g, b); } /** @@ -574,13 +576,7 @@ public class PCLGenerator { * @return true if it's a monochrome image */ public static boolean isMonochromeImage(RenderedImage img) { - ColorModel cm = img.getColorModel(); - if (cm instanceof IndexColorModel) { - IndexColorModel icm = (IndexColorModel)cm; - return icm.getMapSize() == 2; - } else { - return false; - } + return BitmapImageUtil.isMonochromeImage(img); } /** @@ -589,7 +585,7 @@ public class PCLGenerator { * @return true if it's a grayscale image */ public static boolean isGrayscaleImage(RenderedImage img) { - return (img.getColorModel().getColorSpace().getNumComponents() == 1); + return BitmapImageUtil.isGrayscaleImage(img); } private MonochromeBitmapConverter createMonochromeBitmapConverter() { @@ -766,22 +762,7 @@ public class PCLGenerator { } } if (src == null) { - src = new BufferedImage(effDim.width, effDim.height, - BufferedImage.TYPE_BYTE_GRAY); - Graphics2D g2d = src.createGraphics(); - try { - g2d.setBackground(Color.white); - g2d.setColor(Color.black); - g2d.clearRect(0, 0, effDim.width, effDim.height); - - AffineTransform at = new AffineTransform(); - double sx = effDim.getWidth() / orgDim.getWidth(); - double sy = effDim.getHeight() / orgDim.getHeight(); - at.scale(sx, sy); - g2d.drawRenderedImage(img, at); - } finally { - g2d.dispose(); - } + src = BitmapImageUtil.convertToGrayscale(img, effDim); } MonochromeBitmapConverter converter = createMonochromeBitmapConverter(); converter.setHint("quality", "false"); @@ -793,22 +774,9 @@ public class PCLGenerator { setTransparencyMode(sourceTransparency || mask != null, true); paintMonochromeBitmap(red, effResolution); } else { - //TODO untested! RenderedImage effImg = img; if (scaled) { - BufferedImage buf = new BufferedImage(effDim.width, effDim.height, - BufferedImage.TYPE_BYTE_BINARY); - Graphics2D g2d = buf.createGraphics(); - try { - AffineTransform at = new AffineTransform(); - double sx = effDim.getWidth() / orgDim.getWidth(); - double sy = effDim.getHeight() / orgDim.getHeight(); - at.scale(sx, sy); - g2d.drawRenderedImage(img, at); - } finally { - g2d.dispose(); - } - effImg = buf; + effImg = BitmapImageUtil.convertToMonochrome(img, effDim); } setSourceTransparencyMode(sourceTransparency); selectCurrentPattern(0, 0); //Solid black diff --git a/src/java/org/apache/fop/util/BitmapImageUtil.java b/src/java/org/apache/fop/util/BitmapImageUtil.java new file mode 100644 index 000000000..278587b64 --- /dev/null +++ b/src/java/org/apache/fop/util/BitmapImageUtil.java @@ -0,0 +1,173 @@ +/* + * 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.util; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.IndexColorModel; +import java.awt.image.RenderedImage; + +/** + * Utility method for dealing with bitmap images. + */ +public class BitmapImageUtil { + + /** + * Indicates whether an image is a monochrome (1 bit black and white) image. + * @param img the image + * @return true if it's a monochrome image + */ + public static final boolean isMonochromeImage(RenderedImage img) { + return (getColorIndexSize(img) == 2); + } + + /** + * Indicates whether a zero bit indicates a black/dark pixel for a monochrome image. + * @param img the image (must be 1 bit monochrome) + * @return true if a zero bit indicates a black/dark pixel, false for a white/bright pixel + */ + public static final boolean isZeroBlack(RenderedImage img) { + if (!isMonochromeImage(img)) { + throw new IllegalArgumentException("Image is not a monochrome image!"); + } + IndexColorModel icm = (IndexColorModel)img.getColorModel(); + int gray0 = convertToGray(icm.getRGB(0)); + int gray1 = convertToGray(icm.getRGB(1)); + return gray0 < gray1; + } + + /** + * Convert an RGB color value to a grayscale from 0 to 100. + * @param r the red component + * @param g the green component + * @param b the blue component + * @return the gray value + */ + public static final int convertToGray(int r, int g, int b) { + return (r * 30 + g * 59 + b * 11) / 100; + } + + /** + * Convert an RGB color value to a grayscale from 0 to 100. + * @param rgb the RGB value + * @return the gray value + */ + public static final int convertToGray(int rgb) { + int r = (rgb & 0xFF0000) >> 16; + int g = (rgb & 0xFF00) >> 8; + int b = rgb & 0xFF; + return convertToGray(r, g, b); + } + + /** + * Returns the size of the color index if the given image has one. + * @param img the image + * @return the size of the color index or 0 if there's no color index + */ + public static final int getColorIndexSize(RenderedImage img) { + ColorModel cm = img.getColorModel(); + if (cm instanceof IndexColorModel) { + IndexColorModel icm = (IndexColorModel)cm; + return icm.getMapSize(); + } else { + return 0; + } + } + + /** + * Indicates whether an image is a grayscale image. + * @param img the image + * @return true if it's a grayscale image + */ + public static final boolean isGrayscaleImage(RenderedImage img) { + return (img.getColorModel().getColorSpace().getNumComponents() == 1); + } + + /** + * Converts an image to sRGB. Optionally, the image can be scaled. + * @param img the image to be converted + * @param targetDimension the new target dimensions or null if no scaling is necessary + * @return the sRGB image + */ + public static final BufferedImage convertTosRGB(RenderedImage img, + Dimension targetDimension) { + return convertAndScaleImage(img, targetDimension, BufferedImage.TYPE_INT_RGB); + } + + /** + * Converts an image to a grayscale (8 bits) image. Optionally, the image can be scaled. + * @param img the image to be converted + * @param targetDimension the new target dimensions or null if no scaling is necessary + * @return the grayscale image + */ + public static final BufferedImage convertToGrayscale(RenderedImage img, + Dimension targetDimension) { + return convertAndScaleImage(img, targetDimension, BufferedImage.TYPE_BYTE_GRAY); + } + + /** + * Converts an image to a monochrome 1-bit image. Optionally, the image can be scaled. + * @param img the image to be converted + * @param targetDimension the new target dimensions or null if no scaling is necessary + * @return the monochrome image + */ + public static final BufferedImage convertToMonochrome(RenderedImage img, + Dimension targetDimension) { + return convertAndScaleImage(img, targetDimension, BufferedImage.TYPE_BYTE_BINARY); + } + + private static BufferedImage convertAndScaleImage(RenderedImage img, + Dimension targetDimension, int imageType) { + Dimension bmpDimension = targetDimension; + if (bmpDimension == null) { + bmpDimension = new Dimension(img.getWidth(), img.getHeight()); + } + BufferedImage target = new BufferedImage(bmpDimension.width, bmpDimension.height, + imageType); + transferImage(img, target); + return target; + } + + private static void transferImage(RenderedImage source, BufferedImage target) { + Graphics2D g2d = target.createGraphics(); + try { + g2d.setBackground(Color.white); + g2d.setColor(Color.black); + g2d.clearRect(0, 0, target.getWidth(), target.getHeight()); + + AffineTransform at = new AffineTransform(); + if (source.getWidth() != target.getWidth() + || source.getHeight() != target.getHeight()) { + double sx = target.getWidth() / (double)source.getWidth(); + double sy = target.getHeight() / (double)source.getHeight(); + at.scale(sx, sy); + } + g2d.drawRenderedImage(source, at); + } finally { + g2d.dispose(); + } + } + + +} |