001: /*
002: * $RCSfile: JPEGTileCodecDescriptor.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:54 $
010: * $State: Exp $
011: */package javax.media.jai.tilecodec;
012:
013: import java.awt.image.SampleModel; /*
014: import java.util.Collections;
015: import java.util.HashSet;
016: import java.util.Set;
017: */
018: import javax.media.jai.PropertyGenerator;
019: import javax.media.jai.ParameterListDescriptor;
020: import javax.media.jai.ParameterListDescriptorImpl;
021: import javax.media.jai.util.Range;
022:
023: /**
024: * This class is the descriptor for the "JPEG" tile codec. "jpeg" is a
025: * lossy tile codec, which involves compressing the tile data using the
026: * jpeg standard. The format name for the jpeg tile codec is "jpeg".
027: * The encoded stream contains the <code>SampleModel</code> and the
028: * tile's upper left corner position, thus the
029: * <code>includesSampleModelInfo()</code> and
030: * <code>includesLocationInfo()</code> methods in this descriptor return
031: * true.
032: *
033: * <p> This JPEG tile codec works well only on byte type images with 1,
034: * 3 or 4 bands.
035: *
036: * <p> While both the "tileDecoder" and "tileEncoder" registry modes for
037: * the "jpeg" tile codec scheme have the same set of parameters, the
038: * parameters for the "tileDecoder" mode are read-only and will be
039: * ignored if values are set for them in the
040: * <code>TileCodecParameterList</code> passed to the <code>TileDecoder</code>.
041: * The reason for this is that the parameter values needed to control
042: * the decoding process are included in the encoded stream, therefore
043: * parameter values specified externally are ignored. Once the decoding
044: * is completed, the parameter values in the
045: * <code>TileCodecParameterList</code> are updated to reflect the values
046: * specified in the encoded stream.
047: *
048: * <p><table border=1>
049: * <caption>Resource List</caption>
050: * <tr><th>Name</th> <th>Value</th></tr>
051: * <tr><td>Vendor</td> <td>com.sun.media.jai</td></tr>
052: * <tr><td>Description</td> <td>A descriptor to describe the lossy "jpeg" codec
053: * scheme. </td></tr>
054: * <tr><td>DocURL</td> <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/tilecodec/JPEGTileCodecDescriptor.html</td></tr>
055: * <tr><td>Version</td> <td>1.2</td></tr>
056: * <tr><td>quality</td> <td>
057: * <p>A factor that relates to the desired tradeoff
058: * between image quality and the image data
059: * compression ratio. The range of this parameter
060: * is from 0.0 to 1.0. A setting of 1.0 produces the
061: * highest quality image at a lower compression
062: * rate. A setting of 0.0 produces the highest
063: * compression ratio, with a sacrifice to image
064: * quality. The default value is 0.75. <p>In JPEG,
065: * compression, there are two ways to control the
066: * quantization quality: one is define quantization
067: * table for each image component; the other is
068: * define the quality. The later overrides the
069: * former. If neither the quality nor quantization
070: * tables are set, the default setting is used.
071: * <p>When the quality is set, a group of
072: * quantization tables are generated by rescaling
073: * the default quantization tables. For more
074: * infomation, please refer to the links below.
075: * </td></tr>
076: * <tr><td>qualitySet</td> <td>A boolean used to indicate that the
077: * parameter <code>quality</code> is set or
078: * not.</td></tr>
079: * <tr><td>horizontalSubsampling</td> <td>The subsampling rate in the
080: * horizontal direction applied to each image
081: * component to reduce their resolution
082: * prior to encoding.</td></tr>
083: * <tr><td>verticalSubsampling</td> <td>The subsampling rate in the
084: * vertical direction applied to each image
085: * component to reduce their resolution
086: * prior to encoding.</td></tr>
087: * <tr><td>quantizationTableMapping</td> <td>In JPEG compression, several
088: * image components may share one quantization
089: * table. This is the mapping between the
090: * image component and the quantization
091: * tables.</td></tr>
092: * <tr><td>quantizationTable0</td> <td>A quantization table for JPEG codec
093: * is an array of 64 (8x8) expressed in zig-zag
094: * order (see the JPEG spec section K.1). Since
095: * this descriptor defines a JPEG scheme where the
096: * tile has at most 4 components, so at most 4
097: * quantization tables will
098: * be used. This parameter is the first
099: * quantization table. </td></tr>
100: * <tr><td>quantizationTable1</td> <td>The second quantization table.</td></tr>
101: * <tr><td>quantizationTable2</td> <td>The third quantization table.</td></tr>
102: * <tr><td>quantizationTable3</td> <td>The fourth quantization table.</td></tr>
103: * <tr><td>restartInterval</td> <td>JPEG images use restart markers to define
104: * multiple strips or tiles. The restart markers
105: * are inserted periodically into the image data
106: * to delineate image segments known as restart
107: * intervals. To limit the effect of bitstream
108: * errors to a single restart interval, JAI
109: * provides methods to set the restart interval
110: * in JPEG Minimum Coded Units (MCUs).
111: * The default is zero (no restart interval
112: * markers). </td></tr>
113: * <tr><td>writeImageInfo</td> <td>A boolean instructs the encoder to
114: * write the image data to the output
115: * stream. </td></tr>
116: * <tr><td>writeTableInfo</td> <td>A boolean instructs the encoder to
117: * write the table data to the output
118: * stream.</td></tr>
119: * <tr><td>writeJFIFHeader</td> <td>The JPEG File Interchange Format (JFIF)
120: * is a minimal file format that enables JPEG
121: * bitstreams to be exchanged between a wide
122: * variety of platforms and applications.
123: * The parameter instructs the encoder to write
124: * the output stream in the JFIF format.</td></tr>
125: * </table></p>
126: *
127: * <p><table border=1>
128: * <caption>Parameter List</caption>
129: * <tr><th>Name</th> <th>Class Type</th>
130: * <th>Default Value</th></tr>
131: * <tr><td>quality</td> <td>java.lang.Float</td>
132: * <td>0.75</td>
133: * <tr><td>qualitySet</td> <td>java.lang.Boolean</td>
134: * <td>true</td>
135: * <tr><td>horizontalSubsampling</td> <td>integer array</td>
136: * <td>{1,1,1}</td>
137: * <tr><td>verticalSubsampling</td> <td>integer array</td>
138: * <td>{1,1,1}</td>
139: * <tr><td>quantizationTableMapping</td> <td>integer array</td>
140: * <td>{0,1,1}</td>
141: * <tr><td>quantizationTable0</td> <td>integer array</td>
142: * <td>The default Luminance table as defined in
143: * section K.1 of the JPEG specification.</td>
144: * <tr><td>quantizationTable1</td> <td>integer array</td>
145: * <td>The default Chrominance table as defined in
146: * section K.1 of the JPEG specification.</td>
147: * <tr><td>quantizationTable2</td> <td>integer array</td>
148: * <td>The default Chrominance table as defined in
149: * section K.1 of the JPEG specification.</td>
150: * <tr><td>quantizationTable3</td> <td>integer array</td>
151: * <td>The default Chrominance table as defined in
152: * section K.1 of the JPEG specification.</td>
153: * <tr><td>restartInterval</td> <td>java.lang.Integer</td>
154: * <td>0</td>
155: * <tr><td>writeImageInfo</td> <td>java.lang.Boolean</td>
156: * <td>true</td>
157: * <tr><td>writeTableInfo</td> <td>java.lang.Boolean</td>
158: * <td>true</td>
159: * <tr><td>writeJFIFHeader</td> <td>java.lang.Boolean</td>
160: * <td>false</td>
161: * </table></p>
162: *
163: * @see com.sun.image.codec.jpeg.JPEGQTable
164: * @see com.sun.image.codec.jpeg.JPEGDecodeParam
165: * @see com.sun.image.codec.jpeg.JPEGEncodeParam
166: *
167: * @since JAI 1.1
168: */
169: public class JPEGTileCodecDescriptor extends TileCodecDescriptorImpl {
170:
171: private static final int[] lumQuantizationTable = { 16, 11, 12, 14,
172: 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, 40, 26, 24, 22,
173: 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, 57, 51, 56, 55,
174: 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, 109, 81, 87,
175: 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, 100, 120, 92,
176: 101, 103, 99 };
177:
178: private static final int[] chromQuantizationTable = { 17, 18, 18,
179: 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, 99, 99, 99, 99,
180: 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
181: 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
182: 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
183: 99 };
184:
185: // Parameter names
186: private static final String[] paramNames = { "quality",
187: "qualitySet", "horizontalSubsampling",
188: "verticalSubsampling", "quantizationTableMapping",
189: "quantizationTable0", "quantizationTable1",
190: "quantizationTable2", "quantizationTable3",
191: "restartInterval", "writeImageInfo", "writeTableInfo",
192: "writeJFIFHeader" };
193:
194: // Parameter class names
195: private static final Class[] paramClasses = {
196: java.lang.Float.class, java.lang.Boolean.class,
197: int[].class, int[].class, int[].class, int[].class,
198: int[].class, int[].class, int[].class,
199: java.lang.Integer.class, java.lang.Boolean.class,
200: java.lang.Boolean.class, java.lang.Boolean.class };
201:
202: // Parameter default values.
203: private static final Object[] paramDefaults = { new Float(0.75f),
204: new Boolean(true), new int[] { 1, 1, 1 },
205: new int[] { 1, 1, 1 }, new int[] { 0, 1, 1 },
206: lumQuantizationTable, chromQuantizationTable,
207: chromQuantizationTable, chromQuantizationTable,
208: new Integer(0), new Boolean(true), new Boolean(true),
209: new Boolean(false) };
210:
211: /* XXX: If Set can be specified for non EnumeratedParameter types.
212: // Valid values for the boolean valued parameters
213: private static java.util.Set set =
214: Collections.synchronizedSet(new HashSet());
215:
216: static {
217: set.add(Boolean.TRUE);
218: set.add(Boolean.FALSE);
219: }
220: */
221:
222: // Parameters' valid value ranges
223: private static final Object[] validParamValues = {
224: new Range(java.lang.Float.class, new Float(0.0f),
225: new Float(1.0f)),
226: null, // set,
227: null, null, null, null, null, null, null,
228: new Range(java.lang.Integer.class, new Integer(0), null),
229: null, // set,
230: null, // set,
231: null // set
232: };
233:
234: private static ParameterListDescriptor paramListDescriptor = new ParameterListDescriptorImpl(
235: null, paramNames, paramClasses, paramDefaults,
236: validParamValues);
237:
238: /**
239: * Creates a <code>JPEGTileCodecDescriptor</code>
240: */
241: public JPEGTileCodecDescriptor() {
242: super ("jpeg", true, true);
243: }
244:
245: /**
246: * Returns a <code>TileCodecParameterList</code> valid for the
247: * specified modeName and compatible with the supplied
248: * <code>TileCodecParameterList</code>. For example, given a
249: * <code>TileCodecParameterList</code> used to encode a tile with
250: * the modeName being specified as "tileDecoder", this method will return
251: * a <code>TileCodecParameterList</code> sufficient to decode that
252: * same tile.
253: *
254: * <p> If the supplied modeName is one of the valid mode names as
255: * ascertained from the <code>getSupportedNames()</code> method,
256: * this method returns a <code>TileCodecParameterList</code> that
257: * contains values that are compatible for the supplied mode name.
258: *
259: * @param modeName The registry mode to return a valid parameter
260: * list for.
261: * @param otherParamList The parameter list for which a compatible
262: * parameter list for the complementary modeName is
263: * to be found.
264: *
265: * @throws IllegalArgumentException if <code>modeName</code> is null.
266: * @throws IllegalArgumentException if <code>modeName</code> is not
267: * one of the modes valid for this descriptor, i.e those returned
268: * from the getSupportedNames() method.
269: * @throws IllegalArgumentException if <code>otherParamList</code> is null.
270: */
271: public TileCodecParameterList getCompatibleParameters(
272: String modeName, TileCodecParameterList otherParamList) {
273: if (modeName == null) {
274: throw new IllegalArgumentException(JaiI18N
275: .getString("TileCodecDescriptorImpl1"));
276: }
277:
278: if (otherParamList == null) {
279: throw new IllegalArgumentException(JaiI18N
280: .getString("TileCodecDescriptorImpl3"));
281: }
282:
283: String name = getName();
284: if (!otherParamList.getFormatName().equals(name)) {
285: throw new IllegalArgumentException(JaiI18N
286: .getString("TileCodec2"));
287: }
288:
289: if (otherParamList.isValidForMode(modeName))
290: return otherParamList;
291:
292: if (modeName.equalsIgnoreCase("tileDecoder")) {
293: return new TileCodecParameterList(name,
294: new String[] { "tileDecoder" }, otherParamList
295: .getParameterListDescriptor());
296: } else if (modeName.equalsIgnoreCase("tileEncoder")) {
297: return new TileCodecParameterList(name,
298: new String[] { "tileEncoder" }, otherParamList
299: .getParameterListDescriptor());
300: } else {
301: throw new IllegalArgumentException(JaiI18N
302: .getString("TileCodec1"));
303: }
304: }
305:
306: /**
307: * Returns the default parameters for the specified modeName as an
308: * instance of the <code>TileCodecParameterList</code>. If the supplied
309: * modeName is one of the valid mode names as ascertained from the
310: * <code>getSupportedNames()</code> method, this method returns the
311: * default parameters for that mode.
312: *
313: * @param modeName The mode to return the default parameters for.
314: *
315: * @throws IllegalArgumentException if <code>modeName</code> is null.
316: * @throws IllegalArgumentException if <code>modeName</code> is not
317: * one of the modes valid for this descriptor, i.e those returned
318: * from the getSupportedNames() method.
319: */
320: public TileCodecParameterList getDefaultParameters(String modeName) {
321: if (modeName == null)
322: throw new IllegalArgumentException(JaiI18N
323: .getString("TileCodecDescriptorImpl1"));
324:
325: String validNames[] = getSupportedModes();
326: boolean valid = false;
327:
328: for (int i = 0; i < validNames.length; i++) {
329: if (modeName.equalsIgnoreCase(validNames[i])) {
330: valid = true;
331: break;
332: }
333: }
334:
335: if (valid == false) {
336: throw new IllegalArgumentException(JaiI18N
337: .getString("TileCodec1"));
338: }
339:
340: return new TileCodecParameterList("jpeg", new String[] {
341: "tileDecoder", "tileEncoder" }, paramListDescriptor);
342: }
343:
344: /**
345: * Returns the default parameters for the specified modeName as an
346: * instance of the <code>TileCodecParameterList</code>, adding a
347: * "sampleModel" parameter with the specified value to the parameter
348: * list. If the supplied modeName is one of the valid mode names as
349: * ascertained from the <code>getSupportedNames()</code> method, this
350: * method returns the default parameters for that mode.
351: *
352: * <p> This method should be used when includesSampleModelInfo()
353: * returns false. If includesSampleModelInfo() returns true, the
354: * supplied <code>SampleModel</code> is ignored.
355: *
356: * <p>For the JPEG codec, includesSampleModelInfo() returns true, so
357: * the supplied <code> SampleModel</code> is ignored.
358: *
359: * @param modeName The mode to return the default parameters for.
360: * @param sm The <code>SampleModel</code> used to create the
361: * default decoding parameter list.
362: *
363: * @throws IllegalArgumentException if <code>modeName</code> is null.
364: * @throws IllegalArgumentException if <code>modeName</code> is not
365: * one of the modes valid for this descriptor, i.e those returned
366: * from the getSupportedNames() method.
367: */
368: public TileCodecParameterList getDefaultParameters(String modeName,
369: SampleModel sm) {
370: return getDefaultParameters(modeName);
371: }
372:
373: /**
374: * Returns the <code>ParameterListDescriptor</code> that describes
375: * the associated parameters (NOT sources). If the supplied modeName
376: * is one of the valid mode names as ascertained from the
377: * <code>getSupportedNames()</code> method, this method returns a
378: * non-null <code>ParameterListDescriptor</code> with the appropriate
379: * parameters.
380: *
381: * @param modeName The mode to return the ParameterListDescriptor for.
382: *
383: * @throws IllegalArgumentException if <code>modeName</code> is null.
384: * @throws IllegalArgumentException if <code>modeName</code> is not
385: * one of the modes valid for this descriptor, i.e those returned
386: * from the getSupportedNames() method.
387: */
388: public ParameterListDescriptor getParameterListDescriptor(
389: String modeName) {
390: if (modeName == null)
391: throw new IllegalArgumentException(JaiI18N
392: .getString("TileCodecDescriptorImpl1"));
393:
394: String validNames[] = getSupportedModes();
395: boolean valid = false;
396:
397: for (int i = 0; i < validNames.length; i++) {
398: if (modeName.equalsIgnoreCase(validNames[i])) {
399: valid = true;
400: break;
401: }
402: }
403:
404: if (valid == false) {
405: throw new IllegalArgumentException(JaiI18N
406: .getString("TileCodec1"));
407: }
408:
409: return paramListDescriptor;
410: }
411: }
|