View Javadoc
1   /*
2    * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package javax.imageio;
27  
28  import java.awt.Dimension;
29  import java.util.Locale;
30  
31  /**
32   * A class describing how a stream is to be encoded.  Instances of
33   * this class or its subclasses are used to supply prescriptive
34   * "how-to" information to instances of <code>ImageWriter</code>.
35   *
36   * <p> A plug-in for a specific image format may define a subclass of
37   * this class, and return objects of that class from the
38   * <code>getDefaultWriteParam</code> method of its
39   * <code>ImageWriter</code> implementation.  For example, the built-in
40   * JPEG writer plug-in will return instances of
41   * <code>javax.imageio.plugins.jpeg.JPEGImageWriteParam</code>.
42   *
43   * <p> The region of the image to be written is determined by first
44   * intersecting the actual bounds of the image with the rectangle
45   * specified by <code>IIOParam.setSourceRegion</code>, if any.  If the
46   * resulting rectangle has a width or height of zero, the writer will
47   * throw an <code>IIOException</code>. If the intersection is
48   * non-empty, writing will commence with the first subsampled pixel
49   * and include additional pixels within the intersected bounds
50   * according to the horizontal and vertical subsampling factors
51   * specified by {@link IIOParam#setSourceSubsampling
52   * IIOParam.setSourceSubsampling}.
53   *
54   * <p> Individual features such as tiling, progressive encoding, and
55   * compression may be set in one of four modes.
56   * <code>MODE_DISABLED</code> disables the features;
57   * <code>MODE_DEFAULT</code> enables the feature with
58   * writer-controlled parameter values; <code>MODE_EXPLICIT</code>
59   * enables the feature and allows the use of a <code>set</code> method
60   * to provide additional parameters; and
61   * <code>MODE_COPY_FROM_METADATA</code> copies relevant parameter
62   * values from the stream and image metadata objects passed to the
63   * writer.  The default for all features is
64   * <code>MODE_COPY_FROM_METADATA</code>.  Non-standard features
65   * supplied in subclasses are encouraged, but not required to use a
66   * similar scheme.
67   *
68   * <p> Plug-in writers may extend the functionality of
69   * <code>ImageWriteParam</code> by providing a subclass that implements
70   * additional, plug-in specific interfaces.  It is up to the plug-in
71   * to document what interfaces are available and how they are to be
72   * used.  Writers will silently ignore any extended features of an
73   * <code>ImageWriteParam</code> subclass of which they are not aware.
74   * Also, they may ignore any optional features that they normally
75   * disable when creating their own <code>ImageWriteParam</code>
76   * instances via <code>getDefaultWriteParam</code>.
77   *
78   * <p> Note that unless a query method exists for a capability, it must
79   * be supported by all <code>ImageWriter</code> implementations
80   * (<i>e.g.</i> progressive encoding is optional, but subsampling must be
81   * supported).
82   *
83   *
84   * @see ImageReadParam
85   */
86  public class ImageWriteParam extends IIOParam {
87  
88      /**
89       * A constant value that may be passed into methods such as
90       * <code>setTilingMode</code>, <code>setProgressiveMode</code>,
91       * and <code>setCompressionMode</code> to disable a feature for
92       * future writes.  That is, when this mode is set the stream will
93       * <b>not</b> be tiled, progressive, or compressed, and the
94       * relevant accessor methods will throw an
95       * <code>IllegalStateException</code>.
96       *
97       * @see #MODE_EXPLICIT
98       * @see #MODE_COPY_FROM_METADATA
99       * @see #MODE_DEFAULT
100      * @see #setProgressiveMode
101      * @see #getProgressiveMode
102      * @see #setTilingMode
103      * @see #getTilingMode
104      * @see #setCompressionMode
105      * @see #getCompressionMode
106      */
107     public static final int MODE_DISABLED = 0;
108 
109     /**
110      * A constant value that may be passed into methods such as
111      * <code>setTilingMode</code>,
112      * <code>setProgressiveMode</code>, and
113      * <code>setCompressionMode</code> to enable that feature for
114      * future writes.  That is, when this mode is enabled the stream
115      * will be tiled, progressive, or compressed according to a
116      * sensible default chosen internally by the writer in a plug-in
117      * dependent way, and the relevant accessor methods will
118      * throw an <code>IllegalStateException</code>.
119      *
120      * @see #MODE_DISABLED
121      * @see #MODE_EXPLICIT
122      * @see #MODE_COPY_FROM_METADATA
123      * @see #setProgressiveMode
124      * @see #getProgressiveMode
125      * @see #setTilingMode
126      * @see #getTilingMode
127      * @see #setCompressionMode
128      * @see #getCompressionMode
129      */
130     public static final int MODE_DEFAULT = 1;
131 
132     /**
133      * A constant value that may be passed into methods such as
134      * <code>setTilingMode</code> or <code>setCompressionMode</code>
135      * to enable a feature for future writes. That is, when this mode
136      * is set the stream will be tiled or compressed according to
137      * additional information supplied to the corresponding
138      * <code>set</code> methods in this class and retrievable from the
139      * corresponding <code>get</code> methods.  Note that this mode is
140      * not supported for progressive output.
141      *
142      * @see #MODE_DISABLED
143      * @see #MODE_COPY_FROM_METADATA
144      * @see #MODE_DEFAULT
145      * @see #setProgressiveMode
146      * @see #getProgressiveMode
147      * @see #setTilingMode
148      * @see #getTilingMode
149      * @see #setCompressionMode
150      * @see #getCompressionMode
151      */
152     public static final int MODE_EXPLICIT = 2;
153 
154     /**
155      * A constant value that may be passed into methods such as
156      * <code>setTilingMode</code>, <code>setProgressiveMode</code>, or
157      * <code>setCompressionMode</code> to enable that feature for
158      * future writes.  That is, when this mode is enabled the stream
159      * will be tiled, progressive, or compressed based on the contents
160      * of stream and/or image metadata passed into the write
161      * operation, and any relevant accessor methods will throw an
162      * <code>IllegalStateException</code>.
163      *
164      * <p> This is the default mode for all features, so that a read
165      * including metadata followed by a write including metadata will
166      * preserve as much information as possible.
167      *
168      * @see #MODE_DISABLED
169      * @see #MODE_EXPLICIT
170      * @see #MODE_DEFAULT
171      * @see #setProgressiveMode
172      * @see #getProgressiveMode
173      * @see #setTilingMode
174      * @see #getTilingMode
175      * @see #setCompressionMode
176      * @see #getCompressionMode
177      */
178     public static final int MODE_COPY_FROM_METADATA = 3;
179 
180     // If more modes are added, this should be updated.
181     private static final int MAX_MODE = MODE_COPY_FROM_METADATA;
182 
183     /**
184      * A <code>boolean</code> that is <code>true</code> if this
185      * <code>ImageWriteParam</code> allows tile width and tile height
186      * parameters to be set.  By default, the value is
187      * <code>false</code>.  Subclasses must set the value manually.
188      *
189      * <p> Subclasses that do not support writing tiles should ensure
190      * that this value is set to <code>false</code>.
191      */
192     protected boolean canWriteTiles = false;
193 
194     /**
195      * The mode controlling tiling settings, which Must be
196      * set to one of the four <code>MODE_*</code> values.  The default
197      * is <code>MODE_COPY_FROM_METADATA</code>.
198      *
199      * <p> Subclasses that do not writing tiles may ignore this value.
200      *
201      * @see #MODE_DISABLED
202      * @see #MODE_EXPLICIT
203      * @see #MODE_COPY_FROM_METADATA
204      * @see #MODE_DEFAULT
205      * @see #setTilingMode
206      * @see #getTilingMode
207      */
208     protected int tilingMode = MODE_COPY_FROM_METADATA;
209 
210     /**
211      * An array of preferred tile size range pairs.  The default value
212      * is <code>null</code>, which indicates that there are no
213      * preferred sizes.  If the value is non-<code>null</code>, it
214      * must have an even length of at least two.
215      *
216      * <p> Subclasses that do not support writing tiles may ignore
217      * this value.
218      *
219      * @see #getPreferredTileSizes
220      */
221     protected Dimension[] preferredTileSizes = null;
222 
223     /**
224      * A <code>boolean</code> that is <code>true</code> if tiling
225      * parameters have been specified.
226      *
227      * <p> Subclasses that do not support writing tiles may ignore
228      * this value.
229      */
230     protected boolean tilingSet = false;
231 
232     /**
233      * The width of each tile if tiling has been set, or 0 otherwise.
234      *
235      * <p> Subclasses that do not support tiling may ignore this
236      * value.
237      */
238     protected int tileWidth = 0;
239 
240     /**
241      * The height of each tile if tiling has been set, or 0 otherwise.
242      * The initial value is <code>0</code>.
243      *
244      * <p> Subclasses that do not support tiling may ignore this
245      * value.
246      */
247     protected int tileHeight = 0;
248 
249     /**
250      * A <code>boolean</code> that is <code>true</code> if this
251      * <code>ImageWriteParam</code> allows tiling grid offset
252      * parameters to be set.  By default, the value is
253      * <code>false</code>.  Subclasses must set the value manually.
254      *
255      * <p> Subclasses that do not support writing tiles, or that
256      * support writing but not offsetting tiles must ensure that this
257      * value is set to <code>false</code>.
258      */
259     protected boolean canOffsetTiles = false;
260 
261     /**
262      * The amount by which the tile grid origin should be offset
263      * horizontally from the image origin if tiling has been set,
264      * or 0 otherwise.  The initial value is <code>0</code>.
265      *
266      * <p> Subclasses that do not support offsetting tiles may ignore
267      * this value.
268      */
269     protected int tileGridXOffset = 0;
270 
271     /**
272      * The amount by which the tile grid origin should be offset
273      * vertically from the image origin if tiling has been set,
274      * or 0 otherwise.  The initial value is <code>0</code>.
275      *
276      * <p> Subclasses that do not support offsetting tiles may ignore
277      * this value.
278      */
279     protected int tileGridYOffset = 0;
280 
281     /**
282      * A <code>boolean</code> that is <code>true</code> if this
283      * <code>ImageWriteParam</code> allows images to be written as a
284      * progressive sequence of increasing quality passes.  By default,
285      * the value is <code>false</code>.  Subclasses must set the value
286      * manually.
287      *
288      * <p> Subclasses that do not support progressive encoding must
289      * ensure that this value is set to <code>false</code>.
290      */
291     protected boolean canWriteProgressive = false;
292 
293     /**
294      * The mode controlling progressive encoding, which must be set to
295      * one of the four <code>MODE_*</code> values, except
296      * <code>MODE_EXPLICIT</code>.  The default is
297      * <code>MODE_COPY_FROM_METADATA</code>.
298      *
299      * <p> Subclasses that do not support progressive encoding may
300      * ignore this value.
301      *
302      * @see #MODE_DISABLED
303      * @see #MODE_EXPLICIT
304      * @see #MODE_COPY_FROM_METADATA
305      * @see #MODE_DEFAULT
306      * @see #setProgressiveMode
307      * @see #getProgressiveMode
308      */
309     protected int progressiveMode = MODE_COPY_FROM_METADATA;
310 
311     /**
312      * A <code>boolean</code> that is <code>true</code> if this writer
313      * can write images using compression. By default, the value is
314      * <code>false</code>.  Subclasses must set the value manually.
315      *
316      * <p> Subclasses that do not support compression must ensure that
317      * this value is set to <code>false</code>.
318      */
319     protected boolean canWriteCompressed = false;
320 
321     /**
322      * The mode controlling compression settings, which must be set to
323      * one of the four <code>MODE_*</code> values.  The default is
324      * <code>MODE_COPY_FROM_METADATA</code>.
325      *
326      * <p> Subclasses that do not support compression may ignore this
327      * value.
328      *
329      * @see #MODE_DISABLED
330      * @see #MODE_EXPLICIT
331      * @see #MODE_COPY_FROM_METADATA
332      * @see #MODE_DEFAULT
333      * @see #setCompressionMode
334      * @see #getCompressionMode
335      */
336     protected int compressionMode = MODE_COPY_FROM_METADATA;
337 
338     /**
339      * An array of <code>String</code>s containing the names of the
340      * available compression types.  Subclasses must set the value
341      * manually.
342      *
343      * <p> Subclasses that do not support compression may ignore this
344      * value.
345      */
346     protected String[] compressionTypes = null;
347 
348     /**
349      * A <code>String</code> containing the name of the current
350      * compression type, or <code>null</code> if none is set.
351      *
352      * <p> Subclasses that do not support compression may ignore this
353      * value.
354      */
355     protected String compressionType = null;
356 
357     /**
358      * A <code>float</code> containing the current compression quality
359      * setting.  The initial value is <code>1.0F</code>.
360      *
361      * <p> Subclasses that do not support compression may ignore this
362      * value.
363      */
364     protected float compressionQuality = 1.0F;
365 
366     /**
367      * A <code>Locale</code> to be used to localize compression type
368      * names and quality descriptions, or <code>null</code> to use a
369      * default <code>Locale</code>.  Subclasses must set the value
370      * manually.
371      */
372     protected Locale locale = null;
373 
374     /**
375      * Constructs an empty <code>ImageWriteParam</code>.  It is up to
376      * the subclass to set up the instance variables properly.
377      */
378     protected ImageWriteParam() {}
379 
380     /**
381      * Constructs an <code>ImageWriteParam</code> set to use a
382      * given <code>Locale</code>.
383      *
384      * @param locale a <code>Locale</code> to be used to localize
385      * compression type names and quality descriptions, or
386      * <code>null</code>.
387      */
388     public ImageWriteParam(Locale locale) {
389         this.locale = locale;
390     }
391 
392     // Return a deep copy of the array
393     private static Dimension[] clonePreferredTileSizes(Dimension[] sizes) {
394         if (sizes == null) {
395             return null;
396         }
397         Dimension[] temp = new Dimension[sizes.length];
398         for (int i = 0; i < sizes.length; i++) {
399             temp[i] = new Dimension(sizes[i]);
400         }
401         return temp;
402     }
403 
404     /**
405      * Returns the currently set <code>Locale</code>, or
406      * <code>null</code> if only a default <code>Locale</code> is
407      * supported.
408      *
409      * @return the current <code>Locale</code>, or <code>null</code>.
410      */
411     public Locale getLocale() {
412         return locale;
413     }
414 
415     /**
416      * Returns <code>true</code> if the writer can perform tiling
417      * while writing.  If this method returns <code>false</code>, then
418      * <code>setTiling</code> will throw an
419      * <code>UnsupportedOperationException</code>.
420      *
421      * @return <code>true</code> if the writer supports tiling.
422      *
423      * @see #canOffsetTiles()
424      * @see #setTiling(int, int, int, int)
425      */
426     public boolean canWriteTiles() {
427         return canWriteTiles;
428     }
429 
430     /**
431      * Returns <code>true</code> if the writer can perform tiling with
432      * non-zero grid offsets while writing.  If this method returns
433      * <code>false</code>, then <code>setTiling</code> will throw an
434      * <code>UnsupportedOperationException</code> if the grid offset
435      * arguments are not both zero.  If <code>canWriteTiles</code>
436      * returns <code>false</code>, this method will return
437      * <code>false</code> as well.
438      *
439      * @return <code>true</code> if the writer supports non-zero tile
440      * offsets.
441      *
442      * @see #canWriteTiles()
443      * @see #setTiling(int, int, int, int)
444      */
445     public boolean canOffsetTiles() {
446         return canOffsetTiles;
447     }
448 
449     /**
450      * Determines whether the image will be tiled in the output
451      * stream and, if it will, how the tiling parameters will be
452      * determined.  The modes are interpreted as follows:
453      *
454      * <ul>
455      *
456      * <li><code>MODE_DISABLED</code> - The image will not be tiled.
457      * <code>setTiling</code> will throw an
458      * <code>IllegalStateException</code>.
459      *
460      * <li><code>MODE_DEFAULT</code> - The image will be tiled using
461      * default parameters.  <code>setTiling</code> will throw an
462      * <code>IllegalStateException</code>.
463      *
464      * <li><code>MODE_EXPLICIT</code> - The image will be tiled
465      * according to parameters given in the {@link #setTiling setTiling}
466      * method.  Any previously set tiling parameters are discarded.
467      *
468      * <li><code>MODE_COPY_FROM_METADATA</code> - The image will
469      * conform to the metadata object passed in to a write.
470      * <code>setTiling</code> will throw an
471      * <code>IllegalStateException</code>.
472      *
473      * </ul>
474      *
475      * @param mode The mode to use for tiling.
476      *
477      * @exception UnsupportedOperationException if
478      * <code>canWriteTiles</code> returns <code>false</code>.
479      * @exception IllegalArgumentException if <code>mode</code> is not
480      * one of the modes listed above.
481      *
482      * @see #setTiling
483      * @see #getTilingMode
484      */
485     public void setTilingMode(int mode) {
486         if (canWriteTiles() == false) {
487             throw new UnsupportedOperationException("Tiling not supported!");
488         }
489         if (mode < MODE_DISABLED || mode > MAX_MODE) {
490             throw new IllegalArgumentException("Illegal value for mode!");
491         }
492         this.tilingMode = mode;
493         if (mode == MODE_EXPLICIT) {
494             unsetTiling();
495         }
496     }
497 
498     /**
499      * Returns the current tiling mode, if tiling is supported.
500      * Otherwise throws an <code>UnsupportedOperationException</code>.
501      *
502      * @return the current tiling mode.
503      *
504      * @exception UnsupportedOperationException if
505      * <code>canWriteTiles</code> returns <code>false</code>.
506      *
507      * @see #setTilingMode
508      */
509     public int getTilingMode() {
510         if (!canWriteTiles()) {
511             throw new UnsupportedOperationException("Tiling not supported");
512         }
513         return tilingMode;
514     }
515 
516     /**
517      * Returns an array of <code>Dimension</code>s indicating the
518      * legal size ranges for tiles as they will be encoded in the
519      * output file or stream.  The returned array is a copy.
520      *
521      * <p> The information is returned as a set of pairs; the first
522      * element of a pair contains an (inclusive) minimum width and
523      * height, and the second element contains an (inclusive) maximum
524      * width and height.  Together, each pair defines a valid range of
525      * sizes.  To specify a fixed size, use the same width and height
526      * for both elements.  To specify an arbitrary range, a value of
527      * <code>null</code> is used in place of an actual array of
528      * <code>Dimension</code>s.
529      *
530      * <p> If no array is specified on the constructor, but tiling is
531      * allowed, then this method returns <code>null</code>.
532      *
533      * @exception UnsupportedOperationException if the plug-in does
534      * not support tiling.
535      *
536      * @return an array of <code>Dimension</code>s with an even length
537      * of at least two, or <code>null</code>.
538      */
539     public Dimension[] getPreferredTileSizes() {
540         if (!canWriteTiles()) {
541             throw new UnsupportedOperationException("Tiling not supported");
542         }
543         return clonePreferredTileSizes(preferredTileSizes);
544     }
545 
546     /**
547      * Specifies that the image should be tiled in the output stream.
548      * The <code>tileWidth</code> and <code>tileHeight</code>
549      * parameters specify the width and height of the tiles in the
550      * file.  If the tile width or height is greater than the width or
551      * height of the image, the image is not tiled in that dimension.
552      *
553      * <p> If <code>canOffsetTiles</code> returns <code>false</code>,
554      * then the <code>tileGridXOffset</code> and
555      * <code>tileGridYOffset</code> parameters must be zero.
556      *
557      * @param tileWidth the width of each tile.
558      * @param tileHeight the height of each tile.
559      * @param tileGridXOffset the horizontal offset of the tile grid.
560      * @param tileGridYOffset the vertical offset of the tile grid.
561      *
562      * @exception UnsupportedOperationException if the plug-in does not
563      * support tiling.
564      * @exception IllegalStateException if the tiling mode is not
565      * <code>MODE_EXPLICIT</code>.
566      * @exception UnsupportedOperationException if the plug-in does not
567      * support grid offsets, and the grid offsets are not both zero.
568      * @exception IllegalArgumentException if the tile size is not
569      * within one of the allowable ranges returned by
570      * <code>getPreferredTileSizes</code>.
571      * @exception IllegalArgumentException if <code>tileWidth</code>
572      * or <code>tileHeight</code> is less than or equal to 0.
573      *
574      * @see #canWriteTiles
575      * @see #canOffsetTiles
576      * @see #getTileWidth()
577      * @see #getTileHeight()
578      * @see #getTileGridXOffset()
579      * @see #getTileGridYOffset()
580      */
581     public void setTiling(int tileWidth,
582                           int tileHeight,
583                           int tileGridXOffset,
584                           int tileGridYOffset) {
585         if (!canWriteTiles()) {
586             throw new UnsupportedOperationException("Tiling not supported!");
587         }
588         if (getTilingMode() != MODE_EXPLICIT) {
589             throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
590         }
591         if (tileWidth <= 0 || tileHeight <= 0) {
592             throw new IllegalArgumentException
593                 ("tile dimensions are non-positive!");
594         }
595         boolean tilesOffset = (tileGridXOffset != 0) || (tileGridYOffset != 0);
596         if (!canOffsetTiles() && tilesOffset) {
597             throw new UnsupportedOperationException("Can't offset tiles!");
598         }
599         if (preferredTileSizes != null) {
600             boolean ok = true;
601             for (int i = 0; i < preferredTileSizes.length; i += 2) {
602                 Dimension min = preferredTileSizes[i];
603                 Dimension max = preferredTileSizes[i+1];
604                 if ((tileWidth < min.width) ||
605                     (tileWidth > max.width) ||
606                     (tileHeight < min.height) ||
607                     (tileHeight > max.height)) {
608                     ok = false;
609                     break;
610                 }
611             }
612             if (!ok) {
613                 throw new IllegalArgumentException("Illegal tile size!");
614             }
615         }
616 
617         this.tilingSet = true;
618         this.tileWidth = tileWidth;
619         this.tileHeight = tileHeight;
620         this.tileGridXOffset = tileGridXOffset;
621         this.tileGridYOffset = tileGridYOffset;
622     }
623 
624     /**
625      * Removes any previous tile grid parameters specified by calls to
626      * <code>setTiling</code>.
627      *
628      * <p> The default implementation sets the instance variables
629      * <code>tileWidth</code>, <code>tileHeight</code>,
630      * <code>tileGridXOffset</code>, and
631      * <code>tileGridYOffset</code> to <code>0</code>.
632      *
633      * @exception UnsupportedOperationException if the plug-in does not
634      * support tiling.
635      * @exception IllegalStateException if the tiling mode is not
636      * <code>MODE_EXPLICIT</code>.
637      *
638      * @see #setTiling(int, int, int, int)
639      */
640     public void unsetTiling() {
641         if (!canWriteTiles()) {
642             throw new UnsupportedOperationException("Tiling not supported!");
643         }
644         if (getTilingMode() != MODE_EXPLICIT) {
645             throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
646         }
647         this.tilingSet = false;
648         this.tileWidth = 0;
649         this.tileHeight = 0;
650         this.tileGridXOffset = 0;
651         this.tileGridYOffset = 0;
652     }
653 
654     /**
655      * Returns the width of each tile in an image as it will be
656      * written to the output stream.  If tiling parameters have not
657      * been set, an <code>IllegalStateException</code> is thrown.
658      *
659      * @return the tile width to be used for encoding.
660      *
661      * @exception UnsupportedOperationException if the plug-in does not
662      * support tiling.
663      * @exception IllegalStateException if the tiling mode is not
664      * <code>MODE_EXPLICIT</code>.
665      * @exception IllegalStateException if the tiling parameters have
666      * not been set.
667      *
668      * @see #setTiling(int, int, int, int)
669      * @see #getTileHeight()
670      */
671     public int getTileWidth() {
672         if (!canWriteTiles()) {
673             throw new UnsupportedOperationException("Tiling not supported!");
674         }
675         if (getTilingMode() != MODE_EXPLICIT) {
676             throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
677         }
678         if (!tilingSet) {
679             throw new IllegalStateException("Tiling parameters not set!");
680         }
681         return tileWidth;
682     }
683 
684     /**
685      * Returns the height of each tile in an image as it will be written to
686      * the output stream.  If tiling parameters have not
687      * been set, an <code>IllegalStateException</code> is thrown.
688      *
689      * @return the tile height to be used for encoding.
690      *
691      * @exception UnsupportedOperationException if the plug-in does not
692      * support tiling.
693      * @exception IllegalStateException if the tiling mode is not
694      * <code>MODE_EXPLICIT</code>.
695      * @exception IllegalStateException if the tiling parameters have
696      * not been set.
697      *
698      * @see #setTiling(int, int, int, int)
699      * @see #getTileWidth()
700      */
701     public int getTileHeight() {
702         if (!canWriteTiles()) {
703             throw new UnsupportedOperationException("Tiling not supported!");
704         }
705         if (getTilingMode() != MODE_EXPLICIT) {
706             throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
707         }
708         if (!tilingSet) {
709             throw new IllegalStateException("Tiling parameters not set!");
710         }
711         return tileHeight;
712     }
713 
714     /**
715      * Returns the horizontal tile grid offset of an image as it will
716      * be written to the output stream.  If tiling parameters have not
717      * been set, an <code>IllegalStateException</code> is thrown.
718      *
719      * @return the tile grid X offset to be used for encoding.
720      *
721      * @exception UnsupportedOperationException if the plug-in does not
722      * support tiling.
723      * @exception IllegalStateException if the tiling mode is not
724      * <code>MODE_EXPLICIT</code>.
725      * @exception IllegalStateException if the tiling parameters have
726      * not been set.
727      *
728      * @see #setTiling(int, int, int, int)
729      * @see #getTileGridYOffset()
730      */
731     public int getTileGridXOffset() {
732         if (!canWriteTiles()) {
733             throw new UnsupportedOperationException("Tiling not supported!");
734         }
735         if (getTilingMode() != MODE_EXPLICIT) {
736             throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
737         }
738         if (!tilingSet) {
739             throw new IllegalStateException("Tiling parameters not set!");
740         }
741         return tileGridXOffset;
742     }
743 
744     /**
745      * Returns the vertical tile grid offset of an image as it will
746      * be written to the output stream.  If tiling parameters have not
747      * been set, an <code>IllegalStateException</code> is thrown.
748      *
749      * @return the tile grid Y offset to be used for encoding.
750      *
751      * @exception UnsupportedOperationException if the plug-in does not
752      * support tiling.
753      * @exception IllegalStateException if the tiling mode is not
754      * <code>MODE_EXPLICIT</code>.
755      * @exception IllegalStateException if the tiling parameters have
756      * not been set.
757      *
758      * @see #setTiling(int, int, int, int)
759      * @see #getTileGridXOffset()
760      */
761     public int getTileGridYOffset() {
762         if (!canWriteTiles()) {
763             throw new UnsupportedOperationException("Tiling not supported!");
764         }
765         if (getTilingMode() != MODE_EXPLICIT) {
766             throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
767         }
768         if (!tilingSet) {
769             throw new IllegalStateException("Tiling parameters not set!");
770         }
771         return tileGridYOffset;
772     }
773 
774     /**
775      * Returns <code>true</code> if the writer can write out images
776      * as a series of passes of progressively increasing quality.
777      *
778      * @return <code>true</code> if the writer supports progressive
779      * encoding.
780      *
781      * @see #setProgressiveMode
782      * @see #getProgressiveMode
783      */
784     public boolean canWriteProgressive() {
785         return canWriteProgressive;
786     }
787 
788     /**
789      * Specifies that the writer is to write the image out in a
790      * progressive mode such that the stream will contain a series of
791      * scans of increasing quality.  If progressive encoding is not
792      * supported, an <code>UnsupportedOperationException</code> will
793      * be thrown.
794      *
795      * <p>  The mode argument determines how
796      * the progression parameters are chosen, and must be either
797      * <code>MODE_DISABLED</code>,
798      * <code>MODE_COPY_FROM_METADATA</code>, or
799      * <code>MODE_DEFAULT</code>.  Otherwise an
800      * <code>IllegalArgumentException</code> is thrown.
801      *
802      * <p> The modes are interpreted as follows:
803      *
804      * <ul>
805      *   <li><code>MODE_DISABLED</code> - No progression.  Use this to
806      *   turn off progression.
807      *
808      *   <li><code>MODE_COPY_FROM_METADATA</code> - The output image
809      *   will use whatever progression parameters are found in the
810      *   metadata objects passed into the writer.
811      *
812      *   <li><code>MODE_DEFAULT</code> - The image will be written
813      *   progressively, with parameters chosen by the writer.
814      * </ul>
815      *
816      * <p> The default is <code>MODE_COPY_FROM_METADATA</code>.
817      *
818      * @param mode The mode for setting progression in the output
819      * stream.
820      *
821      * @exception UnsupportedOperationException if the writer does not
822      * support progressive encoding.
823      * @exception IllegalArgumentException if <code>mode</code> is not
824      * one of the modes listed above.
825      *
826      * @see #getProgressiveMode
827      */
828     public void setProgressiveMode(int mode) {
829         if (!canWriteProgressive()) {
830             throw new UnsupportedOperationException(
831                 "Progressive output not supported");
832         }
833         if (mode < MODE_DISABLED || mode > MAX_MODE) {
834             throw new IllegalArgumentException("Illegal value for mode!");
835         }
836         if (mode == MODE_EXPLICIT) {
837             throw new IllegalArgumentException(
838                 "MODE_EXPLICIT not supported for progressive output");
839         }
840         this.progressiveMode = mode;
841     }
842 
843     /**
844      * Returns the current mode for writing the stream in a
845      * progressive manner.
846      *
847      * @return the current mode for progressive encoding.
848      *
849      * @exception UnsupportedOperationException if the writer does not
850      * support progressive encoding.
851      *
852      * @see #setProgressiveMode
853      */
854     public int getProgressiveMode() {
855         if (!canWriteProgressive()) {
856             throw new UnsupportedOperationException
857                 ("Progressive output not supported");
858         }
859         return progressiveMode;
860     }
861 
862     /**
863      * Returns <code>true</code> if this writer supports compression.
864      *
865      * @return <code>true</code> if the writer supports compression.
866      */
867     public boolean canWriteCompressed() {
868         return canWriteCompressed;
869     }
870 
871     /**
872      * Specifies whether compression is to be performed, and if so how
873      * compression parameters are to be determined.  The <code>mode</code>
874      * argument must be one of the four modes, interpreted as follows:
875      *
876      * <ul>
877      *   <li><code>MODE_DISABLED</code> - If the mode is set to
878      *   <code>MODE_DISABLED</code>, methods that query or modify the
879      *   compression type or parameters will throw an
880      *   <code>IllegalStateException</code> (if compression is
881      *   normally supported by the plug-in). Some writers, such as JPEG,
882      *   do not normally offer uncompressed output. In this case, attempting
883      *   to set the mode to <code>MODE_DISABLED</code> will throw an
884      *   <code>UnsupportedOperationException</code> and the mode will not be
885      *   changed.
886      *
887      *   <li><code>MODE_EXPLICIT</code> - Compress using the
888      *   compression type and quality settings specified in this
889      *   <code>ImageWriteParam</code>.  Any previously set compression
890      *   parameters are discarded.
891      *
892      *   <li><code>MODE_COPY_FROM_METADATA</code> - Use whatever
893      *   compression parameters are specified in metadata objects
894      *   passed in to the writer.
895      *
896      *   <li><code>MODE_DEFAULT</code> - Use default compression
897      *   parameters.
898      * </ul>
899      *
900      * <p> The default is <code>MODE_COPY_FROM_METADATA</code>.
901      *
902      * @param mode The mode for setting compression in the output
903      * stream.
904      *
905      * @exception UnsupportedOperationException if the writer does not
906      * support compression, or does not support the requested mode.
907      * @exception IllegalArgumentException if <code>mode</code> is not
908      * one of the modes listed above.
909      *
910      * @see #getCompressionMode
911      */
912     public void setCompressionMode(int mode) {
913         if (!canWriteCompressed()) {
914             throw new UnsupportedOperationException(
915                 "Compression not supported.");
916         }
917         if (mode < MODE_DISABLED || mode > MAX_MODE) {
918             throw new IllegalArgumentException("Illegal value for mode!");
919         }
920         this.compressionMode = mode;
921         if (mode == MODE_EXPLICIT) {
922             unsetCompression();
923         }
924     }
925 
926     /**
927      * Returns the current compression mode, if compression is
928      * supported.
929      *
930      * @return the current compression mode.
931      *
932      * @exception UnsupportedOperationException if the writer does not
933      * support compression.
934      *
935      * @see #setCompressionMode
936      */
937     public int getCompressionMode() {
938         if (!canWriteCompressed()) {
939             throw new UnsupportedOperationException(
940                 "Compression not supported.");
941         }
942         return compressionMode;
943     }
944 
945     /**
946      * Returns a list of available compression types, as an array or
947      * <code>String</code>s, or <code>null</code> if a compression
948      * type may not be chosen using these interfaces.  The array
949      * returned is a copy.
950      *
951      * <p> If the writer only offers a single, mandatory form of
952      * compression, it is not necessary to provide any named
953      * compression types.  Named compression types should only be
954      * used where the user is able to make a meaningful choice
955      * between different schemes.
956      *
957      * <p> The default implementation checks if compression is
958      * supported and throws an
959      * <code>UnsupportedOperationException</code> if not.  Otherwise,
960      * it returns a clone of the <code>compressionTypes</code>
961      * instance variable if it is non-<code>null</code>, or else
962      * returns <code>null</code>.
963      *
964      * @return an array of <code>String</code>s containing the
965      * (non-localized) names of available compression types, or
966      * <code>null</code>.
967      *
968      * @exception UnsupportedOperationException if the writer does not
969      * support compression.
970      */
971     public String[] getCompressionTypes() {
972         if (!canWriteCompressed()) {
973             throw new UnsupportedOperationException(
974                 "Compression not supported");
975         }
976         if (compressionTypes == null) {
977             return null;
978         }
979         return (String[])compressionTypes.clone();
980     }
981 
982     /**
983      * Sets the compression type to one of the values indicated by
984      * <code>getCompressionTypes</code>.  If a value of
985      * <code>null</code> is passed in, any previous setting is
986      * removed.
987      *
988      * <p> The default implementation checks whether compression is
989      * supported and the compression mode is
990      * <code>MODE_EXPLICIT</code>.  If so, it calls
991      * <code>getCompressionTypes</code> and checks if
992      * <code>compressionType</code> is one of the legal values.  If it
993      * is, the <code>compressionType</code> instance variable is set.
994      * If <code>compressionType</code> is <code>null</code>, the
995      * instance variable is set without performing any checking.
996      *
997      * @param compressionType one of the <code>String</code>s returned
998      * by <code>getCompressionTypes</code>, or <code>null</code> to
999      * remove any previous setting.
1000      *
1001      * @exception UnsupportedOperationException if the writer does not
1002      * support compression.
1003      * @exception IllegalStateException if the compression mode is not
1004      * <code>MODE_EXPLICIT</code>.
1005      * @exception UnsupportedOperationException if there are no
1006      * settable compression types.
1007      * @exception IllegalArgumentException if
1008      * <code>compressionType</code> is non-<code>null</code> but is not
1009      * one of the values returned by <code>getCompressionTypes</code>.
1010      *
1011      * @see #getCompressionTypes
1012      * @see #getCompressionType
1013      * @see #unsetCompression
1014      */
1015     public void setCompressionType(String compressionType) {
1016         if (!canWriteCompressed()) {
1017             throw new UnsupportedOperationException(
1018                 "Compression not supported");
1019         }
1020         if (getCompressionMode() != MODE_EXPLICIT) {
1021             throw new IllegalStateException
1022                 ("Compression mode not MODE_EXPLICIT!");
1023         }
1024         String[] legalTypes = getCompressionTypes();
1025         if (legalTypes == null) {
1026             throw new UnsupportedOperationException(
1027                 "No settable compression types");
1028         }
1029         if (compressionType != null) {
1030             boolean found = false;
1031             if (legalTypes != null) {
1032                 for (int i = 0; i < legalTypes.length; i++) {
1033                     if (compressionType.equals(legalTypes[i])) {
1034                         found = true;
1035                         break;
1036                     }
1037                 }
1038             }
1039             if (!found) {
1040                 throw new IllegalArgumentException("Unknown compression type!");
1041             }
1042         }
1043         this.compressionType = compressionType;
1044     }
1045 
1046     /**
1047      * Returns the currently set compression type, or
1048      * <code>null</code> if none has been set.  The type is returned
1049      * as a <code>String</code> from among those returned by
1050      * <code>getCompressionTypes</code>.
1051      * If no compression type has been set, <code>null</code> is
1052      * returned.
1053      *
1054      * <p> The default implementation checks whether compression is
1055      * supported and the compression mode is
1056      * <code>MODE_EXPLICIT</code>.  If so, it returns the value of the
1057      * <code>compressionType</code> instance variable.
1058      *
1059      * @return the current compression type as a <code>String</code>,
1060      * or <code>null</code> if no type is set.
1061      *
1062      * @exception UnsupportedOperationException if the writer does not
1063      * support compression.
1064      * @exception IllegalStateException if the compression mode is not
1065      * <code>MODE_EXPLICIT</code>.
1066      *
1067      * @see #setCompressionType
1068      */
1069     public String getCompressionType() {
1070         if (!canWriteCompressed()) {
1071             throw new UnsupportedOperationException(
1072                 "Compression not supported.");
1073         }
1074         if (getCompressionMode() != MODE_EXPLICIT) {
1075             throw new IllegalStateException
1076                 ("Compression mode not MODE_EXPLICIT!");
1077         }
1078         return compressionType;
1079     }
1080 
1081     /**
1082      * Removes any previous compression type and quality settings.
1083      *
1084      * <p> The default implementation sets the instance variable
1085      * <code>compressionType</code> to <code>null</code>, and the
1086      * instance variable <code>compressionQuality</code> to
1087      * <code>1.0F</code>.
1088      *
1089      * @exception UnsupportedOperationException if the plug-in does not
1090      * support compression.
1091      * @exception IllegalStateException if the compression mode is not
1092      * <code>MODE_EXPLICIT</code>.
1093      *
1094      * @see #setCompressionType
1095      * @see #setCompressionQuality
1096      */
1097     public void unsetCompression() {
1098         if (!canWriteCompressed()) {
1099             throw new UnsupportedOperationException(
1100                 "Compression not supported");
1101         }
1102         if (getCompressionMode() != MODE_EXPLICIT) {
1103             throw new IllegalStateException
1104                 ("Compression mode not MODE_EXPLICIT!");
1105         }
1106         this.compressionType = null;
1107         this.compressionQuality = 1.0F;
1108     }
1109 
1110     /**
1111      * Returns a localized version of the name of the current
1112      * compression type, using the <code>Locale</code> returned by
1113      * <code>getLocale</code>.
1114      *
1115      * <p> The default implementation checks whether compression is
1116      * supported and the compression mode is
1117      * <code>MODE_EXPLICIT</code>.  If so, if
1118      * <code>compressionType</code> is <code>non-null</code> the value
1119      * of <code>getCompressionType</code> is returned as a
1120      * convenience.
1121      *
1122      * @return a <code>String</code> containing a localized version of
1123      * the name of the current compression type.
1124      *
1125      * @exception UnsupportedOperationException if the writer does not
1126      * support compression.
1127      * @exception IllegalStateException if the compression mode is not
1128      * <code>MODE_EXPLICIT</code>.
1129      * @exception IllegalStateException if no compression type is set.
1130      */
1131     public String getLocalizedCompressionTypeName() {
1132         if (!canWriteCompressed()) {
1133             throw new UnsupportedOperationException(
1134                 "Compression not supported.");
1135         }
1136         if (getCompressionMode() != MODE_EXPLICIT) {
1137             throw new IllegalStateException
1138                 ("Compression mode not MODE_EXPLICIT!");
1139         }
1140         if (getCompressionType() == null) {
1141             throw new IllegalStateException("No compression type set!");
1142         }
1143         return getCompressionType();
1144     }
1145 
1146     /**
1147      * Returns <code>true</code> if the current compression type
1148      * provides lossless compression.  If a plug-in provides only
1149      * one mandatory compression type, then this method may be
1150      * called without calling <code>setCompressionType</code> first.
1151      *
1152      * <p> If there are multiple compression types but none has
1153      * been set, an <code>IllegalStateException</code> is thrown.
1154      *
1155      * <p> The default implementation checks whether compression is
1156      * supported and the compression mode is
1157      * <code>MODE_EXPLICIT</code>.  If so, if
1158      * <code>getCompressionTypes()</code> is <code>null</code> or
1159      * <code>getCompressionType()</code> is non-<code>null</code>
1160      * <code>true</code> is returned as a convenience.
1161      *
1162      * @return <code>true</code> if the current compression type is
1163      * lossless.
1164      *
1165      * @exception UnsupportedOperationException if the writer does not
1166      * support compression.
1167      * @exception IllegalStateException if the compression mode is not
1168      * <code>MODE_EXPLICIT</code>.
1169      * @exception IllegalStateException if the set of legal
1170      * compression types is non-<code>null</code> and the current
1171      * compression type is <code>null</code>.
1172      */
1173     public boolean isCompressionLossless() {
1174         if (!canWriteCompressed()) {
1175             throw new UnsupportedOperationException(
1176                 "Compression not supported");
1177         }
1178         if (getCompressionMode() != MODE_EXPLICIT) {
1179             throw new IllegalStateException
1180                 ("Compression mode not MODE_EXPLICIT!");
1181         }
1182         if ((getCompressionTypes() != null) &&
1183             (getCompressionType() == null)) {
1184             throw new IllegalStateException("No compression type set!");
1185         }
1186         return true;
1187     }
1188 
1189     /**
1190      * Sets the compression quality to a value between <code>0</code>
1191      * and <code>1</code>.  Only a single compression quality setting
1192      * is supported by default; writers can provide extended versions
1193      * of <code>ImageWriteParam</code> that offer more control.  For
1194      * lossy compression schemes, the compression quality should
1195      * control the tradeoff between file size and image quality (for
1196      * example, by choosing quantization tables when writing JPEG
1197      * images).  For lossless schemes, the compression quality may be
1198      * used to control the tradeoff between file size and time taken
1199      * to perform the compression (for example, by optimizing row
1200      * filters and setting the ZLIB compression level when writing
1201      * PNG images).
1202      *
1203      * <p> A compression quality setting of 0.0 is most generically
1204      * interpreted as "high compression is important," while a setting of
1205      * 1.0 is most generically interpreted as "high image quality is
1206      * important."
1207      *
1208      * <p> If there are multiple compression types but none has been
1209      * set, an <code>IllegalStateException</code> is thrown.
1210      *
1211      * <p> The default implementation checks that compression is
1212      * supported, and that the compression mode is
1213      * <code>MODE_EXPLICIT</code>.  If so, if
1214      * <code>getCompressionTypes()</code> returns <code>null</code> or
1215      * <code>compressionType</code> is non-<code>null</code> it sets
1216      * the <code>compressionQuality</code> instance variable.
1217      *
1218      * @param quality a <code>float</code> between <code>0</code>and
1219      * <code>1</code> indicating the desired quality level.
1220      *
1221      * @exception UnsupportedOperationException if the writer does not
1222      * support compression.
1223      * @exception IllegalStateException if the compression mode is not
1224      * <code>MODE_EXPLICIT</code>.
1225      * @exception IllegalStateException if the set of legal
1226      * compression types is non-<code>null</code> and the current
1227      * compression type is <code>null</code>.
1228      * @exception IllegalArgumentException if <code>quality</code> is
1229      * not between <code>0</code>and <code>1</code>, inclusive.
1230      *
1231      * @see #getCompressionQuality
1232      */
1233     public void setCompressionQuality(float quality) {
1234         if (!canWriteCompressed()) {
1235             throw new UnsupportedOperationException(
1236                 "Compression not supported");
1237         }
1238         if (getCompressionMode() != MODE_EXPLICIT) {
1239             throw new IllegalStateException
1240                 ("Compression mode not MODE_EXPLICIT!");
1241         }
1242         if (getCompressionTypes() != null && getCompressionType() == null) {
1243             throw new IllegalStateException("No compression type set!");
1244         }
1245         if (quality < 0.0F || quality > 1.0F) {
1246             throw new IllegalArgumentException("Quality out-of-bounds!");
1247         }
1248         this.compressionQuality = quality;
1249     }
1250 
1251     /**
1252      * Returns the current compression quality setting.
1253      *
1254      * <p> If there are multiple compression types but none has been
1255      * set, an <code>IllegalStateException</code> is thrown.
1256      *
1257      * <p> The default implementation checks that compression is
1258      * supported and that the compression mode is
1259      * <code>MODE_EXPLICIT</code>.  If so, if
1260      * <code>getCompressionTypes()</code> is <code>null</code> or
1261      * <code>getCompressionType()</code> is non-<code>null</code>, it
1262      * returns the value of the <code>compressionQuality</code>
1263      * instance variable.
1264      *
1265      * @return the current compression quality setting.
1266      *
1267      * @exception UnsupportedOperationException if the writer does not
1268      * support compression.
1269      * @exception IllegalStateException if the compression mode is not
1270      * <code>MODE_EXPLICIT</code>.
1271      * @exception IllegalStateException if the set of legal
1272      * compression types is non-<code>null</code> and the current
1273      * compression type is <code>null</code>.
1274      *
1275      * @see #setCompressionQuality
1276      */
1277     public float getCompressionQuality() {
1278         if (!canWriteCompressed()) {
1279             throw new UnsupportedOperationException(
1280                 "Compression not supported.");
1281         }
1282         if (getCompressionMode() != MODE_EXPLICIT) {
1283             throw new IllegalStateException
1284                 ("Compression mode not MODE_EXPLICIT!");
1285         }
1286         if ((getCompressionTypes() != null) &&
1287             (getCompressionType() == null)) {
1288             throw new IllegalStateException("No compression type set!");
1289         }
1290         return compressionQuality;
1291     }
1292 
1293 
1294     /**
1295      * Returns a <code>float</code> indicating an estimate of the
1296      * number of bits of output data for each bit of input image data
1297      * at the given quality level.  The value will typically lie
1298      * between <code>0</code> and <code>1</code>, with smaller values
1299      * indicating more compression.  A special value of
1300      * <code>-1.0F</code> is used to indicate that no estimate is
1301      * available.
1302      *
1303      * <p> If there are multiple compression types but none has been set,
1304      * an <code>IllegalStateException</code> is thrown.
1305      *
1306      * <p> The default implementation checks that compression is
1307      * supported and the compression mode is
1308      * <code>MODE_EXPLICIT</code>.  If so, if
1309      * <code>getCompressionTypes()</code> is <code>null</code> or
1310      * <code>getCompressionType()</code> is non-<code>null</code>, and
1311      * <code>quality</code> is within bounds, it returns
1312      * <code>-1.0</code>.
1313      *
1314      * @param quality the quality setting whose bit rate is to be
1315      * queried.
1316      *
1317      * @return an estimate of the compressed bit rate, or
1318      * <code>-1.0F</code> if no estimate is available.
1319      *
1320      * @exception UnsupportedOperationException if the writer does not
1321      * support compression.
1322      * @exception IllegalStateException if the compression mode is not
1323      * <code>MODE_EXPLICIT</code>.
1324      * @exception IllegalStateException if the set of legal
1325      * compression types is non-<code>null</code> and the current
1326      * compression type is <code>null</code>.
1327      * @exception IllegalArgumentException if <code>quality</code> is
1328      * not between <code>0</code>and <code>1</code>, inclusive.
1329      */
1330     public float getBitRate(float quality) {
1331         if (!canWriteCompressed()) {
1332             throw new UnsupportedOperationException(
1333                 "Compression not supported.");
1334         }
1335         if (getCompressionMode() != MODE_EXPLICIT) {
1336             throw new IllegalStateException
1337                 ("Compression mode not MODE_EXPLICIT!");
1338         }
1339         if ((getCompressionTypes() != null) &&
1340             (getCompressionType() == null)) {
1341             throw new IllegalStateException("No compression type set!");
1342         }
1343         if (quality < 0.0F || quality > 1.0F) {
1344             throw new IllegalArgumentException("Quality out-of-bounds!");
1345         }
1346         return -1.0F;
1347     }
1348 
1349     /**
1350      * Returns an array of <code>String</code>s that may be used along
1351      * with <code>getCompressionQualityValues</code> as part of a user
1352      * interface for setting or displaying the compression quality
1353      * level.  The <code>String</code> with index <code>i</code>
1354      * provides a description of the range of quality levels between
1355      * <code>getCompressionQualityValues[i]</code> and
1356      * <code>getCompressionQualityValues[i + 1]</code>.  Note that the
1357      * length of the array returned from
1358      * <code>getCompressionQualityValues</code> will always be one
1359      * greater than that returned from
1360      * <code>getCompressionQualityDescriptions</code>.
1361      *
1362      * <p> As an example, the strings "Good", "Better", and "Best"
1363      * could be associated with the ranges <code>[0, .33)</code>,
1364      * <code>[.33, .66)</code>, and <code>[.66, 1.0]</code>.  In this
1365      * case, <code>getCompressionQualityDescriptions</code> would
1366      * return <code>{ "Good", "Better", "Best" }</code> and
1367      * <code>getCompressionQualityValues</code> would return
1368      * <code>{ 0.0F, .33F, .66F, 1.0F }</code>.
1369      *
1370      * <p> If no descriptions are available, <code>null</code> is
1371      * returned.  If <code>null</code> is returned from
1372      * <code>getCompressionQualityValues</code>, this method must also
1373      * return <code>null</code>.
1374      *
1375      * <p> The descriptions should be localized for the
1376      * <code>Locale</code> returned by <code>getLocale</code>, if it
1377      * is non-<code>null</code>.
1378      *
1379      * <p> If there are multiple compression types but none has been set,
1380      * an <code>IllegalStateException</code> is thrown.
1381      *
1382      * <p> The default implementation checks that compression is
1383      * supported and that the compression mode is
1384      * <code>MODE_EXPLICIT</code>.  If so, if
1385      * <code>getCompressionTypes()</code> is <code>null</code> or
1386      * <code>getCompressionType()</code> is non-<code>null</code>, it
1387      * returns <code>null</code>.
1388      *
1389      * @return an array of <code>String</code>s containing localized
1390      * descriptions of the compression quality levels.
1391      *
1392      * @exception UnsupportedOperationException if the writer does not
1393      * support compression.
1394      * @exception IllegalStateException if the compression mode is not
1395      * <code>MODE_EXPLICIT</code>.
1396      * @exception IllegalStateException if the set of legal
1397      * compression types is non-<code>null</code> and the current
1398      * compression type is <code>null</code>.
1399      *
1400      * @see #getCompressionQualityValues
1401      */
1402     public String[] getCompressionQualityDescriptions() {
1403         if (!canWriteCompressed()) {
1404             throw new UnsupportedOperationException(
1405                 "Compression not supported.");
1406         }
1407         if (getCompressionMode() != MODE_EXPLICIT) {
1408             throw new IllegalStateException
1409                 ("Compression mode not MODE_EXPLICIT!");
1410         }
1411         if ((getCompressionTypes() != null) &&
1412             (getCompressionType() == null)) {
1413             throw new IllegalStateException("No compression type set!");
1414         }
1415         return null;
1416     }
1417 
1418     /**
1419      * Returns an array of <code>float</code>s that may be used along
1420      * with <code>getCompressionQualityDescriptions</code> as part of a user
1421      * interface for setting or displaying the compression quality
1422      * level.  See {@link #getCompressionQualityDescriptions
1423      * getCompressionQualityDescriptions} for more information.
1424      *
1425      * <p> If no descriptions are available, <code>null</code> is
1426      * returned.  If <code>null</code> is returned from
1427      * <code>getCompressionQualityDescriptions</code>, this method
1428      * must also return <code>null</code>.
1429      *
1430      * <p> If there are multiple compression types but none has been set,
1431      * an <code>IllegalStateException</code> is thrown.
1432      *
1433      * <p> The default implementation checks that compression is
1434      * supported and that the compression mode is
1435      * <code>MODE_EXPLICIT</code>.  If so, if
1436      * <code>getCompressionTypes()</code> is <code>null</code> or
1437      * <code>getCompressionType()</code> is non-<code>null</code>, it
1438      * returns <code>null</code>.
1439      *
1440      * @return an array of <code>float</code>s indicating the
1441      * boundaries between the compression quality levels as described
1442      * by the <code>String</code>s from
1443      * <code>getCompressionQualityDescriptions</code>.
1444      *
1445      * @exception UnsupportedOperationException if the writer does not
1446      * support compression.
1447      * @exception IllegalStateException if the compression mode is not
1448      * <code>MODE_EXPLICIT</code>.
1449      * @exception IllegalStateException if the set of legal
1450      * compression types is non-<code>null</code> and the current
1451      * compression type is <code>null</code>.
1452      *
1453      * @see #getCompressionQualityDescriptions
1454      */
1455     public float[] getCompressionQualityValues() {
1456         if (!canWriteCompressed()) {
1457             throw new UnsupportedOperationException(
1458                 "Compression not supported.");
1459         }
1460         if (getCompressionMode() != MODE_EXPLICIT) {
1461             throw new IllegalStateException
1462                 ("Compression mode not MODE_EXPLICIT!");
1463         }
1464         if ((getCompressionTypes() != null) &&
1465             (getCompressionType() == null)) {
1466             throw new IllegalStateException("No compression type set!");
1467         }
1468         return null;
1469     }
1470 }