Revamped image loading such that the ResourceManager handles loading images
given a resource path so that it can be smart about loading from files versus streams depending on what works based based on where the resources are coming from. Also moved FastImageIO into com.threerings.resource to avoid a dependence on com.threerings.media in resource. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@310 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -1,174 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/nenya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
IndexColorModel cmodel;
|
||||
synchronized (_origin) { // any old object will do
|
||||
// make sure our colormap array is big enough
|
||||
if (_cmap == null || _cmap.length < msize) {
|
||||
_cmap = new int[msize];
|
||||
}
|
||||
// read in the data and create our colormap
|
||||
ibuf.get(_cmap, 0, msize);
|
||||
cmodel = new IndexColorModel(
|
||||
8, msize, _cmap, 0, DataBuffer.TYPE_BYTE, null);
|
||||
}
|
||||
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
@@ -36,9 +36,6 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.samskivert.swing.RuntimeAdjust;
|
||||
import com.samskivert.util.LRUHashMap;
|
||||
import com.samskivert.util.StringUtil;
|
||||
@@ -393,10 +390,10 @@ public class ImageManager
|
||||
throws IOException {
|
||||
// first attempt to load the image from the specified resource set
|
||||
try {
|
||||
return read(_rmgr.getImageResource(rset, path));
|
||||
return _rmgr.getImageResource(rset, path);
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
// fall back to trying the classpath
|
||||
return read(_rmgr.getImageResource(path));
|
||||
return _rmgr.getImageResource(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,7 +421,7 @@ public class ImageManager
|
||||
Log.debug("Loading image " + key + ".");
|
||||
image = key.daprov.loadImage(key.path);
|
||||
if (image == null) {
|
||||
Log.warning("ImageIO.read(" + key + ") returned null.");
|
||||
Log.warning("ImageDataProvider.loadImage(" + key + ") returned null.");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -438,42 +435,6 @@ public class ImageManager
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for loading images.
|
||||
*/
|
||||
protected static BufferedImage read (ImageInputStream iis)
|
||||
throws IOException
|
||||
{
|
||||
BufferedImage image = ImageIO.read(iis);
|
||||
closeIIS(iis);
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for closing image input streams.
|
||||
*/
|
||||
protected static void closeIIS (ImageInputStream iis)
|
||||
{
|
||||
if (iis == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
iis.close();
|
||||
} catch (IOException ioe) {
|
||||
// jesus fucking hoppalong cassidy christ on a polyester pogo stick!
|
||||
// ImageInputStreamImpl.close() throws a fucking IOException if it's already closed;
|
||||
// there's no way to find out if it's already closed or not, so we have to check for
|
||||
// their bullshit exception; as if we should just "trust" ImageIO.read() to close the
|
||||
// fucking input stream when it's done, especially after the goddamned fiasco with
|
||||
// PNGImageReader not closing it's fucking inflaters; for the love of humanity
|
||||
if (!"closed".equals(ioe.getMessage())) {
|
||||
Log.warning("Failure closing image input '" + iis + "'.");
|
||||
Log.logStackTrace(ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports statistics detailing the image manager cache performance and the current size of the
|
||||
* cached images.
|
||||
@@ -584,7 +545,7 @@ public class ImageManager
|
||||
/** Our default data provider. */
|
||||
protected ImageDataProvider _defaultProvider = new ImageDataProvider() {
|
||||
public BufferedImage loadImage (String path) throws IOException {
|
||||
return read(_rmgr.getImageResource(path));
|
||||
return _rmgr.getImageResource(path);
|
||||
}
|
||||
public String getIdent () {
|
||||
return "rmgr:default";
|
||||
|
||||
@@ -29,12 +29,9 @@ import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.threerings.resource.ResourceBundle;
|
||||
|
||||
import com.threerings.media.image.FastImageIO;
|
||||
import com.threerings.media.image.ImageDataProvider;
|
||||
import com.threerings.media.tile.TileSet;
|
||||
|
||||
@@ -105,11 +102,7 @@ public class TileSetBundle extends HashIntMap
|
||||
public BufferedImage loadImage (String path)
|
||||
throws IOException
|
||||
{
|
||||
if (path.endsWith(FastImageIO.FILE_SUFFIX)) {
|
||||
return FastImageIO.read(_bundle.getResourceFile(path));
|
||||
} else {
|
||||
return ImageIO.read(_bundle.getResourceFile(path));
|
||||
}
|
||||
return _bundle.getImageResource(path);
|
||||
}
|
||||
|
||||
// custom serialization process
|
||||
|
||||
@@ -44,8 +44,8 @@ import org.xml.sax.SAXException;
|
||||
import com.samskivert.io.PersistenceException;
|
||||
|
||||
import com.threerings.media.Log;
|
||||
import com.threerings.media.image.FastImageIO;
|
||||
import com.threerings.media.image.ImageUtil;
|
||||
import com.threerings.resource.FastImageIO;
|
||||
|
||||
import com.threerings.media.tile.ImageProvider;
|
||||
import com.threerings.media.tile.ObjectTileSet;
|
||||
|
||||
@@ -29,9 +29,9 @@ import java.awt.image.WritableRaster;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import com.threerings.media.image.FastImageIO;
|
||||
import com.threerings.media.image.ImageUtil;
|
||||
import com.threerings.media.tile.TileSet;
|
||||
import com.threerings.resource.FastImageIO;
|
||||
|
||||
/**
|
||||
* Contains routines for trimming the images from an existing tileset
|
||||
|
||||
Reference in New Issue
Block a user