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:
Michael Bayne
2007-10-26 19:50:53 +00:00
parent 3e47134b06
commit ecc173d6ad
11 changed files with 86 additions and 87 deletions
@@ -41,7 +41,6 @@ import com.threerings.resource.ResourceManager;
import com.threerings.media.image.BufferedMirage; import com.threerings.media.image.BufferedMirage;
import com.threerings.media.image.Colorization; import com.threerings.media.image.Colorization;
import com.threerings.media.image.FastImageIO;
import com.threerings.media.image.ImageDataProvider; import com.threerings.media.image.ImageDataProvider;
import com.threerings.media.image.ImageManager; import com.threerings.media.image.ImageManager;
import com.threerings.media.image.ImageUtil; import com.threerings.media.image.ImageUtil;
@@ -277,7 +276,7 @@ public class BundledComponentRepository
// from interface ImageDataProvider // from interface ImageDataProvider
public BufferedImage loadImage (String path) throws IOException public BufferedImage loadImage (String path) throws IOException
{ {
return FastImageIO.read(_bundle.getResourceFile(path)); return _bundle.getImageResource(path);
} }
// from interface FrameProvider // from interface FrameProvider
@@ -36,9 +36,6 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import com.samskivert.swing.RuntimeAdjust; import com.samskivert.swing.RuntimeAdjust;
import com.samskivert.util.LRUHashMap; import com.samskivert.util.LRUHashMap;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
@@ -393,10 +390,10 @@ public class ImageManager
throws IOException { throws IOException {
// first attempt to load the image from the specified resource set // first attempt to load the image from the specified resource set
try { try {
return read(_rmgr.getImageResource(rset, path)); return _rmgr.getImageResource(rset, path);
} catch (FileNotFoundException fnfe) { } catch (FileNotFoundException fnfe) {
// fall back to trying the classpath // 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 + "."); Log.debug("Loading image " + key + ".");
image = key.daprov.loadImage(key.path); image = key.daprov.loadImage(key.path);
if (image == null) { if (image == null) {
Log.warning("ImageIO.read(" + key + ") returned null."); Log.warning("ImageDataProvider.loadImage(" + key + ") returned null.");
} }
} catch (Exception e) { } catch (Exception e) {
@@ -438,42 +435,6 @@ public class ImageManager
return image; 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 * Reports statistics detailing the image manager cache performance and the current size of the
* cached images. * cached images.
@@ -584,7 +545,7 @@ public class ImageManager
/** Our default data provider. */ /** Our default data provider. */
protected ImageDataProvider _defaultProvider = new ImageDataProvider() { protected ImageDataProvider _defaultProvider = new ImageDataProvider() {
public BufferedImage loadImage (String path) throws IOException { public BufferedImage loadImage (String path) throws IOException {
return read(_rmgr.getImageResource(path)); return _rmgr.getImageResource(path);
} }
public String getIdent () { public String getIdent () {
return "rmgr:default"; return "rmgr:default";
@@ -29,12 +29,9 @@ import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.util.Iterator; import java.util.Iterator;
import javax.imageio.ImageIO;
import com.samskivert.util.HashIntMap; import com.samskivert.util.HashIntMap;
import com.threerings.resource.ResourceBundle; import com.threerings.resource.ResourceBundle;
import com.threerings.media.image.FastImageIO;
import com.threerings.media.image.ImageDataProvider; import com.threerings.media.image.ImageDataProvider;
import com.threerings.media.tile.TileSet; import com.threerings.media.tile.TileSet;
@@ -105,11 +102,7 @@ public class TileSetBundle extends HashIntMap
public BufferedImage loadImage (String path) public BufferedImage loadImage (String path)
throws IOException throws IOException
{ {
if (path.endsWith(FastImageIO.FILE_SUFFIX)) { return _bundle.getImageResource(path);
return FastImageIO.read(_bundle.getResourceFile(path));
} else {
return ImageIO.read(_bundle.getResourceFile(path));
}
} }
// custom serialization process // custom serialization process
@@ -44,8 +44,8 @@ import org.xml.sax.SAXException;
import com.samskivert.io.PersistenceException; import com.samskivert.io.PersistenceException;
import com.threerings.media.Log; import com.threerings.media.Log;
import com.threerings.media.image.FastImageIO;
import com.threerings.media.image.ImageUtil; import com.threerings.media.image.ImageUtil;
import com.threerings.resource.FastImageIO;
import com.threerings.media.tile.ImageProvider; import com.threerings.media.tile.ImageProvider;
import com.threerings.media.tile.ObjectTileSet; import com.threerings.media.tile.ObjectTileSet;
@@ -29,9 +29,9 @@ import java.awt.image.WritableRaster;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import com.threerings.media.image.FastImageIO;
import com.threerings.media.image.ImageUtil; import com.threerings.media.image.ImageUtil;
import com.threerings.media.tile.TileSet; import com.threerings.media.tile.TileSet;
import com.threerings.resource.FastImageIO;
/** /**
* Contains routines for trimming the images from an existing tileset * Contains routines for trimming the images from an existing tileset
@@ -19,7 +19,7 @@
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.image; package com.threerings.resource;
import java.awt.Point; import java.awt.Point;
@@ -171,12 +171,10 @@ public class Handler extends URLStreamHandler
throw new FileNotFoundException(path); throw new FileNotFoundException(path);
} }
// locate the tile image, then write that subimage back out in PNG // locate the tile image, then write that subimage back out in PNG format into memory and
// format into memory and return an input stream for that // return an input stream for that
ImageInputStream stream = BufferedImage src = StringUtil.isBlank(bundle) ?
StringUtil.isBlank(bundle) ? _rmgr.getImageResource(path) _rmgr.getImageResource(path) : _rmgr.getImageResource(bundle, path);
: _rmgr.getImageResource(bundle, path);
BufferedImage src = ImageIO.read(stream);
Rectangle trect = GeomUtil.getTile( Rectangle trect = GeomUtil.getTile(
src.getWidth(), src.getHeight(), width, height, tidx); src.getWidth(), src.getHeight(), width, height, tidx);
BufferedImage tile = src.getSubimage( BufferedImage tile = src.getSubimage(
@@ -21,6 +21,8 @@
package com.threerings.resource; package com.threerings.resource;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@@ -251,8 +253,7 @@ public class ResourceBundle
} }
// copy the resource into the temporary file // copy the resource into the temporary file
BufferedOutputStream fout = BufferedOutputStream fout = new BufferedOutputStream(new FileOutputStream(tfile));
new BufferedOutputStream(new FileOutputStream(tfile));
InputStream jin = _jarSource.getInputStream(entry); InputStream jin = _jarSource.getInputStream(entry);
IOUtils.copy(jin, fout); IOUtils.copy(jin, fout);
jin.close(); jin.close();
@@ -261,6 +262,16 @@ public class ResourceBundle
return tfile; return tfile;
} }
/**
* Decodes and returns the specified image resource. Returns null if no resource exists at the
* specified path.
*/
public BufferedImage getImageResource (String path)
throws IOException
{
return ResourceManager.loadImage(getResourceFile(path));
}
/** /**
* Returns true if this resource bundle contains the resource with the * Returns true if this resource bundle contains the resource with the
* specified path. This avoids actually loading the resource, in the * specified path. This avoids actually loading the resource, in the
@@ -22,6 +22,7 @@
package com.threerings.resource; package com.threerings.resource;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -43,9 +44,7 @@ import java.util.StringTokenizer;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import javax.imageio.stream.FileImageInputStream; import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import com.samskivert.net.PathUtil; import com.samskivert.net.PathUtil;
import com.samskivert.util.ResultListener; import com.samskivert.util.ResultListener;
@@ -449,28 +448,27 @@ public class ResourceManager
} }
/** /**
* Fetches the specified resource as an {@link ImageInputStream} and one that takes advantage, * Fetches and decodes the specified resource into a {@link BufferedImage}.
* if possible, of caching of unpacked resources on the local filesystem.
* *
* @exception FileNotFoundException thrown if the resource could not be located in any of the * @exception FileNotFoundException thrown if the resource could not be located in any of the
* bundles in the specified set, or if the specified set does not exist. * bundles in the specified set, or if the specified set does not exist.
* @exception IOException thrown if a problem occurs locating or reading the resource. * @exception IOException thrown if a problem occurs locating or reading the resource.
*/ */
public ImageInputStream getImageResource (String path) public BufferedImage getImageResource (String path)
throws IOException throws IOException
{ {
// first look for this resource in our default resource bundle // first look for this resource in our default resource bundle
for (int i = 0; i < _default.length; i++) { for (int i = 0; i < _default.length; i++) {
File file = _default[i].getResourceFile(path); File file = _default[i].getResourceFile(path);
if (file != null) { if (file != null) {
return new FileImageInputStream(file); return loadImage(file);
} }
} }
// fallback next to an unpacked resource file // fallback next to an unpacked resource file
File file = getResourceFile(path); File file = getResourceFile(path);
if (file != null && file.exists()) { if (file != null && file.exists()) {
return new FileImageInputStream(file); return loadImage(file);
} }
// first try a locale-specific file // first try a locale-specific file
@@ -483,7 +481,7 @@ public class ResourceManager
} }
}); });
if (in != null) { if (in != null) {
return new MemoryCacheImageInputStream(new BufferedInputStream(in)); return loadImage(in);
} }
} }
@@ -495,7 +493,7 @@ public class ResourceManager
} }
}); });
if (in != null) { if (in != null) {
return new MemoryCacheImageInputStream(new BufferedInputStream(in)); return loadImage(in);
} }
// if we still haven't found it, we throw an exception // if we still haven't found it, we throw an exception
@@ -549,14 +547,13 @@ public class ResourceManager
} }
/** /**
* Fetches the specified resource as an {@link ImageInputStream} and one that takes advantage, * Fetches and decodes the specified resource into a {@link BufferedImage}.
* if possible, of caching of unpacked resources on the local filesystem.
* *
* @exception FileNotFoundException thrown if the resource could not be located in any of the * @exception FileNotFoundException thrown if the resource could not be located in any of the
* bundles in the specified set, or if the specified set does not exist. * bundles in the specified set, or if the specified set does not exist.
* @exception IOException thrown if a problem occurs locating or reading the resource. * @exception IOException thrown if a problem occurs locating or reading the resource.
*/ */
public ImageInputStream getImageResource (String rset, String path) public BufferedImage getImageResource (String rset, String path)
throws IOException throws IOException
{ {
// grab the resource bundles in the specified resource set // grab the resource bundles in the specified resource set
@@ -569,22 +566,21 @@ public class ResourceManager
// look for the resource in any of the bundles // look for the resource in any of the bundles
int size = bundles.length; int size = bundles.length;
for (int ii = 0; ii < size; ii++) { for (int ii = 0; ii < size; ii++) {
File file = null; BufferedImage image = null;
// try a localized version first // try a localized version first
if (_localePrefix != null) { if (_localePrefix != null) {
file = bundles[ii].getResourceFile(PathUtil.appendPath(_localePrefix, path)); image = bundles[ii].getImageResource(PathUtil.appendPath(_localePrefix, path));
} }
// if we didn't find that, try generic // if we didn't find that, try generic
if (file == null) { if (image == null) {
file = bundles[ii].getResourceFile(path); image = bundles[ii].getImageResource(path);
} }
if (file != null) { if (image != null) {
// Log.info("Found image resource [rset=" + rset + // Log.info("Found image resource [rset=" + rset +
// ", bundle=" + bundles[ii].getSource() + // ", bundle=" + bundles[ii].getSource() + ", path=" + path + "].");
// ", path=" + path + ", file=" + file + "]."); return image;
return new FileImageInputStream(file);
} }
} }
@@ -653,6 +649,47 @@ public class ResourceManager
} }
} }
/**
* Loads an image from the supplied file. Supports {@link FastImageIO} files and formats
* supported by {@link ImageIO}.
*/
protected static BufferedImage loadImage (File file)
throws IOException
{
if (file == null) {
return null;
} else if (file.getName().endsWith(FastImageIO.FILE_SUFFIX)) {
return FastImageIO.read(file);
} else {
return ImageIO.read(file);
}
}
/**
* Loads an image from the supplied input stream. Supports formats supported by {@link ImageIO}
* but not {@link FastImageIO}.
*/
protected static BufferedImage loadImage (InputStream iis)
throws IOException
{
BufferedImage image = ImageIO.read(iis);
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 its fucking inflaters; for the love of humanity
if (!"closed".equals(ioe.getMessage())) {
Log.warning("Failure closing image input '" + iis + "'.");
Log.logStackTrace(ioe);
}
}
return image;
}
/** Used to unpack bundles on a separate thread. */ /** Used to unpack bundles on a separate thread. */
protected static class Unpacker extends Thread protected static class Unpacker extends Thread
{ {
@@ -26,7 +26,7 @@ import javax.imageio.ImageIO;
import java.io.*; import java.io.*;
import com.threerings.media.image.FastImageIO; import com.threerings.resource.FastImageIO;
/** /**
* Tests our image loading speed. * Tests our image loading speed.
@@ -159,7 +159,7 @@ public class ViewerApp
public void run () public void run ()
{ {
// show the window // show the window
_frame.show(); _frame.setVisible(true);
_framemgr.start(); _framemgr.start();
} }