/* * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.imageio.plugins.jpeg; import javax.imageio.IIOException;import javax.imageio.ImageWriter;import javax.imageio.ImageWriteParam;import javax.imageio.IIOImage;import javax.imageio.ImageTypeSpecifier;import javax.imageio.metadata.IIOMetadata;import javax.imageio.metadata.IIOMetadataFormatImpl;import javax.imageio.metadata.IIOInvalidTreeException;import javax.imageio.spi.ImageWriterSpi;import javax.imageio.stream.ImageOutputStream;import javax.imageio.plugins.jpeg.JPEGImageWriteParam;import javax.imageio.plugins.jpeg.JPEGQTable;import javax.imageio.plugins.jpeg.JPEGHuffmanTable; import org.w3c.dom.Node; import java.awt.image.Raster;import java.awt.image.WritableRaster;import java.awt.image.SampleModel;import java.awt.image.DataBuffer;import java.awt.image.DataBufferByte;import java.awt.image.ColorModel;import java.awt.image.IndexColorModel;import java.awt.image.ColorConvertOp;import java.awt.image.RenderedImage;import java.awt.image.BufferedImage;import java.awt.color.ColorSpace;import java.awt.color.ICC_ColorSpace;import java.awt.color.ICC_Profile;import java.awt.Dimension;import java.awt.Rectangle;import java.awt.Transparency; import java.io.IOException; import java.util.List;import java.util.ArrayList;import java.util.Iterator; import sun.java2d.Disposer;import sun.java2d.DisposerRecord; public class JPEGImageWriter extends ImageWriter { ///////// Private variables private boolean debug = false; /** * The following variable contains a pointer to the IJG library * structure for this reader. It is assigned in the constructor * and then is passed in to every native call. It is set to 0 * by dispose to avoid disposing twice. */ private long structPointer = 0; /** The output stream we write to */ private ImageOutputStream ios = null; /** The Raster we will write from */ private Raster srcRas = null; /** An intermediate Raster holding compressor-friendly data */ private WritableRaster raster = null; /** * Set to true if we are writing an image with an * indexed ColorModel */ private boolean indexed = false; private IndexColorModel indexCM = null; private boolean convertTosRGB = false; // Used by PhotoYCC only private WritableRaster converted = null; private boolean isAlphaPremultiplied = false; private ColorModel srcCM = null; /** * If there are thumbnails to be written, this is the list. */ private List thumbnails = null; /** * If metadata should include an icc profile, store it here. */ private ICC_Profile iccProfile = null; private int sourceXOffset = 0; private int sourceYOffset = 0; private int sourceWidth = 0; private int [] srcBands = null; private int sourceHeight = 0; /** Used when calling listeners */ private int currentImage = 0; private ColorConvertOp convertOp = null; private JPEGQTable [] streamQTables = null; private JPEGHuffmanTable[] streamDCHuffmanTables = null; private JPEGHuffmanTable[] streamACHuffmanTables = null; // Parameters for writing metadata private boolean ignoreJFIF = false; // If it's there, use it private boolean forceJFIF = false; // Add one for the thumbnails private boolean ignoreAdobe = false; // If it's there, use it private int newAdobeTransform = JPEG.ADOBE_IMPOSSIBLE; // Change if needed private boolean writeDefaultJFIF = false; private boolean writeAdobe = false; private JPEGMetadata metadata = null; private boolean sequencePrepared = false; private int numScans = 0; /** The referent to be registered with the Disposer. */ private Object disposerReferent = new Object(); /** The DisposerRecord that handles the actual disposal of this writer. */ private DisposerRecord disposerRecord; ///////// End of Private variables ///////// Protected variables protected static final int WARNING_DEST_IGNORED = 0; protected static final int WARNING_STREAM_METADATA_IGNORED = 1; protected static final int WARNING_DEST_METADATA_COMP_MISMATCH = 2; protected static final int WARNING_DEST_METADATA_JFIF_MISMATCH = 3; protected static final int WARNING_DEST_METADATA_ADOBE_MISMATCH = 4; protected static final int WARNING_IMAGE_METADATA_JFIF_MISMATCH = 5; protected static final int WARNING_IMAGE_METADATA_ADOBE_MISMATCH = 6; protected static final int WARNING_METADATA_NOT_JPEG_FOR_RASTER = 7; protected static final int WARNING_NO_BANDS_ON_INDEXED = 8; protected static final int WARNING_ILLEGAL_THUMBNAIL = 9; protected static final int WARNING_IGNORING_THUMBS = 10; protected static final int WARNING_FORCING_JFIF = 11; protected static final int WARNING_THUMB_CLIPPED = 12; protected static final int WARNING_METADATA_ADJUSTED_FOR_THUMB = 13; protected static final int WARNING_NO_RGB_THUMB_AS_INDEXED = 14; protected static final int WARNING_NO_GRAY_THUMB_AS_INDEXED = 15; private static final int MAX_WARNING = WARNING_NO_GRAY_THUMB_AS_INDEXED; ///////// End of Protected variables ///////// static initializer static { java.security.AccessController.doPrivileged( new sun.security.action.LoadLibraryAction("jpeg")); initWriterIDs(ImageOutputStream.class, JPEGQTable.class, JPEGHuffmanTable.class); } //////// Public API public JPEGImageWriter(ImageWriterSpi originator) { super(originator); structPointer = initJPEGImageWriter(); disposerRecord = new JPEGWriterDisposerRecord(structPointer); Disposer.addRecord(disposerReferent, disposerRecord); } public void setOutput(Object output) { setThreadLock(); try { super.setOutput(output); // validates output resetInternalState(); ios = (ImageOutputStream) output; // so this will always work // Set the native destination setDest(structPointer, ios); } finally { clearThreadLock(); } } public ImageWriteParam getDefaultWriteParam() { return new JPEGImageWriteParam(null); } public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) { setThreadLock(); try { return new JPEGMetadata(param, this); } finally { clearThreadLock(); } } public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) { setThreadLock(); try { return new JPEGMetadata(imageType, param, this); } finally { clearThreadLock(); } } public IIOMetadata convertStreamMetadata(IIOMetadata inData, ImageWriteParam param) { // There isn't much we can do. If it's one of ours, then // return it. Otherwise just return null. We use it only // for tables, so we can't get a default and modify it, // as this will usually not be what is intended. if (inData instanceof JPEGMetadata) { JPEGMetadata jpegData = (JPEGMetadata) inData; if (jpegData.isStream) { return inData; } } return null; } public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) { setThreadLock(); try { return convertImageMetadataOnThread(inData, imageType, param); } finally { clearThreadLock(); } } private IIOMetadata convertImageMetadataOnThread(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) { // If it's one of ours, just return it if (inData instanceof JPEGMetadata) { JPEGMetadata jpegData = (JPEGMetadata) inData; if (!jpegData.isStream) { return inData; } else { // Can't convert stream metadata to image metadata // XXX Maybe this should put out a warning? return null; } } // If it's not one of ours, create a default and set it from // the standard tree from the input, if it exists. if (inData.isStandardMetadataFormatSupported()) { String formatName = IIOMetadataFormatImpl.standardMetadataFormatName; Node tree = inData.getAsTree(formatName); if (tree != null) { JPEGMetadata jpegData = new JPEGMetadata(imageType, param, this); try { jpegData.setFromTree(formatName, tree); } catch (IIOInvalidTreeException e) { // Other plug-in generates bogus standard tree // XXX Maybe this should put out a warning? return null; } return jpegData; } } return null; } public int getNumThumbnailsSupported(ImageTypeSpecifier imageType, ImageWriteParam param, IIOMetadata streamMetadata, IIOMetadata imageMetadata) { if (jfifOK(imageType, param, streamMetadata, imageMetadata)) { return Integer.MAX_VALUE; } return 0; } static final Dimension [] preferredThumbSizes = {new Dimension(1, 1), new Dimension(255, 255)}; public Dimension[] getPreferredThumbnailSizes(ImageTypeSpecifier imageType, ImageWriteParam param, IIOMetadata streamMetadata, IIOMetadata imageMetadata) { if (jfifOK(imageType, param, streamMetadata, imageMetadata)) { return (Dimension [])preferredThumbSizes.clone(); } return null; } private boolean jfifOK(ImageTypeSpecifier imageType, ImageWriteParam param, IIOMetadata streamMetadata, IIOMetadata imageMetadata) { // If the image type and metadata are JFIF compatible, return true if ((imageType != null) && (!JPEG.isJFIFcompliant(imageType, true))) { return false; } if (imageMetadata != null) { JPEGMetadata metadata = null; if (imageMetadata instanceof JPEGMetadata) { metadata = (JPEGMetadata) imageMetadata; } else { metadata = (JPEGMetadata)convertImageMetadata(imageMetadata, imageType, param); } // metadata must have a jfif node if (metadata.findMarkerSegment (JFIFMarkerSegment.class, true) == null){ return false; } } return true; } public boolean canWriteRasters() { return true; } public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException { setThreadLock(); try { writeOnThread(streamMetadata, image, param); } finally { clearThreadLock(); } } private void writeOnThread(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException { if (ios == null) { throw new IllegalStateException("Output has not been set!"); } if (image == null) { throw new IllegalArgumentException("image is null!"); } // if streamMetadata is not null, issue a warning if (streamMetadata != null) { warningOccurred(WARNING_STREAM_METADATA_IGNORED); } // Obtain the raster and image, if there is one boolean rasterOnly = image.hasRaster(); RenderedImage rimage = null; if (rasterOnly) { srcRas = image.getRaster(); } else { rimage = image.getRenderedImage(); if (rimage instanceof BufferedImage) { // Use the Raster directly. srcRas = ((BufferedImage)rimage).getRaster(); } else if (rimage.getNumXTiles() == 1 && rimage.getNumYTiles() == 1) { // Get the unique tile. srcRas = rimage.getTile(rimage.getMinTileX(), rimage.getMinTileY()); // Ensure the Raster has dimensions of the image, // as the tile dimensions might differ. if (srcRas.getWidth() != rimage.getWidth() || srcRas.getHeight() != rimage.getHeight()) { srcRas = srcRas.createChild(srcRas.getMinX(), srcRas.getMinY(), rimage.getWidth(), rimage.getHeight(), srcRas.getMinX(), srcRas.getMinY(), null); } } else { // Image is tiled so get a contiguous raster by copying. srcRas = rimage.getData(); } } // Now determine if we are using a band subset // By default, we are using all source bands int numSrcBands = srcRas.getNumBands(); indexed = false; indexCM = null; ColorModel cm = null; ColorSpace cs = null; isAlphaPremultiplied = false; srcCM = null; if (!rasterOnly) { cm = rimage.getColorModel(); if (cm != null) { cs = cm.getColorSpace(); if (cm instanceof IndexColorModel) { indexed = true; indexCM = (IndexColorModel) cm; numSrcBands = cm.getNumComponents(); } if (cm.isAlphaPremultiplied()) { isAlphaPremultiplied = true; srcCM = cm; } } } srcBands = JPEG.bandOffsets[numSrcBands-1]; int numBandsUsed = numSrcBands; // Consult the param to determine if we're writing a subset if (param != null) { int[] sBands = param.getSourceBands(); if (sBands != null) { if (indexed) { warningOccurred(WARNING_NO_BANDS_ON_INDEXED); } else { srcBands = sBands; numBandsUsed = srcBands.length; if (numBandsUsed > numSrcBands) { throw new IIOException ("ImageWriteParam specifies too many source bands"); } } } } boolean usingBandSubset = (numBandsUsed != numSrcBands); boolean fullImage = ((!rasterOnly) && (!usingBandSubset)); int [] bandSizes = null; if (!indexed) { bandSizes = srcRas.getSampleModel().getSampleSize(); // If this is a subset, we must adjust bandSizes if (usingBandSubset) { int [] temp = new int [numBandsUsed]; for (int i = 0; i < numBandsUsed; i++) { temp[i] = bandSizes[srcBands[i]]; } bandSizes = temp; } } else { int [] tempSize = srcRas.getSampleModel().getSampleSize(); bandSizes = new int [numSrcBands]; for (int i = 0; i < numSrcBands; i++) { bandSizes[i] = tempSize[0]; // All the same } } for (int i = 0; i < bandSizes.length; i++) { // 4450894 part 1: The IJG libraries are compiled so they only // handle <= 8-bit samples. We now check the band sizes and throw // an exception for images, such as USHORT_GRAY, with > 8 bits // per sample. if (bandSizes[i] > 8) { throw new IIOException("Sample size must be <= 8"); } // 4450894 part 2: We expand IndexColorModel images to full 24- // or 32-bit in grabPixels() for each scanline. For indexed // images such as BYTE_BINARY, we need to ensure that we update // bandSizes to account for the scaling from 1-bit band sizes // to 8-bit. if (indexed) { bandSizes[i] = 8; }Lines 1–500 of 1809. Load more
/* * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.imageio.plugins.jpeg; import javax.imageio.IIOException; import javax.imageio.ImageWriter; import javax.imageio.ImageWriteParam; import javax.imageio.IIOImage; import javax.imageio.ImageTypeSpecifier; import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadataFormatImpl; import javax.imageio.metadata.IIOInvalidTreeException; import javax.imageio.spi.ImageWriterSpi; import javax.imageio.stream.ImageOutputStream; import javax.imageio.plugins.jpeg.JPEGImageWriteParam; import javax.imageio.plugins.jpeg.JPEGQTable; import javax.imageio.plugins.jpeg.JPEGHuffmanTable; import org.w3c.dom.Node; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.SampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.ColorConvertOp; import java.awt.image.RenderedImage; import java.awt.image.BufferedImage; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; import java.awt.color.ICC_Profile; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.Transparency; import java.io.IOException; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import sun.java2d.Disposer; import sun.java2d.DisposerRecord; public class JPEGImageWriter extends ImageWriter { ///////// Private variables private boolean debug = false; /** * The following variable contains a pointer to the IJG library * structure for this reader. It is assigned in the constructor * and then is passed in to every native call. It is set to 0 * by dispose to avoid disposing twice. */ private long structPointer = 0; /** The output stream we write to */ private ImageOutputStream ios = null; /** The Raster we will write from */ private Raster srcRas = null; /** An intermediate Raster holding compressor-friendly data */ private WritableRaster raster = null; /** * Set to true if we are writing an image with an * indexed ColorModel */ private boolean indexed = false; private IndexColorModel indexCM = null; private boolean convertTosRGB = false; // Used by PhotoYCC only private WritableRaster converted = null; private boolean isAlphaPremultiplied = false; private ColorModel srcCM = null; /** * If there are thumbnails to be written, this is the list. */ private List thumbnails = null; /** * If metadata should include an icc profile, store it here. */ private ICC_Profile iccProfile = null; private int sourceXOffset = 0; private int sourceYOffset = 0; private int sourceWidth = 0; private int [] srcBands = null; private int sourceHeight = 0; /** Used when calling listeners */ private int currentImage = 0; private ColorConvertOp convertOp = null; private JPEGQTable [] streamQTables = null; private JPEGHuffmanTable[] streamDCHuffmanTables = null; private JPEGHuffmanTable[] streamACHuffmanTables = null; // Parameters for writing metadata private boolean ignoreJFIF = false; // If it's there, use it private boolean forceJFIF = false; // Add one for the thumbnails private boolean ignoreAdobe = false; // If it's there, use it private int newAdobeTransform = JPEG.ADOBE_IMPOSSIBLE; // Change if needed private boolean writeDefaultJFIF = false; private boolean writeAdobe = false; private JPEGMetadata metadata = null; private boolean sequencePrepared = false; private int numScans = 0; /** The referent to be registered with the Disposer. */ private Object disposerReferent = new Object(); /** The DisposerRecord that handles the actual disposal of this writer. */ private DisposerRecord disposerRecord; ///////// End of Private variables ///////// Protected variables protected static final int WARNING_DEST_IGNORED = 0; protected static final int WARNING_STREAM_METADATA_IGNORED = 1; protected static final int WARNING_DEST_METADATA_COMP_MISMATCH = 2; protected static final int WARNING_DEST_METADATA_JFIF_MISMATCH = 3; protected static final int WARNING_DEST_METADATA_ADOBE_MISMATCH = 4; protected static final int WARNING_IMAGE_METADATA_JFIF_MISMATCH = 5; protected static final int WARNING_IMAGE_METADATA_ADOBE_MISMATCH = 6; protected static final int WARNING_METADATA_NOT_JPEG_FOR_RASTER = 7; protected static final int WARNING_NO_BANDS_ON_INDEXED = 8; protected static final int WARNING_ILLEGAL_THUMBNAIL = 9; protected static final int WARNING_IGNORING_THUMBS = 10; protected static final int WARNING_FORCING_JFIF = 11; protected static final int WARNING_THUMB_CLIPPED = 12; protected static final int WARNING_METADATA_ADJUSTED_FOR_THUMB = 13; protected static final int WARNING_NO_RGB_THUMB_AS_INDEXED = 14; protected static final int WARNING_NO_GRAY_THUMB_AS_INDEXED = 15; private static final int MAX_WARNING = WARNING_NO_GRAY_THUMB_AS_INDEXED; ///////// End of Protected variables ///////// static initializer static { java.security.AccessController.doPrivileged( new sun.security.action.LoadLibraryAction("jpeg")); initWriterIDs(ImageOutputStream.class, JPEGQTable.class, JPEGHuffmanTable.class); } //////// Public API public JPEGImageWriter(ImageWriterSpi originator) { super(originator); structPointer = initJPEGImageWriter(); disposerRecord = new JPEGWriterDisposerRecord(structPointer); Disposer.addRecord(disposerReferent, disposerRecord); } public void setOutput(Object output) { setThreadLock(); try { super.setOutput(output); // validates output resetInternalState(); ios = (ImageOutputStream) output; // so this will always work // Set the native destination setDest(structPointer, ios); } finally { clearThreadLock(); } } public ImageWriteParam getDefaultWriteParam() { return new JPEGImageWriteParam(null); } public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) { setThreadLock(); try { return new JPEGMetadata(param, this); } finally { clearThreadLock(); } } public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) { setThreadLock(); try { return new JPEGMetadata(imageType, param, this); } finally { clearThreadLock(); } } public IIOMetadata convertStreamMetadata(IIOMetadata inData, ImageWriteParam param) { // There isn't much we can do. If it's one of ours, then // return it. Otherwise just return null. We use it only // for tables, so we can't get a default and modify it, // as this will usually not be what is intended. if (inData instanceof JPEGMetadata) { JPEGMetadata jpegData = (JPEGMetadata) inData; if (jpegData.isStream) { return inData; } } return null; } public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) { setThreadLock(); try { return convertImageMetadataOnThread(inData, imageType, param); } finally { clearThreadLock(); } } private IIOMetadata convertImageMetadataOnThread(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) { // If it's one of ours, just return it if (inData instanceof JPEGMetadata) { JPEGMetadata jpegData = (JPEGMetadata) inData; if (!jpegData.isStream) { return inData; } else { // Can't convert stream metadata to image metadata // XXX Maybe this should put out a warning? return null; } } // If it's not one of ours, create a default and set it from // the standard tree from the input, if it exists. if (inData.isStandardMetadataFormatSupported()) { String formatName = IIOMetadataFormatImpl.standardMetadataFormatName; Node tree = inData.getAsTree(formatName); if (tree != null) { JPEGMetadata jpegData = new JPEGMetadata(imageType, param, this); try { jpegData.setFromTree(formatName, tree); } catch (IIOInvalidTreeException e) { // Other plug-in generates bogus standard tree // XXX Maybe this should put out a warning? return null; } return jpegData; } } return null; } public int getNumThumbnailsSupported(ImageTypeSpecifier imageType, ImageWriteParam param, IIOMetadata streamMetadata, IIOMetadata imageMetadata) { if (jfifOK(imageType, param, streamMetadata, imageMetadata)) { return Integer.MAX_VALUE; } return 0; } static final Dimension [] preferredThumbSizes = {new Dimension(1, 1), new Dimension(255, 255)}; public Dimension[] getPreferredThumbnailSizes(ImageTypeSpecifier imageType, ImageWriteParam param, IIOMetadata streamMetadata, IIOMetadata imageMetadata) { if (jfifOK(imageType, param, streamMetadata, imageMetadata)) { return (Dimension [])preferredThumbSizes.clone(); } return null; } private boolean jfifOK(ImageTypeSpecifier imageType, ImageWriteParam param, IIOMetadata streamMetadata, IIOMetadata imageMetadata) { // If the image type and metadata are JFIF compatible, return true if ((imageType != null) && (!JPEG.isJFIFcompliant(imageType, true))) { return false; } if (imageMetadata != null) { JPEGMetadata metadata = null; if (imageMetadata instanceof JPEGMetadata) { metadata = (JPEGMetadata) imageMetadata; } else { metadata = (JPEGMetadata)convertImageMetadata(imageMetadata, imageType, param); } // metadata must have a jfif node if (metadata.findMarkerSegment (JFIFMarkerSegment.class, true) == null){ return false; } } return true; } public boolean canWriteRasters() { return true; } public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException { setThreadLock(); try { writeOnThread(streamMetadata, image, param); } finally { clearThreadLock(); } } private void writeOnThread(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException { if (ios == null) { throw new IllegalStateException("Output has not been set!"); } if (image == null) { throw new IllegalArgumentException("image is null!"); } // if streamMetadata is not null, issue a warning if (streamMetadata != null) { warningOccurred(WARNING_STREAM_METADATA_IGNORED); } // Obtain the raster and image, if there is one boolean rasterOnly = image.hasRaster(); RenderedImage rimage = null; if (rasterOnly) { srcRas = image.getRaster(); } else { rimage = image.getRenderedImage(); if (rimage instanceof BufferedImage) { // Use the Raster directly. srcRas = ((BufferedImage)rimage).getRaster(); } else if (rimage.getNumXTiles() == 1 && rimage.getNumYTiles() == 1) { // Get the unique tile. srcRas = rimage.getTile(rimage.getMinTileX(), rimage.getMinTileY()); // Ensure the Raster has dimensions of the image, // as the tile dimensions might differ. if (srcRas.getWidth() != rimage.getWidth() || srcRas.getHeight() != rimage.getHeight()) { srcRas = srcRas.createChild(srcRas.getMinX(), srcRas.getMinY(), rimage.getWidth(), rimage.getHeight(), srcRas.getMinX(), srcRas.getMinY(), null); } } else { // Image is tiled so get a contiguous raster by copying. srcRas = rimage.getData(); } } // Now determine if we are using a band subset // By default, we are using all source bands int numSrcBands = srcRas.getNumBands(); indexed = false; indexCM = null; ColorModel cm = null; ColorSpace cs = null; isAlphaPremultiplied = false; srcCM = null; if (!rasterOnly) { cm = rimage.getColorModel(); if (cm != null) { cs = cm.getColorSpace(); if (cm instanceof IndexColorModel) { indexed = true; indexCM = (IndexColorModel) cm; numSrcBands = cm.getNumComponents(); } if (cm.isAlphaPremultiplied()) { isAlphaPremultiplied = true; srcCM = cm; } } } srcBands = JPEG.bandOffsets[numSrcBands-1]; int numBandsUsed = numSrcBands; // Consult the param to determine if we're writing a subset if (param != null) { int[] sBands = param.getSourceBands(); if (sBands != null) { if (indexed) { warningOccurred(WARNING_NO_BANDS_ON_INDEXED); } else { srcBands = sBands; numBandsUsed = srcBands.length; if (numBandsUsed > numSrcBands) { throw new IIOException ("ImageWriteParam specifies too many source bands"); } } } } boolean usingBandSubset = (numBandsUsed != numSrcBands); boolean fullImage = ((!rasterOnly) && (!usingBandSubset)); int [] bandSizes = null; if (!indexed) { bandSizes = srcRas.getSampleModel().getSampleSize(); // If this is a subset, we must adjust bandSizes if (usingBandSubset) { int [] temp = new int [numBandsUsed]; for (int i = 0; i < numBandsUsed; i++) { temp[i] = bandSizes[srcBands[i]]; } bandSizes = temp; } } else { int [] tempSize = srcRas.getSampleModel().getSampleSize(); bandSizes = new int [numSrcBands]; for (int i = 0; i < numSrcBands; i++) { bandSizes[i] = tempSize[0]; // All the same } } for (int i = 0; i < bandSizes.length; i++) { // 4450894 part 1: The IJG libraries are compiled so they only // handle <= 8-bit samples. We now check the band sizes and throw // an exception for images, such as USHORT_GRAY, with > 8 bits // per sample. if (bandSizes[i] > 8) { throw new IIOException("Sample size must be <= 8"); } // 4450894 part 2: We expand IndexColorModel images to full 24- // or 32-bit in grabPixels() for each scanline. For indexed // images such as BYTE_BINARY, we need to ensure that we update // bandSizes to account for the scaling from 1-bit band sizes // to 8-bit. if (indexed) { bandSizes[i] = 8; }