The amazing fast image loading extravaganza. Images load in 1/4th the time

and generate almost zero garbage during the loading process (unlike the
ImageIO loaders which practically generate an entire image worth of
garbage during the load).


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2474 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2003-04-27 06:32:15 +00:00
parent a54e01a0fd
commit a4e09831c5
@@ -0,0 +1,155 @@
//
// $Id: FastImageIO.java,v 1.1 2003/04/27 06:32:15 mdb Exp $
package com.threerings.media.image;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.WritableRaster;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
/**
* Provides routines for writing and reading uncompressed 8-bit color
* mapped images in a manner that is extremely fast and generates a
* minimal amount of garbage during the loading process.
*/
public class FastImageIO
{
/** A suffix for use when storing raw images in bundles or on the file
* system. */
public static final String FILE_SUFFIX = ".raw";
/**
* Returns true if the supplied image is of a format that is supported
* by the fast image I/O services, false if not.
*/
public static boolean canWrite (BufferedImage image)
{
return (image.getColorModel() instanceof IndexColorModel) &&
(image.getRaster().getDataBuffer() instanceof DataBufferByte);
}
/**
* Writes the supplied image to the supplied output stream.
*
* @exception IOException thrown if an error occurs writing to the
* output stream.
*/
public static void write (BufferedImage image, OutputStream out)
throws IOException
{
DataOutputStream dout = new DataOutputStream(out);
// write the image dimensions
int width = image.getWidth(), height = image.getHeight();
dout.writeInt(width);
dout.writeInt(height);
// write the color model information
IndexColorModel cmodel = (IndexColorModel)image.getColorModel();
int tpixel = cmodel.getTransparentPixel();
dout.writeInt(tpixel);
int msize = cmodel.getMapSize();
int[] map = new int[msize];
cmodel.getRGBs(map);
dout.writeInt(msize);
for (int ii = 0; ii < map.length; ii++) {
dout.writeInt(map[ii]);
}
// write the raster data
DataBufferByte dbuf = (DataBufferByte)image.getRaster().getDataBuffer();
byte[] data = dbuf.getData();
if (data.length != width * height) {
String errmsg = "Raster data not same size as image! [" +
width + "x" + height + " != " + data.length + "]";
throw new IllegalStateException(errmsg);
}
dout.write(data);
dout.flush();
}
/**
* Reads an image from the supplied file (which must contain an image
* previously written via a call to {@link #write}).
*
* @exception IOException thrown if an error occurs reading from the
* file.
*/
public static BufferedImage read (File file)
throws IOException
{
RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel fchan = raf.getChannel();
try {
MappedByteBuffer mbuf = fchan.map(
FileChannel.MapMode.READ_ONLY, 0, file.length());
// read in our integer fields
IntBuffer ibuf = mbuf.asIntBuffer();
int width = ibuf.get();
int height = ibuf.get();
int tpixel = ibuf.get();
int msize = ibuf.get();
if (width > Short.MAX_VALUE || width < 0 ||
height > Short.MAX_VALUE || height < 0) {
throw new IOException("Bogus image size " +
width + "x" + height);
}
// make sure our colormap array is big enough
if (_cmap == null || _cmap.length < msize) {
_cmap = new int[msize];
}
// read in the colormap
ibuf.get(_cmap, 0, msize);
// advance the byte buffer accordingly
mbuf.position(ibuf.position() * 4);
// read in the image data itself
byte[] data = new byte[width*height];
mbuf.get(data);
// create the image from our component parts
DataBuffer dbuf = new DataBufferByte(data, data.length, 0);
int[] offsets = new int[] { 0 };
PixelInterleavedSampleModel smodel =
new PixelInterleavedSampleModel(
DataBuffer.TYPE_BYTE, width, height, 1, width, offsets);
WritableRaster raster = WritableRaster.createWritableRaster(
smodel, dbuf, _origin);
IndexColorModel cmodel = new IndexColorModel(
8, msize, _cmap, 0, DataBuffer.TYPE_BYTE, null);
return new BufferedImage(cmodel, raster, false, null);
} finally {
fchan.close();
raf.close();
}
}
/** Used when loading our color map. */
protected static int[] _cmap;
/** Used when creating our writable raster. */
protected static Point _origin = new Point(0, 0);
}