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.plugins.jpeg;
27  
28  import java.util.Locale;
29  import javax.imageio.ImageWriteParam;
30  
31  import com.sun.imageio.plugins.jpeg.JPEG;
32  
33  /**
34   * This class adds the ability to set JPEG quantization and Huffman
35   * tables when using the built-in JPEG writer plug-in, and to request that
36   * optimized Huffman tables be computed for an image.  An instance of
37   * this class will be returned from the
38   * <code>getDefaultImageWriteParam</code> methods of the built-in JPEG
39   * <code>ImageWriter</code>.
40  
41   * <p> The principal purpose of these additions is to allow the
42   * specification of tables to use in encoding abbreviated streams.
43   * The built-in JPEG writer will also accept an ordinary
44   * <code>ImageWriteParam</code>, in which case the writer will
45   * construct the necessary tables internally.
46   *
47   * <p> In either case, the quality setting in an <code>ImageWriteParam</code>
48   * has the same meaning as for the underlying library: 1.00 means a
49   * quantization table of all 1's, 0.75 means the "standard", visually
50   * lossless quantization table, and 0.00 means aquantization table of
51   * all 255's.
52   *
53   * <p> While tables for abbreviated streams are often specified by
54   * first writing an abbreviated stream containing only the tables, in
55   * some applications the tables are fixed ahead of time.  This class
56   * allows the tables to be specified directly from client code.
57   *
58   * <p> Normally, the tables are specified in the
59   * <code>IIOMetadata</code> objects passed in to the writer, and any
60   * tables included in these objects are written to the stream.
61   * If no tables are specified in the metadata, then an abbreviated
62   * stream is written.  If no tables are included in the metadata and
63   * no tables are specified in a <code>JPEGImageWriteParam</code>, then
64   * an abbreviated stream is encoded using the "standard" visually
65   * lossless tables.  This class is necessary for specifying tables
66   * when an abbreviated stream must be written without writing any tables
67   * to a stream first.  In order to use this class, the metadata object
68   * passed into the writer must contain no tables, and no stream metadata
69   * must be provided.  See {@link JPEGQTable JPEGQTable} and
70   * {@link JPEGHuffmanTable JPEGHuffmanTable} for more
71   * information on the default tables.
72   *
73   * <p> The default <code>JPEGImageWriteParam</code> returned by the
74   * <code>getDefaultWriteParam</code> method of the writer contains no
75   * tables.  Default tables are included in the default
76   * <code>IIOMetadata</code> objects returned by the writer.
77   *
78   * <p> If the metadata does contain tables, the tables given in a
79   * <code>JPEGImageWriteParam</code> are ignored.  Furthermore, once a
80   * set of tables has been written, only tables in the metadata can
81   * override them for subsequent writes, whether to the same stream or
82   * a different one.  In order to specify new tables using this class,
83   * the {@link javax.imageio.ImageWriter#reset reset}
84   * method of the writer must be called.
85   *
86   * <p>
87   * For more information about the operation of the built-in JPEG plug-ins,
88   * see the <A HREF="../../metadata/doc-files/jpeg_metadata.html">JPEG
89   * metadata format specification and usage notes</A>.
90   *
91   */
92  public class JPEGImageWriteParam extends ImageWriteParam {
93  
94      private JPEGQTable[] qTables = null;
95      private JPEGHuffmanTable[] DCHuffmanTables = null;
96      private JPEGHuffmanTable[] ACHuffmanTables = null;
97      private boolean optimizeHuffman = false;
98      private String[] compressionNames = {"JPEG"};
99      private float[] qualityVals = { 0.00F, 0.30F, 0.75F, 1.00F };
100     private String[] qualityDescs = {
101         "Low quality",       // 0.00 -> 0.30
102         "Medium quality",    // 0.30 -> 0.75
103         "Visually lossless"  // 0.75 -> 1.00
104     };
105 
106     /**
107      * Constructs a <code>JPEGImageWriteParam</code>.  Tiling is not
108      * supported.  Progressive encoding is supported. The default
109      * progressive mode is MODE_DISABLED.  A single form of compression,
110      * named "JPEG", is supported.  The default compression quality is
111      * 0.75.
112      *
113      * @param locale a <code>Locale</code> to be used by the
114      * superclass to localize compression type names and quality
115      * descriptions, or <code>null</code>.
116      */
117     public JPEGImageWriteParam(Locale locale) {
118         super(locale);
119         this.canWriteProgressive = true;
120         this.progressiveMode = MODE_DISABLED;
121         this.canWriteCompressed = true;
122         this.compressionTypes = compressionNames;
123         this.compressionType = compressionTypes[0];
124         this.compressionQuality = JPEG.DEFAULT_QUALITY;
125     }
126 
127     /**
128      * Removes any previous compression quality setting.
129      *
130      * <p> The default implementation resets the compression quality
131      * to <code>0.75F</code>.
132      *
133      * @exception IllegalStateException if the compression mode is not
134      * <code>MODE_EXPLICIT</code>.
135      */
136     public void unsetCompression() {
137         if (getCompressionMode() != MODE_EXPLICIT) {
138             throw new IllegalStateException
139                 ("Compression mode not MODE_EXPLICIT!");
140         }
141         this.compressionQuality = JPEG.DEFAULT_QUALITY;
142     }
143 
144     /**
145      * Returns <code>false</code> since the JPEG plug-in only supports
146      * lossy compression.
147      *
148      * @return <code>false</code>.
149      *
150      * @exception IllegalStateException if the compression mode is not
151      * <code>MODE_EXPLICIT</code>.
152      */
153     public boolean isCompressionLossless() {
154         if (getCompressionMode() != MODE_EXPLICIT) {
155             throw new IllegalStateException
156                 ("Compression mode not MODE_EXPLICIT!");
157         }
158         return false;
159     }
160 
161     public String[] getCompressionQualityDescriptions() {
162         if (getCompressionMode() != MODE_EXPLICIT) {
163             throw new IllegalStateException
164                 ("Compression mode not MODE_EXPLICIT!");
165         }
166         if ((getCompressionTypes() != null) &&
167             (getCompressionType() == null)) {
168             throw new IllegalStateException("No compression type set!");
169         }
170         return (String[])qualityDescs.clone();
171     }
172 
173     public float[] getCompressionQualityValues() {
174         if (getCompressionMode() != MODE_EXPLICIT) {
175             throw new IllegalStateException
176                 ("Compression mode not MODE_EXPLICIT!");
177         }
178         if ((getCompressionTypes() != null) &&
179             (getCompressionType() == null)) {
180             throw new IllegalStateException("No compression type set!");
181         }
182         return (float[])qualityVals.clone();
183     }
184     /**
185      * Returns <code>true</code> if tables are currently set.
186      *
187      * @return <code>true</code> if tables are present.
188      */
189     public boolean areTablesSet() {
190         return (qTables != null);
191     }
192 
193     /**
194      * Sets the quantization and Huffman tables to use in encoding
195      * abbreviated streams.  There may be a maximum of 4 tables of
196      * each type.  These tables are ignored if tables are specified in
197      * the metadata.  All arguments must be non-<code>null</code>.
198      * The two arrays of Huffman tables must have the same number of
199      * elements.  The table specifiers in the frame and scan headers
200      * in the metadata are assumed to be equivalent to indices into
201      * these arrays.  The argument arrays are copied by this method.
202      *
203      * @param qTables An array of quantization table objects.
204      * @param DCHuffmanTables An array of Huffman table objects.
205      * @param ACHuffmanTables An array of Huffman table objects.
206      *
207      * @exception IllegalArgumentException if any of the arguments
208      * is <code>null</code> or has more than 4 elements, or if the
209      * numbers of DC and AC tables differ.
210      *
211      * @see #unsetEncodeTables
212      */
213     public void setEncodeTables(JPEGQTable[] qTables,
214                                 JPEGHuffmanTable[] DCHuffmanTables,
215                                 JPEGHuffmanTable[] ACHuffmanTables) {
216         if ((qTables == null) ||
217             (DCHuffmanTables == null) ||
218             (ACHuffmanTables == null) ||
219             (qTables.length > 4) ||
220             (DCHuffmanTables.length > 4) ||
221             (ACHuffmanTables.length > 4) ||
222             (DCHuffmanTables.length != ACHuffmanTables.length)) {
223                 throw new IllegalArgumentException("Invalid JPEG table arrays");
224         }
225         this.qTables = (JPEGQTable[])qTables.clone();
226         this.DCHuffmanTables = (JPEGHuffmanTable[])DCHuffmanTables.clone();
227         this.ACHuffmanTables = (JPEGHuffmanTable[])ACHuffmanTables.clone();
228     }
229 
230     /**
231      * Removes any quantization and Huffman tables that are currently
232      * set.
233      *
234      * @see #setEncodeTables
235      */
236     public void unsetEncodeTables() {
237         this.qTables = null;
238         this.DCHuffmanTables = null;
239         this.ACHuffmanTables = null;
240     }
241 
242     /**
243      * Returns a copy of the array of quantization tables set on the
244      * most recent call to <code>setEncodeTables</code>, or
245      * <code>null</code> if tables are not currently set.
246      *
247      * @return an array of <code>JPEGQTable</code> objects, or
248      * <code>null</code>.
249      *
250      * @see #setEncodeTables
251      */
252     public JPEGQTable[] getQTables() {
253         return (qTables != null) ? (JPEGQTable[])qTables.clone() : null;
254     }
255 
256     /**
257      * Returns a copy of the array of DC Huffman tables set on the
258      * most recent call to <code>setEncodeTables</code>, or
259      * <code>null</code> if tables are not currently set.
260      *
261      * @return an array of <code>JPEGHuffmanTable</code> objects, or
262      * <code>null</code>.
263      *
264      * @see #setEncodeTables
265      */
266     public JPEGHuffmanTable[] getDCHuffmanTables() {
267         return (DCHuffmanTables != null)
268             ? (JPEGHuffmanTable[])DCHuffmanTables.clone()
269             : null;
270     }
271 
272     /**
273      * Returns a copy of the array of AC Huffman tables set on the
274      * most recent call to <code>setEncodeTables</code>, or
275      * <code>null</code> if tables are not currently set.
276      *
277      * @return an array of <code>JPEGHuffmanTable</code> objects, or
278      * <code>null</code>.
279      *
280      * @see #setEncodeTables
281      */
282     public JPEGHuffmanTable[] getACHuffmanTables() {
283         return (ACHuffmanTables != null)
284             ? (JPEGHuffmanTable[])ACHuffmanTables.clone()
285             : null;
286     }
287 
288     /**
289      * Tells the writer to generate optimized Huffman tables
290      * for the image as part of the writing process.  The
291      * default is <code>false</code>.  If this flag is set
292      * to <code>true</code>, it overrides any tables specified
293      * in the metadata.  Note that this means that any image
294      * written with this flag set to <code>true</code> will
295      * always contain Huffman tables.
296      *
297      * @param optimize A boolean indicating whether to generate
298      * optimized Huffman tables when writing.
299      *
300      * @see #getOptimizeHuffmanTables
301      */
302     public void setOptimizeHuffmanTables(boolean optimize) {
303         optimizeHuffman = optimize;
304     }
305 
306     /**
307      * Returns the value passed into the most recent call
308      * to <code>setOptimizeHuffmanTables</code>, or
309      * <code>false</code> if <code>setOptimizeHuffmanTables</code>
310      * has never been called.
311      *
312      * @return <code>true</code> if the writer will generate optimized
313      * Huffman tables.
314      *
315      * @see #setOptimizeHuffmanTables
316      */
317     public boolean getOptimizeHuffmanTables() {
318         return optimizeHuffman;
319     }
320 }