jdk/src/java.desktop/share/classes/javax/imageio/ImageWriteParam.java
changeset 25859 3317bb8137f4
parent 22584 eed64ee05369
child 35667 ed476aba94de
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/ImageWriteParam.java	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,1470 @@
+/*
+ * Copyright (c) 2000, 2014, 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 javax.imageio;
+
+import java.awt.Dimension;
+import java.util.Locale;
+
+/**
+ * A class describing how a stream is to be encoded.  Instances of
+ * this class or its subclasses are used to supply prescriptive
+ * "how-to" information to instances of <code>ImageWriter</code>.
+ *
+ * <p> A plug-in for a specific image format may define a subclass of
+ * this class, and return objects of that class from the
+ * <code>getDefaultWriteParam</code> method of its
+ * <code>ImageWriter</code> implementation.  For example, the built-in
+ * JPEG writer plug-in will return instances of
+ * <code>javax.imageio.plugins.jpeg.JPEGImageWriteParam</code>.
+ *
+ * <p> The region of the image to be written is determined by first
+ * intersecting the actual bounds of the image with the rectangle
+ * specified by <code>IIOParam.setSourceRegion</code>, if any.  If the
+ * resulting rectangle has a width or height of zero, the writer will
+ * throw an <code>IIOException</code>. If the intersection is
+ * non-empty, writing will commence with the first subsampled pixel
+ * and include additional pixels within the intersected bounds
+ * according to the horizontal and vertical subsampling factors
+ * specified by {@link IIOParam#setSourceSubsampling
+ * IIOParam.setSourceSubsampling}.
+ *
+ * <p> Individual features such as tiling, progressive encoding, and
+ * compression may be set in one of four modes.
+ * <code>MODE_DISABLED</code> disables the features;
+ * <code>MODE_DEFAULT</code> enables the feature with
+ * writer-controlled parameter values; <code>MODE_EXPLICIT</code>
+ * enables the feature and allows the use of a <code>set</code> method
+ * to provide additional parameters; and
+ * <code>MODE_COPY_FROM_METADATA</code> copies relevant parameter
+ * values from the stream and image metadata objects passed to the
+ * writer.  The default for all features is
+ * <code>MODE_COPY_FROM_METADATA</code>.  Non-standard features
+ * supplied in subclasses are encouraged, but not required to use a
+ * similar scheme.
+ *
+ * <p> Plug-in writers may extend the functionality of
+ * <code>ImageWriteParam</code> by providing a subclass that implements
+ * additional, plug-in specific interfaces.  It is up to the plug-in
+ * to document what interfaces are available and how they are to be
+ * used.  Writers will silently ignore any extended features of an
+ * <code>ImageWriteParam</code> subclass of which they are not aware.
+ * Also, they may ignore any optional features that they normally
+ * disable when creating their own <code>ImageWriteParam</code>
+ * instances via <code>getDefaultWriteParam</code>.
+ *
+ * <p> Note that unless a query method exists for a capability, it must
+ * be supported by all <code>ImageWriter</code> implementations
+ * (<i>e.g.</i> progressive encoding is optional, but subsampling must be
+ * supported).
+ *
+ *
+ * @see ImageReadParam
+ */
+public class ImageWriteParam extends IIOParam {
+
+    /**
+     * A constant value that may be passed into methods such as
+     * <code>setTilingMode</code>, <code>setProgressiveMode</code>,
+     * and <code>setCompressionMode</code> to disable a feature for
+     * future writes.  That is, when this mode is set the stream will
+     * <b>not</b> be tiled, progressive, or compressed, and the
+     * relevant accessor methods will throw an
+     * <code>IllegalStateException</code>.
+     *
+     * @see #MODE_EXPLICIT
+     * @see #MODE_COPY_FROM_METADATA
+     * @see #MODE_DEFAULT
+     * @see #setProgressiveMode
+     * @see #getProgressiveMode
+     * @see #setTilingMode
+     * @see #getTilingMode
+     * @see #setCompressionMode
+     * @see #getCompressionMode
+     */
+    public static final int MODE_DISABLED = 0;
+
+    /**
+     * A constant value that may be passed into methods such as
+     * <code>setTilingMode</code>,
+     * <code>setProgressiveMode</code>, and
+     * <code>setCompressionMode</code> to enable that feature for
+     * future writes.  That is, when this mode is enabled the stream
+     * will be tiled, progressive, or compressed according to a
+     * sensible default chosen internally by the writer in a plug-in
+     * dependent way, and the relevant accessor methods will
+     * throw an <code>IllegalStateException</code>.
+     *
+     * @see #MODE_DISABLED
+     * @see #MODE_EXPLICIT
+     * @see #MODE_COPY_FROM_METADATA
+     * @see #setProgressiveMode
+     * @see #getProgressiveMode
+     * @see #setTilingMode
+     * @see #getTilingMode
+     * @see #setCompressionMode
+     * @see #getCompressionMode
+     */
+    public static final int MODE_DEFAULT = 1;
+
+    /**
+     * A constant value that may be passed into methods such as
+     * <code>setTilingMode</code> or <code>setCompressionMode</code>
+     * to enable a feature for future writes. That is, when this mode
+     * is set the stream will be tiled or compressed according to
+     * additional information supplied to the corresponding
+     * <code>set</code> methods in this class and retrievable from the
+     * corresponding <code>get</code> methods.  Note that this mode is
+     * not supported for progressive output.
+     *
+     * @see #MODE_DISABLED
+     * @see #MODE_COPY_FROM_METADATA
+     * @see #MODE_DEFAULT
+     * @see #setProgressiveMode
+     * @see #getProgressiveMode
+     * @see #setTilingMode
+     * @see #getTilingMode
+     * @see #setCompressionMode
+     * @see #getCompressionMode
+     */
+    public static final int MODE_EXPLICIT = 2;
+
+    /**
+     * A constant value that may be passed into methods such as
+     * <code>setTilingMode</code>, <code>setProgressiveMode</code>, or
+     * <code>setCompressionMode</code> to enable that feature for
+     * future writes.  That is, when this mode is enabled the stream
+     * will be tiled, progressive, or compressed based on the contents
+     * of stream and/or image metadata passed into the write
+     * operation, and any relevant accessor methods will throw an
+     * <code>IllegalStateException</code>.
+     *
+     * <p> This is the default mode for all features, so that a read
+     * including metadata followed by a write including metadata will
+     * preserve as much information as possible.
+     *
+     * @see #MODE_DISABLED
+     * @see #MODE_EXPLICIT
+     * @see #MODE_DEFAULT
+     * @see #setProgressiveMode
+     * @see #getProgressiveMode
+     * @see #setTilingMode
+     * @see #getTilingMode
+     * @see #setCompressionMode
+     * @see #getCompressionMode
+     */
+    public static final int MODE_COPY_FROM_METADATA = 3;
+
+    // If more modes are added, this should be updated.
+    private static final int MAX_MODE = MODE_COPY_FROM_METADATA;
+
+    /**
+     * A <code>boolean</code> that is <code>true</code> if this
+     * <code>ImageWriteParam</code> allows tile width and tile height
+     * parameters to be set.  By default, the value is
+     * <code>false</code>.  Subclasses must set the value manually.
+     *
+     * <p> Subclasses that do not support writing tiles should ensure
+     * that this value is set to <code>false</code>.
+     */
+    protected boolean canWriteTiles = false;
+
+    /**
+     * The mode controlling tiling settings, which Must be
+     * set to one of the four <code>MODE_*</code> values.  The default
+     * is <code>MODE_COPY_FROM_METADATA</code>.
+     *
+     * <p> Subclasses that do not writing tiles may ignore this value.
+     *
+     * @see #MODE_DISABLED
+     * @see #MODE_EXPLICIT
+     * @see #MODE_COPY_FROM_METADATA
+     * @see #MODE_DEFAULT
+     * @see #setTilingMode
+     * @see #getTilingMode
+     */
+    protected int tilingMode = MODE_COPY_FROM_METADATA;
+
+    /**
+     * An array of preferred tile size range pairs.  The default value
+     * is <code>null</code>, which indicates that there are no
+     * preferred sizes.  If the value is non-<code>null</code>, it
+     * must have an even length of at least two.
+     *
+     * <p> Subclasses that do not support writing tiles may ignore
+     * this value.
+     *
+     * @see #getPreferredTileSizes
+     */
+    protected Dimension[] preferredTileSizes = null;
+
+    /**
+     * A <code>boolean</code> that is <code>true</code> if tiling
+     * parameters have been specified.
+     *
+     * <p> Subclasses that do not support writing tiles may ignore
+     * this value.
+     */
+    protected boolean tilingSet = false;
+
+    /**
+     * The width of each tile if tiling has been set, or 0 otherwise.
+     *
+     * <p> Subclasses that do not support tiling may ignore this
+     * value.
+     */
+    protected int tileWidth = 0;
+
+    /**
+     * The height of each tile if tiling has been set, or 0 otherwise.
+     * The initial value is <code>0</code>.
+     *
+     * <p> Subclasses that do not support tiling may ignore this
+     * value.
+     */
+    protected int tileHeight = 0;
+
+    /**
+     * A <code>boolean</code> that is <code>true</code> if this
+     * <code>ImageWriteParam</code> allows tiling grid offset
+     * parameters to be set.  By default, the value is
+     * <code>false</code>.  Subclasses must set the value manually.
+     *
+     * <p> Subclasses that do not support writing tiles, or that
+     * support writing but not offsetting tiles must ensure that this
+     * value is set to <code>false</code>.
+     */
+    protected boolean canOffsetTiles = false;
+
+    /**
+     * The amount by which the tile grid origin should be offset
+     * horizontally from the image origin if tiling has been set,
+     * or 0 otherwise.  The initial value is <code>0</code>.
+     *
+     * <p> Subclasses that do not support offsetting tiles may ignore
+     * this value.
+     */
+    protected int tileGridXOffset = 0;
+
+    /**
+     * The amount by which the tile grid origin should be offset
+     * vertically from the image origin if tiling has been set,
+     * or 0 otherwise.  The initial value is <code>0</code>.
+     *
+     * <p> Subclasses that do not support offsetting tiles may ignore
+     * this value.
+     */
+    protected int tileGridYOffset = 0;
+
+    /**
+     * A <code>boolean</code> that is <code>true</code> if this
+     * <code>ImageWriteParam</code> allows images to be written as a
+     * progressive sequence of increasing quality passes.  By default,
+     * the value is <code>false</code>.  Subclasses must set the value
+     * manually.
+     *
+     * <p> Subclasses that do not support progressive encoding must
+     * ensure that this value is set to <code>false</code>.
+     */
+    protected boolean canWriteProgressive = false;
+
+    /**
+     * The mode controlling progressive encoding, which must be set to
+     * one of the four <code>MODE_*</code> values, except
+     * <code>MODE_EXPLICIT</code>.  The default is
+     * <code>MODE_COPY_FROM_METADATA</code>.
+     *
+     * <p> Subclasses that do not support progressive encoding may
+     * ignore this value.
+     *
+     * @see #MODE_DISABLED
+     * @see #MODE_EXPLICIT
+     * @see #MODE_COPY_FROM_METADATA
+     * @see #MODE_DEFAULT
+     * @see #setProgressiveMode
+     * @see #getProgressiveMode
+     */
+    protected int progressiveMode = MODE_COPY_FROM_METADATA;
+
+    /**
+     * A <code>boolean</code> that is <code>true</code> if this writer
+     * can write images using compression. By default, the value is
+     * <code>false</code>.  Subclasses must set the value manually.
+     *
+     * <p> Subclasses that do not support compression must ensure that
+     * this value is set to <code>false</code>.
+     */
+    protected boolean canWriteCompressed = false;
+
+    /**
+     * The mode controlling compression settings, which must be set to
+     * one of the four <code>MODE_*</code> values.  The default is
+     * <code>MODE_COPY_FROM_METADATA</code>.
+     *
+     * <p> Subclasses that do not support compression may ignore this
+     * value.
+     *
+     * @see #MODE_DISABLED
+     * @see #MODE_EXPLICIT
+     * @see #MODE_COPY_FROM_METADATA
+     * @see #MODE_DEFAULT
+     * @see #setCompressionMode
+     * @see #getCompressionMode
+     */
+    protected int compressionMode = MODE_COPY_FROM_METADATA;
+
+    /**
+     * An array of <code>String</code>s containing the names of the
+     * available compression types.  Subclasses must set the value
+     * manually.
+     *
+     * <p> Subclasses that do not support compression may ignore this
+     * value.
+     */
+    protected String[] compressionTypes = null;
+
+    /**
+     * A <code>String</code> containing the name of the current
+     * compression type, or <code>null</code> if none is set.
+     *
+     * <p> Subclasses that do not support compression may ignore this
+     * value.
+     */
+    protected String compressionType = null;
+
+    /**
+     * A <code>float</code> containing the current compression quality
+     * setting.  The initial value is <code>1.0F</code>.
+     *
+     * <p> Subclasses that do not support compression may ignore this
+     * value.
+     */
+    protected float compressionQuality = 1.0F;
+
+    /**
+     * A <code>Locale</code> to be used to localize compression type
+     * names and quality descriptions, or <code>null</code> to use a
+     * default <code>Locale</code>.  Subclasses must set the value
+     * manually.
+     */
+    protected Locale locale = null;
+
+    /**
+     * Constructs an empty <code>ImageWriteParam</code>.  It is up to
+     * the subclass to set up the instance variables properly.
+     */
+    protected ImageWriteParam() {}
+
+    /**
+     * Constructs an <code>ImageWriteParam</code> set to use a
+     * given <code>Locale</code>.
+     *
+     * @param locale a <code>Locale</code> to be used to localize
+     * compression type names and quality descriptions, or
+     * <code>null</code>.
+     */
+    public ImageWriteParam(Locale locale) {
+        this.locale = locale;
+    }
+
+    // Return a deep copy of the array
+    private static Dimension[] clonePreferredTileSizes(Dimension[] sizes) {
+        if (sizes == null) {
+            return null;
+        }
+        Dimension[] temp = new Dimension[sizes.length];
+        for (int i = 0; i < sizes.length; i++) {
+            temp[i] = new Dimension(sizes[i]);
+        }
+        return temp;
+    }
+
+    /**
+     * Returns the currently set <code>Locale</code>, or
+     * <code>null</code> if only a default <code>Locale</code> is
+     * supported.
+     *
+     * @return the current <code>Locale</code>, or <code>null</code>.
+     */
+    public Locale getLocale() {
+        return locale;
+    }
+
+    /**
+     * Returns <code>true</code> if the writer can perform tiling
+     * while writing.  If this method returns <code>false</code>, then
+     * <code>setTiling</code> will throw an
+     * <code>UnsupportedOperationException</code>.
+     *
+     * @return <code>true</code> if the writer supports tiling.
+     *
+     * @see #canOffsetTiles()
+     * @see #setTiling(int, int, int, int)
+     */
+    public boolean canWriteTiles() {
+        return canWriteTiles;
+    }
+
+    /**
+     * Returns <code>true</code> if the writer can perform tiling with
+     * non-zero grid offsets while writing.  If this method returns
+     * <code>false</code>, then <code>setTiling</code> will throw an
+     * <code>UnsupportedOperationException</code> if the grid offset
+     * arguments are not both zero.  If <code>canWriteTiles</code>
+     * returns <code>false</code>, this method will return
+     * <code>false</code> as well.
+     *
+     * @return <code>true</code> if the writer supports non-zero tile
+     * offsets.
+     *
+     * @see #canWriteTiles()
+     * @see #setTiling(int, int, int, int)
+     */
+    public boolean canOffsetTiles() {
+        return canOffsetTiles;
+    }
+
+    /**
+     * Determines whether the image will be tiled in the output
+     * stream and, if it will, how the tiling parameters will be
+     * determined.  The modes are interpreted as follows:
+     *
+     * <ul>
+     *
+     * <li><code>MODE_DISABLED</code> - The image will not be tiled.
+     * <code>setTiling</code> will throw an
+     * <code>IllegalStateException</code>.
+     *
+     * <li><code>MODE_DEFAULT</code> - The image will be tiled using
+     * default parameters.  <code>setTiling</code> will throw an
+     * <code>IllegalStateException</code>.
+     *
+     * <li><code>MODE_EXPLICIT</code> - The image will be tiled
+     * according to parameters given in the {@link #setTiling setTiling}
+     * method.  Any previously set tiling parameters are discarded.
+     *
+     * <li><code>MODE_COPY_FROM_METADATA</code> - The image will
+     * conform to the metadata object passed in to a write.
+     * <code>setTiling</code> will throw an
+     * <code>IllegalStateException</code>.
+     *
+     * </ul>
+     *
+     * @param mode The mode to use for tiling.
+     *
+     * @exception UnsupportedOperationException if
+     * <code>canWriteTiles</code> returns <code>false</code>.
+     * @exception IllegalArgumentException if <code>mode</code> is not
+     * one of the modes listed above.
+     *
+     * @see #setTiling
+     * @see #getTilingMode
+     */
+    public void setTilingMode(int mode) {
+        if (canWriteTiles() == false) {
+            throw new UnsupportedOperationException("Tiling not supported!");
+        }
+        if (mode < MODE_DISABLED || mode > MAX_MODE) {
+            throw new IllegalArgumentException("Illegal value for mode!");
+        }
+        this.tilingMode = mode;
+        if (mode == MODE_EXPLICIT) {
+            unsetTiling();
+        }
+    }
+
+    /**
+     * Returns the current tiling mode, if tiling is supported.
+     * Otherwise throws an <code>UnsupportedOperationException</code>.
+     *
+     * @return the current tiling mode.
+     *
+     * @exception UnsupportedOperationException if
+     * <code>canWriteTiles</code> returns <code>false</code>.
+     *
+     * @see #setTilingMode
+     */
+    public int getTilingMode() {
+        if (!canWriteTiles()) {
+            throw new UnsupportedOperationException("Tiling not supported");
+        }
+        return tilingMode;
+    }
+
+    /**
+     * Returns an array of <code>Dimension</code>s indicating the
+     * legal size ranges for tiles as they will be encoded in the
+     * output file or stream.  The returned array is a copy.
+     *
+     * <p> The information is returned as a set of pairs; the first
+     * element of a pair contains an (inclusive) minimum width and
+     * height, and the second element contains an (inclusive) maximum
+     * width and height.  Together, each pair defines a valid range of
+     * sizes.  To specify a fixed size, use the same width and height
+     * for both elements.  To specify an arbitrary range, a value of
+     * <code>null</code> is used in place of an actual array of
+     * <code>Dimension</code>s.
+     *
+     * <p> If no array is specified on the constructor, but tiling is
+     * allowed, then this method returns <code>null</code>.
+     *
+     * @exception UnsupportedOperationException if the plug-in does
+     * not support tiling.
+     *
+     * @return an array of <code>Dimension</code>s with an even length
+     * of at least two, or <code>null</code>.
+     */
+    public Dimension[] getPreferredTileSizes() {
+        if (!canWriteTiles()) {
+            throw new UnsupportedOperationException("Tiling not supported");
+        }
+        return clonePreferredTileSizes(preferredTileSizes);
+    }
+
+    /**
+     * Specifies that the image should be tiled in the output stream.
+     * The <code>tileWidth</code> and <code>tileHeight</code>
+     * parameters specify the width and height of the tiles in the
+     * file.  If the tile width or height is greater than the width or
+     * height of the image, the image is not tiled in that dimension.
+     *
+     * <p> If <code>canOffsetTiles</code> returns <code>false</code>,
+     * then the <code>tileGridXOffset</code> and
+     * <code>tileGridYOffset</code> parameters must be zero.
+     *
+     * @param tileWidth the width of each tile.
+     * @param tileHeight the height of each tile.
+     * @param tileGridXOffset the horizontal offset of the tile grid.
+     * @param tileGridYOffset the vertical offset of the tile grid.
+     *
+     * @exception UnsupportedOperationException if the plug-in does not
+     * support tiling.
+     * @exception IllegalStateException if the tiling mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception UnsupportedOperationException if the plug-in does not
+     * support grid offsets, and the grid offsets are not both zero.
+     * @exception IllegalArgumentException if the tile size is not
+     * within one of the allowable ranges returned by
+     * <code>getPreferredTileSizes</code>.
+     * @exception IllegalArgumentException if <code>tileWidth</code>
+     * or <code>tileHeight</code> is less than or equal to 0.
+     *
+     * @see #canWriteTiles
+     * @see #canOffsetTiles
+     * @see #getTileWidth()
+     * @see #getTileHeight()
+     * @see #getTileGridXOffset()
+     * @see #getTileGridYOffset()
+     */
+    public void setTiling(int tileWidth,
+                          int tileHeight,
+                          int tileGridXOffset,
+                          int tileGridYOffset) {
+        if (!canWriteTiles()) {
+            throw new UnsupportedOperationException("Tiling not supported!");
+        }
+        if (getTilingMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
+        }
+        if (tileWidth <= 0 || tileHeight <= 0) {
+            throw new IllegalArgumentException
+                ("tile dimensions are non-positive!");
+        }
+        boolean tilesOffset = (tileGridXOffset != 0) || (tileGridYOffset != 0);
+        if (!canOffsetTiles() && tilesOffset) {
+            throw new UnsupportedOperationException("Can't offset tiles!");
+        }
+        if (preferredTileSizes != null) {
+            boolean ok = true;
+            for (int i = 0; i < preferredTileSizes.length; i += 2) {
+                Dimension min = preferredTileSizes[i];
+                Dimension max = preferredTileSizes[i+1];
+                if ((tileWidth < min.width) ||
+                    (tileWidth > max.width) ||
+                    (tileHeight < min.height) ||
+                    (tileHeight > max.height)) {
+                    ok = false;
+                    break;
+                }
+            }
+            if (!ok) {
+                throw new IllegalArgumentException("Illegal tile size!");
+            }
+        }
+
+        this.tilingSet = true;
+        this.tileWidth = tileWidth;
+        this.tileHeight = tileHeight;
+        this.tileGridXOffset = tileGridXOffset;
+        this.tileGridYOffset = tileGridYOffset;
+    }
+
+    /**
+     * Removes any previous tile grid parameters specified by calls to
+     * <code>setTiling</code>.
+     *
+     * <p> The default implementation sets the instance variables
+     * <code>tileWidth</code>, <code>tileHeight</code>,
+     * <code>tileGridXOffset</code>, and
+     * <code>tileGridYOffset</code> to <code>0</code>.
+     *
+     * @exception UnsupportedOperationException if the plug-in does not
+     * support tiling.
+     * @exception IllegalStateException if the tiling mode is not
+     * <code>MODE_EXPLICIT</code>.
+     *
+     * @see #setTiling(int, int, int, int)
+     */
+    public void unsetTiling() {
+        if (!canWriteTiles()) {
+            throw new UnsupportedOperationException("Tiling not supported!");
+        }
+        if (getTilingMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
+        }
+        this.tilingSet = false;
+        this.tileWidth = 0;
+        this.tileHeight = 0;
+        this.tileGridXOffset = 0;
+        this.tileGridYOffset = 0;
+    }
+
+    /**
+     * Returns the width of each tile in an image as it will be
+     * written to the output stream.  If tiling parameters have not
+     * been set, an <code>IllegalStateException</code> is thrown.
+     *
+     * @return the tile width to be used for encoding.
+     *
+     * @exception UnsupportedOperationException if the plug-in does not
+     * support tiling.
+     * @exception IllegalStateException if the tiling mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception IllegalStateException if the tiling parameters have
+     * not been set.
+     *
+     * @see #setTiling(int, int, int, int)
+     * @see #getTileHeight()
+     */
+    public int getTileWidth() {
+        if (!canWriteTiles()) {
+            throw new UnsupportedOperationException("Tiling not supported!");
+        }
+        if (getTilingMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
+        }
+        if (!tilingSet) {
+            throw new IllegalStateException("Tiling parameters not set!");
+        }
+        return tileWidth;
+    }
+
+    /**
+     * Returns the height of each tile in an image as it will be written to
+     * the output stream.  If tiling parameters have not
+     * been set, an <code>IllegalStateException</code> is thrown.
+     *
+     * @return the tile height to be used for encoding.
+     *
+     * @exception UnsupportedOperationException if the plug-in does not
+     * support tiling.
+     * @exception IllegalStateException if the tiling mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception IllegalStateException if the tiling parameters have
+     * not been set.
+     *
+     * @see #setTiling(int, int, int, int)
+     * @see #getTileWidth()
+     */
+    public int getTileHeight() {
+        if (!canWriteTiles()) {
+            throw new UnsupportedOperationException("Tiling not supported!");
+        }
+        if (getTilingMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
+        }
+        if (!tilingSet) {
+            throw new IllegalStateException("Tiling parameters not set!");
+        }
+        return tileHeight;
+    }
+
+    /**
+     * Returns the horizontal tile grid offset of an image as it will
+     * be written to the output stream.  If tiling parameters have not
+     * been set, an <code>IllegalStateException</code> is thrown.
+     *
+     * @return the tile grid X offset to be used for encoding.
+     *
+     * @exception UnsupportedOperationException if the plug-in does not
+     * support tiling.
+     * @exception IllegalStateException if the tiling mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception IllegalStateException if the tiling parameters have
+     * not been set.
+     *
+     * @see #setTiling(int, int, int, int)
+     * @see #getTileGridYOffset()
+     */
+    public int getTileGridXOffset() {
+        if (!canWriteTiles()) {
+            throw new UnsupportedOperationException("Tiling not supported!");
+        }
+        if (getTilingMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
+        }
+        if (!tilingSet) {
+            throw new IllegalStateException("Tiling parameters not set!");
+        }
+        return tileGridXOffset;
+    }
+
+    /**
+     * Returns the vertical tile grid offset of an image as it will
+     * be written to the output stream.  If tiling parameters have not
+     * been set, an <code>IllegalStateException</code> is thrown.
+     *
+     * @return the tile grid Y offset to be used for encoding.
+     *
+     * @exception UnsupportedOperationException if the plug-in does not
+     * support tiling.
+     * @exception IllegalStateException if the tiling mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception IllegalStateException if the tiling parameters have
+     * not been set.
+     *
+     * @see #setTiling(int, int, int, int)
+     * @see #getTileGridXOffset()
+     */
+    public int getTileGridYOffset() {
+        if (!canWriteTiles()) {
+            throw new UnsupportedOperationException("Tiling not supported!");
+        }
+        if (getTilingMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
+        }
+        if (!tilingSet) {
+            throw new IllegalStateException("Tiling parameters not set!");
+        }
+        return tileGridYOffset;
+    }
+
+    /**
+     * Returns <code>true</code> if the writer can write out images
+     * as a series of passes of progressively increasing quality.
+     *
+     * @return <code>true</code> if the writer supports progressive
+     * encoding.
+     *
+     * @see #setProgressiveMode
+     * @see #getProgressiveMode
+     */
+    public boolean canWriteProgressive() {
+        return canWriteProgressive;
+    }
+
+    /**
+     * Specifies that the writer is to write the image out in a
+     * progressive mode such that the stream will contain a series of
+     * scans of increasing quality.  If progressive encoding is not
+     * supported, an <code>UnsupportedOperationException</code> will
+     * be thrown.
+     *
+     * <p>  The mode argument determines how
+     * the progression parameters are chosen, and must be either
+     * <code>MODE_DISABLED</code>,
+     * <code>MODE_COPY_FROM_METADATA</code>, or
+     * <code>MODE_DEFAULT</code>.  Otherwise an
+     * <code>IllegalArgumentException</code> is thrown.
+     *
+     * <p> The modes are interpreted as follows:
+     *
+     * <ul>
+     *   <li><code>MODE_DISABLED</code> - No progression.  Use this to
+     *   turn off progression.
+     *
+     *   <li><code>MODE_COPY_FROM_METADATA</code> - The output image
+     *   will use whatever progression parameters are found in the
+     *   metadata objects passed into the writer.
+     *
+     *   <li><code>MODE_DEFAULT</code> - The image will be written
+     *   progressively, with parameters chosen by the writer.
+     * </ul>
+     *
+     * <p> The default is <code>MODE_COPY_FROM_METADATA</code>.
+     *
+     * @param mode The mode for setting progression in the output
+     * stream.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support progressive encoding.
+     * @exception IllegalArgumentException if <code>mode</code> is not
+     * one of the modes listed above.
+     *
+     * @see #getProgressiveMode
+     */
+    public void setProgressiveMode(int mode) {
+        if (!canWriteProgressive()) {
+            throw new UnsupportedOperationException(
+                "Progressive output not supported");
+        }
+        if (mode < MODE_DISABLED || mode > MAX_MODE) {
+            throw new IllegalArgumentException("Illegal value for mode!");
+        }
+        if (mode == MODE_EXPLICIT) {
+            throw new IllegalArgumentException(
+                "MODE_EXPLICIT not supported for progressive output");
+        }
+        this.progressiveMode = mode;
+    }
+
+    /**
+     * Returns the current mode for writing the stream in a
+     * progressive manner.
+     *
+     * @return the current mode for progressive encoding.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support progressive encoding.
+     *
+     * @see #setProgressiveMode
+     */
+    public int getProgressiveMode() {
+        if (!canWriteProgressive()) {
+            throw new UnsupportedOperationException
+                ("Progressive output not supported");
+        }
+        return progressiveMode;
+    }
+
+    /**
+     * Returns <code>true</code> if this writer supports compression.
+     *
+     * @return <code>true</code> if the writer supports compression.
+     */
+    public boolean canWriteCompressed() {
+        return canWriteCompressed;
+    }
+
+    /**
+     * Specifies whether compression is to be performed, and if so how
+     * compression parameters are to be determined.  The <code>mode</code>
+     * argument must be one of the four modes, interpreted as follows:
+     *
+     * <ul>
+     *   <li><code>MODE_DISABLED</code> - If the mode is set to
+     *   <code>MODE_DISABLED</code>, methods that query or modify the
+     *   compression type or parameters will throw an
+     *   <code>IllegalStateException</code> (if compression is
+     *   normally supported by the plug-in). Some writers, such as JPEG,
+     *   do not normally offer uncompressed output. In this case, attempting
+     *   to set the mode to <code>MODE_DISABLED</code> will throw an
+     *   <code>UnsupportedOperationException</code> and the mode will not be
+     *   changed.
+     *
+     *   <li><code>MODE_EXPLICIT</code> - Compress using the
+     *   compression type and quality settings specified in this
+     *   <code>ImageWriteParam</code>.  Any previously set compression
+     *   parameters are discarded.
+     *
+     *   <li><code>MODE_COPY_FROM_METADATA</code> - Use whatever
+     *   compression parameters are specified in metadata objects
+     *   passed in to the writer.
+     *
+     *   <li><code>MODE_DEFAULT</code> - Use default compression
+     *   parameters.
+     * </ul>
+     *
+     * <p> The default is <code>MODE_COPY_FROM_METADATA</code>.
+     *
+     * @param mode The mode for setting compression in the output
+     * stream.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support compression, or does not support the requested mode.
+     * @exception IllegalArgumentException if <code>mode</code> is not
+     * one of the modes listed above.
+     *
+     * @see #getCompressionMode
+     */
+    public void setCompressionMode(int mode) {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported.");
+        }
+        if (mode < MODE_DISABLED || mode > MAX_MODE) {
+            throw new IllegalArgumentException("Illegal value for mode!");
+        }
+        this.compressionMode = mode;
+        if (mode == MODE_EXPLICIT) {
+            unsetCompression();
+        }
+    }
+
+    /**
+     * Returns the current compression mode, if compression is
+     * supported.
+     *
+     * @return the current compression mode.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support compression.
+     *
+     * @see #setCompressionMode
+     */
+    public int getCompressionMode() {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported.");
+        }
+        return compressionMode;
+    }
+
+    /**
+     * Returns a list of available compression types, as an array or
+     * <code>String</code>s, or <code>null</code> if a compression
+     * type may not be chosen using these interfaces.  The array
+     * returned is a copy.
+     *
+     * <p> If the writer only offers a single, mandatory form of
+     * compression, it is not necessary to provide any named
+     * compression types.  Named compression types should only be
+     * used where the user is able to make a meaningful choice
+     * between different schemes.
+     *
+     * <p> The default implementation checks if compression is
+     * supported and throws an
+     * <code>UnsupportedOperationException</code> if not.  Otherwise,
+     * it returns a clone of the <code>compressionTypes</code>
+     * instance variable if it is non-<code>null</code>, or else
+     * returns <code>null</code>.
+     *
+     * @return an array of <code>String</code>s containing the
+     * (non-localized) names of available compression types, or
+     * <code>null</code>.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support compression.
+     */
+    public String[] getCompressionTypes() {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported");
+        }
+        if (compressionTypes == null) {
+            return null;
+        }
+        return compressionTypes.clone();
+    }
+
+    /**
+     * Sets the compression type to one of the values indicated by
+     * <code>getCompressionTypes</code>.  If a value of
+     * <code>null</code> is passed in, any previous setting is
+     * removed.
+     *
+     * <p> The default implementation checks whether compression is
+     * supported and the compression mode is
+     * <code>MODE_EXPLICIT</code>.  If so, it calls
+     * <code>getCompressionTypes</code> and checks if
+     * <code>compressionType</code> is one of the legal values.  If it
+     * is, the <code>compressionType</code> instance variable is set.
+     * If <code>compressionType</code> is <code>null</code>, the
+     * instance variable is set without performing any checking.
+     *
+     * @param compressionType one of the <code>String</code>s returned
+     * by <code>getCompressionTypes</code>, or <code>null</code> to
+     * remove any previous setting.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support compression.
+     * @exception IllegalStateException if the compression mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception UnsupportedOperationException if there are no
+     * settable compression types.
+     * @exception IllegalArgumentException if
+     * <code>compressionType</code> is non-<code>null</code> but is not
+     * one of the values returned by <code>getCompressionTypes</code>.
+     *
+     * @see #getCompressionTypes
+     * @see #getCompressionType
+     * @see #unsetCompression
+     */
+    public void setCompressionType(String compressionType) {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported");
+        }
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException
+                ("Compression mode not MODE_EXPLICIT!");
+        }
+        String[] legalTypes = getCompressionTypes();
+        if (legalTypes == null) {
+            throw new UnsupportedOperationException(
+                "No settable compression types");
+        }
+        if (compressionType != null) {
+            boolean found = false;
+            if (legalTypes != null) {
+                for (int i = 0; i < legalTypes.length; i++) {
+                    if (compressionType.equals(legalTypes[i])) {
+                        found = true;
+                        break;
+                    }
+                }
+            }
+            if (!found) {
+                throw new IllegalArgumentException("Unknown compression type!");
+            }
+        }
+        this.compressionType = compressionType;
+    }
+
+    /**
+     * Returns the currently set compression type, or
+     * <code>null</code> if none has been set.  The type is returned
+     * as a <code>String</code> from among those returned by
+     * <code>getCompressionTypes</code>.
+     * If no compression type has been set, <code>null</code> is
+     * returned.
+     *
+     * <p> The default implementation checks whether compression is
+     * supported and the compression mode is
+     * <code>MODE_EXPLICIT</code>.  If so, it returns the value of the
+     * <code>compressionType</code> instance variable.
+     *
+     * @return the current compression type as a <code>String</code>,
+     * or <code>null</code> if no type is set.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support compression.
+     * @exception IllegalStateException if the compression mode is not
+     * <code>MODE_EXPLICIT</code>.
+     *
+     * @see #setCompressionType
+     */
+    public String getCompressionType() {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported.");
+        }
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException
+                ("Compression mode not MODE_EXPLICIT!");
+        }
+        return compressionType;
+    }
+
+    /**
+     * Removes any previous compression type and quality settings.
+     *
+     * <p> The default implementation sets the instance variable
+     * <code>compressionType</code> to <code>null</code>, and the
+     * instance variable <code>compressionQuality</code> to
+     * <code>1.0F</code>.
+     *
+     * @exception UnsupportedOperationException if the plug-in does not
+     * support compression.
+     * @exception IllegalStateException if the compression mode is not
+     * <code>MODE_EXPLICIT</code>.
+     *
+     * @see #setCompressionType
+     * @see #setCompressionQuality
+     */
+    public void unsetCompression() {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported");
+        }
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException
+                ("Compression mode not MODE_EXPLICIT!");
+        }
+        this.compressionType = null;
+        this.compressionQuality = 1.0F;
+    }
+
+    /**
+     * Returns a localized version of the name of the current
+     * compression type, using the <code>Locale</code> returned by
+     * <code>getLocale</code>.
+     *
+     * <p> The default implementation checks whether compression is
+     * supported and the compression mode is
+     * <code>MODE_EXPLICIT</code>.  If so, if
+     * <code>compressionType</code> is <code>non-null</code> the value
+     * of <code>getCompressionType</code> is returned as a
+     * convenience.
+     *
+     * @return a <code>String</code> containing a localized version of
+     * the name of the current compression type.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support compression.
+     * @exception IllegalStateException if the compression mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception IllegalStateException if no compression type is set.
+     */
+    public String getLocalizedCompressionTypeName() {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported.");
+        }
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException
+                ("Compression mode not MODE_EXPLICIT!");
+        }
+        if (getCompressionType() == null) {
+            throw new IllegalStateException("No compression type set!");
+        }
+        return getCompressionType();
+    }
+
+    /**
+     * Returns <code>true</code> if the current compression type
+     * provides lossless compression.  If a plug-in provides only
+     * one mandatory compression type, then this method may be
+     * called without calling <code>setCompressionType</code> first.
+     *
+     * <p> If there are multiple compression types but none has
+     * been set, an <code>IllegalStateException</code> is thrown.
+     *
+     * <p> The default implementation checks whether compression is
+     * supported and the compression mode is
+     * <code>MODE_EXPLICIT</code>.  If so, if
+     * <code>getCompressionTypes()</code> is <code>null</code> or
+     * <code>getCompressionType()</code> is non-<code>null</code>
+     * <code>true</code> is returned as a convenience.
+     *
+     * @return <code>true</code> if the current compression type is
+     * lossless.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support compression.
+     * @exception IllegalStateException if the compression mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception IllegalStateException if the set of legal
+     * compression types is non-<code>null</code> and the current
+     * compression type is <code>null</code>.
+     */
+    public boolean isCompressionLossless() {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported");
+        }
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException
+                ("Compression mode not MODE_EXPLICIT!");
+        }
+        if ((getCompressionTypes() != null) &&
+            (getCompressionType() == null)) {
+            throw new IllegalStateException("No compression type set!");
+        }
+        return true;
+    }
+
+    /**
+     * Sets the compression quality to a value between <code>0</code>
+     * and <code>1</code>.  Only a single compression quality setting
+     * is supported by default; writers can provide extended versions
+     * of <code>ImageWriteParam</code> that offer more control.  For
+     * lossy compression schemes, the compression quality should
+     * control the tradeoff between file size and image quality (for
+     * example, by choosing quantization tables when writing JPEG
+     * images).  For lossless schemes, the compression quality may be
+     * used to control the tradeoff between file size and time taken
+     * to perform the compression (for example, by optimizing row
+     * filters and setting the ZLIB compression level when writing
+     * PNG images).
+     *
+     * <p> A compression quality setting of 0.0 is most generically
+     * interpreted as "high compression is important," while a setting of
+     * 1.0 is most generically interpreted as "high image quality is
+     * important."
+     *
+     * <p> If there are multiple compression types but none has been
+     * set, an <code>IllegalStateException</code> is thrown.
+     *
+     * <p> The default implementation checks that compression is
+     * supported, and that the compression mode is
+     * <code>MODE_EXPLICIT</code>.  If so, if
+     * <code>getCompressionTypes()</code> returns <code>null</code> or
+     * <code>compressionType</code> is non-<code>null</code> it sets
+     * the <code>compressionQuality</code> instance variable.
+     *
+     * @param quality a <code>float</code> between <code>0</code>and
+     * <code>1</code> indicating the desired quality level.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support compression.
+     * @exception IllegalStateException if the compression mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception IllegalStateException if the set of legal
+     * compression types is non-<code>null</code> and the current
+     * compression type is <code>null</code>.
+     * @exception IllegalArgumentException if <code>quality</code> is
+     * not between <code>0</code>and <code>1</code>, inclusive.
+     *
+     * @see #getCompressionQuality
+     */
+    public void setCompressionQuality(float quality) {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported");
+        }
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException
+                ("Compression mode not MODE_EXPLICIT!");
+        }
+        if (getCompressionTypes() != null && getCompressionType() == null) {
+            throw new IllegalStateException("No compression type set!");
+        }
+        if (quality < 0.0F || quality > 1.0F) {
+            throw new IllegalArgumentException("Quality out-of-bounds!");
+        }
+        this.compressionQuality = quality;
+    }
+
+    /**
+     * Returns the current compression quality setting.
+     *
+     * <p> If there are multiple compression types but none has been
+     * set, an <code>IllegalStateException</code> is thrown.
+     *
+     * <p> The default implementation checks that compression is
+     * supported and that the compression mode is
+     * <code>MODE_EXPLICIT</code>.  If so, if
+     * <code>getCompressionTypes()</code> is <code>null</code> or
+     * <code>getCompressionType()</code> is non-<code>null</code>, it
+     * returns the value of the <code>compressionQuality</code>
+     * instance variable.
+     *
+     * @return the current compression quality setting.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support compression.
+     * @exception IllegalStateException if the compression mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception IllegalStateException if the set of legal
+     * compression types is non-<code>null</code> and the current
+     * compression type is <code>null</code>.
+     *
+     * @see #setCompressionQuality
+     */
+    public float getCompressionQuality() {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported.");
+        }
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException
+                ("Compression mode not MODE_EXPLICIT!");
+        }
+        if ((getCompressionTypes() != null) &&
+            (getCompressionType() == null)) {
+            throw new IllegalStateException("No compression type set!");
+        }
+        return compressionQuality;
+    }
+
+
+    /**
+     * Returns a <code>float</code> indicating an estimate of the
+     * number of bits of output data for each bit of input image data
+     * at the given quality level.  The value will typically lie
+     * between <code>0</code> and <code>1</code>, with smaller values
+     * indicating more compression.  A special value of
+     * <code>-1.0F</code> is used to indicate that no estimate is
+     * available.
+     *
+     * <p> If there are multiple compression types but none has been set,
+     * an <code>IllegalStateException</code> is thrown.
+     *
+     * <p> The default implementation checks that compression is
+     * supported and the compression mode is
+     * <code>MODE_EXPLICIT</code>.  If so, if
+     * <code>getCompressionTypes()</code> is <code>null</code> or
+     * <code>getCompressionType()</code> is non-<code>null</code>, and
+     * <code>quality</code> is within bounds, it returns
+     * <code>-1.0</code>.
+     *
+     * @param quality the quality setting whose bit rate is to be
+     * queried.
+     *
+     * @return an estimate of the compressed bit rate, or
+     * <code>-1.0F</code> if no estimate is available.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support compression.
+     * @exception IllegalStateException if the compression mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception IllegalStateException if the set of legal
+     * compression types is non-<code>null</code> and the current
+     * compression type is <code>null</code>.
+     * @exception IllegalArgumentException if <code>quality</code> is
+     * not between <code>0</code>and <code>1</code>, inclusive.
+     */
+    public float getBitRate(float quality) {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported.");
+        }
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException
+                ("Compression mode not MODE_EXPLICIT!");
+        }
+        if ((getCompressionTypes() != null) &&
+            (getCompressionType() == null)) {
+            throw new IllegalStateException("No compression type set!");
+        }
+        if (quality < 0.0F || quality > 1.0F) {
+            throw new IllegalArgumentException("Quality out-of-bounds!");
+        }
+        return -1.0F;
+    }
+
+    /**
+     * Returns an array of <code>String</code>s that may be used along
+     * with <code>getCompressionQualityValues</code> as part of a user
+     * interface for setting or displaying the compression quality
+     * level.  The <code>String</code> with index <code>i</code>
+     * provides a description of the range of quality levels between
+     * <code>getCompressionQualityValues[i]</code> and
+     * <code>getCompressionQualityValues[i + 1]</code>.  Note that the
+     * length of the array returned from
+     * <code>getCompressionQualityValues</code> will always be one
+     * greater than that returned from
+     * <code>getCompressionQualityDescriptions</code>.
+     *
+     * <p> As an example, the strings "Good", "Better", and "Best"
+     * could be associated with the ranges <code>[0, .33)</code>,
+     * <code>[.33, .66)</code>, and <code>[.66, 1.0]</code>.  In this
+     * case, <code>getCompressionQualityDescriptions</code> would
+     * return <code>{ "Good", "Better", "Best" }</code> and
+     * <code>getCompressionQualityValues</code> would return
+     * <code>{ 0.0F, .33F, .66F, 1.0F }</code>.
+     *
+     * <p> If no descriptions are available, <code>null</code> is
+     * returned.  If <code>null</code> is returned from
+     * <code>getCompressionQualityValues</code>, this method must also
+     * return <code>null</code>.
+     *
+     * <p> The descriptions should be localized for the
+     * <code>Locale</code> returned by <code>getLocale</code>, if it
+     * is non-<code>null</code>.
+     *
+     * <p> If there are multiple compression types but none has been set,
+     * an <code>IllegalStateException</code> is thrown.
+     *
+     * <p> The default implementation checks that compression is
+     * supported and that the compression mode is
+     * <code>MODE_EXPLICIT</code>.  If so, if
+     * <code>getCompressionTypes()</code> is <code>null</code> or
+     * <code>getCompressionType()</code> is non-<code>null</code>, it
+     * returns <code>null</code>.
+     *
+     * @return an array of <code>String</code>s containing localized
+     * descriptions of the compression quality levels.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support compression.
+     * @exception IllegalStateException if the compression mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception IllegalStateException if the set of legal
+     * compression types is non-<code>null</code> and the current
+     * compression type is <code>null</code>.
+     *
+     * @see #getCompressionQualityValues
+     */
+    public String[] getCompressionQualityDescriptions() {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported.");
+        }
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException
+                ("Compression mode not MODE_EXPLICIT!");
+        }
+        if ((getCompressionTypes() != null) &&
+            (getCompressionType() == null)) {
+            throw new IllegalStateException("No compression type set!");
+        }
+        return null;
+    }
+
+    /**
+     * Returns an array of <code>float</code>s that may be used along
+     * with <code>getCompressionQualityDescriptions</code> as part of a user
+     * interface for setting or displaying the compression quality
+     * level.  See {@link #getCompressionQualityDescriptions
+     * getCompressionQualityDescriptions} for more information.
+     *
+     * <p> If no descriptions are available, <code>null</code> is
+     * returned.  If <code>null</code> is returned from
+     * <code>getCompressionQualityDescriptions</code>, this method
+     * must also return <code>null</code>.
+     *
+     * <p> If there are multiple compression types but none has been set,
+     * an <code>IllegalStateException</code> is thrown.
+     *
+     * <p> The default implementation checks that compression is
+     * supported and that the compression mode is
+     * <code>MODE_EXPLICIT</code>.  If so, if
+     * <code>getCompressionTypes()</code> is <code>null</code> or
+     * <code>getCompressionType()</code> is non-<code>null</code>, it
+     * returns <code>null</code>.
+     *
+     * @return an array of <code>float</code>s indicating the
+     * boundaries between the compression quality levels as described
+     * by the <code>String</code>s from
+     * <code>getCompressionQualityDescriptions</code>.
+     *
+     * @exception UnsupportedOperationException if the writer does not
+     * support compression.
+     * @exception IllegalStateException if the compression mode is not
+     * <code>MODE_EXPLICIT</code>.
+     * @exception IllegalStateException if the set of legal
+     * compression types is non-<code>null</code> and the current
+     * compression type is <code>null</code>.
+     *
+     * @see #getCompressionQualityDescriptions
+     */
+    public float[] getCompressionQualityValues() {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException(
+                "Compression not supported.");
+        }
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException
+                ("Compression mode not MODE_EXPLICIT!");
+        }
+        if ((getCompressionTypes() != null) &&
+            (getCompressionType() == null)) {
+            throw new IllegalStateException("No compression type set!");
+        }
+        return null;
+    }
+}