1 /*
2 * Copyright 2000-2005 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 <code>JPEGQTable</code>} and
70 * {@link JPEGHuffmanTable <code>JPEGHuffmanTable</code>} 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 <code>reset</code>}
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 }