com.github.jaiimageio.stream.FileChannelImageOutputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jai-imageio-core Show documentation
Show all versions of jai-imageio-core Show documentation
Java Advanced Imaging Image I/O Tools API core, but without the classes
involved with javax.media.jai dependencies, JPEG2000 or
codecLibJIIO, meaning that this library can be distributed under the
modified BSD license and should be GPL compatible.
The newest version!
/*
* $RCSfile: FileChannelImageOutputStream.java,v $
*
*
* Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for
* use in the design, construction, operation or maintenance of any
* nuclear facility.
*
* $Revision: 1.1 $
* $Date: 2005/02/11 05:01:20 $
* $State: Exp $
*/
package com.github.jaiimageio.stream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.nio.channels.FileChannel;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStreamImpl;
/**
* A class which implements ImageOutputStream
using a
* FileChannel
as the eventual data destination.
*
* Memory mapping is used for reading and direct buffers for writing.
* Only methods which provide significant performance improvement with
* respect to the superclass implementation are overridden. Overridden
* methods are not commented individually unless some noteworthy aspect
* of the implementation must be described.
*
* The methods of this class are not synchronized.
*
* @see javax.imageio.stream.ImageOutputStream
* @see java.nio
* @see java.nio.channels.FileChannel
*/
public class FileChannelImageOutputStream extends ImageOutputStreamImpl {
/** The size of the write buffer. */
// XXX Should this be a different value? Smaller?
// Should there be an instance variable with public
// accessor/mutator methods?
private static final int DEFAULT_WRITE_BUFFER_SIZE = 1048576;
/** The FileChannel
data destination. */
private FileChannel channel;
/** A ByteBuffer
used for writing to the channel. */
private ByteBuffer byteBuffer;
/** An ImageInputStream
used for reading. */
private ImageInputStream readStream = null;
/**
* Test method.
*
* @param args Command line arguments (ignored).
* @throws Throwable for any Exception or Error.
*/
/* XXX
public static void main(String[] args) throws Throwable {
// Create files.
java.io.RandomAccessFile fileFIOS =
new java.io.RandomAccessFile("fios.tmp", "rw");
java.io.RandomAccessFile fileFCIOS =
new java.io.RandomAccessFile("fcios.tmp", "rw");
// Create streams.
javax.imageio.stream.ImageOutputStream fios =
new javax.imageio.stream.FileImageOutputStream(fileFIOS);
javax.imageio.stream.ImageOutputStream fcios =
new FileChannelImageOutputStream(fileFCIOS.getChannel());
int datumSize = 4;
// Write some ints (257, 258)
fios.writeInts(new int[] {(int)256, (int)257,
(int)258, (int)59},
1, 2);
fcios.writeInts(new int[] {(int)256, (int)257,
(int)258, (int)259},
1, 2);
// Write a byte.
fios.write(127);
fcios.write(127);
// Write some more ints (262, 263, 264).
fios.writeInts(new int[] {(int)260, (int)261, (int)262,
(int)263, (int)264, (int)265}, 2, 3);
fcios.writeInts(new int[] {(int)260, (int)261, (int)262,
(int)263, (int)264, (int)265}, 2, 3);
// Seek to byte location.
fios.seek(2*datumSize);
fcios.seek(2*datumSize);
// Read and print the byte.
System.out.println(fios.read());
System.out.println(fcios.read());
// Seek to beginning.
fios.seek(0);
fcios.seek(0);
int[] fiosInts = new int[10];
int[] fciosInts = new int[10];
// Read the ints.
fios.readFully(fiosInts, 1, 2);
fcios.readFully(fciosInts, 1, 2);
// Print the ints
for(int i = 0; i < 2; i++) {
System.out.println((int)fiosInts[i+1]+" "+(int)fciosInts[i+1]);
}
// Seek to second set of ints
fios.seek(2*datumSize+1);
fcios.seek(2*datumSize+1);
// Read the ints.
fios.readFully(fiosInts, 1, 3);
fcios.readFully(fciosInts, 1, 3);
// Print the ints
for(int i = 0; i < 3; i++) {
System.out.println((int)fiosInts[i+1]+" "+(int)fciosInts[i+1]);
}
}
*/
/**
* Constructs a FileChannelImageOutputStream
from a
* FileChannel
. The initial position of the stream
* stream is taken to be the position of the FileChannel
* parameter when this constructor is invoked. The stream and flushed
* positions are therefore both initialized to
* channel.position()
.
*
* @param channel the destination FileChannel
.
*
* @throws IllegalArgumentException if channel
is
* null
or is not open.
* @throws IOException if a method invoked on channel
* throws an IOException
.
*/
public FileChannelImageOutputStream(FileChannel channel)
throws IOException {
// Check the parameter.
if(channel == null) {
throw new IllegalArgumentException("channel == null");
} else if(!channel.isOpen()) {
throw new IllegalArgumentException("channel.isOpen() == false");
}
// Save the channel reference.
this.channel = channel;
// Set stream and flushed positions to initial channel position.
this.streamPos = this.flushedPos = channel.position();
// Allocate the write buffer.
byteBuffer = ByteBuffer.allocateDirect(DEFAULT_WRITE_BUFFER_SIZE);
// Create the read stream (initially zero-sized).
readStream = new FileChannelImageInputStream(channel);
}
/**
* Returns an ImageInputStream
for reading. The
* returned stream has byte order, stream and flushed positions,
* and bit offset set to the current values for this stream.
*/
private ImageInputStream getImageInputStream() throws IOException {
// Write any unwritten bytes.
flushBuffer();
// Sync input stream state to state of this stream.
readStream.setByteOrder(byteOrder);
readStream.seek(streamPos);
readStream.flushBefore(flushedPos);
readStream.setBitOffset(bitOffset);
return readStream;
}
/**
* Write to the FileChannel
any remaining bytes in
* the byte output buffer.
*/
private void flushBuffer() throws IOException {
if(byteBuffer.position() != 0) {
// Set the limit to the position.
byteBuffer.limit(byteBuffer.position());
// Set the position to zero.
byteBuffer.position(0);
// Write all bytes between zero and the previous position.
channel.write(byteBuffer);
// Prepare for subsequent put() calls if any.
byteBuffer.clear();
}
}
// --- Implementation of superclass abstract methods. ---
public int read() throws IOException {
checkClosed();
bitOffset = 0;
ImageInputStream inputStream = getImageInputStream();
streamPos++;
return inputStream.read();
}
public int read(byte[] b, int off, int len) throws IOException {
// Check parameters.
if(off < 0 || len < 0 || off + len > b.length) {
throw new IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > b.length");
} else if(len == 0) {
return 0;
}
checkClosed();
bitOffset = 0;
ImageInputStream inputStream = getImageInputStream();
int numBytesRead = inputStream.read(b, off, len);
streamPos += numBytesRead;
return numBytesRead;
}
public void write(int b) throws IOException {
write(new byte[] {(byte)(b&0xff)}, 0, 1);
}
public void write(byte[] b, int off, int len) throws IOException {
// Check parameters.
if(off < 0 || len < 0 || off + len > b.length) {
// NullPointerException will be thrown before this if b is null.
throw new IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > b.length");
} else if(len == 0) {
return;
}
// Flush any bits in the current byte.
flushBits();
// Zero the number of bytes to put.
int numPut = 0;
// Loop until all bytes have been put.
do {
// Determine number of bytes to put.
int numToPut = Math.min(len - numPut,
byteBuffer.remaining());
// If no bytes to put, the buffer has to be full as len
// is always greater than numPut so flush it and return
// to start of loop.
if(numToPut == 0) {
flushBuffer();
continue;
}
// Put the bytes in the buffer.
byteBuffer.put(b, off + numPut, numToPut);
// Increment the put counter.
numPut += numToPut;
} while(numPut < len);
// Increment the stream position.
streamPos += len;
}
// --- Overriding of superclass methods. ---
// --- Bulk read methods ---
public void readFully(char[] c, int off, int len) throws IOException {
getImageInputStream().readFully(c, off, len);
streamPos += 2*len;
}
public void readFully(short[] s, int off, int len) throws IOException {
getImageInputStream().readFully(s, off, len);
streamPos += 2*len;
}
public void readFully(int[] i, int off, int len) throws IOException {
getImageInputStream().readFully(i, off, len);
streamPos += 4*len;
}
public void readFully(long[] l, int off, int len) throws IOException {
getImageInputStream().readFully(l, off, len);
streamPos += 8*len;
}
public void readFully(float[] f, int off, int len) throws IOException {
getImageInputStream().readFully(f, off, len);
streamPos += 4*len;
}
public void readFully(double[] d, int off, int len) throws IOException {
getImageInputStream().readFully(d, off, len);
streamPos += 8*len;
}
// --- Bulk write methods ---
public void writeChars(char[] c, int off, int len) throws IOException {
// Check parameters.
if(off < 0 || len < 0 || off + len > c.length) {
// NullPointerException will be thrown before this if c is null.
throw new IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > c.length");
} else if(len == 0) {
return;
}
// Flush any bits in the current byte.
flushBits();
// Zero the number of chars put.
int numPut = 0;
// Get view buffer.
CharBuffer viewBuffer = byteBuffer.asCharBuffer();
// Loop until all chars have been put.
do {
// Determine number of chars to put.
int numToPut = Math.min(len - numPut,
viewBuffer.remaining());
// If no chars to put, the buffer has to be full as len
// is always greater than numPut so flush it and return
// to start of loop.
if(numToPut == 0) {
flushBuffer();
continue;
}
// Put the chars in the buffer.
viewBuffer.put(c, off + numPut, numToPut);
// Sync the ByteBuffer position.
byteBuffer.position(byteBuffer.position() + 2*numToPut);
// Increment the put counter.
numPut += numToPut;
} while(numPut < len);
// Increment the stream position.
streamPos += 2*len;
}
public void writeShorts(short[] s, int off, int len) throws IOException {
// Check parameters.
if(off < 0 || len < 0 || off + len > s.length) {
// NullPointerException will be thrown before this if s is null.
throw new IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > c.length");
} else if(len == 0) {
return;
}
// Flush any bits in the current byte.
flushBits();
// Zero the number of shorts put.
int numPut = 0;
// Get view buffer.
ShortBuffer viewBuffer = byteBuffer.asShortBuffer();
// Loop until all shorts have been put.
do {
// Determine number of shorts to put.
int numToPut = Math.min(len - numPut,
viewBuffer.remaining());
// If no shorts to put, the buffer has to be full as len
// is always greater than numPut so flush it and return
// to start of loop.
if(numToPut == 0) {
flushBuffer();
continue;
}
// Put the shorts in the buffer.
viewBuffer.put(s, off + numPut, numToPut);
// Sync the ByteBuffer position.
byteBuffer.position(byteBuffer.position() + 2*numToPut);
// Increment the put counter.
numPut += numToPut;
} while(numPut < len);
// Increment the stream position.
streamPos += 2*len;
}
public void writeInts(int[] i, int off, int len) throws IOException {
// Check parameters.
if(off < 0 || len < 0 || off + len > i.length) {
// NullPointerException will be thrown before this if i is null.
throw new IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > c.length");
} else if(len == 0) {
return;
}
// Flush any bits in the current byte.
flushBits();
// Zero the number of ints put.
int numPut = 0;
// Get view buffer.
IntBuffer viewBuffer = byteBuffer.asIntBuffer();
// Loop until all ints have been put.
do {
// Determine number of ints to put.
int numToPut = Math.min(len - numPut,
viewBuffer.remaining());
// If no ints to put, the buffer has to be full as len
// is always greater than numPut so flush it and return
// to start of loop.
if(numToPut == 0) {
flushBuffer();
continue;
}
// Put the ints in the buffer.
viewBuffer.put(i, off + numPut, numToPut);
// Sync the ByteBuffer position.
byteBuffer.position(byteBuffer.position() + 4*numToPut);
// Increment the put counter.
numPut += numToPut;
} while(numPut < len);
// Increment the stream position.
streamPos += 4*len;
}
public void writeLongs(long[] l, int off, int len) throws IOException {
// Check parameters.
if(off < 0 || len < 0 || off + len > l.length) {
// NullPointerException will be thrown before this if l is null.
throw new IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > c.length");
} else if(len == 0) {
return;
}
// Flush any bits in the current byte.
flushBits();
// Zero the number of longs put.
int numPut = 0;
// Get view buffer.
LongBuffer viewBuffer = byteBuffer.asLongBuffer();
// Loop until all longs have been put.
do {
// Determine number of longs to put.
int numToPut = Math.min(len - numPut,
viewBuffer.remaining());
// If no longs to put, the buffer has to be full as len
// is always greater than numPut so flush it and return
// to start of loop.
if(numToPut == 0) {
flushBuffer();
continue;
}
// Put the longs in the buffer.
viewBuffer.put(l, off + numPut, numToPut);
// Sync the ByteBuffer position.
byteBuffer.position(byteBuffer.position() + 8*numToPut);
// Increment the put counter.
numPut += numToPut;
} while(numPut < len);
// Increment the stream position.
streamPos += 8*len;
}
public void writeFloats(float[] f, int off, int len) throws IOException {
// Check parameters.
if(off < 0 || len < 0 || off + len > f.length) {
// NullPointerException will be thrown before this if c is null.
throw new IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > f.length");
} else if(len == 0) {
return;
}
// Flush any bits in the current byte.
flushBits();
// Zero the number of floats put.
int numPut = 0;
// Get view buffer.
FloatBuffer viewBuffer = byteBuffer.asFloatBuffer();
// Loop until all floats have been put.
do {
// Determine number of floats to put.
int numToPut = Math.min(len - numPut,
viewBuffer.remaining());
// If no floats to put, the buffer has to be full as len
// is always greater than numPut so flush it and return
// to start of loop.
if(numToPut == 0) {
flushBuffer();
continue;
}
// Put the floats in the buffer.
viewBuffer.put(f, off + numPut, numToPut);
// Sync the ByteBuffer position.
byteBuffer.position(byteBuffer.position() + 4*numToPut);
// Increment the put counter.
numPut += numToPut;
} while(numPut < len);
// Increment the stream position.
streamPos += 4*len;
}
public void writeDoubles(double[] d, int off, int len) throws IOException {
// Check parameters.
if(off < 0 || len < 0 || off + len > d.length) {
// NullPointerException will be thrown before this if d is null.
throw new IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > d.length");
} else if(len == 0) {
return;
}
// Flush any bits in the current byte.
flushBits();
// Zero the number of doubles put.
int numPut = 0;
// Get view buffer.
DoubleBuffer viewBuffer = byteBuffer.asDoubleBuffer();
// Loop until all doubles have been put.
do {
// Determine number of doubles to put.
int numToPut = Math.min(len - numPut,
viewBuffer.remaining());
// If no doubles to put, the buffer has to be full as len
// is always greater than numPut so flush it and return
// to start of loop.
if(numToPut == 0) {
flushBuffer();
continue;
}
// Put the doubles in the buffer.
viewBuffer.put(d, off + numPut, numToPut);
// Sync the ByteBuffer position.
byteBuffer.position(byteBuffer.position() + 8*numToPut);
// Increment the put counter.
numPut += numToPut;
} while(numPut < len);
// Increment the stream position.
streamPos += 8*len;
}
// --- Other methods ---
/**
* Invokes the superclass method, writes any unwritten data, and
* sets the internal reference to the source FileChannel
* to null
. The source FileChannel
is not
* closed.
*
* @exception IOException if an error occurs.
*/
// Note that this method is called by the superclass finalize()
// so this class does not need to implement finalize().
public void close() throws IOException {
// Flush any unwritten data in the buffer.
flushBuffer();
// Close the read channel and clear the reference to it.
readStream.close();
readStream = null;
// Clear reference to the channel.
channel = null;
// Clear reference to the internal ByteBuffer.
byteBuffer = null;
// Chain to the superclass.
super.close();
}
/**
* Returns the number of bytes currently in the FileChannel
.
* If an IOException
is encountered when querying the
* channel's size, -1L will be returned.
*
* @return The number of bytes in the channel
* -1L to indicate unknown length.
*/
public long length() {
// Initialize to value indicating unknown length.
long length = -1L;
// Set length to current size with respect to initial position.
try {
length = channel.size();
} catch(IOException e) {
// Default to unknown length.
}
return length;
}
/**
* Invokes the superclass method, writes any unwritten data,
* and sets the channel position to the supplied parameter.
*/
public void seek(long pos) throws IOException {
super.seek(pos);
// Flush any unwritten data in the buffer.
flushBuffer();
// Set the FileChannel position for WritableByteChannel.write().
channel.position(pos);
}
public void setByteOrder(ByteOrder networkByteOrder) {
super.setByteOrder(networkByteOrder);
byteBuffer.order(networkByteOrder);
}
}