I don't suggest combining everything into a single image, as you'll delay the 'initial appearance' of any images until the entire file downloads. Better to let the visible stuff load ASAP and optimize the rest for overall throughput.
If the images are under 50KB each, for example, it may make sense to combine them as sprite images, but I'd suggest combining, say, 10 at a time. If the images are tiny, like 5KB each, 30 at a time would be reasonable.
You've got a series of bottlenecks here to worry about
- HTTP overhead and concurrent connection limitations (sprite images help with this)
- Server throughput overhead (solvable with correct disk caching and edge caching)
- Browser rendering overhead (Sprites are best for icons and small thumbnails; they shouldn't be gigantic, try to keep it under 1 megapixel).
Joining existing images together and re-compressing in jpeg form can sometimes improve compression, other times it will introduce artifacts. For example, mixing line art and photographs is a bad idea. Jpeg is bad at line art. Use PNG, even PNG-8 if you want. Sometimes you'll find that JPEG at 100% quality ends up smaller than the GIF or PNG version, but most of the time, line art is best stored in PNG form.
If you have IDs for these photos, and plan on going the dynamic route with disk caching, it does simplify your task significantly.
The ImageResizer library will let you 'plug in' to its dynamic pipeline and disk caching system quite easily.
In this case, you'll implement IVirtualImageProvider
and IVirtualBitmapFile
. See the Gradient plugin for a simple example of generating a bitmap and letting the pipeline handle the rest.
You'll have to define your url syntax and look for it in the FileExists and GetFile methods. Something like /combine-images.ashx?idlist=34,56,79,23&dir=horizontal&width=50&height=50.
Then you'll have to load up each of the images, resize it with the Managed API into a bitmap instance, and draw it on the canvas you've allocated.
Most of your bottleneck will probably be in pulling the images from SQL. If you've got limited RAM on the server, a serial approach might be safer than a parallel one (I.e, get one image at a time from SQL, resize and draw it, dispose data, move on). If your source images are tiny to start with, a multi-threaded apprach might be okay. Just remember you need to make it fast even if it's disk cached, because requests default to timing out after 30 seconds. Done properly, you should be able to serve a 10 image sprite in under 2 seconds with a empty disk cache, 20ms once it's cached.
If this seems a bit overwhelming, I do offer custom plugin development, but there is a queue.
There are a lot of advantages to a 1 file-per-request approach, and it might be worth trying the ImageResizer's disk cache and SqlReader plugins before hopping on the Sprite bandwagon. Not saying it's the wrong approach, but uncached MVC+SQL can add a lot of overhead that might be contributing.