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