0001: /*
0002: * $RCSfile: ImageWriteDescriptor.java,v $
0003: *
0004: *
0005: * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
0006: *
0007: * Redistribution and use in source and binary forms, with or without
0008: * modification, are permitted provided that the following conditions
0009: * are met:
0010: *
0011: * - Redistribution of source code must retain the above copyright
0012: * notice, this list of conditions and the following disclaimer.
0013: *
0014: * - Redistribution in binary form must reproduce the above copyright
0015: * notice, this list of conditions and the following disclaimer in
0016: * the documentation and/or other materials provided with the
0017: * distribution.
0018: *
0019: * Neither the name of Sun Microsystems, Inc. or the names of
0020: * contributors may be used to endorse or promote products derived
0021: * from this software without specific prior written permission.
0022: *
0023: * This software is provided "AS IS," without a warranty of any
0024: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0025: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0026: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0027: * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0028: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0029: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0030: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0031: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0032: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0033: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0034: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0035: * POSSIBILITY OF SUCH DAMAGES.
0036: *
0037: * You acknowledge that this software is not designed or intended for
0038: * use in the design, construction, operation or maintenance of any
0039: * nuclear facility.
0040: *
0041: * $Revision: 1.1 $
0042: * $Date: 2005/02/11 05:01:56 $
0043: * $State: Exp $
0044: */
0045: package com.sun.media.jai.operator;
0046:
0047: import java.awt.Dimension;
0048: import java.awt.RenderingHints;
0049: import java.awt.image.BufferedImage;
0050: import java.awt.image.RenderedImage;
0051: import java.awt.image.renderable.ParameterBlock;
0052: import java.awt.image.renderable.RenderableImage;
0053: import java.io.File;
0054: import java.io.IOException;
0055: import java.net.Socket;
0056: import java.util.Collection;
0057: import java.util.EventListener;
0058: import java.util.Iterator;
0059: import java.util.Locale;
0060: import javax.imageio.ImageIO;
0061: import javax.imageio.ImageWriteParam;
0062: import javax.imageio.ImageWriter;
0063: import javax.imageio.metadata.IIOMetadata;
0064: import javax.imageio.spi.ImageWriterSpi;
0065: import javax.imageio.stream.ImageOutputStream;
0066: import javax.media.jai.CollectionOp;
0067: import javax.media.jai.JAI;
0068: import javax.media.jai.OperationDescriptorImpl;
0069: import javax.media.jai.RenderableOp;
0070: import javax.media.jai.RenderedOp;
0071: import javax.media.jai.registry.CollectionRegistryMode;
0072: import javax.media.jai.registry.RenderableRegistryMode;
0073: import javax.media.jai.registry.RenderedRegistryMode;
0074:
0075: /**
0076: * An <code>OperationDescriptor</code> describing the "ImageWrite" operation.
0077: *
0078: * <p>The "ImageWrite" operation uses the
0079: * <a href="http://java.sun.com/j2se/1.4/docs/guide/imageio/index.html">Java
0080: * Image I/O Framework</a> to write images to an output destination. Which
0081: * formats may be written depends on which {@link javax.imageio.ImageWriter}
0082: * plug-ins are registered with the Image I/O Framework when the operation is
0083: * invoked.</p>
0084: *
0085: * <p>The output destination will usually be an
0086: * {@link javax.imageio.stream.ImageOutputStream}, but may be a
0087: * {@link java.io.File}, {@link java.io.RandomAccessFile},
0088: * {@link java.io.OutputStream}, {@link java.net.Socket},
0089: * {@link java.nio.channels.WritableByteChannel}, file path represented as a
0090: * <code>String</code> or some other type compatible with a writer plug-in. The
0091: * {@link javax.imageio.ImageIO} class should be used to specify the location
0092: * and enable the use of cache files via its <code>setCacheDirectory()</code>
0093: * and <code>setUseCache()</code> methods, respectively. Note that this cache
0094: * pertains to image stream caching and is unrelated to the JAI
0095: * <code>TileCache</code>. If an {@link javax.imageio.stream.ImageOutputStream}
0096: * is created internally by the operation, for example from a
0097: * {@link java.io.File}-valued <a href="#ParamOutput">Output</a> parameter,
0098: * then it will be flushed automatically if and only if the operation is not
0099: * in <a href="#CollectionMode">collection mode</a> and pixel replacement is
0100: * not occurring.</p>
0101: *
0102: * <p>The "ImageWrite" operation supports <a href="#RenderedMode">rendered</a>,
0103: * <a href="#RenderableMode">renderable</a>, and
0104: * <a href="#CollectionMode">collection</a> modes and requires a single
0105: * source. The operation is "immediate" for all modes as specified by
0106: * <code>OperationDescriptor.isImmediate()</code> so that
0107: * {@link #isImmediate()} returns <code>true</code>. The operation will
0108: * therefore be rendered when created via either <code>JAI.create[NS]()</code>
0109: * or <code>JAI.createCollection[NS]()</code>.
0110: * A {@link java.awt.RenderingHints} object supplied when the
0111: * operation is created will have no effect except with respect to the
0112: * mapping of <code>JAI.KEY_INTERPOLATION</code> and then only in renderable
0113: * mode.</p>
0114: *
0115: * <p>Image properties are used to pass metadata and other information to the
0116: * writer plug-in and to make available metadata as actually written to the
0117: * output destination. Property handling is mode-specific.</p>
0118: *
0119: * <p><table border=1>
0120: * <caption><b>Resource List</b></caption>
0121: * <tr><th>Name</th> <th>Value</th></tr>
0122: * <tr><td>GlobalName</td> <td>ImageWrite</td></tr>
0123: * <tr><td>LocalName</td> <td>ImageWrite</td></tr>
0124: * <tr><td>Vendor</td> <td>com.sun.media.jai</td></tr>
0125: * <tr><td>Description</td> <td>Writes an image using the Java Image I/O Framework.</td></tr>
0126: * <tr><td>DocURL</td> <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-imageio-1_0-rc-docs/com/sun/media/jai/operator/ImageWriteDescriptor.html</td></tr>
0127: * <tr><td>Version</td> <td>1.0</td></tr>
0128: * <tr><td>arg0Desc</td> <td>The output destination.</td></tr>
0129: * <tr><td>arg1Desc</td> <td>The format name of the output.</td></tr>
0130: * <tr><td>arg2Desc</td> <td>Whether to use image metadata properties as fallbacks.</td></tr>
0131: * <tr><td>arg3Desc</td> <td>Whether to transcode metadata before writing.</td></tr>
0132: * <tr><td>arg4Desc</td> <td>Whether to verify the validity of the output destination.</td></tr>
0133: * <tr><td>arg5Desc</td> <td>Whether to allow pixel replacement in the output image.</td></tr>
0134: * <tr><td>arg6Desc</td> <td>The tile size of the output image.</td></tr>
0135: * <tr><td>arg7Desc</td> <td>Stream metadata to write to the output.</td></tr>
0136: * <tr><td>arg8Desc</td> <td>Image metadata to write to the output.</td></tr>
0137: * <tr><td>arg9Desc</td> <td>Thumbnails to write to the output.</td></tr>
0138: * <tr><td>arg10Desc</td> <td>EventListeners to be registered with the ImageWriter.</td></tr>
0139: * <tr><td>arg11Desc</td> <td>The Locale for the ImageWriter to use.</td></tr>
0140: * <tr><td>arg12Desc</td> <td>Java Image I/O write parameter instance.</td></tr>
0141: * <tr><td>arg13Desc</td> <td>Java Image I/O writer instance.</td></tr>
0142: * </table></p>
0143: *
0144: * <h2><a name="RenderedMode"</a>Rendered Mode</h2>
0145: *
0146: * In rendered mode the "ImageWrite" operation writes a
0147: * {@link java.awt.image.RenderedImage} to the specified output destination.
0148: *
0149: * <h3><a name="RenderedModeParameters"</a>Rendered Mode Parameters</h3>
0150: *
0151: * The parameter list of the "ImageWrite" operation in rendered mode is
0152: * as in the following table.
0153: *
0154: * <p><table border=1>
0155: * <caption><b>Rendered Mode Parameter List</b></caption>
0156: * <tr><th>Name</th> <th>Class Type</th>
0157: * <th>Default Value</th></tr>
0158: * <tr><td><a href="#ParamOutput">
0159: * Output</a></td> <td>java.lang.Object.class</td>
0160: * <td>NO_PARAMETER_DEFAULT</td>
0161: * <tr><td><a href="#ParamFormat">
0162: * Format</a></td> <td>java.lang.String</td>
0163: * <td>null</td>
0164: * <tr><td><a href="#ParamUseProperties">
0165: * UseProperties</a></td> <td>java.lang.Boolean</td>
0166: * <td>TRUE</td>
0167: * <tr><td><a href="#ParamTranscode">
0168: * Transcode</a></td> <td>java.lang.Boolean</td>
0169: * <td>TRUE</td>
0170: * <tr><td><a href="#ParamVerifyOutput">
0171: * VerifyOutput</a></td> <td>java.lang.Boolean</td>
0172: * <td>TRUE</td>
0173: * <tr><td><a href="#ParamAllowPixelReplacement">
0174: * AllowPixelReplacement</a></td> <td>java.lang.Boolean</td>
0175: * <td>FALSE</td>
0176: * <tr><td><a href="#ParamTileSize">
0177: * TileSize</a></td> <td>java.awt.Dimension</td>
0178: * <td>null</td>
0179: * <tr><td><a href="#ParamStreamMetadata">
0180: * StreamMetadata</a></td> <td>javax.imageio.metadata.IIOMetadata</td>
0181: * <td>null</td>
0182: * <tr><td><a href="#ParamImageMetadata">
0183: * ImageMetadata</a></td> <td>javax.imageio.metadata.IIOMetadata</td>
0184: * <td>null</td>
0185: * <tr><td><a href="#ParamThumbnails">
0186: * Thumbnails</a></td> <td>java.awt.BufferedImage[]</td>
0187: * <td>null</td>
0188: * <tr><td><a href="#ParamListeners">
0189: * Listeners</a></td> <td>java.util.EventListener[]</td>
0190: * <td>null</td>
0191: * <tr><td><a href="#ParamLocale">
0192: * Locale</a></td> <td>java.util.Locale</td>
0193: * <td>null</td>
0194: * <tr><td><a href="#ParamWriteParam">
0195: * WriteParam</a></td> <td>javax.imageio.ImageWriteParam</td>
0196: * <td>null</td>
0197: * <tr><td><a href="#ParamWriter">
0198: * Writer</a></td> <td>javax.imageio.ImageWriter</td>
0199: * <td>null</td>
0200: * </table></p>
0201: *
0202: * <p>The rendered mode parameters are handled as follows:
0203: *
0204: * <ul>
0205: * <p><li><a name="ParamOutput"</a>
0206: * If Output is a String it is assumed to represent a file path.
0207: * </li></p>
0208: * <p><li><a name="ParamFormat"</a>
0209: * Format will be used to obtain an ImageWriter if one is not supplied. If
0210: * this parameter is null and Writer is non-null and has an originating
0211: * ImageWriterSpi, then the first format name listed by that provider will be
0212: * used. If Writer is null and Output is a File or a String, an attempt will
0213: * be made to derive the format name from the suffix of the file path. If
0214: * this fails, then the format will default to "PNG" as this is the most
0215: * versatile writer plug-in in the Java 2 core.
0216: * </li></p>
0217: * <p><li><a name="ParamUseProperties"</a>
0218: * If UseProperties is TRUE, then if stream or image metadata or thumbnails
0219: * are not provided as parameters, an attempt will be made to derive them
0220: * from the source image using the respective image properties previously
0221: * described for the "ImageRead" operation.
0222: * </li></p>
0223: * <p><li><a name="ParamTranscode"</a>
0224: * If Transcode is TRUE, then any stream or metadata derived either from
0225: * operation parameters or source image properties will be converted using
0226: * the ImageWriter's implementation of ImageTranscoder.
0227: * </li></p>
0228: * <p><li><a name="ParamVerifyOutput"</a>
0229: * If VerifyOutput is TRUE, then if the Output is a File or a String it will
0230: * be verified that a file at the specified location may either be overwritten
0231: * or created. If Output is a Socket, it will be verified that it is bound,
0232: * connected, not closed, and its write-half is not shut down. If any of
0233: * these checks fails, an exception will be thrown when the operation is
0234: * created. This parameter is ignored for other output types.
0235: * </li></p>
0236: * <p><li><a name="ParamAllowPixelReplacement"</a>
0237: * If AllowPixelReplacement is TRUE, and the ImageWriter supports pixel
0238: * replacement, then a construct will be enabled to allow "live" updating
0239: * of the output in response to RenderingChangeEvents or "InvalidRegion"
0240: * events.
0241: * </li></p>
0242: * <p><li><a name="ParamTileSize"</a>
0243: * TileSize specifies the desired tile size; it is used as defined by the
0244: * <a href="#RenderedModeTiling">tiling algorithm</a>. This parameter is
0245: * ignored if the ImageWriter does not support tiling.
0246: * Regardless of the capabilities of the writer, an exception will be thrown
0247: * when the operation is created if this parameter is non-null and either
0248: * its width or height is not positive.
0249: * </li></p>
0250: * <p><li><a name="ParamStreamMetadata"</a>
0251: * If StreamMetadata is non-null, then the parameter will take priority over
0252: * the corresponding image property as the source of stream metadata to
0253: * be written.
0254: * </li></p>
0255: * <p><li><a name="ParamImageMetadata"</a>
0256: * If ImageMetadata is non-null, then the parameter will take priority over
0257: * the corresponding image property as the source of image metadata to
0258: * be written.
0259: * </li></p>
0260: * <p><li><a name="ParamThumbnails"</a>
0261: * If Thumbnails is non-null, then the parameter will take priority over
0262: * the corresponding image property as the source of image thumbnails to
0263: * be written.
0264: * </li></p>
0265: * <p><li><a name="ParamListeners"</a>
0266: * Listeners will be used to set any progress or warning listeners of the
0267: * ImageWriter. Each element in the java.util.EventListener array will be
0268: * added for all types of listener it implements. For example if a listener
0269: * object implements all of the javax.imageio.event.IIOWrite*Listeners
0270: * interfaces then it will be added as a listener of each of the three types.
0271: * Any elements in the array which do not implement any of the
0272: * IIOWrite*Listeners will be ignored.
0273: * </li></p>
0274: * <p><li><a name="ParamLocale"</a>
0275: * Locale will be used to set the Locale of the ImageWriter. This parameter
0276: * overrides the equivalent setting of the Writer parameter if the latter is
0277: * also supplied.
0278: * </li></p>
0279: * <p><li><a name="ParamWriteParam"</a>
0280: * If WriteParam is null, an ImageWriteParam will be derived internally using
0281: * ImageWriter.getDefaultWriteParam().
0282: * </li></p>
0283: * <p><li><a name="ParamWriter"</a>
0284: * If Writer is null, an attempt will be made to find an ImageWriter capable
0285: * of writing the image. If this attempt to obtain an ImageWriter fails, an
0286: * exception will be thrown.
0287: * </li></p>
0288: * </ul>
0289: * </p>
0290: *
0291: * <h4><a name="SyncPolicy"</a>Parameters and Synchronization Policy</h4>
0292: *
0293: * Similarly to the case of any ImageReadParam or ImageReader supplied to the
0294: * "ImageRead" operation, any ImageWriteParam or ImageWriter supplied to the
0295: * "ImageWrite" operation is subject to modification within the operation
0296: * classes. A policy similar to the
0297: * <a href="./ImageReadDescriptor.html#SyncPolicy">"ImageRead"
0298: * synchronization policy</a> therefore applies as well for "ImageWrite".
0299: *
0300: * <p>In the Sun Microsystems implementation of this operation these potential
0301: * conflicts have been mitigated to a certain extent:
0302: *
0303: * <ul>
0304: * <p><li>
0305: * If the param is cloneable then it is cloned and the clone used internally.
0306: * Otherwise if the param is an instance of ImageWriteParam itself rather than
0307: * of a subclass thereof, i.e., getClass().getName() invoked on the param
0308: * returns "javax.imageio.ImageWriteParam", then a new ImageWriteParam is
0309: * constructed and the settings of the original param copied to it. If the
0310: * param is not cloneable and is an instance of a proper subclass of
0311: * ImageWriteParam then it is used directly.</li></p>
0312: *
0313: * <p><li>
0314: * The only ImageWriter methods invoked after rendering are
0315: * prepareReplacePixels(int,Rectangle), replacePixels(Raster,ImageWriteParam),
0316: * and endReplacePixels() and these are invoked within a method synchronized
0317: * on the ImageWriter object.</li></p>
0318: * </ul>
0319: * </p>
0320: *
0321: * <h3><a name="RenderedModeTiling"</a>Tiling</h3>
0322: *
0323: * The following algorithm is used to determine the tile size of the
0324: * image written to the output destination:
0325: *
0326: * <pre>
0327: * if ImageWriter cannot write tiles
0328: * output is untiled
0329: * else
0330: * if TileSize parameter is non-null
0331: * set tile size to TileSize
0332: * else
0333: * if WriteParam is null
0334: * set tile size to source tile size
0335: * else
0336: * if tilingMode is ImageWriteParam.MODE_EXPLICIT
0337: * if tile dimension is set in WriteParam
0338: * set tile size to tile dimension from WriteParam
0339: * else
0340: * if preferred tile dimension is set in WriteParam
0341: * set tile size to average of first two preferred dimensions
0342: * else
0343: * set tile size to source tile size
0344: * else // tilingMode is not ImageWriteParam.MODE_EXPLICIT
0345: * the plug-in decides the tile size
0346: * </pre>
0347: *
0348: * There is no mechanism to set the tile grid offsets of the output.
0349: *
0350: * <h3><a name="RenderedModePixelReplacement"</a>Pixel Replacement</h3>
0351: *
0352: * If AllowPixelReplacement is TRUE, the ImageWriter can replace pixels, and
0353: * the source is a PlanarImage, then the rendering of the operation
0354: * will respond to RenderingChangeEvents and Shape-valued PropertyChangeEvents
0355: * named "InvalidRegion". The rendering will be automatically registered as
0356: * a sink of the rendering of the operation node's source. As the source
0357: * rendering does not usually generate events, the calling code must also
0358: * explicitly register the "ImageWrite" rendering as a sink of the source
0359: * node. By whatever means the event is generated, when the rendering
0360: * receives such an event, it will determine the indices of all tiles which
0361: * overlap the invalid region and will replace the pixels of all these tiles
0362: * in the output.
0363: *
0364: * <p>Note that this behavior differs from what would happen if the RenderedOp
0365: * created by the operation received a RenderingChangeEvent: in this case a
0366: * new rendering of the node would be created using the ParameterBlock and
0367: * RenderingHints currently in effect. This would cause the entire image to be
0368: * rewritten at the current position of the output. This will also happen
0369: * when AllowPixelReplacement is FALSE. In effect in both of these cases the
0370: * behavior in response to a RenderingChangeEvent is unspecified and the result
0371: * will likely be unexpected.</p>
0372: *
0373: * <p>To avoid any inadvertent overwriting of the destination as a result of
0374: * events received by the RenderedOp, the following usage is recommended when
0375: * the objective is automatic pixel replacement:
0376: *
0377: * <pre>
0378: * // Sources, parameters, and hints.
0379: * ParameterBlock args;
0380: * RenderingHints hints;
0381: *
0382: * // Create the OperationNode.
0383: * RenderedOp imageWriteNode = JAI.create("ImageWrite", args, hints);
0384: *
0385: * // Get the rendering which already exists due to "immediate" status.
0386: * RenderedImage imageWriteRendering = imageWriteNode.getRendering();
0387: *
0388: * // Unhook the OperationNode as a sink of its source OperationNode.
0389: * imageWriteNode.getSourceImage(0).removeSink(imageWriteNode);
0390: *
0391: * // Add the rendering as a sink of the source OperationNode.
0392: * imageWriteNode.getSourceImage(0).addSink(imageWriteRendering);
0393: *
0394: * // Free the OperationNode for garbage collection.
0395: * imageWriteNode = null;
0396: * </pre>
0397: *
0398: * At this point a reference to imageWriteRendering must be held as long as the
0399: * data of the source of the operation may change. Then provided the events are
0400: * correctly propagated to imageWriteRendering, the data in the output file
0401: * will be automatically updated to match the source data.</p>
0402: *
0403: * <p>If pixel replacement is not the objective and inadvertent overwriting is
0404: * to be avoided then the safest approach would be the following:
0405: *
0406: * <pre>
0407: * // Create the OperationNode.
0408: * RenderedOp imageWriteNode = JAI.create("ImageWrite", args, hints);
0409: *
0410: * // Unhook the OperationNode as a sink of its source
0411: * imageWriteNode.getSourceImage(0).removeSink(imageWriteNode);
0412: * </pre>
0413: *
0414: * The image is written by the first statement and no reference to the
0415: * rendering need be retained as before.
0416: *
0417: * <h3><a name="RenderedModeProperties"</a>Image Properties in Rendered Mode</h3>
0418: *
0419: * Image properties are used for metadata, thumbnails, and writer-related
0420: * information. The following properties may be set on the RenderedOp created
0421: * for the "ImageWrite" operation in rendered mode:
0422: *
0423: * <p><table border=1>
0424: * <caption><b>Rendered Mode Image Properties</b></caption>
0425: * <tr>
0426: * <th>Property Name</th>
0427: * <th>Type</th>
0428: * <th>Comment</th>
0429: * </tr>
0430: * <tr>
0431: * <td>JAI.ImageWriteParam</td>
0432: * <td>ImageWriteParam</td>
0433: * <td>Set to ImageWriteParam actually used which may differ from the one passed in.</td>
0434: * </tr>
0435: * <tr>
0436: * <td>JAI.ImageWriter</td>
0437: * <td>ImageWriter</td>
0438: * <td>Set to ImageWriter actually used.</td>
0439: * </tr>
0440: * <tr>
0441: * <td>JAI.ImageMetadata</td>
0442: * <td>IIOMetadata</td>
0443: * <td>Set if and only if image metadata are available; may be transcoded.</td>
0444: * </tr>
0445: * <tr>
0446: * <td>JAI.StreamMetadata</td>
0447: * <td>IIOMetadata</td>
0448: * <td>Set if and only if stream metadata are available; may be transcoded.</td>
0449: * </tr>
0450: * <tr>
0451: * <td>JAI.Thumbnails</td>
0452: * <td>BufferedImage[]</td>
0453: * <td>Set if and only thumbnails are provided and the writer supportes writing them.</td>
0454: * </tr>
0455: * </table></p>
0456: *
0457: * <p>If a given property is not set, this implies of course that the names of
0458: * absent properties will not appear in the array returned by getPropertyNames()
0459: * and getProperty() invoked to obtain absent properties will return
0460: * java.awt.Image.UndefinedProperty as usual.</p>
0461: *
0462: * <p>The ImageWriter and ImageWriteParam may be used for subsequent invocations
0463: * of the operation or for informational purposes. Care should be taken in using
0464: * these property values with respect to the synchronization issues previously
0465: * discussed.</p>
0466: *
0467: * <p>Metadata properties will be set to those actually written to the output. They
0468: * may be derived either from input parameters or source properties depending on
0469: * the values of the StreamMetadata, ImageMetadata, and UseProperties parameters.
0470: * They will be transcoded data if Transcode is TRUE and the ImageWriter supports
0471: * transcoding.</p>
0472: *
0473: * <p>All properties will be set when the node is rendered.</p>
0474: *
0475: * <h2><a name="RenderableMode"</a>Renderable Mode</h2>
0476: *
0477: * In renderable mode the "ImageWrite" operation requires a
0478: * {@link java.awt.image.renderable.RenderableImage} source and writes a
0479: * {@link java.awt.image.RenderedImage} to the specified output destination.
0480: * As the "immediate" designation specified by {@link #isImmediate()}
0481: * has no effect in renderable mode, no image will be written without further
0482: * action by the calling code. To write an image, createRendering(),
0483: * createScaledRendering(), or createDefaultRendering()
0484: * must be invoked. Each of these will create a RenderedImage by forwarding the
0485: * createRendering() or equivalent call to the source image. The resulting
0486: * RenderedImage will be written to the output according to the
0487: * <a href="#RenderedMode">rendered mode</a> operation of "ImageWrite".
0488: * If a mapping of <code>JAI.KEY_INTERPOLATION</code> is supplied via a
0489: * <code>RenderingHints</code> passed to the operation, then the interpolation
0490: * type it specifies will be used to create the rendering if interpolation is
0491: * required.
0492: *
0493: * <h3><a name="RenderableModeParameters"</a>Renderable Mode Parameters</h3>
0494: *
0495: * The parameter list of the "ImageRead" operation in renderable mode is
0496: * identical to the <a href="#RenderedModeParameters">rendered mode
0497: * parameter list</a>.
0498: *
0499: * <h3>Pixel Replacement in Renderable Mode</h3>
0500: *
0501: * Pixel replacement pertains only to RenderedImages generated by rendering the
0502: * RenderableOp. It may occur if the same conditions apply as described for
0503: * pixel replacement in rendered mode. Due to the unspecified nature of the
0504: * underlying rendered sources of any rendering, this is not a recommended
0505: * procedure.
0506: *
0507: * <h3>Image Properties in Renderable Mode</h3>
0508: *
0509: * The RenderableOp node itself does not have any ImageWrite-related
0510: * properties. Any RenderedImages created by rendering the RenderableOp
0511: * (thereby writing an image to the output as described), may have
0512: * <a href="#RenderedModeProperties">rendered mode properties</a> set.
0513: *
0514: * <h2><a name="CollectionMode"</a>Collection Mode</h2>
0515: *
0516: * In collection mode the "ImageWrite" operation requires a
0517: * {@link java.util.Collection} source and writes its contents to the
0518: * specified output destination.
0519: *
0520: * <p>The Collection is treated as a sequence of images which will be
0521: * extracted from the Collection in the order returned by a new Iterator.
0522: * Elements in the Collection which are not RenderedImages will be ignored.
0523: * The derived sequence of images will then be written to the output.</p>
0524: *
0525: * <p>If there is only one RenderedImage in the source Collection, this image
0526: * will be written as done in rendered mode operation. If there is more than
0527: * one RenderedImage, the sequence of RenderedImages will be written as an
0528: * image sequence. In the latter case the ImageWriter must be able to write
0529: * sequences.</p>
0530: *
0531: * <h3><a name="CollectionModeParameters"</a>Collection Mode Parameters</h3>
0532: *
0533: * Identical parameter list to rendered mode except:
0534: *
0535: * <p><table border=1>
0536: * <caption><b>Collection Mode Parameter Differences</b></caption>
0537: * <tr><th>Name</th> <th>Class Type</th>
0538: * <th>Default Value</th></tr>
0539: * <tr><td>ImageMetadata</td> <td>javax.imageio.metadataIIOMetadata[]</td>
0540: * <td>null</td>
0541: * <tr><td>Thumbnails</td> <td>java.awt.image.BufferedImage[][]</td>
0542: * <td>null</td>
0543: * </table></p>
0544: *
0545: * <ul>
0546: * <p><li>
0547: * If the source is not a CollectionOp then the number of RenderedImages in
0548: * the source is counted. If it is not at least one then an exception is
0549: * thrown when the operation is created. If it is greater than one, then
0550: * the ImageWriter is checked to determine whether it can write sequences.
0551: * If it cannot then an exception is thrown when the operation is created.
0552: * </li></p>
0553: * <p><li>
0554: * The first index of the thumbnails array corresponds to the ordinal position
0555: * of the image in the collection and the second index to the thumbnails of
0556: * that image.
0557: * </li></p>
0558: * </ul>
0559: *
0560: * <p>
0561: * The change to the ImageMetadata and Thumbnails parameters is that there can
0562: * now be a distinct image metadata object and thumbnail array for each image
0563: * in the Collection. The components of these respective arrays will be indexed
0564: * using the sequence of RenderedImages extracted from the source Collection by
0565: * the Iterator. It is the responsibility of the caller to ensure that this
0566: * sequencing is correct. In this context it is advisable to use a source
0567: * Collection which maintains the order of its elements such as a List.
0568: * </p>
0569: *
0570: * <h3>Pixel Replacement in Collection Mode</h3>
0571: *
0572: * If the value of the AllowPixelReplacement parameter is TRUE, then the
0573: * rendered Collection will contain RenderedImages which are registered as
0574: * listeners of their respective sources. Each image in the rendered Collection
0575: * will however be a rendering as opposed to a RenderedOp. This obviates the
0576: * need to unhook such a RenderedOp from its source as suggested. Two actions
0577: * on the part of the application are however necessary in this case: 1) the
0578: * sequence must be manually ended, and 2) the Collection node must be removed
0579: * as a sink of its source Collection. The first action is necessary as
0580: * pixels may be replaced at various times in various images in the sequence
0581: * and it is not possible to terminate the sequence at rendering time, and there
0582: * is no reliable mechanism to detect programmatically when this may later be
0583: * effected. The second action is necessary because a CollectionChangeEvent
0584: * received by the Collection node would cause the node to be re-rendered, i.e.,
0585: * the collection data to be rewritten using the current state of all parameters.
0586: * This will in fact also happen when AllowPixelReplacement is FALSE. In effect
0587: * in both of these cases the behavior in response to a CollectionChangeEvent
0588: * is unspecified and the result will likely be unexpected.
0589: *
0590: * <p>
0591: * To ensure proper termination of the image sequence and avoid any inadvertent
0592: * overwriting of the destination as a result of events received by the
0593: * CollectionOp, the following usage is recommended when the objective is
0594: * automatic pixel replacement:
0595: *
0596: * <pre>
0597: * // Sources, parameters, and hints.
0598: * ParameterBlock args;
0599: * RenderingHints hints;
0600: *
0601: * // Create the Collection.
0602: * CollectionImage imageWriteCollection =
0603: * (CollectionImage)JAI.createCollection("ImageWrite", args, hints);
0604: *
0605: * // Unhook the Collection node from the source to avoid
0606: * // re-renderings caused by CollectionChangeEvents.
0607: * if(args.getSource(0) instanceof CollectionImage) {
0608: * CollectionImage sourceCollection =
0609: * (CollectionImage)args.getSource(0);
0610: * sourceCollection.removeSink(imageWriteCollection);
0611: * }
0612: *
0613: * // !!! Pixel replacement activity happens here ... !!!
0614: *
0615: * // Get the ImageWriter.
0616: * ImageWriter writer =
0617: * (ImageWriter)imageWriteCollection.getProperty("JAI.ImageWriter");
0618: *
0619: * // End the sequence if necessary.
0620: * if(writer.canWriteSequence()) {
0621: * writer.endWriteSequence();
0622: * }
0623: * </pre>
0624: * </p>
0625: *
0626: * <p>
0627: * Using the foregoing construct, all pixels in all images written to the output
0628: * sequence will remain current with the in-memory data of their respective
0629: * source provided all events are propagated as expected. Note that it is not
0630: * necessary to end the sequence manually if pixel replacement is not allowed or
0631: * is not supported. Also the sequence must be manually ended if and only if the
0632: * writer is capable of writing sequences. This permits pixel replacement to
0633: * work in the case where the source collection contains only a single image
0634: * and the writer supports pixel replacement but cannot write sequences.
0635: * </p>
0636: *
0637: * <p>
0638: * If pixel replacement is not the objective, i.e., AllowPixelReplacement is
0639: * FALSE, and inadvertent overwriting is to be avoided then the safest approach
0640: * would be the following:
0641: *
0642: * <pre>
0643: * // Create the Collection.
0644: * Collection imageWriteCollection =
0645: * JAI.create("ImageWrite", args, hints);
0646: *
0647: * // Unhook the Collection node from the source to avoid
0648: * // re-renderings caused by CollectionChangeEvents.
0649: * if(args.getSource(0) instanceof CollectionImage) {
0650: * CollectionImage sourceCollection =
0651: * (CollectionImage)args.getSource(0);
0652: * sourceCollection.removeSink(imageWriteCollection);
0653: * }
0654: * </pre>
0655: *
0656: * The image is written by the first statement and no reference to the
0657: * rendering need be retained.</p>
0658: *
0659: * <h3>Image Properties in Collection Mode</h3>
0660: *
0661: * Contingent on parameter settings and the presence of the appropriate
0662: * metadata, the rendered Collection may have the "JAI.StreamMetadata",
0663: * "JAI.ImageReadParam", and "JAI.ImageReader" properties set. Each
0664: * RenderedImage in the Collection may contain
0665: * <a href="#RenderedModeProperties">rendered mode properties</a>
0666: * contingent on parameter settings and data availability. Metadata
0667: * properties may be transcoded.
0668: *
0669: * @see javax.media.jai.OperationDescriptor
0670: * @see javax.imageio.ImageWriter
0671: * @see javax.imageio.ImageWriteParam
0672: * @see javax.imageio.metadata.IIOMetadata
0673: * @see javax.imageio.stream.ImageOutputStream
0674: */
0675: public class ImageWriteDescriptor extends OperationDescriptorImpl {
0676:
0677: // Property name constants have package access for image factory use.
0678:
0679: /** ImageWriteParam property name "JAI.ImageWriteParam". */
0680: public static final String PROPERTY_NAME_IMAGE_WRITE_PARAM = "JAI.ImageWriteParam";
0681:
0682: /** ImageWriter property name "JAI.ImageWriter". */
0683: public static final String PROPERTY_NAME_IMAGE_WRITER = "JAI.ImageWriter";
0684:
0685: /**
0686: * Image metadata property name. Set to same value as
0687: * {@link ImageReadDescriptor#PROPERTY_NAME_METADATA_IMAGE}.
0688: */
0689: public static final String PROPERTY_NAME_METADATA_IMAGE = ImageReadDescriptor.PROPERTY_NAME_METADATA_IMAGE;
0690:
0691: /**
0692: * Stream metadata property name. Set to same value as
0693: * {@link ImageReadDescriptor#PROPERTY_NAME_METADATA_STREAM}.
0694: */
0695: public static final String PROPERTY_NAME_METADATA_STREAM = ImageReadDescriptor.PROPERTY_NAME_METADATA_STREAM;
0696:
0697: /**
0698: * Thumbnail property name. Set to same value as
0699: * {@link ImageReadDescriptor#PROPERTY_NAME_THUMBNAILS}.
0700: */
0701: public static final String PROPERTY_NAME_THUMBNAILS = ImageReadDescriptor.PROPERTY_NAME_THUMBNAILS;
0702:
0703: /**
0704: * Test method.
0705: *
0706: * @param args {inputFile, outputFile [, mode]}
0707: * @throws Throwable any error.
0708: */
0709: /* XXX
0710: public static void main(String[] args) throws Throwable {
0711: String inputFile = args[0];
0712: String outputFile = args[1];
0713: String modeName = args.length > 2 ?
0714: args[2] : RenderedRegistryMode.MODE_NAME;
0715: String formatName = args.length > 3 ?
0716: args[3] : null;
0717:
0718: ParameterBlock pb = new ParameterBlock();
0719: pb.set(new java.io.File(outputFile), 0);
0720: if(formatName != null) {
0721: pb.set(formatName, 1);
0722: }
0723:
0724: java.awt.image.RenderedImage[] images = null;
0725: if(modeName.equalsIgnoreCase(RenderedRegistryMode.MODE_NAME)) {
0726: pb.addSource(ImageIO.read(new java.io.File(inputFile)));
0727: images = new java.awt.image.RenderedImage[1];
0728: pb.set(new Dimension(128, 128), 6);
0729: images[0] = JAI.create("ImageWrite", pb, null);
0730: PrintProps.print((javax.media.jai.PropertySource)images[0]);
0731: } else if(modeName.equalsIgnoreCase(RenderableRegistryMode.MODE_NAME)) {
0732: ParameterBlock renderablePB = new ParameterBlock();
0733: renderablePB.addSource(ImageIO.read(new java.io.File(inputFile)));
0734: pb.addSource(javax.media.jai.JAI.createRenderable("renderable", renderablePB));
0735: java.awt.image.renderable.RenderableImage ri =
0736: JAI.createRenderable("ImageWrite", pb, null);
0737: PrintProps.print((javax.media.jai.PropertySource)ri);
0738: images = new java.awt.image.RenderedImage[1];
0739: //java.awt.image.renderable.RenderContext rc =
0740: // new java.awt.image.renderable.RenderContext(
0741: // new java.awt.geom.AffineTransform(42, 0, 0, 42, 0, 0));
0742: //images[0] = ri.createRendering(rc);
0743: images[0] = ri.createDefaultRendering();
0744: PrintProps.print((javax.media.jai.PropertySource)images[0]);
0745: } else if(modeName.equalsIgnoreCase(CollectionRegistryMode.MODE_NAME)) {
0746: java.util.ArrayList sourceCollection = new java.util.ArrayList();
0747: Object input =
0748: ImageIO.createImageInputStream(new java.io.File(inputFile));
0749: javax.imageio.ImageReader reader =
0750: (javax.imageio.ImageReader)ImageIO.getImageReaders(input).next();
0751: reader.setInput(input);
0752: int imageIndex = 0;
0753: do {
0754: try {
0755: RenderedImage nextImage = reader.read(imageIndex);
0756: sourceCollection.add(nextImage);
0757: } catch(IndexOutOfBoundsException e) {
0758: break;
0759: }
0760: imageIndex++;
0761: } while(true);
0762: pb.addSource(sourceCollection);
0763: java.util.Collection imageCollection =
0764: JAI.createCollection("ImageWrite", pb, null);
0765: PrintProps.print((javax.media.jai.PropertySource)imageCollection);
0766: images = new java.awt.image.RenderedImage[imageCollection.size()];
0767: imageCollection.toArray(images);
0768: } else {
0769: throw new UnsupportedOperationException(modeName+" mode not supported");
0770: }
0771:
0772: final java.awt.Frame frame = new java.awt.Frame();
0773: frame.addWindowListener(new java.awt.event.WindowAdapter() {
0774: public void windowClosing(java.awt.event.WindowEvent e) {
0775: frame.setEnabled(false);
0776: frame.dispose();
0777: }
0778: });
0779:
0780: int gridSide = (int)(Math.sqrt(images.length) + 0.5);
0781: frame.setLayout(new java.awt.GridLayout(gridSide, gridSide));
0782: java.awt.Dimension screenSize =
0783: java.awt.Toolkit.getDefaultToolkit().getScreenSize();
0784: int width =
0785: Math.min(screenSize.width/gridSide, images[0].getWidth());
0786: int height =
0787: Math.min(screenSize.height/gridSide, images[0].getHeight());
0788: for(int i = 0; i < images.length; i++) {
0789: javax.media.jai.widget.ScrollingImagePanel panel =
0790: new javax.media.jai.widget.ScrollingImagePanel(images[i],
0791: width, //image.getWidth(),
0792: height);//image.getHeight());
0793: frame.add(panel);
0794: }
0795: frame.pack();
0796: frame.show();
0797: }
0798: */
0799:
0800: /**
0801: * The name of the operation.
0802: */
0803: private static final String OPERATION_NAME = "ImageWrite";
0804:
0805: /**
0806: * The resource strings that provide the general documentation and
0807: * specify the parameter list for the "ImageWrite" operation.
0808: */
0809: private static final String[][] resources = {
0810: { "GlobalName", "ImageWrite" },
0811: { "LocalName", "ImageWrite" },
0812: { "Vendor", "com.sun.media.jai" },
0813: { "Description", I18N.getString("ImageWriteDescriptor0") },
0814: {
0815: "DocURL",
0816: "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ImageWriteDescriptor.html" },
0817: { "Version", I18N.getString("DescriptorVersion") },
0818: { "arg0Desc", I18N.getString("ImageWriteDescriptor1") },
0819: { "arg1Desc", I18N.getString("ImageWriteDescriptor2") },
0820: { "arg2Desc", I18N.getString("ImageWriteDescriptor3") },
0821: { "arg3Desc", I18N.getString("ImageWriteDescriptor4") },
0822: { "arg4Desc", I18N.getString("ImageWriteDescriptor5") },
0823: { "arg5Desc", I18N.getString("ImageWriteDescriptor6") },
0824: { "arg6Desc", I18N.getString("ImageWriteDescriptor7") },
0825: { "arg7Desc", I18N.getString("ImageWriteDescriptor8") },
0826: { "arg8Desc", I18N.getString("ImageWriteDescriptor9") },
0827: { "arg9Desc", I18N.getString("ImageWriteDescriptor10") },
0828: { "arg10Desc", I18N.getString("ImageWriteDescriptor11") },
0829: { "arg11Desc", I18N.getString("ImageWriteDescriptor12") },
0830: { "arg12Desc", I18N.getString("ImageWriteDescriptor13") },
0831: { "arg13Desc", I18N.getString("ImageWriteDescriptor14") } };
0832:
0833: /** The parameter names for the "ImageWrite" operation. */
0834: private static final String[] paramNames = { "Output", "Format",
0835: "UseProperties", "Transcode", "VerifyOutput",
0836: "AllowPixelReplacement", "TileSize", "StreamMetadata",
0837: "ImageMetadata", "Thumbnails", "Listeners", "Locale",
0838: "WriteParam", "Writer" };
0839:
0840: /** The parameter class types for rendered mode of "ImageWrite". */
0841: private static final Class[] renderedParamClasses = {
0842: java.lang.Object.class, // Output
0843: java.lang.String.class, // Format
0844: java.lang.Boolean.class, // UseProperties
0845: java.lang.Boolean.class, // Transcode
0846: java.lang.Boolean.class, // VerifyOutput
0847: java.lang.Boolean.class, // AllowPixelReplacement
0848: java.awt.Dimension.class, // TileSize
0849: javax.imageio.metadata.IIOMetadata.class, // StreamMetadata
0850: javax.imageio.metadata.IIOMetadata.class, // ImageMetadata
0851: java.awt.image.BufferedImage[].class, // Thumbnails
0852: java.util.EventListener[].class, // Listeners
0853: java.util.Locale.class, // Locale
0854: javax.imageio.ImageWriteParam.class, // WriteParam
0855: javax.imageio.ImageWriter.class // Writer
0856: };
0857:
0858: /** The parameter default values for rendered mode of "ImageWrite". */
0859: private static final Object[] renderedParamDefaults = {
0860: NO_PARAMETER_DEFAULT, // Output
0861: null, // Format
0862: Boolean.TRUE, // UseProperties
0863: Boolean.TRUE, // Transcode
0864: Boolean.TRUE, // VerifyOutput
0865: Boolean.FALSE, // AllowPixelReplacement
0866: null, // TileSize
0867: null, // StreamMetadata
0868: null, // ImageMetadata
0869: null, // Thumbnails
0870: null, // Listeners
0871: null, // Locale
0872: null, // WriteParam
0873: null // Writer
0874: };
0875:
0876: /** The parameter class types for renderable mode of "ImageWrite". */
0877: private static final Class[] renderableParamClasses = renderedParamClasses;
0878:
0879: /** The parameter default values for renderable mode of "ImageWrite". */
0880: private static final Object[] renderableParamDefaults = renderedParamDefaults;
0881:
0882: /** The parameter class types for collection mode of "ImageWrite". */
0883: private static final Class[] collectionParamClasses = {
0884: java.lang.Object.class, // Output
0885: java.lang.String.class, // Format
0886: java.lang.Boolean.class, // UseProperties
0887: java.lang.Boolean.class, // Transcode
0888: java.lang.Boolean.class, // VerifyOutput
0889: java.lang.Boolean.class, // AllowPixelReplacement
0890: java.awt.Dimension.class, // TileSize
0891: javax.imageio.metadata.IIOMetadata.class, // StreamMetadata
0892: javax.imageio.metadata.IIOMetadata[].class, // ImageMetadata
0893: java.awt.image.BufferedImage[][].class, // Thumbnails
0894: java.util.EventListener[].class, // Listeners
0895: java.util.Locale.class, // Locale
0896: javax.imageio.ImageWriteParam.class, // WriteParam
0897: javax.imageio.ImageWriter.class // Writer
0898: };
0899:
0900: /** The parameter default values for collection mode of "ImageWrite". */
0901: private static final Object[] collectionParamDefaults = renderedParamDefaults;
0902:
0903: /** Constructor. */
0904: public ImageWriteDescriptor() {
0905: super (
0906: resources,
0907: new String[] { RenderedRegistryMode.MODE_NAME,
0908: RenderableRegistryMode.MODE_NAME,
0909: CollectionRegistryMode.MODE_NAME },
0910: null, // sourceNames
0911: new Class[][] { { RenderedImage.class },
0912: { RenderableImage.class }, { Collection.class } }, // sourceClasses
0913: paramNames,
0914: new Class[][] { renderedParamClasses,
0915: renderableParamClasses, collectionParamClasses },
0916: new Object[][] { renderedParamDefaults,
0917: renderableParamDefaults,
0918: collectionParamDefaults }, new Object[][] {
0919: null, null, null }); // validParamValues
0920: }
0921:
0922: /**
0923: * Type-safe convenience method for creating a {@link RenderedOp}
0924: * representing the "ImageWrite" operation in rendered mode. The
0925: * method packs the source and parameters into a new
0926: * <code>ParameterBlock</code> and invokes
0927: * {@link JAI#create(String,ParameterBlock,RenderingHints)}.
0928: *
0929: * @param source The image to be written.
0930: * @param output The output destination.
0931: * @param format The format name of the output.
0932: * @param useProperties Whether to use image metadata properties as
0933: * fallbacks.
0934: * @param transcode Whether to transcode metadata before writing.
0935: * @param verifyOutput Whether to verify the validity of the output
0936: * destination.
0937: * @param allowPixelReplacement Whether to allow pixel replacement
0938: * in the output image.
0939: * @param tileSize The tile size of the output image.
0940: * @param streamMetadata Stream metadata to write to the output.
0941: * @param imageMetadata Image metadata to write to the output.
0942: * @param thumbnails Thumbnails to write to the output.
0943: * @param listeners EventListeners to be registered with the ImageWriter.
0944: * @param locale The Locale for the ImageWriter to use.
0945: * @param writeParam Java Image I/O write parameter instance.
0946: * @param writer Java Image I/O writer instance.
0947: * @param hints Operation hints.
0948: * @return a reference to the operation source.
0949: */
0950: public static RenderedOp create(RenderedImage source,
0951: ImageOutputStream output, String format,
0952: Boolean useProperties, Boolean transcode,
0953: Boolean verifyOutput, Boolean allowPixelReplacement,
0954: Dimension tileSize, IIOMetadata streamMetadata,
0955: IIOMetadata imageMetadata, BufferedImage[] thumbnails,
0956: EventListener[] listeners, Locale locale,
0957: ImageWriteParam writeParam, ImageWriter writer,
0958: RenderingHints hints) {
0959:
0960: ParameterBlock args = new ParameterBlock();
0961:
0962: args.addSource(source);
0963:
0964: args.add(output);
0965: args.add(format);
0966: args.add(useProperties);
0967: args.add(transcode);
0968: args.add(verifyOutput);
0969: args.add(allowPixelReplacement);
0970: args.add(tileSize);
0971: args.add(streamMetadata);
0972: args.add(imageMetadata);
0973: args.add(thumbnails);
0974: args.add(listeners);
0975: args.add(locale);
0976: args.add(writeParam);
0977: args.add(writer);
0978:
0979: return JAI.create(OPERATION_NAME, args, hints);
0980: }
0981:
0982: /**
0983: * Type-safe convenience method for creating a {@link Collection}
0984: * representing the "ImageWrite" operation in collection mode. The
0985: * method packs the source and parameters into a new
0986: * <code>ParameterBlock</code> and invokes
0987: * {@link JAI#createCollection(String,ParameterBlock,RenderingHints)}.
0988: *
0989: * @param source The collection to be written.
0990: * @param output The output destination.
0991: * @param format The format name of the output.
0992: * @param useProperties Whether to use image metadata properties as
0993: * fallbacks.
0994: * @param transcode Whether to transcode metadata before writing.
0995: * @param verifyOutput Whether to verify the validity of the output
0996: * destination.
0997: * @param allowPixelReplacement Whether to allow pixel replacement
0998: * in the output image.
0999: * @param tileSize The tile size of the output image.
1000: * @param streamMetadata Stream metadata to write to the output.
1001: * @param imageMetadata Image metadata to write to the output.
1002: * @param thumbnails Thumbnails to write to the output.
1003: * @param listeners EventListeners to be registered with the ImageWriter.
1004: * @param locale The Locale for the ImageWriter to use.
1005: * @param writeParam Java Image I/O write parameter instance.
1006: * @param writer Java Image I/O writer instance.
1007: * @param hints Operation hints.
1008: * @return a reference to the operation source.
1009: */
1010: public static Collection createCollection(Collection source,
1011: ImageOutputStream output, String format,
1012: Boolean useProperties, Boolean transcode,
1013: Boolean verifyOutput, Boolean allowPixelReplacement,
1014: Dimension tileSize, IIOMetadata streamMetadata,
1015: IIOMetadata[] imageMetadata, BufferedImage[][] thumbnails,
1016: EventListener[] listeners, Locale locale,
1017: ImageWriteParam writeParam, ImageWriter writer,
1018: RenderingHints hints) {
1019:
1020: ParameterBlock args = new ParameterBlock();
1021:
1022: args.addSource(source);
1023:
1024: args.add(output);
1025: args.add(format);
1026: args.add(useProperties);
1027: args.add(transcode);
1028: args.add(verifyOutput);
1029: args.add(allowPixelReplacement);
1030: args.add(tileSize);
1031: args.add(streamMetadata);
1032: args.add(imageMetadata);
1033: args.add(thumbnails);
1034: args.add(listeners);
1035: args.add(locale);
1036: args.add(writeParam);
1037: args.add(writer);
1038:
1039: return JAI.createCollection(OPERATION_NAME, args, hints);
1040: }
1041:
1042: /**
1043: * Type-safe convenience method for creating a {@link RenderableOp}
1044: * representing the "ImageWrite" operation in renderable mode. The
1045: * method packs the source and parameters into a new
1046: * <code>ParameterBlock</code> and invokes
1047: * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}.
1048: *
1049: * @param source The renderable source to be written.
1050: * @param output The output destination.
1051: * @param format The format name of the output.
1052: * @param useProperties Whether to use image metadata properties as
1053: * fallbacks.
1054: * @param transcode Whether to transcode metadata before writing.
1055: * @param verifyOutput Whether to verify the validity of the output
1056: * destination.
1057: * @param allowPixelReplacement Whether to allow pixel replacement
1058: * in the output image.
1059: * @param tileSize The tile size of the output image.
1060: * @param streamMetadata Stream metadata to write to the output.
1061: * @param imageMetadata Image metadata to write to the output.
1062: * @param thumbnails Thumbnails to write to the output.
1063: * @param listeners EventListeners to be registered with the ImageWriter.
1064: * @param locale The Locale for the ImageWriter to use.
1065: * @param writeParam Java Image I/O write parameter instance.
1066: * @param writer Java Image I/O writer instance.
1067: * @param hints Operation hints.
1068: * @return a reference to the operation source.
1069: */
1070: public static RenderableOp createRenderable(RenderableImage source,
1071: ImageOutputStream output, String format,
1072: Boolean useProperties, Boolean transcode,
1073: Boolean verifyOutput, Boolean allowPixelReplacement,
1074: Dimension tileSize, IIOMetadata streamMetadata,
1075: IIOMetadata imageMetadata, BufferedImage[] thumbnails,
1076: EventListener[] listeners, Locale locale,
1077: ImageWriteParam writeParam, ImageWriter writer,
1078: RenderingHints hints) {
1079: ParameterBlock args = new ParameterBlock();
1080:
1081: args.addSource(source);
1082:
1083: args.add(output);
1084: args.add(format);
1085: args.add(useProperties);
1086: args.add(transcode);
1087: args.add(verifyOutput);
1088: args.add(allowPixelReplacement);
1089: args.add(tileSize);
1090: args.add(streamMetadata);
1091: args.add(imageMetadata);
1092: args.add(thumbnails);
1093: args.add(listeners);
1094: args.add(locale);
1095: args.add(writeParam);
1096: args.add(writer);
1097:
1098: return JAI.createRenderable(OPERATION_NAME, args, hints);
1099: }
1100:
1101: /**
1102: * Returns true indicating that the operation should be rendered
1103: * immediately during a call to <code>JAI.create[]()</code> or
1104: * <code>JAI.createCollection[NS]()</code>.
1105: *
1106: * @see javax.media.jai.OperationDescriptor
1107: */
1108: public boolean isImmediate() {
1109: return true;
1110: }
1111:
1112: /**
1113: * Validates the parameters in the supplied <code>ParameterBlock</code>.
1114: *
1115: * <p>In addition to the standard validation performed by the
1116: * corresponding superclass method, this method verifies the following:
1117: * <ul>
1118: * <li>if <i>VerifyOutput</i> is <code>TRUE</code> and <i>Output</i>
1119: * is a <code>File</code> or <code>String</code>, whether the
1120: * corresponding physical file is writable, i.e., exists and may
1121: * be overwritten or does not exist and may be created; and</li>
1122: * <li>if <i>VerifyOutput</i> is <code>TRUE</code> and <i>Output</i>
1123: * is a <code>Socket</code>, whether it is bound, connected, open,
1124: * and the write-half is not shut down; and</li>
1125: * <li>if in collection mode (<code>modeName</code> equals
1126: * {@link CollectionRegistryMode#MODE_NAME}), the source is not a
1127: * {@link CollectionOp}, and the size of the source
1128: * {@link Collection} is greater than unity, whether the
1129: * {@link ImageWriter} <i>cannot</i> write sequences.</li>
1130: * </ul>
1131: *
1132: * If the superclass method finds that the arguments are invalid, or if
1133: * this method determines that any of the foregoing conditions is true,
1134: * an error message will be appended to <code>msg</code> and
1135: * <code>false</code> will be returned; otherwise <code>true</code> will
1136: * be returned.</p>
1137: *
1138: * @param modeName The operation mode.
1139: * @param args The source and parameters of the operation.
1140: * @param msg A container for any error messages.
1141: *
1142: * @return Whether the supplied parameters are valid.
1143: */
1144: protected boolean validateParameters(String modeName,
1145: ParameterBlock args, StringBuffer msg) {
1146: if (!super .validateParameters(modeName, args, msg)) {
1147: return false;
1148: }
1149:
1150: // Get the Output parameter.
1151: Object output = args.getObjectParameter(0);
1152:
1153: // Check the output if so requested by "VerifyOutput".
1154: Boolean verifyOutput = (Boolean) args.getObjectParameter(4);
1155: if (verifyOutput.booleanValue()) {
1156: if (output instanceof File || output instanceof String) {
1157: // Set file and path variables.
1158: File file = null;
1159: String path = null;
1160: if (output instanceof File) {
1161: file = (File) output;
1162: path = file.getPath();
1163: } else if (output instanceof String) {
1164: path = (String) output;
1165: file = new File(path);
1166: }
1167:
1168: // Perform non-destructive test that the file
1169: // may be created and written.
1170: try {
1171: if (file.exists()) {
1172: if (!file.canWrite()) {
1173: // Cannot write to existing file.
1174: msg
1175: .append(file.getPath()
1176: + " "
1177: + I18N
1178: .getString("ImageWriteDescriptor15"));
1179: return false;
1180: }
1181: } else {
1182: if (!file.createNewFile()) {
1183: // Cannot create file.
1184: msg
1185: .append(file.getPath()
1186: + " "
1187: + I18N
1188: .getString("ImageWriteDescriptor16"));
1189: return false;
1190: }
1191: file.delete();
1192: }
1193: } catch (IOException ioe) {
1194: // I/O exception during createNewFile().
1195: msg.append(file.getPath() + " "
1196: + I18N.getString("ImageWriteDescriptor17")
1197: + " " + ioe.getMessage());
1198: return false;
1199: } catch (SecurityException se) {
1200: // Security exception during exists(), canWrite(),
1201: // createNewFile(), or delete().
1202: msg.append(file.getPath() + " "
1203: + I18N.getString("ImageWriteDescriptor18")
1204: + " " + se.getMessage());
1205: return false;
1206: }
1207: } else if (output instanceof Socket) {
1208: Socket socket = (Socket) output;
1209:
1210: if (socket.isOutputShutdown()) {
1211: msg.append("\"" + socket + "\": "
1212: + I18N.getString("ImageWriteDescriptor19"));
1213: return false;
1214: } else if (socket.isClosed()) {
1215: msg.append("\"" + socket + "\": "
1216: + I18N.getString("ImageWriteDescriptor20"));
1217: return false;
1218: } else if (!socket.isBound()) {
1219: msg.append("\"" + socket + "\": "
1220: + I18N.getString("ImageWriteDescriptor21"));
1221: return false;
1222: } else if (!socket.isConnected()) {
1223: msg.append("\"" + socket + "\": "
1224: + I18N.getString("ImageWriteDescriptor22"));
1225: return false;
1226: }
1227: }
1228: }
1229:
1230: // Get the Format parameter.
1231: String format = (String) args.getObjectParameter(1);
1232:
1233: // Get the ImageWriter parameter.
1234: ImageWriter writer = (ImageWriter) args.getObjectParameter(13);
1235:
1236: if (format == null) {
1237: // Attempt to get the format from the ImageWriter provider.
1238: if (writer != null) {
1239:
1240: // Get the SPI.
1241: ImageWriterSpi spi = writer.getOriginatingProvider();
1242:
1243: // Set from the SPI.
1244: if (spi != null) {
1245: format = spi.getFormatNames()[0];
1246: }
1247: }
1248:
1249: // Attempt to deduce the format from the file suffix.
1250: if (format == null
1251: && (output instanceof File || output instanceof String)) {
1252:
1253: // Set the file name string.
1254: String name = output instanceof File ? ((File) output)
1255: .getName() : (String) output;
1256:
1257: // Extract the suffix.
1258: String suffix = name
1259: .substring(name.lastIndexOf(".") + 1);
1260:
1261: // Get the writers of that suffix.
1262: Iterator writers = ImageIO
1263: .getImageWritersBySuffix(suffix);
1264:
1265: if (writers != null) {
1266: // Get the first writer.
1267: writer = (ImageWriter) writers.next();
1268:
1269: if (writer != null) {
1270: // Get the SPI.
1271: ImageWriterSpi spi = writer
1272: .getOriginatingProvider();
1273:
1274: // Set from the SPI.
1275: if (spi != null) {
1276: format = spi.getFormatNames()[0];
1277: }
1278: }
1279: }
1280: }
1281:
1282: // Default to the most versatile core Java Image I/O writer.
1283: if (format == null) {
1284: format = "PNG";
1285: }
1286:
1287: // Replace the format setting.
1288: if (format != null) {
1289: args.set(format, 1);
1290: }
1291: }
1292:
1293: // Check the tile size parameter if present.
1294: Dimension tileSize = (Dimension) args.getObjectParameter(6);
1295: if (tileSize != null
1296: && (tileSize.width <= 0 || tileSize.height <= 0)) {
1297: msg.append(I18N.getString("ImageWriteDescriptor23"));
1298: return false;
1299: }
1300:
1301: // For collection mode, verify that the source collection contains
1302: // at least one RenderedImage and that the writer can handle sequences
1303: // if there is more than one RenderedImage in the source collection.
1304: if (modeName.equalsIgnoreCase(CollectionRegistryMode.MODE_NAME)) {
1305: // Get the source collection.
1306: Collection source = (Collection) args.getSource(0);
1307:
1308: // If the source collection is a CollectionOp do not perform this
1309: // check as invoking source.size() will render the node.
1310: if (!(source instanceof CollectionOp)) {
1311:
1312: // Determine the number of RenderedImages in the Collection.
1313: int numRenderedImages = 0;
1314: Iterator iter = source.iterator();
1315: while (iter.hasNext()) {
1316: if (iter.next() instanceof RenderedImage) {
1317: numRenderedImages++;
1318: }
1319: }
1320:
1321: if (numRenderedImages == 0) {
1322: msg
1323: .append(I18N
1324: .getString("ImageWriteDescriptor24"));
1325: return false;
1326: } else if (numRenderedImages > 1) {
1327: // Get the writer parameter.
1328: writer = (ImageWriter) args.getObjectParameter(13);
1329:
1330: // If the parameter writer is null, get one based on the
1331: // format.
1332: if (writer == null && format != null) {
1333: // Get the writers of that format.
1334: Iterator writers = ImageIO
1335: .getImageWritersByFormatName(format);
1336:
1337: if (writers != null) {
1338: // Get the first writer.
1339: writer = (ImageWriter) writers.next();
1340: }
1341: }
1342:
1343: if (writer != null) {
1344: // Check that the writer can write sequences.
1345: if (!writer.canWriteSequence()) {
1346: msg
1347: .append(I18N
1348: .getString("ImageWriteDescriptor25"));
1349: return false;
1350: }
1351: }
1352: }
1353: }
1354: }
1355:
1356: return true;
1357: }
1358: }
|