001: /*
002: * $RCSfile: ImageWriteCRIF.java,v $
003: *
004: *
005: * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * - Redistribution of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * - Redistribution in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * Neither the name of Sun Microsystems, Inc. or the names of
020: * contributors may be used to endorse or promote products derived
021: * from this software without specific prior written permission.
022: *
023: * This software is provided "AS IS," without a warranty of any
024: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
025: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
026: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027: * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
028: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
029: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
031: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035: * POSSIBILITY OF SUCH DAMAGES.
036: *
037: * You acknowledge that this software is not designed or intended for
038: * use in the design, construction, operation or maintenance of any
039: * nuclear facility.
040: *
041: * $Revision: 1.1 $
042: * $Date: 2005/02/11 05:01:55 $
043: * $State: Exp $
044: */
045: package com.sun.media.jai.imageioimpl;
046:
047: import java.awt.Dimension;
048: import java.awt.RenderingHints;
049: import java.awt.image.BufferedImage;
050: import java.awt.image.RenderedImage;
051: import java.awt.image.renderable.ParameterBlock;
052: import java.awt.image.renderable.RenderableImage;
053: import java.awt.image.renderable.RenderContext;
054: import java.io.IOException;
055: import java.io.OutputStream;
056: import java.io.RandomAccessFile;
057: import java.net.Socket;
058: import java.util.Arrays;
059: import java.util.EventListener;
060: import java.util.Iterator;
061: import java.util.Locale;
062: import javax.imageio.IIOImage;
063: import javax.imageio.ImageIO;
064: import javax.imageio.ImageTypeSpecifier;
065: import javax.imageio.ImageWriter;
066: import javax.imageio.ImageWriteParam;
067: import javax.imageio.event.IIOWriteProgressListener;
068: import javax.imageio.event.IIOWriteWarningListener;
069: import javax.imageio.metadata.IIOMetadata;
070: import javax.imageio.stream.ImageOutputStream;
071: import javax.media.jai.CRIFImpl;
072: import javax.media.jai.PlanarImage;
073: import javax.media.jai.RenderedImageAdapter;
074: import javax.media.jai.WritablePropertySource;
075: import com.sun.media.jai.operator.ImageWriteDescriptor;
076:
077: public final class ImageWriteCRIF extends CRIFImpl {
078: public static void main(String[] args) throws Throwable {
079: java.io.File inFile = new java.io.File(args[0]);
080: java.io.File outFile = new java.io.File(args[1]);
081: String format = args.length > 2 ? args[2] : "png";
082: String mode = args.length > 3 ? args[3] : "rendered";
083:
084: int imageIndex = 0;
085:
086: javax.imageio.stream.ImageInputStream inStream = javax.imageio.ImageIO
087: .createImageInputStream(inFile);
088:
089: java.util.Iterator iter = javax.imageio.ImageIO
090: .getImageReaders(inStream);
091: javax.imageio.ImageReader reader = (javax.imageio.ImageReader) iter
092: .next();
093:
094: reader.setInput(inStream);
095:
096: RenderedImage image = reader.read(imageIndex);
097:
098: javax.imageio.metadata.IIOMetadata streamMetadata = reader
099: .getStreamMetadata();
100: javax.imageio.metadata.IIOMetadata imageMetadata = reader
101: .getImageMetadata(imageIndex);
102:
103: java.awt.image.BufferedImage[] thumbnails = null;
104: if (reader.hasThumbnails(imageIndex)) {
105: int numThumbnails = reader.getNumThumbnails(imageIndex);
106: thumbnails = new java.awt.image.BufferedImage[numThumbnails];
107: for (int i = 0; i < numThumbnails; i++) {
108: thumbnails[i] = reader.readThumbnail(imageIndex, i);
109: }
110: }
111:
112: ImageWriteCRIF crif = new ImageWriteCRIF();
113:
114: ParameterBlock pb = new ParameterBlock();
115:
116: if (mode.equalsIgnoreCase("rendered")) {
117: pb.addSource(image);
118: } else if (mode.equalsIgnoreCase("renderable")) {
119: ParameterBlock renderablePB = new ParameterBlock();
120: renderablePB.addSource(image);
121: RenderableImage renderable = javax.media.jai.JAI
122: .createRenderable("renderable", renderablePB);
123: pb.addSource(renderable);
124: }
125:
126: pb.add(outFile); // Output
127: pb.add(format); // Format
128:
129: pb.add(Boolean.TRUE); // UseProperties
130: pb.add(Boolean.TRUE); // Transcode
131: pb.add(Boolean.TRUE); // VerifyOutput
132: pb.add(Boolean.TRUE); // AllowPixelReplacement
133:
134: pb.add(null); // TileSize
135:
136: pb.add(streamMetadata);
137: pb.add(imageMetadata);
138: pb.add(thumbnails);
139:
140: pb.add(null); // EventListener[]
141: pb.add(null); // Locale
142:
143: pb.add(null); // ImageWriteParam
144: pb.add(null); // ImageWriter
145:
146: if (mode.equalsIgnoreCase("rendered")) {
147: crif.create(pb, null);
148: } else if (mode.equalsIgnoreCase("renderable")) {
149: java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform(
150: 256, 0, 0, 512, 0, 0);
151: crif.create(new RenderContext(transform), pb);
152: }
153: }
154:
155: public ImageWriteCRIF() {
156: super ();
157: }
158:
159: /**
160: * Attempt to create an {@link ImageOutputStream} for the supplied
161: * output. The following sequence is effected:
162: * <ol>
163: * <li><ul>
164: * <li>If <code>output</code> is an <code>ImageOutputStream</code> it
165: * is cast and returned.</li>
166: * <li>If <code>output</code> is a <code>String</code> it is converted
167: * to a read-write <code>RandomAccessFile</code>.</li>
168: * <li>If <code>output</code> is a <code>Socket</code> it is converted
169: * to an <code>OutputStream</code>.</li>
170: * </ul></li>
171: * <li><code>ImageIO.createImageOutputStream()</code> is invoked
172: * with parameter set to the (possibly converted) output and the
173: * value it returns (which could be <code>null</code>) is returned
174: * to the caller.</li>
175: * </ol>
176: *
177: * @param output An <code>Object</code> to be used as the destination,
178: * such as a <code>String</code>, <code>File</code>, writable
179: * <code>RandomAccessFile</code>, <code>OutputStream</code>, writable
180: * <code>Socket</code>, or writable <code>Channel</code>.
181: *
182: * @return An <code>ImageOutputStream</code> or <code>null</code>.
183: */
184: static ImageOutputStream getImageOutputStream(Object output) {
185: // The value to be returned.
186: ImageOutputStream stream = null;
187:
188: // If already an ImageOutputStream just cast.
189: if (output instanceof ImageOutputStream) {
190: stream = (ImageOutputStream) output;
191: } else {
192: if (output instanceof String) {
193: // If output is a String replace it with a RandomAccessFile.
194: try {
195: // 'output' is conditionally checked for writability
196: // in the OperationDescriptor.
197: output = new RandomAccessFile((String) output, "rw");
198: } catch (Exception e) {
199: throw new RuntimeException(I18N
200: .getString("ImageWriteCRIF0")
201: + " " + output);
202: }
203: } else if (output instanceof Socket) {
204: // If output is a Socket replace it with an OutputStream.
205: try {
206: // XXX check binding, connection, closed, shutdown
207: // as these could have changed.
208: output = ((Socket) output).getOutputStream();
209: } catch (Exception e) {
210: throw new RuntimeException(I18N
211: .getString("ImageWriteCRIF1")
212: + " " + output);
213: }
214: }
215:
216: // Create the ImageOutputStream.
217: try {
218: stream = ImageIO.createImageOutputStream(output);
219: } catch (IOException e) {
220: throw new RuntimeException(e);
221: }
222: }
223:
224: return stream;
225: }
226:
227: /**
228: * {@link RenderedImageFactory} implementation.
229: */
230: public RenderedImage create(ParameterBlock pb, RenderingHints rh) {
231: return create(0, false, pb, rh);
232: }
233:
234: private static ImageWriteParam getWriteParam(ImageWriteParam param,
235: ImageWriter writer) {
236: // Set default to original ImageWriteParam.
237: ImageWriteParam newParam = param;
238:
239: if (param == null) {
240: newParam = writer.getDefaultWriteParam();
241: } else if (param.getClass().getName().equals(
242: "javax.imageio.ImageWriteParam")) {
243: // The ImageWriteParam passed in is non-null. As the
244: // ImageWriteParam class is not Cloneable, if the param
245: // class is simply ImageWriteParam, then create a new
246: // ImageWriteParam instance and set all its fields
247: // which were set in param. This will eliminate problems
248: // with concurrent modification of param for the cases
249: // in which there is not a special ImageWriteParam used.
250:
251: // Create a new ImageWriteParam instance.
252: newParam = writer.getDefaultWriteParam();
253:
254: // Set all fields which need to be set.
255:
256: // IIOParamController field.
257: if (param.hasController()) {
258: newParam.setController(param.getController());
259: }
260:
261: // Destination fields.
262: newParam.setDestinationOffset(param.getDestinationOffset());
263: newParam.setDestinationType(param.getDestinationType());
264:
265: // Source fields.
266: newParam.setSourceBands(param.getSourceBands());
267: newParam.setSourceRegion(param.getSourceRegion());
268: newParam.setSourceSubsampling(
269: param.getSourceXSubsampling(), param
270: .getSourceYSubsampling(), param
271: .getSubsamplingXOffset(), param
272: .getSubsamplingYOffset());
273:
274: // Compression.
275: if (param.canWriteCompressed()) {
276: int compressionMode = param.getCompressionMode();
277: newParam.setCompressionMode(compressionMode);
278: if (compressionMode == ImageWriteParam.MODE_EXPLICIT) {
279: newParam.setCompressionQuality(param
280: .getCompressionQuality());
281: newParam.setCompressionType(param
282: .getCompressionType());
283: }
284: }
285:
286: // Progressive
287: if (param.canWriteProgressive()) {
288: newParam.setProgressiveMode(param.getProgressiveMode());
289: }
290:
291: // Tiling
292: if (param.canWriteTiles()) {
293: int tilingMode = param.getTilingMode();
294: newParam.setTilingMode(tilingMode);
295: if (tilingMode == ImageWriteParam.MODE_EXPLICIT) {
296: newParam.setTiling(param.getTileWidth(), param
297: .getTileHeight(), param
298: .getTileGridXOffset(), param
299: .getTileGridYOffset());
300: }
301: }
302: }
303:
304: return newParam;
305: }
306:
307: /**
308: * If tiling is supported, determine the appropriate tile size and
309: * set it on the returned param if necessary. The returned param
310: * will either be a new ImageWriteParam or the one passed in with
311: * its tiling settings possibly modified.
312: */
313: private static ImageWriteParam setTileSize(ImageWriteParam param,
314: ImageWriter writer, Dimension tileSize, RenderedImage source) {
315:
316: ImageWriteParam returnParam = getWriteParam(param, writer);
317:
318: // If tiling possible set tile size if needed.
319: if (returnParam.canWriteTiles()) {
320: if (tileSize != null) {
321: // Check tile size.
322: if (tileSize.width <= 0 || tileSize.height <= 0) {
323: throw new IllegalArgumentException(
324: "tileSize.width <= 0 || tileSize.height <= 0");
325: }
326:
327: // Use specified tile size.
328: returnParam
329: .setTilingMode(ImageWriteParam.MODE_EXPLICIT);
330: returnParam.setTiling(tileSize.width, tileSize.height,
331: 0, 0); // XXX set tile offsets?
332: } else if (param == null) {
333: if (source.getNumXTiles() > 1
334: || source.getNumYTiles() > 1) {
335: // Null tile size and param args: use source tile size.
336: returnParam
337: .setTilingMode(ImageWriteParam.MODE_EXPLICIT);
338: returnParam.setTiling(source.getTileWidth(), source
339: .getTileHeight(), 0, 0); // XXX set tile offsets?
340: }
341: } else if (returnParam.getTilingMode() == ImageWriteParam.MODE_EXPLICIT) {
342: // Param passed in has explicit mode set but the tile
343: // grid might not actually be set.
344: boolean setTileSize = false;
345:
346: // Save reference to preferred tile size array.
347: Dimension[] preferredTileSizes = returnParam
348: .getPreferredTileSizes();
349:
350: // Set the tile width.
351: int tileWidth = 0;
352: try {
353: // Try to get it from the param.
354: tileWidth = returnParam.getTileWidth();
355: } catch (IllegalStateException e) {
356: // Not set in the param.
357: setTileSize = true;
358:
359: if (preferredTileSizes != null
360: && preferredTileSizes.length >= 2
361: && preferredTileSizes[0].width > 0
362: && preferredTileSizes[1].width > 0) {
363: // Use average of first two preferred tile widths.
364: tileWidth = (preferredTileSizes[0].width + preferredTileSizes[1].width) / 2;
365: } else {
366: // Use source image tile width.
367: tileWidth = source.getTileWidth();
368: }
369: }
370:
371: // Set the tile height.
372: int tileHeight = 0;
373: try {
374: // Try to get it from the param.
375: tileHeight = returnParam.getTileHeight();
376: } catch (IllegalStateException e) {
377: // Not set in the param.
378: setTileSize = true;
379:
380: if (preferredTileSizes != null
381: && preferredTileSizes.length >= 2
382: && preferredTileSizes[0].height > 0
383: && preferredTileSizes[1].height > 0) {
384: // Use average of first two preferred tile heights.
385: tileHeight = (preferredTileSizes[0].height + preferredTileSizes[1].height) / 2;
386: } else {
387: // Use source image tile height.
388: tileHeight = source.getTileHeight();
389: }
390: }
391:
392: // Set the tile size if not previously set in the param.
393: if (setTileSize) {
394: returnParam.setTiling(tileWidth, tileHeight, 0, 0); // XXX set tile offsets?
395: }
396: }
397: }
398:
399: return returnParam;
400: }
401:
402: static RenderedImage create(int imageIndex,
403: boolean writeToSequence, ParameterBlock pb,
404: RenderingHints rh) {
405:
406: // Value to be returned.
407: RenderedImage image = null;
408:
409: // Get the source image.
410: RenderedImage source = pb.getRenderedSource(0);
411:
412: // Get the writer parameters.
413: ImageWriteParam param = (ImageWriteParam) pb
414: .getObjectParameter(12);
415:
416: // Set the target image type.
417: ImageTypeSpecifier destinationType = null;
418: if (param != null) {
419: destinationType = param.getDestinationType();
420: }
421: if (destinationType == null) {
422: destinationType = new ImageTypeSpecifier(source);
423: }
424:
425: // Get the writer.
426: ImageWriter writer = (ImageWriter) pb.getObjectParameter(13);
427:
428: if (writer == null) {
429: // Get the format. Should be non-null from OperationDescriptor.
430: String format = (String) pb.getObjectParameter(1);
431:
432: // Find a writer.
433: Iterator writers = ImageIO.getImageWriters(destinationType,
434: format);
435:
436: // Get the writer.
437: if (writers != null && writers.hasNext()) {
438: writer = (ImageWriter) writers.next();
439: }
440: }
441:
442: // XXX What if no writer? Exception?
443: if (writer != null) {
444: // XXX Replace ImageWriter parameter in ParameterBlock?
445:
446: ImageOutputStream streamToClose = null;
447:
448: // Set the output if not writing to a sequence (in which
449: // case the output should already be set.
450: if (!writeToSequence) {
451: // Get the output.
452: Object output = pb.getObjectParameter(0);
453:
454: // Try to get an ImageOutputStream.
455: ImageOutputStream stream = getImageOutputStream(output);
456:
457: // Set stream to close if not writing to a sequence.
458: streamToClose = stream != output ? stream : null;
459:
460: // Set the writer's output.
461: writer.setOutput(stream != null ? stream : output);
462: }
463:
464: // Get the property use flag.
465: boolean useProperties = ((Boolean) pb.getObjectParameter(2))
466: .booleanValue();
467:
468: // Get the transcoding flag.
469: boolean transcode = ((Boolean) pb.getObjectParameter(3))
470: .booleanValue();
471:
472: IIOMetadata streamMetadata = null;
473: if (!writeToSequence) {
474: // Get the stream metadata.
475: streamMetadata = (IIOMetadata) pb.getObjectParameter(7);
476:
477: // If null, get stream metadata from source properties
478: // if allowed.
479: if (streamMetadata == null && useProperties) {
480: Object streamMetadataProperty = source
481: .getProperty(ImageWriteDescriptor.PROPERTY_NAME_METADATA_STREAM);
482: if (streamMetadataProperty instanceof IIOMetadata) {
483: streamMetadata = (IIOMetadata) streamMetadataProperty;
484: }
485: }
486:
487: // Transcode the stream metadata if requested.
488: if (streamMetadata != null && transcode) {
489: // Overwrite the stream metadata with transcoded metadata.
490: streamMetadata = writer.convertStreamMetadata(
491: streamMetadata, param);
492: }
493: }
494:
495: // Get the image metadata.
496: IIOMetadata imageMetadata = (IIOMetadata) pb
497: .getObjectParameter(8);
498:
499: // If null, get image metadata from source properties if allowed.
500: if (imageMetadata == null && useProperties) {
501: Object imageMetadataProperty = source
502: .getProperty(ImageWriteDescriptor.PROPERTY_NAME_METADATA_IMAGE);
503: if (imageMetadataProperty instanceof IIOMetadata) {
504: imageMetadata = (IIOMetadata) imageMetadataProperty;
505: }
506: }
507:
508: // Transcode the image metadata if requested.
509: if (imageMetadata != null && transcode) {
510: // Overwrite the image metadata with transcoded metadata.
511: imageMetadata = writer.convertImageMetadata(
512: imageMetadata, destinationType, param);
513: }
514:
515: // Get the thumbnails if supported by the writer.
516: BufferedImage[] thumbnails = null;
517: if (writer.getNumThumbnailsSupported(destinationType,
518: param, streamMetadata, imageMetadata) > 0) {
519: thumbnails = (BufferedImage[]) pb.getObjectParameter(9);
520:
521: // If null, get thumbnails from source properties if allowed.
522: if (thumbnails == null && useProperties) {
523: Object thumbnailsProperty = source
524: .getProperty(ImageWriteDescriptor.PROPERTY_NAME_METADATA_IMAGE);
525: if (thumbnailsProperty instanceof BufferedImage[]) {
526: thumbnails = (BufferedImage[]) thumbnailsProperty;
527: }
528: }
529: }
530:
531: // Get the locale parameter and set on the writer.
532: Locale locale = (Locale) pb.getObjectParameter(11);
533: if (locale != null) {
534: writer.setLocale(locale);
535: }
536:
537: // Get the listeners parameter and set on the writer.
538: EventListener[] listeners = (EventListener[]) pb
539: .getObjectParameter(10);
540: if (listeners != null) {
541: for (int i = 0; i < listeners.length; i++) {
542: EventListener listener = listeners[i];
543: if (listener instanceof IIOWriteProgressListener) {
544: writer
545: .addIIOWriteProgressListener((IIOWriteProgressListener) listener);
546: }
547: if (listener instanceof IIOWriteWarningListener) {
548: writer
549: .addIIOWriteWarningListener((IIOWriteWarningListener) listener);
550: }
551: }
552: }
553:
554: // Set the tile size.
555: // XXX Replace ImageWriteParam parameter in ParameterBlock?
556: param = setTileSize(param, writer, (Dimension) pb
557: .getObjectParameter(6), source);
558:
559: // Create the IIOImage container.
560: IIOImage iioImage = new IIOImage(source,
561: thumbnails != null ? Arrays.asList(thumbnails)
562: : null, imageMetadata);
563:
564: try {
565: // Write the image.
566: if (writeToSequence) {
567: writer.writeToSequence(iioImage, param);
568: } else {
569: writer.write(streamMetadata, iioImage, param);
570: }
571:
572: // Get the pixel replacement parameter.
573: boolean allowPixelReplacement = ((Boolean) pb
574: .getObjectParameter(5)).booleanValue();
575:
576: // Set the return value.
577: if (allowPixelReplacement
578: && source instanceof PlanarImage
579: && writer.canReplacePixels(imageIndex)) {
580:
581: // Create an image which is a PropertyChangeListener of
582: // "invalidregion" events including RenderingChangeEvents.
583: image = new PixelReplacementImage(source, rh,
584: param, writer, imageIndex, streamToClose);
585:
586: // Register the image as a sink of its source so that
587: // it automatically receives events.
588: ((PlanarImage) source).addSink(image);
589: } else if (!writeToSequence) {
590: Object writerOutput = writer.getOutput();
591: if (writerOutput != pb.getObjectParameter(0)
592: && writerOutput instanceof ImageOutputStream) {
593: // This block is executed if and only if pixel
594: // replacement is not occurring, a sequence is
595: // not being written, and an ImageOutputStream
596: // inaccessible to the application is set on the
597: // ImageWriter.
598: ((ImageOutputStream) writerOutput).flush();
599: }
600:
601: // Set the return value to the original image or
602: // a wrapped version thereof.
603: image = source instanceof WritablePropertySource ? source
604: : new RenderedImageAdapter(source);
605: }
606:
607: // Set required properties.
608: WritablePropertySource wps = (WritablePropertySource) image;
609:
610: // Set the ImageWriteParam property.
611: wps
612: .setProperty(
613: ImageWriteDescriptor.PROPERTY_NAME_IMAGE_WRITE_PARAM,
614: param);
615:
616: // Set the ImageWriter property.
617: wps
618: .setProperty(
619: ImageWriteDescriptor.PROPERTY_NAME_IMAGE_WRITER,
620: writer);
621:
622: // Set the stream metadata property.
623: if (streamMetadata != null) {
624: wps
625: .setProperty(
626: ImageWriteDescriptor.PROPERTY_NAME_METADATA_STREAM,
627: streamMetadata);
628: }
629:
630: // Set the image metadata property.
631: if (imageMetadata != null) {
632: wps
633: .setProperty(
634: ImageWriteDescriptor.PROPERTY_NAME_METADATA_IMAGE,
635: imageMetadata);
636: }
637:
638: // Set the thumbnail property.
639: if (thumbnails != null) {
640: wps
641: .setProperty(
642: ImageWriteDescriptor.PROPERTY_NAME_THUMBNAILS,
643: thumbnails);
644: }
645: } catch (IOException e) {
646: throw new RuntimeException(e);
647: }
648: }
649:
650: return image;
651: }
652: }
|