001/* JPEGImageWriteParam.java --
002 Copyright (C)  2006  Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING.  If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library.  Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module.  An independent module is a module which is not derived from
033 or based on this library.  If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so.  If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039package javax.imageio.plugins.jpeg;
040
041import java.util.Locale;
042import java.util.PropertyResourceBundle;
043import java.util.ResourceBundle;
044import javax.imageio.ImageWriteParam;
045
046/**
047 * The JPEGImageWriteParam class can be used to specify tables and
048 * settings used in the JPEG encoding process.  Encoding tables are
049 * taken from the metadata associated with the output stream, and
050 * failing that (if the metadata tables are null) from an instance of
051 * JPEGImageWriteParam.  The default metadata uses the standard JPEG
052 * tables from the JPEGQTable and JPEGHuffmanTable classes.  Non-null
053 * metadata tables override JPEGImageWriteParam tables.  Compression
054 * settings range from 1.0, best compression, through 0.75, default
055 * compression, to 0.0, worst compression.
056 *
057 * A JPEGImageWriteParam instance is retrieved from the built-in JPEG
058 * ImageWriter using the getDefaultImageWriteParam method.
059 */
060public class JPEGImageWriteParam
061  extends ImageWriteParam
062{
063  private JPEGQTable[] qTables;
064  private JPEGHuffmanTable[] DCHuffmanTables;
065  private JPEGHuffmanTable[] ACHuffmanTables;
066  private boolean optimize;
067  private String[] compressionQualityDescriptions;
068  private float[] compressionQualityValues;
069
070  /**
071   * Localized messages are stored in separate files.
072   */
073  private ResourceBundle messages;
074
075  /**
076   * Construct a JPEGImageWriteParam with the following state: tiling
077   * is not supported, progressive mode is supported, initial
078   * progressive mode is MODE_DISABLED, compression is supported, one
079   * compression type named "JPEG" is supported and the default
080   * compression quality is 0.75f.  Compression type names and
081   * compression quality descriptions are localized to the given
082   * locale.
083   *
084   * @param locale the locale used for message localization
085   */
086  public JPEGImageWriteParam(Locale locale)
087  {
088    super(locale);
089
090    // Get localized compression type and compression quality
091    // description strings for the given locale.
092    messages = PropertyResourceBundle.getBundle
093      ("javax/imageio/plugins/jpeg/MessagesBundle", locale);
094
095    // Initialize inherited ImageWriter fields.
096    canWriteTiles = false;
097    canWriteProgressive = true;
098    progressiveMode = MODE_DISABLED;
099    canWriteCompressed = true;
100    compressionTypes = new String[]
101      {
102        messages.getString("compression.types.jpeg")
103      };
104    compressionType = compressionTypes[0];
105    compressionQuality = 0.75f;
106  }
107
108  /**
109   * Reset the compression quality to 0.75f.
110   */
111  public void unsetCompression()
112  {
113    compressionQuality = 0.75f;
114  }
115
116  /**
117   * Check if compression algorithm is lossless.  JPEGImageWriteParam
118   * overrides this ImageWriteParam method to always return false
119   * since JPEG compression is inherently lossy.
120   *
121   * @return false
122   */
123  public boolean isCompressionLossless()
124  {
125    return false;
126  }
127
128  /**
129   * Retrieve an array of compression quality descriptions.  These
130   * messages are localized using the locale provided upon
131   * construction.  Each compression quality description in the
132   * returned array corresponds to the compression quality value at
133   * the same index in the array returned by
134   * getCompressionQualityValues.
135   *
136   * @return an array of strings each of which describes a compression
137   * quality value
138   */
139  public String[] getCompressionQualityDescriptions()
140  {
141    // Make sure exceptions are thrown when this image write param is
142    // in the wrong state.
143    super.getCompressionQualityDescriptions();
144
145    if (compressionQualityDescriptions == null)
146      {
147        compressionQualityDescriptions = new String[]
148          {
149            messages.getString("compression.minimum"),
150            messages.getString("compression.default"),
151            messages.getString("compression.maximum")
152          };
153      }
154
155    return compressionQualityDescriptions;
156  }
157
158  /**
159   * Retrieve an array of compression quality values, ordered from
160   * lowest quality to highest quality.
161   *
162   * @return an array of compressions quality values
163   */
164  public float[] getCompressionQualityValues()
165  {
166    // Make sure exceptions are thrown when this image write param is
167    // in the wrong state.
168    super.getCompressionQualityValues();
169
170    if (compressionQualityValues == null)
171      compressionQualityValues = new float[] { 0.05f, 0.75f, 0.95f };
172
173    return compressionQualityValues;
174  }
175
176  /**
177   * Check if the encoding tables are set.
178   *
179   * @return true if the encoding tables are set, false otherwise
180   */
181  public boolean areTablesSet()
182  {
183    // If qTables is not null then all tables are set.
184    return (qTables != null);
185  }
186
187  /**
188   * Set the quantization and Huffman tables that will be used to
189   * encode the stream.  Copies are created of the array arguments.
190   * The number of Huffman tables must be the same in both Huffman
191   * table arrays.  No argument may be null and no array may be longer
192   * than four elements.
193   *
194   * @param qTables JPEG quantization tables
195   * @param DCHuffmanTables JPEG DC Huffman tables
196   * @param ACHuffmanTables JPEG AC Huffman tables
197   *
198   * @throws IllegalArgumentException if any argument is null, if any
199   * of the arrays are longer than four elements, or if the Huffman
200   * table arrays do not have the same number of elements
201   */
202  public void setEncodeTables(JPEGQTable[] qTables,
203                              JPEGHuffmanTable[] DCHuffmanTables,
204                              JPEGHuffmanTable[] ACHuffmanTables)
205  {
206    if (qTables == null || DCHuffmanTables == null || ACHuffmanTables == null)
207      throw new IllegalArgumentException("null argument");
208
209    if (qTables.length > 4 || DCHuffmanTables.length > 4
210        || ACHuffmanTables.length > 4)
211      throw new IllegalArgumentException("argument has too many elements");
212
213    if (DCHuffmanTables.length != ACHuffmanTables.length)
214      throw new IllegalArgumentException("Huffman table arrays differ in length");
215
216    // Do a shallow copy.  JPEGQTable's data is not directly
217    // modifyable since JPEGQTable.getTable returns a copy.  Therefore
218    // it is safe to have multiple references to a single JPEGQTable.
219    // Likewise for JPEGHuffmanTable.
220    this.qTables = (JPEGQTable[]) qTables.clone();
221    this.DCHuffmanTables = (JPEGHuffmanTable[]) DCHuffmanTables.clone();
222    this.ACHuffmanTables = (JPEGHuffmanTable[]) ACHuffmanTables.clone();
223  }
224
225  /**
226   * Clear the quantization and Huffman encoding tables.
227   */
228  public void unsetEncodeTables()
229  {
230    qTables = null;
231    DCHuffmanTables = null;
232    ACHuffmanTables = null;
233  }
234
235  /**
236   * Retrieve the quantization tables.
237   *
238   * @returns an array of JPEG quantization tables
239   */
240  public JPEGQTable[] getQTables()
241  {
242    return qTables == null ? qTables : (JPEGQTable[]) qTables.clone();
243  }
244
245  /**
246   * Retrieve the DC Huffman tables.
247   *
248   * @return an array of JPEG DC Huffman tables
249   */
250  public JPEGHuffmanTable[] getDCHuffmanTables()
251  {
252    return DCHuffmanTables == null ? DCHuffmanTables
253      : (JPEGHuffmanTable[]) DCHuffmanTables.clone();
254  }
255
256  /**
257   * Retrieve the AC Huffman tables.
258   *
259   * @return an array of JPEG AC Huffman tables
260   */
261  public JPEGHuffmanTable[] getACHuffmanTables()
262  {
263    return ACHuffmanTables == null ? ACHuffmanTables
264      : (JPEGHuffmanTable[]) ACHuffmanTables.clone();
265  }
266
267  /**
268   * Specify whether or not Huffman tables written to the output
269   * stream should be optimized.  Every image encoded with this flag
270   * set will contain a Huffman table, and the generated Huffman
271   * tables will override those specified in the metadata.
272   *
273   * @param optimize true to generate optimized Huffman tables, false
274   * otherwise
275   */
276  public void setOptimizeHuffmanTables(boolean optimize)
277  {
278    this.optimize = optimize;
279  }
280
281  /**
282   * Check whether or not Huffman tables written to the output stream
283   * will be optimized.  Unless otherwise set using
284   * setOptimizeHuffmanTables, this returns false.
285   *
286   * @return true Huffman tables written to the output stream will be
287   * optimized, false otherwise
288   */
289  public boolean getOptimizeHuffmanTables()
290  {
291    return optimize;
292  }
293}