1 /*
   2  * Copyright (c) 2000, 2014, 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 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 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 = qTables.clone();
 226         this.DCHuffmanTables = DCHuffmanTables.clone();
 227         this.ACHuffmanTables = 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) ? 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             ? 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             ? 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 }