001: /*
002: * $RCSfile: FileChannelImageOutputStream.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:20 $
043: * $State: Exp $
044: */
045: package com.sun.media.imageio.stream;
046:
047: import java.io.File;
048: import java.io.IOException;
049: import java.nio.ByteBuffer;
050: import java.nio.ByteOrder;
051: import java.nio.CharBuffer;
052: import java.nio.DoubleBuffer;
053: import java.nio.FloatBuffer;
054: import java.nio.IntBuffer;
055: import java.nio.LongBuffer;
056: import java.nio.ShortBuffer;
057: import java.nio.channels.FileChannel;
058: import javax.imageio.stream.ImageInputStream;
059: import javax.imageio.stream.ImageOutputStreamImpl;
060:
061: /**
062: * A class which implements <code>ImageOutputStream</code> using a
063: * <code>FileChannel</code> as the eventual data destination.
064: *
065: * <p>Memory mapping is used for reading and direct buffers for writing.
066: * Only methods which provide significant performance improvement with
067: * respect to the superclass implementation are overridden. Overridden
068: * methods are not commented individually unless some noteworthy aspect
069: * of the implementation must be described.</p>
070: *
071: * <p>The methods of this class are <b>not</b> synchronized.</p>
072: *
073: * @see javax.imageio.stream.ImageOutputStream
074: * @see java.nio
075: * @see java.nio.channels.FileChannel
076: */
077: public class FileChannelImageOutputStream extends ImageOutputStreamImpl {
078:
079: /** The size of the write buffer. */
080: // XXX Should this be a different value? Smaller?
081: // Should there be an instance variable with public
082: // accessor/mutator methods?
083: private static final int DEFAULT_WRITE_BUFFER_SIZE = 1048576;
084:
085: /** The <code>FileChannel</code> data destination. */
086: private FileChannel channel;
087:
088: /** A <code>ByteBuffer</code> used for writing to the channel. */
089: private ByteBuffer byteBuffer;
090:
091: /** An <code>ImageInputStream</code> used for reading. */
092: private ImageInputStream readStream = null;
093:
094: /**
095: * Test method.
096: *
097: * @param args Command line arguments (ignored).
098: * @throws Throwable for any Exception or Error.
099: */
100: /* XXX
101: public static void main(String[] args) throws Throwable {
102: // Create files.
103: java.io.RandomAccessFile fileFIOS =
104: new java.io.RandomAccessFile("fios.tmp", "rw");
105: java.io.RandomAccessFile fileFCIOS =
106: new java.io.RandomAccessFile("fcios.tmp", "rw");
107:
108: // Create streams.
109: javax.imageio.stream.ImageOutputStream fios =
110: new javax.imageio.stream.FileImageOutputStream(fileFIOS);
111: javax.imageio.stream.ImageOutputStream fcios =
112: new FileChannelImageOutputStream(fileFCIOS.getChannel());
113:
114: int datumSize = 4;
115:
116: // Write some ints (257, 258)
117: fios.writeInts(new int[] {(int)256, (int)257,
118: (int)258, (int)59},
119: 1, 2);
120: fcios.writeInts(new int[] {(int)256, (int)257,
121: (int)258, (int)259},
122: 1, 2);
123:
124: // Write a byte.
125: fios.write(127);
126: fcios.write(127);
127:
128: // Write some more ints (262, 263, 264).
129: fios.writeInts(new int[] {(int)260, (int)261, (int)262,
130: (int)263, (int)264, (int)265}, 2, 3);
131: fcios.writeInts(new int[] {(int)260, (int)261, (int)262,
132: (int)263, (int)264, (int)265}, 2, 3);
133:
134: // Seek to byte location.
135: fios.seek(2*datumSize);
136: fcios.seek(2*datumSize);
137:
138: // Read and print the byte.
139: System.out.println(fios.read());
140: System.out.println(fcios.read());
141:
142: // Seek to beginning.
143: fios.seek(0);
144: fcios.seek(0);
145:
146: int[] fiosInts = new int[10];
147: int[] fciosInts = new int[10];
148:
149: // Read the ints.
150: fios.readFully(fiosInts, 1, 2);
151: fcios.readFully(fciosInts, 1, 2);
152:
153: // Print the ints
154: for(int i = 0; i < 2; i++) {
155: System.out.println((int)fiosInts[i+1]+" "+(int)fciosInts[i+1]);
156: }
157:
158: // Seek to second set of ints
159: fios.seek(2*datumSize+1);
160: fcios.seek(2*datumSize+1);
161:
162: // Read the ints.
163: fios.readFully(fiosInts, 1, 3);
164: fcios.readFully(fciosInts, 1, 3);
165:
166: // Print the ints
167: for(int i = 0; i < 3; i++) {
168: System.out.println((int)fiosInts[i+1]+" "+(int)fciosInts[i+1]);
169: }
170: }
171: */
172:
173: /**
174: * Constructs a <code>FileChannelImageOutputStream</code> from a
175: * <code>FileChannel</code>. The initial position of the stream
176: * stream is taken to be the position of the <code>FileChannel</code>
177: * parameter when this constructor is invoked. The stream and flushed
178: * positions are therefore both initialized to
179: * <code>channel.position()</code>.
180: *
181: * @param channel the destination <code>FileChannel</code>.
182: *
183: * @throws IllegalArgumentException if <code>channel</code> is
184: * <code>null</code> or is not open.
185: * @throws IOException if a method invoked on <code>channel</code>
186: * throws an <code>IOException</code>.
187: */
188: public FileChannelImageOutputStream(FileChannel channel)
189: throws IOException {
190:
191: // Check the parameter.
192: if (channel == null) {
193: throw new IllegalArgumentException("channel == null");
194: } else if (!channel.isOpen()) {
195: throw new IllegalArgumentException(
196: "channel.isOpen() == false");
197: }
198:
199: // Save the channel reference.
200: this .channel = channel;
201:
202: // Set stream and flushed positions to initial channel position.
203: this .streamPos = this .flushedPos = channel.position();
204:
205: // Allocate the write buffer.
206: byteBuffer = ByteBuffer
207: .allocateDirect(DEFAULT_WRITE_BUFFER_SIZE);
208:
209: // Create the read stream (initially zero-sized).
210: readStream = new FileChannelImageInputStream(channel);
211: }
212:
213: /**
214: * Returns an <code>ImageInputStream</code> for reading. The
215: * returned stream has byte order, stream and flushed positions,
216: * and bit offset set to the current values for this stream.
217: */
218: private ImageInputStream getImageInputStream() throws IOException {
219: // Write any unwritten bytes.
220: flushBuffer();
221:
222: // Sync input stream state to state of this stream.
223: readStream.setByteOrder(byteOrder);
224: readStream.seek(streamPos);
225: readStream.flushBefore(flushedPos);
226: readStream.setBitOffset(bitOffset);
227:
228: return readStream;
229: }
230:
231: /**
232: * Write to the <code>FileChannel</code> any remaining bytes in
233: * the byte output buffer.
234: */
235: private void flushBuffer() throws IOException {
236: if (byteBuffer.position() != 0) {
237: // Set the limit to the position.
238: byteBuffer.limit(byteBuffer.position());
239:
240: // Set the position to zero.
241: byteBuffer.position(0);
242:
243: // Write all bytes between zero and the previous position.
244: channel.write(byteBuffer);
245:
246: // Prepare for subsequent put() calls if any.
247: byteBuffer.clear();
248: }
249: }
250:
251: // --- Implementation of superclass abstract methods. ---
252:
253: public int read() throws IOException {
254: checkClosed();
255: bitOffset = 0;
256:
257: ImageInputStream inputStream = getImageInputStream();
258:
259: streamPos++;
260:
261: return inputStream.read();
262: }
263:
264: public int read(byte[] b, int off, int len) throws IOException {
265: // Check parameters.
266: if (off < 0 || len < 0 || off + len > b.length) {
267: throw new IndexOutOfBoundsException(
268: "off < 0 || len < 0 || off + len > b.length");
269: } else if (len == 0) {
270: return 0;
271: }
272:
273: checkClosed();
274: bitOffset = 0;
275:
276: ImageInputStream inputStream = getImageInputStream();
277:
278: int numBytesRead = inputStream.read(b, off, len);
279:
280: streamPos += numBytesRead;
281:
282: return numBytesRead;
283: }
284:
285: public void write(int b) throws IOException {
286: write(new byte[] { (byte) (b & 0xff) }, 0, 1);
287: }
288:
289: public void write(byte[] b, int off, int len) throws IOException {
290:
291: // Check parameters.
292: if (off < 0 || len < 0 || off + len > b.length) {
293: // NullPointerException will be thrown before this if b is null.
294: throw new IndexOutOfBoundsException(
295: "off < 0 || len < 0 || off + len > b.length");
296: } else if (len == 0) {
297: return;
298: }
299:
300: // Flush any bits in the current byte.
301: flushBits();
302:
303: // Zero the number of bytes to put.
304: int numPut = 0;
305:
306: // Loop until all bytes have been put.
307: do {
308: // Determine number of bytes to put.
309: int numToPut = Math.min(len - numPut, byteBuffer
310: .remaining());
311:
312: // If no bytes to put, the buffer has to be full as len
313: // is always greater than numPut so flush it and return
314: // to start of loop.
315: if (numToPut == 0) {
316: flushBuffer();
317: continue;
318: }
319:
320: // Put the bytes in the buffer.
321: byteBuffer.put(b, off + numPut, numToPut);
322:
323: // Increment the put counter.
324: numPut += numToPut;
325: } while (numPut < len);
326:
327: // Increment the stream position.
328: streamPos += len;
329: }
330:
331: // --- Overriding of superclass methods. ---
332:
333: // --- Bulk read methods ---
334:
335: public void readFully(char[] c, int off, int len)
336: throws IOException {
337: getImageInputStream().readFully(c, off, len);
338: streamPos += 2 * len;
339: }
340:
341: public void readFully(short[] s, int off, int len)
342: throws IOException {
343: getImageInputStream().readFully(s, off, len);
344: streamPos += 2 * len;
345: }
346:
347: public void readFully(int[] i, int off, int len) throws IOException {
348: getImageInputStream().readFully(i, off, len);
349: streamPos += 4 * len;
350: }
351:
352: public void readFully(long[] l, int off, int len)
353: throws IOException {
354: getImageInputStream().readFully(l, off, len);
355: streamPos += 8 * len;
356: }
357:
358: public void readFully(float[] f, int off, int len)
359: throws IOException {
360: getImageInputStream().readFully(f, off, len);
361: streamPos += 4 * len;
362: }
363:
364: public void readFully(double[] d, int off, int len)
365: throws IOException {
366: getImageInputStream().readFully(d, off, len);
367: streamPos += 8 * len;
368: }
369:
370: // --- Bulk write methods ---
371:
372: public void writeChars(char[] c, int off, int len)
373: throws IOException {
374:
375: // Check parameters.
376: if (off < 0 || len < 0 || off + len > c.length) {
377: // NullPointerException will be thrown before this if c is null.
378: throw new IndexOutOfBoundsException(
379: "off < 0 || len < 0 || off + len > c.length");
380: } else if (len == 0) {
381: return;
382: }
383:
384: // Flush any bits in the current byte.
385: flushBits();
386:
387: // Zero the number of chars put.
388: int numPut = 0;
389:
390: // Get view buffer.
391: CharBuffer viewBuffer = byteBuffer.asCharBuffer();
392:
393: // Loop until all chars have been put.
394: do {
395: // Determine number of chars to put.
396: int numToPut = Math.min(len - numPut, viewBuffer
397: .remaining());
398:
399: // If no chars to put, the buffer has to be full as len
400: // is always greater than numPut so flush it and return
401: // to start of loop.
402: if (numToPut == 0) {
403: flushBuffer();
404: continue;
405: }
406:
407: // Put the chars in the buffer.
408: viewBuffer.put(c, off + numPut, numToPut);
409:
410: // Sync the ByteBuffer position.
411: byteBuffer.position(byteBuffer.position() + 2 * numToPut);
412:
413: // Increment the put counter.
414: numPut += numToPut;
415: } while (numPut < len);
416:
417: // Increment the stream position.
418: streamPos += 2 * len;
419: }
420:
421: public void writeShorts(short[] s, int off, int len)
422: throws IOException {
423:
424: // Check parameters.
425: if (off < 0 || len < 0 || off + len > s.length) {
426: // NullPointerException will be thrown before this if s is null.
427: throw new IndexOutOfBoundsException(
428: "off < 0 || len < 0 || off + len > c.length");
429: } else if (len == 0) {
430: return;
431: }
432:
433: // Flush any bits in the current byte.
434: flushBits();
435:
436: // Zero the number of shorts put.
437: int numPut = 0;
438:
439: // Get view buffer.
440: ShortBuffer viewBuffer = byteBuffer.asShortBuffer();
441:
442: // Loop until all shorts have been put.
443: do {
444: // Determine number of shorts to put.
445: int numToPut = Math.min(len - numPut, viewBuffer
446: .remaining());
447:
448: // If no shorts to put, the buffer has to be full as len
449: // is always greater than numPut so flush it and return
450: // to start of loop.
451: if (numToPut == 0) {
452: flushBuffer();
453: continue;
454: }
455:
456: // Put the shorts in the buffer.
457: viewBuffer.put(s, off + numPut, numToPut);
458:
459: // Sync the ByteBuffer position.
460: byteBuffer.position(byteBuffer.position() + 2 * numToPut);
461:
462: // Increment the put counter.
463: numPut += numToPut;
464: } while (numPut < len);
465:
466: // Increment the stream position.
467: streamPos += 2 * len;
468: }
469:
470: public void writeInts(int[] i, int off, int len) throws IOException {
471:
472: // Check parameters.
473: if (off < 0 || len < 0 || off + len > i.length) {
474: // NullPointerException will be thrown before this if i is null.
475: throw new IndexOutOfBoundsException(
476: "off < 0 || len < 0 || off + len > c.length");
477: } else if (len == 0) {
478: return;
479: }
480:
481: // Flush any bits in the current byte.
482: flushBits();
483:
484: // Zero the number of ints put.
485: int numPut = 0;
486:
487: // Get view buffer.
488: IntBuffer viewBuffer = byteBuffer.asIntBuffer();
489:
490: // Loop until all ints have been put.
491: do {
492: // Determine number of ints to put.
493: int numToPut = Math.min(len - numPut, viewBuffer
494: .remaining());
495:
496: // If no ints to put, the buffer has to be full as len
497: // is always greater than numPut so flush it and return
498: // to start of loop.
499: if (numToPut == 0) {
500: flushBuffer();
501: continue;
502: }
503:
504: // Put the ints in the buffer.
505: viewBuffer.put(i, off + numPut, numToPut);
506:
507: // Sync the ByteBuffer position.
508: byteBuffer.position(byteBuffer.position() + 4 * numToPut);
509:
510: // Increment the put counter.
511: numPut += numToPut;
512: } while (numPut < len);
513:
514: // Increment the stream position.
515: streamPos += 4 * len;
516: }
517:
518: public void writeLongs(long[] l, int off, int len)
519: throws IOException {
520:
521: // Check parameters.
522: if (off < 0 || len < 0 || off + len > l.length) {
523: // NullPointerException will be thrown before this if l is null.
524: throw new IndexOutOfBoundsException(
525: "off < 0 || len < 0 || off + len > c.length");
526: } else if (len == 0) {
527: return;
528: }
529:
530: // Flush any bits in the current byte.
531: flushBits();
532:
533: // Zero the number of longs put.
534: int numPut = 0;
535:
536: // Get view buffer.
537: LongBuffer viewBuffer = byteBuffer.asLongBuffer();
538:
539: // Loop until all longs have been put.
540: do {
541: // Determine number of longs to put.
542: int numToPut = Math.min(len - numPut, viewBuffer
543: .remaining());
544:
545: // If no longs to put, the buffer has to be full as len
546: // is always greater than numPut so flush it and return
547: // to start of loop.
548: if (numToPut == 0) {
549: flushBuffer();
550: continue;
551: }
552:
553: // Put the longs in the buffer.
554: viewBuffer.put(l, off + numPut, numToPut);
555:
556: // Sync the ByteBuffer position.
557: byteBuffer.position(byteBuffer.position() + 8 * numToPut);
558:
559: // Increment the put counter.
560: numPut += numToPut;
561: } while (numPut < len);
562:
563: // Increment the stream position.
564: streamPos += 8 * len;
565: }
566:
567: public void writeFloats(float[] f, int off, int len)
568: throws IOException {
569:
570: // Check parameters.
571: if (off < 0 || len < 0 || off + len > f.length) {
572: // NullPointerException will be thrown before this if c is null.
573: throw new IndexOutOfBoundsException(
574: "off < 0 || len < 0 || off + len > f.length");
575: } else if (len == 0) {
576: return;
577: }
578:
579: // Flush any bits in the current byte.
580: flushBits();
581:
582: // Zero the number of floats put.
583: int numPut = 0;
584:
585: // Get view buffer.
586: FloatBuffer viewBuffer = byteBuffer.asFloatBuffer();
587:
588: // Loop until all floats have been put.
589: do {
590: // Determine number of floats to put.
591: int numToPut = Math.min(len - numPut, viewBuffer
592: .remaining());
593:
594: // If no floats to put, the buffer has to be full as len
595: // is always greater than numPut so flush it and return
596: // to start of loop.
597: if (numToPut == 0) {
598: flushBuffer();
599: continue;
600: }
601:
602: // Put the floats in the buffer.
603: viewBuffer.put(f, off + numPut, numToPut);
604:
605: // Sync the ByteBuffer position.
606: byteBuffer.position(byteBuffer.position() + 4 * numToPut);
607:
608: // Increment the put counter.
609: numPut += numToPut;
610: } while (numPut < len);
611:
612: // Increment the stream position.
613: streamPos += 4 * len;
614: }
615:
616: public void writeDoubles(double[] d, int off, int len)
617: throws IOException {
618:
619: // Check parameters.
620: if (off < 0 || len < 0 || off + len > d.length) {
621: // NullPointerException will be thrown before this if d is null.
622: throw new IndexOutOfBoundsException(
623: "off < 0 || len < 0 || off + len > d.length");
624: } else if (len == 0) {
625: return;
626: }
627:
628: // Flush any bits in the current byte.
629: flushBits();
630:
631: // Zero the number of doubles put.
632: int numPut = 0;
633:
634: // Get view buffer.
635: DoubleBuffer viewBuffer = byteBuffer.asDoubleBuffer();
636:
637: // Loop until all doubles have been put.
638: do {
639: // Determine number of doubles to put.
640: int numToPut = Math.min(len - numPut, viewBuffer
641: .remaining());
642:
643: // If no doubles to put, the buffer has to be full as len
644: // is always greater than numPut so flush it and return
645: // to start of loop.
646: if (numToPut == 0) {
647: flushBuffer();
648: continue;
649: }
650:
651: // Put the doubles in the buffer.
652: viewBuffer.put(d, off + numPut, numToPut);
653:
654: // Sync the ByteBuffer position.
655: byteBuffer.position(byteBuffer.position() + 8 * numToPut);
656:
657: // Increment the put counter.
658: numPut += numToPut;
659: } while (numPut < len);
660:
661: // Increment the stream position.
662: streamPos += 8 * len;
663: }
664:
665: // --- Other methods ---
666:
667: /**
668: * Invokes the superclass method, writes any unwritten data, and
669: * sets the internal reference to the source <code>FileChannel</code>
670: * to <code>null</code>. The source <code>FileChannel</code> is not
671: * closed.
672: *
673: * @exception IOException if an error occurs.
674: */
675: // Note that this method is called by the superclass finalize()
676: // so this class does not need to implement finalize().
677: public void close() throws IOException {
678: // Flush any unwritten data in the buffer.
679: flushBuffer();
680:
681: // Close the read channel and clear the reference to it.
682: readStream.close();
683: readStream = null;
684:
685: // Clear reference to the channel.
686: channel = null;
687:
688: // Clear reference to the internal ByteBuffer.
689: byteBuffer = null;
690:
691: // Chain to the superclass.
692: super .close();
693: }
694:
695: /**
696: * Returns the number of bytes currently in the <code>FileChannel</code>.
697: * If an <code>IOException</code> is encountered when querying the
698: * channel's size, -1L will be returned.
699: *
700: * @return The number of bytes in the channel
701: * -1L to indicate unknown length.
702: */
703: public long length() {
704: // Initialize to value indicating unknown length.
705: long length = -1L;
706:
707: // Set length to current size with respect to initial position.
708: try {
709: length = channel.size();
710: } catch (IOException e) {
711: // Default to unknown length.
712: }
713:
714: return length;
715: }
716:
717: /**
718: * Invokes the superclass method, writes any unwritten data,
719: * and sets the channel position to the supplied parameter.
720: */
721: public void seek(long pos) throws IOException {
722: super .seek(pos);
723:
724: // Flush any unwritten data in the buffer.
725: flushBuffer();
726:
727: // Set the FileChannel position for WritableByteChannel.write().
728: channel.position(pos);
729: }
730:
731: public void setByteOrder(ByteOrder networkByteOrder) {
732: super.setByteOrder(networkByteOrder);
733: byteBuffer.order(networkByteOrder);
734: }
735: }
|