Jump to content
Search Community

ImageCacher

Markavian test
Moderator Tag

Recommended Posts

Hey all,

 

I wrote an extention to ImageLoader called ImageCache, which takes a snapshot of a loaded bitmap and stores it in a local cache.

That way, when subsequent calls for the same bitmap data are made they can be delivered to the screen instantly without any delays.

 

Also, I found that this optimization helps with prototyping where I need an external image on the stage "now", but want to write a preloader further down the line. This way the image can be preloaded either before hand, or loaded when required, with the benefit of no-delay drawing if preloaded, and without having to rewrite my display code after the preloader is written.

 

I think I've covered the correct event hooks, but haven't used LoaderMax much - so if anyone could test/improve this code please let me know.

 

/**
* VERSION: 1.00
* DATE: 2011-05-26
* AS3
**/
package com.greensock.loading
{
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.events.Event;

/**
 * Extended version of ImageLoader for bitmaps, jpegs, gif, pngs that creates a bitmap cache after each image load and instantly completes subsequent requests for the same URL.
 *
 * See ImageLoader class for full greensock documentation on using LoaderMax and the ImageLoader.
 * Example usage:
 * 
	image = new ImageCacher('images/photo1.jpg', {smoothing:true});
	image.load();

 *
 * author: John Beech, johnbeech@mkv25.net
 */
public class ImageCacher extends ImageLoader
{
	private static var _classActivated:Boolean = _activateClass("ImageCacher", ImageCacher, "jpg,jpeg,png,gif,bmp");

	protected static var BITMAP_INDEX:Object = {};

	public function ImageCacher(urlOrRequest:*, vars:Object=null)
	{
		super(urlOrRequest, vars);
		_type = "ImageCacher";
	}

	/* Check cache for bitmap first before performing load, unless told to flushContent */
	override public function load(flushContent:Boolean=false):void
	{
		var bitmap:BitmapData = GetBitmap(this.url);
		if(bitmap == null || !flushContent)
		{
			super.load(flushContent);
		}
		else
		{
			_content = new Bitmap(bitmap, "auto", Boolean(this.vars.smoothing != false));
			super._completeHandler();
		}
	}

	/* On completion take a snapshot of the content and store in cache */
	override protected function _completeHandler(e:Event=null):void
	{
		_determineScriptAccess();
		if (!_scriptAccessDenied)
		{
			var bitmap:BitmapData = new BitmapData(_content.width, _content.height, true, 0x0000FF);
			bitmap.draw(_content);

			ImageCacher.SetBitmap(this.url, bitmap);
		}
		else
		{
			throw new Error("Security sandbox exception, cannot cache image.");
		}
		super._completeHandler(e);
	}

	/* Store a loaded bitmap in an internal cache */
	public static function SetBitmap(name:String, bitmap:BitmapData):void
	{
		BITMAP_INDEX[name] = bitmap;
	}

	/* Return a loaded bitmap from the internal cache */
	public static function GetBitmap(name:String):BitmapData
	{
		return BITMAP_INDEX[name];
	}

}
}

Link to comment
Share on other sites

Thanks for sharing.

 

I'd be careful about a few things though:

 

1) Garbage collection - the way this is written won't allow the BitmapDatas to ever be unloaded or garbage collected. The more images you load, the more memory you use and it can never be reduced because you're holding references in a static class member.

 

2) It doesn't appear to honor noCache:true

 

3) It's using a lot more memory than necessary because you're creating a new BitmapData for each loader rather than leveraging the same one over and over again. That slows things down too (creating a new instance and draw()-ing the _content). You can simply copy the original Bitmap's BitmapData.

 

SLOW/HEAVY:

var bitmap:BitmapData = new BitmapData(_content.width, _content.height, true, 0x0000FF);
bitmap.draw(_content);

 

FASTER/LIGHTER:

bitmap = _content.bitmapData;

 

4) There's the potential for multiple images to be loaded for the same URL. For example, let's say I set up 20 ImageCacher instances for the same URL and then I call load() on them all - they'll all end up loading the remote file. The only time the cached image will be pulled is if load() is called AFTER another ImageCacher of the same URL has finished loading. Not the end of the world by any means - just thought I'd mention it.

Link to comment
Share on other sites

Thanks for giving it some consideration- you know how your classes work a lot better then I do.

 

My custom image loader / image holder class has a preloader system that checks for existing URLs being loaded, but doesn't work with the LoaderMax.

 

Is that not something that could be written into the LoaderMax framework; check for existing URLs being loaded and build up an library of content - rather then creating/loading individual stage instances for each asset? If you think of content stored in a library, then you can get developers to "clear" the library of unused assets.

 

The problem I have at the moment is that assets aren't quite ready quick enough because of the inherent delay going off to the server/cache to check for a downloaded file, rather then pulling items straight from memory.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...