Introduction
On most web pages, images make up most of the files loaded by the browser to show the page.
This is especially important for mobile sites, because of
low download speeds,
high network latencies and high bandwidth costs for your visitors.
As a result, you can significantly reduce bandwidth costs and page load times by compressing large images,
properly resizing thumbnails
and combining small images into
CSS sprites
to cut down on request/response overhead.
The CSS Sprite Generator does all this for you.
All you need to do is install it in your web site.
It is all controlled from your web.config, so no need to make changes to your site.
The package generates compressed and combined images on the fly when a page loads for the first time.
That way, there is no need to change your build process.
CPU overhead is kept to a minimum through caching, so after the initial page load, in most cases there is no more image processing for that page.
To make it easy to get started,
this package has sensible default settings, so it works out of the box without additional configuration
(you can fine tune later with over 25 settings).
Additionally,
there is a
Quick Start
section right after the
Installation
section.
And the solution in the download contains 10 simple but fully functional demo sites that show various features of the package.
If you like this article, please
vote for it.
Contents
Requirements
- ASP.NET 4 or higher
- IIS 6 or higher for your live site
This package can run in shared hosting accounts that only provide
Medium trust, such as those provided by GoDaddy.
It can also run in accounts that provide Full trust.
Features compared with the ASP.NET Sprite and Image Optimization Library
This section compares the ASP.NET CSS Sprite Generator package
against the other major package that generates sprites on the fly for ASP.NET sites, Microsoft's
ASP.NET Sprite and Image Optimization Library.
The biggest difference between the two packages is that the
ASP.NET CSS Sprite Generator package is much easier to install and configure:
-
With the ASP.NET Sprite and Image Optimization Library, you have to move all images that you want combined into sprites to a separate
App_Sprites directory. You also have to manually replace the img tags of those images with special user controls.
Additional configuration involves adding special settings.xml files.
-
With the ASP.NET CSS Sprite Generator package, you just add a few lines to your web.config - no need to move images or change your pages.
Below is a more detailed feature comparison of the ASP.NET CSS Sprite Generator package
and the ASP.NET Sprite and Image Optimization Library:
| CSS Sprite Generator package |
ASP.NET Sprite and Image Optimization Library
|
Combines images (except animated images) into sprites on the fly. When you add an image to a page,
it will be picked up, without requiring additional build steps.
| |
|
Processes dynamically generated image tags, such as from a database.
| |
|
Images can optionally be combined based
on file type, width, height and other properties
(details).
| |
|
Allows you to combine all images in a specific directory into sprites that are shared amongst your pages, to maximise browser caching
(details).
| |
|
Uses
sophisticated
algorithms
to generate the smallest possible sprites.
| |
|
Processes PNG GIF and JPEG images.
| |
|
Processes CSS background images. Caters for repeating background images and background images used
with the sliding door
technique (such as used with flexible buttons and tabs)
(details).
| |
|
Very easy to install - just add a .dll to your site and add a few lines to your web.config
(installation instructions).
All
configuration
is done in your web.config. No need to reorganize your images or to use special controls.
| |
|
Allows you to switch features on or off per page or per group of pages
(details).
| |
|
Lets you compress PNG and GIF files on the fly by reducing their color depth.
Choose one of 6 built in algorithms or use the default (details).
| |
|
Lets you compress JPEG files on the fly by reducing their image quality
(details).
| |
|
Lets you physically resize images on the fly (for example for thumbnails), either via
configuration in web.config
(details)
or by setting width and/or height
attributes on your img tags
(details).
| |
|
Lets you set the maximum file size for sprites, so sprites that grow too big are automatically
split into smaller sprites
(details).
| |
|
The additional generated CSS required to correctly
show the generated sprites
(details)
can be inlined or automatically placed in a separate CSS file
(details).
| |
|
Can be used with sites hosted on low cost shared hosting plans, such as GoDaddy's
(details).
| |
|
Allows you to configure the package so it only kicks in in Release mode.
That way, you see your individual unprocessed images while developing, and
reap the performance improvement in your live site
(details).
| |
|
Has the option to throw an exception when an image is missing, so you quickly detect missing images
(details).
| |
|
Generates inlined images
| |
|
Supports web farms
| |
|
Supports MVC3 Razor
| |
|
To reduce CPU overhead and disk accesses caused by the package, caches intermediate results.
Cache entries are removed when the underlying file is changed,
so you'll never serve outdated files.
|
|
|
The features shown above can all be switched on and off individually in the ASP.NET CSS Sprite Generator package via the web.config file
(full description). If you just install the package and not do any further configuration,
it
combines all PNG and GIF images (except animated images) that are no more than 100px wide and high into sprites - which is most commonly
what you want.
By default, it only kicks in when the site is in Release mode
(details).
This package is just one way of improving the performance of your web site.
My recently released book
ASP.NET Performance Secrets
shows how to pinpoint the biggest performance bottlenecks in your
web site, using various tools and performance counters built into Windows, IIS and SQL Server.
It then shows how to fix those bottlenecks. It covers all environments used by a web site -
the web server, the database server, and the browser. The book is extremely hands on - the aim is to
improve web site performance today, without wading through a lot of theory first.
Introduction to CSS Sprites
What are CSS sprites
Web pages often contain many small images, which all have to be requested by the browser individually and
then served up by the server.
You can save bandwidth and page load time by combining these small images into a single slightly larger image, for these reasons:
-
Each file request going from the browser to the server incurs a fixed overhead imposed by the network protocols.
That overhead can be significant for small image files. Sending fewer files means less request/response overhead.
-
Each image file carries some housekeeping overhead within the file itself. Combining small images gives you a combined image
that is a lot smaller than the sum of the individual images.
Take for example this HTML:
<img src="cssspritegenerator/contactus.png" width="50" height="50" />
<img src="cssspritegenerator/cart.png" width="50" height="50" />
<img src="cssspritegenerator/print.png" width="46" height="50" />
Which loads these images:
|
|
|
|
cart.png |
contactus.png |
print.png |
|
Size: 2.21 KB |
Size: 2.02KB |
Size: 2.05KB |
Total Size: 6.28KB |
50px x 50px |
50px x 50px |
46px x 50px |
|
Combining these images gives you this combined image:
|
combined.png |
Size: 3.94KB |
146px x 50px |
At 3.94KB, the size of this combined image is only 63% of the combined size of the three individual images.
Showing the individual images
Although the three images have now been combined into one single image,
they can still be shown as individual images using a CSS technique called CSS Sprites.
The CSS
background
property lets you specify an offset into the background image from where you want to show the background image as background.
That can be used to slide the combined image over each img tag, such that the correct area is visible:
<img style="width: 50px; height: 50px; background: url(combined.png) -0px -0px;" />
<img style="width: 50px; height: 50px; background: url(combined.png) -50px -0px;" />
<img style="width: 46px; height: 50px; background: url(combined.png) -100px -0px;" />
-
The cart image sits right at the left edge horizontally and right at the top vertically within the combined image, so it gets offset 0px 0px.
-
The contactus image sits 50px away from the left edge, and right at the top vertically, so it gets -50px 0px.
-
And finally the print image sits 100px away from the left edge, and right at the top vertically, so it gets -100px 0px.
Note that the img tags have a width and height equal to the width and height of the original individual images.
That way, you only see the portions of the combined image that correspond with the individual images.
One problem remains: without a src attribute, the img tags may show as a broken image.
You can circumvent this by setting the src to a transparent 1 by 1 pixel image. Because this image takes only 60 bytes, the most
efficient solution is to inline it:
<img src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" style="width: 50px; height: 50px; background: url(combined.png) -0px -0px;" />
<img src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" style="width: 50px; height: 50px; background: url(combined.png) -50px -0px;" />
<img src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" style="width: 46px; height: 50px; background: url(combined.png) -100px -0px;" />
Although the additional inlined image and CSS look big, their overhead should be minimal because
they are highly compressible as they have lots of repeating text. This assumes that your web server has
compression switched on
(details),
which is normally the case.
Additionally, instead of having the additional CSS inlined in the img tags, you can put this in an external .css file
and use CSS classes to link the img tags with their CSS.
The package supports both options via the
inlineSpriteStyles
property.
In case you're wondering about writing all this CSS and HTML, this is all done for you by the package. This section simply shows how it's done.
Inlined images are not supported by Internet Explorer 7 and 6. These browsers have about 5% traffic share (June 2012).
Rather than introducing a complicated solution for this small and declining traffic share, the package is simply
switched off for requests from these browsers, so they receive the original images.
Controlling the size of your sprites
If you were to combine images indiscriminately into sprites, you might wind up with very large sprite images that are slow to load.
Now that most browsers in use today load at least 6 images concurrently,
you may actually be better off having a couple of medium sized sprites rather than one big sprite.
To manage sprite sizes, you can determine the maximum width, height
and file size of the images that will be combined.
You can also set a maximum sprite size - so sprites that grow too big get split in two.
Using Shared Hosting
Your site may be using a shared hosting plan, which means that it shares a web server with many other web site.
Many companies that offer these plans,
such as
GoDaddy,
restrict the operations that each web site can perform so they don't affect each other.
In technical terms, this means your live site has
trust level Medium. In your development environment
it has has trust level Full, meaning it can do whatever it wants.
The package was specifically build so it can run with trust level Medium, so you should have no problem using it with your site
in a shared hosting plan.
The only issue is that the sprites generated by the package may have somewhat greater file sizes in
trust level Medium
than in trust level Full.
This is because some of the algorithms required to reduce the number of colors in an image are not available
in
trust level Medium.
More details
here.
Installation
Take these steps to add the ASP.NET CSS Sprite Generator package to your web site:
- Compile the package:
- Download the zip file with the source code, and unzip in a directory.
- Open the CssSpriteGenerator.sln file in Visual Studio 2010 or later.
- You'll find that the sources are organized in a solution, with these elements:
- Project CssSpriteGenerator is the actual package.
- A number of demo sites with examples of the use of the package. You can ignore these if you want.
- Compile the solution. This will produce a CssSpriteGenerator.dll file in the CssSpriteGenerator\bin folder.
- Update your web site:
-
Add a reference to CssSpriteGenerator.dll to your web site (in Visual Studio, right click your web site, choose Add Reference).
-
Add the custom section cssSpriteGenerator to the configSections section of your web.config:
<configuration>
...
<configSections>
...
<section name="cssSpriteGenerator" type="CssSpriteGenerator.ConfigSection" requirePermission="false"/>
...
</configSections>
...
</configuration>
-
Add some initial configuration settings:
<configuration>
...
<configSections>
...
<section name="cssSpriteGenerator" type="CssSpriteGenerator.ConfigSection" requirePermission="false"/>
...
</configSections>
...
<cssSpriteGenerator active="Always">
<imageGroups>
<add maxWidth = "100" maxHeight = "100" maxSpriteSize = "30000" samePixelFormat = "true" />
</imageGroups>
</cssSpriteGenerator>
...
</configuration>
In the
Quick Start you'll see what these settings do.
-
Add a folder called App_Browsers to the root folder of your web site: Right click your web site in Visual Studio, choose Add ASP.NET Folder, choose App_Browsers.
-
Use your favorite text editor (such as Notepad) to create a text file in the App_Browsers folder. Call it PageAdapter.browser. Into that file, copy and paste this code:
<browsers>
<browser refID="Default">
<controlAdapters>
<adapter controlType="System.Web.UI.Page"
adapterType="CssSpriteGenerator.CSSSpriteGenerator_PageAdapter" />
</controlAdapters>
</browser>
</browsers>
This tells ASP.NET to leave processing of the page to the class CssSpriteGenerator.CSSSpriteGenerator_PageAdapter (which is part of the package).
Quick Start
The full story about all the options and how to configure them
is in the
configuration
section. But there are many options, so this section introduces you to the ones that may be
most relevant to you, to get you started quickly.
The downloaded solution contains a series of demo sites with working examples.
The demo site DemoSite_QuickStart was set up to make it easy to follow this section - apply the examples further on in this section
to its web.config and than run the site to see how the changes leads to different sprites being generated.
This section looks at these scenarios:
Default configuration
Right after you've done the installation:
-
The package is active in both Debug and Release mode.
-
Only images no wider or higher than 100 pixels will be combined into sprites.
-
Sprites will be split when grower larger than 30,000 bytes.
-
Images will be segragated by pixel format, so images with 8 bits per pixel
won't go in the same sprite as images with 24 bits per pixel
(why).
To give it a try:
-
Open a page on your site that has multiple images with width and height less than 100px.
-
View the source of the page to see the html for the sprite(s).
-
You will find a new directory ___spritegen in the root of your site with the generated sprite(s).
Using the package in Release mode only
You can control when the package is active with the
active property
in the cssSpriteGenerator element in your web.config file.
To make sure the package is only active in Release mode, set it to
ReleaseModeOnly:
<configuration>
...
<cssSpriteGenerator active="ReleaseModeOnly">
...
</cssSpriteGenerator>
...
</configuration>
Processing bigger images
To allow bigger images to be combined into sprites,
change the
maxWidth or
maxHeight properties on the
imageGroup
in your cssSpriteGenerator element, like so:
<configuration>
...
<cssSpriteGenerator active="Always">
<imageGroups>
<add maxWidth="200" maxHeight="300" maxSpriteSize = "30000" samePixelFormat = "true"/>
</imageGroups>
</cssSpriteGenerator>
...
</configuration>
Image groups
are a fundamental concept in the package.
When an image is found
on the current page,
in a group of background images, or
in a directory on the server,
it is first added to a group. You can set limitations on the images that can be added to a group, such as
maximum width,
maximum height,
file path, etc.
The images in the group are then combined into one or more sprites. If an image doesn't get added to any group, it is simply ignored.
Later on,
you'll see how you can have multiple groups, and post processing options per group.
Shared sprites
By default, the package checks the current page for images to combine into a sprite.
However, you might want it to generate sprite(s) for all your small images, irrespective of which page they appear on.
That way, you might get a single sprite with all your little icons.
Those sprites can then be shared among your pages.
That way, when your visitor moves to another page on your site,
the shared sprite is probably still in their
browser cache, so their browser doesn't need
to retrieve it from your server.
You can
get the package to read images not just from the web page, but also from a folder on the web server with the
processFolderImages and
imageFolderPathMatch
properties. They then get assigned to groups, so you can filter on image width and height, etc.
Assuming your images sit in the directory images on your web server, you'd write something like
this:
<configuration>
...
<cssSpriteGenerator active="Always"
processFolderImages="true" imageFolderPathMatch="\\images\\" ... >
<imageGroups>
<add maxWidth = "100" maxHeight = "100" maxSpriteSize = "30000" samePixelFormat = "true" />
</imageGroups>
</cssSpriteGenerator>
...
</configuration>
The package now reads images from both the page and the images folder.
As before, only those images no wider or higher than 100 pixels will be combined into sprites.
Why the double back slashes in \\images\\?
imageFolderPathMatch takes a regular expression, and the backslash is a special character in regular expressions, so it needs to be
escaped with another backslash.
Compressing big JPEG images
Many web sites have large photos that would take a lot less bandwidth if
someone compressed them. Compressing images can save
a lot of bytes without visible loss of image quality, but often people don't take the time to do it.
You can compress a JPEG image by
adding your photos to a sprite, and then
reducing the image quality of that sprite with the
jpegQuality property. Setting it to 70% often has good results.
However, you wouldn't want to combine multiple big photo files into a sprite, because you might wind up with very large sprites that are slow to load.
To ensure that each photo goes into its own sprite, use the
giveOwnSprite property.
You can make this all work by using multiple groups:
-
One group for your big photos. These will be compressed and there will be only one image per sprite.
-
Another group for your small images. Here multiple images can be combined into a sprite.
The result looks like this:
<configuration>
...
<cssSpriteGenerator active="Always" >
<imageGroups>
<add filePathMatch="\\photos\\"
jpegQuality="70" giveOwnSprite="true" />
<add maxWidth = "100" maxHeight = "100" maxSpriteSize = "30000" samePixelFormat = "true" />
</imageGroups>
</cssSpriteGenerator>
...
</configuration>
The first group will only contain images that have \photos\ in their file path. Any image matching this
will get its own sprite, which will be reduced to 70% quality.
Any images that don't have \photos\ in their file path are then matched against the second group - so they will be combined into sprites
if they are no wider or higher then 100 pixels. Images not matching the second group either won't be combined into sprites.
The compressed photos wind up in the ___spritegen directory along with the other generated images.
Check their file sizes to see how many bytes you're saving.
If you're trying this out on the DemoSite_QuickStart site, you'll find that the file size of the large photo
is reduced from 79.3KB to 58.6KB - a 26% saving without visible loss of quality.
Generating thumbnails
Your site may have thumbnails that link to the full sized photos.
If you have a few photos, making the thumbnails yourself isn't hard.
But if you have many photos, that becomes laborious.
Or you could use the original image and
use the width and height attributes of the img tag
to reduce its size on the page. However, this causes the browser to load the full sized original
image file, which is not an optimal use of your bandwidth.
To cater for this,
the package
physically resizes images whose
width and height attributes are different from their physical width and height.
Or you could use the
resizeWidth and/or resizeHeight
property:
<configuration>
...
<cssSpriteGenerator ... >
<imageGroups>
<add filePathMatch="\\photos\\" resizeHeight="200" ... />
</imageGroups>
</cssSpriteGenerator>
...
</configuration>
Use the
pageUrlMatch property
to only
resize the photos on your thumbnail pages, and not on the pages with the original images.
If your thumbnail page is called
thumbnail.aspx, you'd write:
<configuration>
...
<cssSpriteGenerator ... >
<imageGroups>
<add filePathMatch="\\photos\\" resizeHeight="200"
pageUrlMatch="thumbnail.aspx" ... />
</imageGroups>
</cssSpriteGenerator>
...
</configuration>
Configuration
Configuration for the package falls in three categories:
-
Overall Configuration
- settings that apply to the package as a whole, such as whether it is active or not,
whether it processes page images, etc.
-
Image Groups
- determine which images are processed and how.
You've already come across groups in the
quick start section.
-
cssImages elements
- tell the package which CSS background images to process, and set restrictions
on how they can be combined into sprites to cater for repeating background images.
These categories are discussed in the following sections.
Overall Configuration
The package supports these package wide configuration settings:
Overall Switches
Image Gathering
Options that determine how the package finds the images to be processed
Sprite Generation
Options that determine how the package generates sprites
Overall Switches
active
Determines when the package is active. When it is not active, it doesn't affect your site at all and none of the other attributes or child elements listed in this section have any effect.
Value |
Description |
Never |
Package is never active, irrespective of debug mode |
Always |
Package is always active, irrespective of debug mode |
ReleaseModeOnly (default) |
Package is only active in Release mode |
DebugModeOnly |
Package is only active in Debug mode |
Example
<cssSpriteGenerator active="Always" >
...
</cssSpriteGenerator>
Whether your site is in Debug or Release mode depends on the debug attribute of the compilation element
in your web.config file. If that attribute is set to false, your site is in Release mode (as it should be when live).
When it is set true, it is in Debug mode (as it should be in development). It looks like this in web.config:
<configuration>
...
<system.web>
<compilation debug="true">
...
</compilation>
...
</system.web>
...
</configuration>
By default, the package is only active in Release mode - so you won't see any effect while you're developing.
To ensure that the package is active in both Release mode and in Debug mode, set active to Always, as shown in
the example above.
Note that the active property acts as a master switch for the whole package.
If it isn't active, none of the other properties have any effect.
exceptionOnMissingFile
Determines whether the package throws an exception when an image file is missing.
Value |
Description |
Never (default) |
The package never throws an exception when an image file is missing. |
Always |
The package always throws an exception when an image file is missing. |
ReleaseModeOnly |
The package only throws an exception if the site is in Release mode. |
DebugModeOnly |
The package only throws an exception if the site is in Debug mode. |
Example
<cssSpriteGenerator exceptionOnMissingFile="DebugModeOnly" ... >
...
</cssSpriteGenerator>
In order to process an image, the package has to actually read that image. What happens if the image file cannot be found on the web server?
That is determined by the exceptionOnMissingFile attribute:
- If exceptionOnMissingFile is active (see table above) and the package finds that an image file cannot be found,
it throws an exception with the path of the image. That makes it easier to find missing images.
-
If exceptionOnMissingFile is not active, the package doesn't throw an exception and recovers by
not processing the image. For example, if the image was found via a img tag, it will leave the tag alone.
If all images should be present in your development environment, than it makes sense to set exceptionOnMissingFile
to DebugModeOnly. That way, you quickly find broken images while developing your site, while preventing exceptions in
your live site where you probably prefer a broken image over an exception.
Keep in mind that if you want exceptions while the site is in Debug mode, you have to ensure that the package is actually
active in Debug mode - set active to Always to make that happen.
Image Gathering
processPageImages
Determines whether images on the current page are processed
Value |
Description |
Never |
Images on the current page are never processed |
Always (default) |
Images on the current page are always processed |
ReleaseModeOnly |
Images on the current page are processed if the site is in Release mode. |
DebugModeOnly |
Images on the current page are processed if the site is in Debug mode. |
Example
<cssSpriteGenerator processPageImages="Never" ... >
</cssSpriteGenerator>
Normally when a page on your site opens, the package finds all the img tags on the page, assigns them to
image groups and processes the groups into sprites. It then replaces the img tags
with div tags that show that part of the sprite matching the original image - so your page still looks the same but loads quicker
(details).
When you switch off processPageImages, the package no longer goes through the page looking for img tags. It also won't
replace any img tags.
You would switch processPageImages off if you only wanted to
process CSS background images.
processCssImages
Determines whether the CSS background images listed in
cssImages are processed
Value |
Description |
Never |
CSS background images are never processed |
Always (default) |
CSS background images are always processed |
ReleaseModeOnly |
CSS background images are processed if the site is in Release mode. |
DebugModeOnly |
CSS background images are processed if the site is in Debug mode. |
Example
<cssSpriteGenerator processCssImages="Never" ... >
</cssSpriteGenerator>
If this option is active (the default), than the CSS background images you've listed in
the
cssImages element
are processed by the package.
processFolderImages
Determines whether images stored in one or more folders on the web server are processed.
See the sample web site DemoSite_FolderImages in the downloaded solution.
Value |
Description |
Never (default) |
The package never processes images from folders on the web server |
Always |
The package always processes images from folders on the web server |
ReleaseModeOnly |
The package only processes images from folders on the web server if the site is in Release mode |
DebugModeOnly |
The package only processes images from folders on the web server if the site is in Debug mode |
Example
<cssSpriteGenerator processFolderImages="Always" ... >
</cssSpriteGenerator>
In addition to having the package find images on the current web page (if
processPageImages is active) and from the
cssImages element (if
processCssImages is active), you can also get it to find images
from one or more folders on the web server.
Why would you want to add images to a sprite that are not actually on the page? Take a very simple web site that uses 3 small icons on its pages, but not all icons appear on
all pages:
Page | Icons used |
default.aspx | contactus.png, cart.png, print.png |
contactus.aspx | cart.png, print.png |
cart.aspx | contactus.png, print.png |
If you had the package only read images from the current web page, than it would create different sprites for each page, because each page
has a different set of icons. However, it would be far more efficient to have all 3 icons in the one sprite. That way, when a visitor moves from
one page to the other, that single sprite is still in browser cache, so doesn't have to be loaded again over the Internet.
The way to do that is to get the package to read all icons when it creates a sprite, not just the ones that are on the current web page.
To get the package to find images
from one or more folders on the web server, set processFolderImages active. You then also need to set
imageFolderPathMatch to determine specifically which images should be
read.
Just as with images taken from the current web page and from
the cssImages elements, images taken from one or more folders are first assigned to
image groups. Only images assigned to a group can become part of a sprite.
This means that if you want to add particular images to a sprite even if they are not on the current web page and not in the
cssImages element, you have to take these steps:
-
Set processFolderImages active;
-
Make sure that the file paths of the desired images match imageFolderPathMatch;
-
Make sure that the images match a group, so they can be worked into a sprite. The images taken from folders
do not all have to match the same group.
For example, if you have a folder images\icons with PNG and GIF images and you want all those images to be made into a sprite
irrespective of whether they appeared on the current page, you could use:
<!---->
<cssSpriteGenerator processFolderImages="Always"
imageFolderPathMatch="\\images\\icons\\.*?(png|gif)$" ... >
<imageGroups>
...
<!---->
<add groupName="icons" filePathMatch="\\images\\icons\\.*?png$" />
<!---->
<add groupName="icons" filePathMatch="\\images\\icons\\.*?gif$" />
</imageGroups>
</cssSpriteGenerator>
imageFolderPathMatch
If the
processFolderImages property is active, than
this property is required. It determines which image files are read.
Type |
Default |
string (regular expression) | none |
Example
<cssSpriteGenerator imageFolderPathMatch="\\images\\img1" ... >
</cssSpriteGenerator>
For details on the folder images feature, see
processFolderImages.
The imageFolderPathMatch property is a regular expression.
If processFolderImages is active, than the package
looks at the file paths of all PNG, GIF and JPEG files in the root directory of the site and its subdirectories.
Those image files whose paths match imageFolderPathMatch will be processed.
For example, to process all image files in directory
images\img1, you would use:
<!---->
<cssSpriteGenerator imageFolderPathMatch="\\images\\img1\\" ... >
</cssSpriteGenerator>
(it uses double backslashes because the backslash is a special character in regular expressions, so needs to be escaped with another backslash)
If you wanted to process only the PNG files in directory
images\img1, you would use:
<!---->
<cssSpriteGenerator imageFolderPathMatch="\\images\\img1\\.*?png$" ... >
</cssSpriteGenerator>
If you want to process all image files in the root directory of the site and its subdirectories, use:
<!---->
<cssSpriteGenerator imageFolderPathMatch="\\" ... >
</cssSpriteGenerator>
If your site writes images to the root directory of the site or one of its sub directories (for example if visitors can upload images),
than make sure that those images are stored in a directory that isn't matched by
imageFolderPathMatch, otherwise they could wind up in sprites. Those sprites would get bigger with every image added.
Additionally, if your site writes images frequently, such as more than once every 5 minutes, consider storing these images
outside the root directory of the site. For faster processing, the package keeps the structure of the root directory and its
sub directories in cache. Each time an image is added to the root directory or one of its sub directories,
that cache entry is removed to ensure it isn't outdated, meaning that the package has to rebuild that cache entry again.
Sprite Generation
inlineSpriteStyles
Determines whether the styling needed with the div tags
that replace the img tags is inline or in a separate .css file.
Value |
Description |
false | Additional styling is placed in a separate .css file |
true (default) | Additional styling is inline |
Example
<cssSpriteGenerator inlineSpriteStyles="false" ... >
</cssSpriteGenerator>
In the section about
how sprites work,
you saw how the div tags that replace your img tags use additional styling to show the correct
area of the sprite - using background, width, height, etc.
If you leave inlineSpriteStyles at true, the package inlines all the additional styling.
That gives you div tags such as this:
<div style="width: 32px; height: 32px; background: url(/TestSite/___spritegen/2-0-0-90- ... -53.png) -200px -0px; display:inline-block;"></div>
However, if you set inlineSpriteStyles to false,
the additional styling is placed in a separate .css file which is generated in the same
directory as the sprite images themselves. The generated div tags will refer to the styling via CSS classes.
This gives you div tags such as this:
<div class="c0___spritegen" > </div>
In that case, make sure that the head tags of your pages have runat="server", so the package can
add a link tag for the .css file to the head section:
<head runat="server">
Here are the advantages/disadvantages of setting inlineSpriteStyles to false:
-
The advantage of using a separate .css file is that the next time the page is loaded, the .css file may still be in browser cache - so all that additional styling doesn't have
to be loaded over the Internet. This would apply if your visitors tend to hang around on your site, hitting several pages in the one visit.
-
The drawback of using a separate .css file is that if that file is not in browser cache, the browser has one more file to load.
Also, if your web server uses
compression,
the additional inlined styles don't add much to the number of bytes going over the Internet because they are highly compressible.
There are a few cases where it may make sense to place the additional CSS in a separate CSS file by
setting inlineSpriteStyles to false:
-
You're pretty sure that on many visits the separate CSS file will be in browser cache.
-
You use the
cssImages element to process CSS background images.
In that case, the package always generates a separate CSS file with styles that override existing
CSS styles to make them work with the new sprites. If you now set inlineSpriteStyles to false,
the additional CSS for the div tags will
go into the same CSS file as the CSS for the background images, rather than a separate CSS file. This means
you've reduced the size of your .aspx pages by taking out the inlined styles, without incurring an additional CSS file load.
generatedFolder
Sets the folder where the generated .css file (if there is one) and the sprites are stored.
Type |
Default |
string | ___spritegen |
Example
<cssSpriteGenerator generatedFolder="generated\sprites" ... >
</cssSpriteGenerator>
This folder will be created (if it doesn't already exist) in the root folder of the site.
You can include one or more \ in this property. In that case, you'll get a folder and one or more subfolders.
classPostfix
Postfix used to ensure that generated class names are unique.
Type |
Default |
string | ___spritegen |
Example
<cssSpriteGenerator classPostfix="___generated" ... >
</cssSpriteGenerator>
If you set inlineSpriteStyles to false, the package generates a .css file
with the additional CSS needed to
make the sprites work. To make sure that the names of the generated CSS classes do not clash with
the other CSS classes in your site, the contents of classPostfix is appended to the generated class names.
If you find that the names of the generated CSS classes clash with the names of other CSS classes in your site,
you can change classPostfix to fix this.
Image Groups
You use image groups to determine which images will be processed, and how they will be processed.
For a full introduction, refer to the
quick start section.
You can have multiple image groups. This is useful when:
-
You want to use different post processing options for specific groups of images.
For example, you might only want to use
jpegQuality
on images over a given size.
-
You want to make your sprites more cacheable, by having sprites common to all pages or common to a group of pages.
That makes it easy for the package to reuse sprites for different pages.
Each group can have the following properties:
Image Filtering
These properties determine which images go into which image group.
Sprite Generation
These properties influence how sprites are generated, such as their image type.
Image Processing
These properties let you manipulate the individual images in the group.
Group Creation
These properties make it easier to manage your image groups.
Image Filtering
maxSize
Sets the maximum size in bytes of images belonging to this group. Images whose file size is larger than this
will not be added to this group.
Type |
Default |
Int32
|
No size limit
|
Example
<cssSpriteGenerator ... >
<imageGroups>
<add maxSize="2000" ... />
</imageGroups>
</cssSpriteGenerator>
If you use width and/or height
attributes in your img tags that are different from the physical width and/or height of the images,
read on. Otherwise, you can
skip the rest of this section.
If you use width and/or height
attributes that are different from the physical width and/or height of the images,
the package will auto resize the physical image in memory (not on disk!) before adding it to the sprite, unless
the disableAutoResize property is true
(more details about this feature).
Because of this, the package will estimate the size in bytes of the resized image in order to work out
whether to add it to a group. Take this situation:
| physical width |
physical height |
physical size |
physical image |
100px |
200px |
3000 bytes |
If you set maxSize to 2000 for a group, than normally this image would not be added because its file size is too big.
Now if you use that image with this tag:
<img src="..." width="100" height="100" />
The image as shown on the page will now have half the height of the physical image.
The package than makes a very rough estimate of the file size that this image would have had if it had been physically resized
to the given width and height.
In this case, the area of the image (width x height) has been halved, so the package
divides
the physical size of the image by 2:
| width in sprite |
height in sprite |
estimated size |
image in sprite |
100px |
100px |
1500 bytes |
Because the estimated size is now only 1500 bytes, it will now be added
to a group with maxSize set to 2000.
One last twist here is that the size of the image as it goes into the sprite can not only be set by the
width and height properties on the img tag, but also by the
resizeWidth and
resizeHeight properties of the image group. However, these are only applied after an image has been
added to a group, so they are not used to determine whether to add an image to the group in the first place.
maxWidth
Sets the maximum width in pixels of images belonging to this group. Images whose width is larger than this
will not be added to this group.
Type |
Default |
Int32
|
No width limit
|
Example
<cssSpriteGenerator ... >
<imageGroups>
<add maxWidth="50" ... />
</imageGroups>
</cssSpriteGenerator>
If you used a
width
attribute in an img tag, than that width will be used to decide whether the image is not too wide, rather than the physical width of the image
(provided you didn't set disableAutoResize to true).
For example, if maxWidth is 50 for a group, than an image that is 60px wide will normally not be included in that group.
However, if you had the following image tag, the width property will be used and the image will be included:
<img src="width60px.png" width="50" />
This feature is not meant to encourage you to use width or height properties that are inconsistent with the physical image size.
But if you did, than this is how the package will handle this.
maxHeight
Sets the maximum height in pixels of images belonging to this group. Images whose height is larger than this
will not be added to this group.
Type |
Default |
Int32
|
No height limit
|
Example
<cssSpriteGenerator ... >
<imageGroups>
<add maxHeight="50" ... />
</imageGroups>
</cssSpriteGenerator>
Similar to
maxWidth,
if you used a
height
property in an img tag, than that height will be used to decide whether the image is not too high, rather than the physical height of the image
(provided you didn't set disableAutoResize to true).
maxPixelFormat
Sets the maximum bits per pixel of images belonging to this group. Images that have more bits per pixel
than this
will not be added to this group. JPEG images will not be added to this group either, irrespective of the value of maxPixelFormat.
Example
<cssSpriteGenerator ... >
<imageGroups>
<add maxPixelFormat="Format8bppIndexed" ... />
</imageGroups>
</cssSpriteGenerator>
filePathMatch
This is a regular expression (tutorial).
If this is set, images whose file path does not match this will not be included in the group.
Type |
Default |
string (regular expression)
|
empty (no restriction)
|
Example
<cssSpriteGenerator ... >
<imageGroups>
<!--only include GIF and PNG images in the group-->
<add filePathMatch="(png|gif)$" ... />
</imageGroups>
</cssSpriteGenerator>
Note that filePathMatch matches against the file path of the image on the web server,
not against the url of the image on your site.
To only include images in the icons directory,
set filePathMatch to \\icons\\, not to /icons/.
You need to double the backslashes (\\), because the backslash is a special character in regular expressions, so needs to be escaped with another backslash.
pageUrlMatch
This is a regular expression.
If this is set, than this group is only used if the url of the current page matches this.
If the url of the current page does not match pageUrlMatch, the package acts as though the group doesn't exist.
Type |
Default |
string (regular expression)
|
empty (no restriction)
|
Example
<cssSpriteGenerator ... >
<imageGroups>
<!--do not use this group if the current page has "/excludeme/" in its url-->
<add pageUrlMatch="^((?!/excludeme/).)*$" ... />
</imageGroups>
</cssSpriteGenerator>
Note that whereas filePathMatch matches against the file path of an image,
pageUrlMatch matches against the absolute url of the current page.
To use an image group only with pages in directory special, set
pageUrlMatch to /special/, not to \\special\\.
The example above shows how to make sure that an image group is used for all pages, except those in a particular directory.
As you see, making this happen in a regular expression is a bit awkward
(details).
The demo site DemoSite_Gallery in the solution in the download shows how
pageUrlMatch can be used to resize images only on the home page, while keeping their sizes the same on all other pages.
Sprite Generation
maxSpriteSize
Sets the maximum size of a sprite in bytes.
Example
<cssSpriteGenerator ... >
<imageGroups>
<add maxSpriteSize="10000" ... />
</imageGroups>
</cssSpriteGenerator>
If you have a lot of images to put into sprites,
it's better to spread them over a number of reasonably sized sprites, rather than one very big sprite. That allows the browser to
load the sprites in parallel.
To achieve this, you can set
maxSpriteSize. While adding images to a sprite, the package keeps track of the total file size of all images added.
If that goes over maxSpriteSize, it writes the sprite and starts a new one. As a result, one group could generate multiple sprites.
Note that the package doesn't attempt to work out how big the sprite will be after it has been written to disk - that would take a lot of CPU cycles.
It simply adds up the file sizes of the images going into the sprite.
You may have resized one or more images in the group with the
resizeWidth and
resizeHeight properties,
or with the
width and/or height attributes on the img tag.
In that case, the package estimates the file size of the resized image and uses that
to calculate the current size of the sprite.
samePixelFormat
Specifies whether all images in a sprite have the same pixel format.
Example
<cssSpriteGenerator ... >
<imageGroups>
<add samePixelFormat="false" ... />
</imageGroups>
</cssSpriteGenerator>
If this property is true, images will be segregated by pixel format when they are combined into a sprite.
So images with 4 bits per pixel will go into a sprite with 4 bits per pixel, images with 8 bits per pixel will go into a sprite with 8 bits per pixel, etc.
JPEG images will go into a separate sprite.
Setting samePixelFormat to true gives you more sprites, and therefore more request/response overhead, but
also far fewer bytes going over the wire.
Assume you have a number of 4 bit per pixel PNG images and an 8 bits per pixel PNG image on your page. If they all go into a single sprite,
that sprite will have 8 bits per pixel to accommodate the 8 bits per pixel image. But this means that the original 4 bits per pixel images now take
8 bits per pixel as well. The result is often a sprite that takes more bytes than the total sizes of the individual images.
If it is more important to you to reduce the number of sprites to a minimum, rather than minimize sprites sizes,
set samePixelFormat to false.
However, this may give you ballooning sprite sizes when PNG (typically 8 bits per pixel) and JPEG images (24 bits per pixel) are combined.
You can avoid this by segragating images by image type using sameImageType.
That way, 4 bits per pixel PNGs and 8 bits per pixel PNGs can be combined into a single sprite, but JPEG images go into their own sprite.
sameImageType
Specifies whether all images in a sprite have the same image type (GIF, PNG or JPEG).
Example
<cssSpriteGenerator ... >
<imageGroups>
<add sameImageType="true" ... />
</imageGroups>
</cssSpriteGenerator>
If this property is true, images will be segregated by image type when they are combined into a sprite.
So PNG images will go into a PNG sprite, JPEG images into a JPEG sprite and GIF images into a GIF sprite
(assuming you didn't set spriteImageType).
Also see samePixelFormat.
spriteImageType
Sets the image type of the sprite.
Value |
Description |
Png (default, but see below) |
Sprite will be written to disk as a PNG file.
Recommended for sprites containing simple icons, drawings, etc. |
Gif |
Sprite will be written to disk as a GIF file.
This option is included for completeness. PNG images tend to be more efficient than GIF images, so use
Png if you can.
|
Jpg |
Sprite will be written to disk as a JPEG file.
Recommended for sprites containing compressed photos, etc. |
Example
<cssSpriteGenerator ... >
<imageGroups>
<add spriteImageType="Jpg" ... />
</imageGroups>
</cssSpriteGenerator>
Here is how the package works out what image type to use for a sprite:
-
If spriteImageType has been set, that image type is used.
-
Otherwise, if all images making up the sprite have the same image type, that image type is used.
Also see sameImageType.
-
Otherwise, PNG is used as the image type.
giveOwnSprite
Lets you give all images in the group a sprite of their own.
Value |
Description |
false (default) | Images in the group are combined into sprites. |
true | Instead of combining images into sprites, each image in the group gets its own sprite. |
Example
<cssSpriteGenerator ... >
<imageGroups>
<add giveOwnSprite="true" ... />
</imageGroups>
</cssSpriteGenerator>
The reason you combine images into sprites is to reduce the request/response overhead for the browser
of loading each individual image. For bigger images however, the request/response overhead is not significant, so normally
you wouldn't combine those into sprites. Otherwise you could wind up with very big sprites that take a long time to load by the browser.
On the other hand, the package allows you to do all sorts of good things with sprites, such as compressing JPEG sprites, or resizing images to make thumbnails on the fly.
It would be good if you could use those features with bigger images as well.
The solution is to add the bigger images to a group and to set
giveOwnSprite to true. That way, the images in the group will all get a sprite of their own, so they are not combined with other images. Than you can use
jpegQuality or
pixelFormat
to compress the resulting sprite and/or
resizeWidth and
resizeHeight to resize them - without winding up with massive sprites.
When you look at the html generated by the package, you will find that it generates normal img tags for sprites that contain
only one image. This because such a sprite is essentially a normal image, so there is no need
for additional CSS.
The demo site DemoSite_CompressedJpeg in the downloaded solution uses the giveOwnSprite property to stop big images from being
combined into sprites.
Image Processing
resizeWidth
Lets you set the width of all images in the group.
Also see resizeHeight.
Type |
Default |
Int32
|
Don't resize
|
Example
<cssSpriteGenerator ... >
<imageGroups>
<add resizeWidth="50" ... />
</imageGroups>
</cssSpriteGenerator>
resizeWidth can be used to create thumbnails on the fly, so you don't have to make them yourself.
Take for example a page "thumbnails.aspx" where you want to show thumbnails of bigger images. You want each thumbnail to be
50px wide. Normally, you would have to create separate thumbnail images - but with resizeWidth you can simply use image tags that refer
to the full sized images:
<!---->
<img src="bigimage1.jpg" />
<img src="bigimage2.jpg" />
<img src="bigimage3.jpg" />
<img src="bigimage4.jpg" />
To resize the big images on the fly so they are only 50px wide, you'd make sure that the JPEG images are included in a group.
For that group, set resizeWidth to 50. And make sure that the group is only used for page thumbnails.aspx:
<cssSpriteGenerator ... >
<imageGroups>
<add filePathMatch="\.jpg" resizeWidth="50" pageUrlMatch="thumbnails\.aspx$" ... />
</imageGroups>
</cssSpriteGenerator>
Note that the images are physically resized before they are added to the sprite, so you will get both a smaller image and savings in bandwidth.
Your original image files will not be changed though - it all happens in memory.
If that is more convenient, you could also achieve the same smaller size and the same bandwidth savings without using
resizeWidth,
by simply adding a width property to the image tags
(details):
<!---->
<img src="bigimage1.jpg" width="50" />
<img src="bigimage2.jpg" width="50" />
<img src="bigimage3.jpg" width="50" />
<img src="bigimage4.jpg" width="50" />
resizeHeight
Lets you set the height of all images in the group.
Also see resizeWidth.
Type |
Default |
Int32
|
Don't resize
|
Example
<cssSpriteGenerator ... >
<imageGroups>
<add resizeHeight="100" ... />
</imageGroups>
</cssSpriteGenerator>
You would use this to generate thumbnails on the fly with a given height,
in exactly the same way as you would generate thumbnails with a given width using
resizeWidth.
You can combine resizeHeight and resizeWidth. If you use only one, than the package will adjust the other dimension
so the image keeps the same aspect ratio. So if you cut the height in half (such as from 200px to 100px), it cuts the width in half as well.
If you set both, it simply uses both. For example:
Original Image |
Group |
Resulting Image |
Width | Height | resizeWidth | resizeHeight | Width | Height |
100 | 200 | not set | not set | 100 | 200 |
100 | 200 | 50 | not set | 50 | 100 |
100 | 200 | not set | 20 | 10 | 20 |
100 | 200 | 50 | 20 | 50 | 20 |
Note that if you set both resizeWidth and resizeHeight,
you can easily change the aspect ratio of the image, which may not look good.
jpegQuality
Only works if the sprite generated via this group is a JPEG image. In that case, this lets you reduce the image quality, and thereby the file size, of the sprite.
Type |
Default |
Int32 (between 0 and 100)
|
No compression
|
Example
<cssSpriteGenerator ... >
<imageGroups>
<add jpegQuality="70" ... />
</imageGroups>
</cssSpriteGenerator>
jpegQuality is a percentage of the quality of the sprite as it would have been if you hadn't specified jpegQuality.
For example, if you set jpegQuality to 70, than the quality of the sprite will be reduced to 70% of its "natural" quality.
This can dramatically reduce the file size of the sprite.
The optimal setting for jpegQuality depends on the sprite - you would determine this through experimentation.
Setting quality higher than 90 may actually result in a greater file size.
Values between 50 and 70 tend to give good reductions in size without being too noticable to human eyes.
The best use of jpegQuality is probably to reduce the file size of photos. Image files produced by digital cameras
tend to be very big, and can be easily compressed without visible loss of quality.
To effectively compress large JPEG images, you would use these properties:
-
You may decide you don't want to combine these large images into sprites because the resulting sprites would be very big (although you certainly could).
To achieve
that you'd set
giveOwnSprite
to true.
-
You want to set
filePathMatch to
"jpg$", so the group only picks up JPEG images.
Because this is a regular expression, you can use this to only select images from particular directories as well.
-
Finally, set
spriteImageType
to"Jpg". Otherwise the images will be converted to PNG images, which for photos is not optimal.
This would result in something like:
<cssSpriteGenerator ... >
<imageGroups>
<add jpegQuality="70" giveOwnSprite="true" filePathMatch="jpg$"
spriteImageType="Jpg" ... />
</imageGroups>
</cssSpriteGenerator>
Suppose you want to combine small JPEG images into sprites along with small PNG and GIF images, while compressing the big JPEG images?
You can do this by using the fact that the package matches images with whatever group comes first:
<cssSpriteGenerator ... >
<imageGroups>
<!--matches all images that are 200px by 300px or smaller-->
<add maxWidth="200" maxHeight="300"/>
<!--matches all remaining JPEG images.
These images will be bigger than 200px by 300px otherwise they would have
matched the preceding group.-->
<add jpegQuality="70" giveOwnSprite="true"
filePathMatch="jpg$" spriteImageType="Jpg" ... />
</imageGroups>
</cssSpriteGenerator>
The demo site DemoSite_CompressedJpeg in the downloaded solution uses the jpegQuality property to reduce the quality of big jpeg files to 70%.
pixelFormat
Only applies if the sprite is generated as a PNG or GIF image.
Sets the pixel format of the sprite.
Value | Nbr. Colors | Bit Depth (bits per pixel) | Resulting pixel format |
DontCare (default) | | | Pixel format of the constituent image with the highest bits per pixel.
Format48bppRgb when combining an image
using Format16bppGrayScale with colored images. |
Format1bppIndexed | 2 | 1 | Uses color table with 2 colors (black and white). |
Format4bppIndexed | 16 | 4 | Uses color table with 16 colors. |
Format8bppIndexed | 256 | 8 | Uses color table with 256 colors. |
Format16bppGrayScale | 65,536 shades of gray | 16 | The color information specifies 65536 shades of gray. |
Format16bppRgb555 | 32,768 | 16 | 5 bits each for the red, green, and blue components. The remaining bit is not used. |
Format16bppRgb565 | 65,536 | 16 | 5 bits for the red component, 6 bits for the green component, and 5 bits for the blue component. |
Format16bppArgb1555 | 32,768 | 16 | 5 bits each for the red, green and blue components, and 1 bit is alpha. |
Format24bppRgb | 16,777,216 | 24 | 8 bits each for the red, green, and blue components. |
Format32bppRgb | 16,777,216 | 32 | 8 bits each for the red, green, and blue components. Remaining 8 bits not used. |
Format32bppArgb | 4,294,967,296 | 32 | 8 bits each for the alpha, red, green, and blue components. |
Format32bppPArgb | 4,294,967,296 | 32 | 8 bits each for the alpha, red, green, and blue components. The red, green, and blue components are premultiplied, according to the alpha component. |
Example
<cssSpriteGenerator ... >
<imageGroups>
<add pixelFormat="Format24bppRgb" ... />
</imageGroups>
</cssSpriteGenerator>
Images in PNG and GIF format can have different pixel formats. This helps in reducing the image file size. For example, if you produce
a PNG image in Photoshop that has no more than 16 colors, you would give it a color depth
of no more than 16 colors, giving you an image that takes 4 bits per pixel (Format4bppIndexed).
If you used more than 16, you'd have to give it the next higher
color depth of 256 colors, taking 8 bits per pixel (Format8bppIndexed).
The higher the color depth, the bigger the file size.
Normally, the package generates sprites with the right pixel format. But sometimes you want to override the pixel format:
The demo site DemoSite_CompressedPng in the downloaded solution uses pixelFormat
to reduce the color depth of a sprite.
Pixel format is quite a topic in its own right. Here we'll look at:
Finding out bits per pixel of an image
Finding out the bit depth, dimensions, etc. of an image doesn't require an image editor, at least if you use Windows 7 and possibly Vista:
-
Right click the image file in Windows Explorer;
-
Choose Properties;
-
Open the Details tab.
Combining images with different pixel formats
When the package combines multiple images into the one sprite, those images may have different pixel formats -
one image may use Format4bppIndexed because it has fewer than 16 colors,
while another one may use Format8bppIndexed because it uses more than 16 colors, etc.
To ensure that the constituent images all look good on your page, by default the package sets the pixel format
of the sprite to that of the constituent image with the highest pixel format
- which would be Format8bppIndexed in the above example. You can override this by setting
pixelFormat.
Combining images with same bit depth but different palettes
When you create an image in Photoshop or some other image editing program and you give it a color depth of
16 or 256 colors (corresponding to pixel formats
Format4bppIndexed and Format8bppIndexed),
the program will create a palette of colors inside the image file with the colors you used in the image.
The 4 or 8 bits for each pixel than form an index into the palette.
This means that an image with lots of shades of red would have a palette with lots of shades of red. Another image
with the same color depth but with lots of shades of blue would have a palette with lots of shaded of blue.
So both images would have completely different palettes, even though their color depths are the same.
To cope with this, the package initially creates a sprite image with pixel format Format32bppArgb (32 bits per pixel) and then adds
the constituent images. That way, even if those images have widely different palettes, they will all keep their colors in the inital sprite.
However, to reduce the file size of the sprite, the package
then reduces the color depth of the sprite to that of the constituent image with the most colors.
If the image with the highest color depth uses 4 or 8 bits, that means the sprite itself will also use 4 or 8 bits per pixel -
meaning it uses a palette. The challenge then is to come up with a palette that suits the entire sprite,
even if the original images had widely different palettes.
The package has a number of clever algorithms built in to work out the colors on the sprite's palette
(choose one yourself or let the package choose one for you).
But if the constituent images have widely different colors,
this may not work well and you could wind up with images on the page that
don't look right.
In that case, you can force the package to go for a pixel format with more bits per pixel (such as
Format24bppRgb), to keep image quality up at the expense of a bigger sprite.
Because of all this, if you have lots of images that use a palette (4 or 8 bits per pixel), it makes sense to group images that have similar colors together -
all the "red" images in one sprite, all the "blue" images in another sprite, etc.
This issue doesn't arise with higher color depths, because in that case the image no longer has a palette.
If you allow 24 bits per pixel or more (pixel format Format24bppRgb or higher), any palette would contain
16,777,216 colors or more - which doesn't make sense. So in that case the bits for each
pixel represent a color directly, rather than a position in a palette.
Resizing changes the pixel format
If you use resizeWidth, resizeHeight or
auto resizing, than the package has to resize the image on the fly.
The problem is that the resized image often requires more colors than the original, to give it good color transitions.
Because this is a complicated issue that takes many CPU cycles to optimize,
the package keeps it simple and generates a resized image with at least 24 bits per pixel (pixel format Format24bppRgb), to cater for
all the possibly required colors.
As a result, because at least one of the constituent images
now has 24 bits per pixel or more,
you'll wind up with a sprite that itself has at least 24 bits per pixel ore more as well.
However, that could be more than actually necessary.
You can set pixelFormat to a lower pixel format, such as
Format8bppIndexed, to see if that reduces the file size of the sprite while
maintaining an acceptable image quality.
Group images with similar pixel formats together
If you have 9 simple images that use no more than 16 colors (4 bits per pixel is enough) while a 10th more colorful image
uses more than 16 colors (requires 8 bits per pixel or more),
you may want to group only the 9 simple images together.
That way, the resulting sprite takes only 4 bits per pixel.
If you added the 10th more colorful image to the group, that would force the sprite to have 8 bits per pixel - meaning
it would have a greater file size.
Optimizing images
Some images on your site may be unoptimized - such as images that have
a higher color depth than necessary, or that use the JPG format even though they have few colors.
If you combine these unoptimized images into a sprite, the sprite will wind up with a pixel format that
is higher than necessary, causing it to have a higher file size.
Rather than editing each icon yourself, you can get the package to effectively do this for
you by setting pixelFormat to a lower pixel format.
Palette based pixel formats not used with some types of shared hosting
As you saw in this section, the package initially creates sprites in a pixel format that doesn't
use a palette. It may then try to convert the sprite to a pixel format with only 4 or 8 bits
to reduce its file size, meaning it will have a palette.
The problem is that the algorithms that calculate the palette use low level code to access all colors in the sprite
so it can work out the optimal palette. This needs to be low level code to make it fast enough.
However, running this low level code can only be done if your site
runs in an environment with
trust level Full.
That is not an issue in your development environment or if you have your own web server - there you always have trust level
Full.
It even works fine with many cheap shared hosting plans that give you
trust level
Full even though your site shares a web server with other sites.
However, some shared hosting plans, such as GoDaddy
only give your site trust level
Medium, to ensure that the sites sharing the web server don't affect each other. That prevents the package from
using the low level code to work out the palette.
As a result, if your site runs in an account with trust level Medium,
it doesn't convert sprites to any pixel format that requires a palette
(Format1bppIndexed, Format4bppIndexed or Format8bppIndexed), even if you tell it to by setting
pixelFormat. Instead, it will use Format24bppRgb, which doesn't use a palette.
paletteAlgorithm
Lets you choose which algorithm is used to find the optimal palette, when the package
produces a sprite with a pixel format that uses palettes.
Algorithm |
|
Windows |
Uses palette with standard Windows colors. Very fast. Best choice for images that only use
safe colors.
When target pixel format is Format1bppIndexed (1 bit per pixel), the package always uses this algorithm.
|
HSB | Slower, excellent results with images with many colors |
MedianCut | Slower, often good results |
Octree | Quick, reasonable results |
Popularity | Very quick, but results are poor with images with many colors |
Uniform | Very qick, but results are poor with images with many colors. Only works when targeting 8 bits per pixel (Format8bppIndexed).
For 4 bits per pixel, the package will use HSB instead of Uniform |
More information about these algorithms is
here.
Example
<cssSpriteGenerator ... >
<imageGroups>
<add paletteAlgorithm="Windows" ... />
</imageGroups>
</cssSpriteGenerator>
For details related to the pixel format of the sprites generated from this group,
see pixelFormat.
Say you have an image of 1000 colors, and you want to reduce the color depth so it takes only 8 bits per pixel, to reduce its file size. 8 bits per pixel
means you'll be using a palette with only 256 colors. What 256 colors will you choose so the new image still looks like the original to human eyes?
This is obviously a tricky task, especially seeing that the algorithm also needs to minimize CPU usage. People have come up with different algorithms to
achieve this. The issue is that an algorithm that is best in some situations is not necessarily the best
in other situations. So rather than locking you into
one
algorithm, the package allows you to expirement with different algorithms if the default doesn't work for you.
The demo site DemoSite_CompressedPng in the downloaded solution uses paletteAlgorithm
when reducing the color depth of a sprite.
disableAutoResize
Lets you switch off the Auto Resize feature (described below).
Value |
Description |
false (default) |
Images automatically resized according to width and height image tag properties. |
true | Auto resizing switched off. Do not combine with giveOwnSprite="false". |
Example
<cssSpriteGenerator ... >
<imageGroups>
<add disableAutoResize="true" ... />
</imageGroups>
</cssSpriteGenerator>
As you have seen,
when the package turns images into sprites, it replace img tags with div tags - where the div
has a width and height that match the width and height of the original image, so it can show precisely the area of the sprite taken by the original image.
However, you may have
an img tag with width and/or height attributes that do not correspond with the
width and height of the physical image. For example:
<!---->
<img src="physically100wide.png" width="50" />
The issue is that you cannot resize background images with a CSS tag, like you can with img tags.
To overcome this, the package physically resizes the image before adding it to the sprite - a feature called Auto Resize.
This happens in memory - your original image file is not affected. It also makes sure that the width and height of the div
are
as specified by the
width and/or height properties of the img tag.
If you set only the width property in the img tag, or only the height property,
the package will preserve the aspect ratio of the image - so it still looks the same on the page.
If there are multipe img tags on the page referring to the same physical image but with different
width and/or height attributes, than the package generates versions for each width/height
before adding them to the sprite.
Auto Resize only works with the
width and/or height attributes on an img tag.
It doesn't work if you set the width or height in CSS.
Keep in mind that if you use
resizeWidth and/or
resizeHeight with your group,
those override any
width and/or height properties
on the img tag - so the Auto Resize feature does not come into play then.
The DemoSite_AutoResized web site in the downloaded solution shows auto resizing in action.
If you want to, you can disable the Auto Resize feature by setting
disableAutoResize
to true. However, as shown above, that wouldn't work when combining images into sprites.
So the package only allows you to do this if you also set
giveOwnSprite to true, because in that case
the sprite can be shown with an img tag
with width and height attributes, rather than with a background image.
It would make sense to disable the Auto Resize feature
if your page showed the same physical image a number of times, with different sizes.
In that case, you would want to use
one physical image rather than multiple resized images - so the browser
needs to load only one physical image.
Group Creation
groupName
Sets the name of the group.
You can't have more than one group with the same name (but you can have multiple groups without a name).
Type |
Default |
string
|
emtpy
|
Example
<cssSpriteGenerator ... >
<imageGroups>
<add groupName="largejpg" ... />
</imageGroups>
</cssSpriteGenerator>
You would give a group a name for these reasons:
-
So another group can refer to it using subsetOf.
-
To give it a descriptive name, to make it easier to later on remember what the group is for.
subsetOf
Lets groups inherit properties from other groups.
Type |
Default |
string
|
empty
|
Example
<cssSpriteGenerator ... >
<imageGroups>
<add subsetOf="basegroup" ... />
<add groupName="basegroup" ... />
</imageGroups>
</cssSpriteGenerator>
When you set subsetOf for a group to the name of some other group,
the group inherits all properties from the other group - except for the ones that it sets itself.
Take for example:
<cssSpriteGenerator ... >
<imageGroups>
<add subsetOf="pnggroup" filePathMatch="\.jpg" />
<add groupName="pnggroup" maxHeight="100" filePathMatch="\.png" />
</imageGroups>
</cssSpriteGenerator>
The lowest group has maxHeight set to 100 and filePathMatch set to \.png. So it matches
PNG files that are not higher than 100px.
The group above it inherits from pnggroup. It doesn't set maxHeight itself, so it inherits that from pnggroup.
But it does set filePathMatch to \.jpg, thereby overriding the filePathMatch it gets from pnggroup. As a result,
it matches JPEG files that are no higher than 100px.
CSS Background Images
The package has the ability to combine CSS background images into sprites, to compress them, etc.
This is very useful because background images tend to be very small which means that their request/response
overhead is proportionally high,
so there is a lot to be gained from combining them into sprites.
The cssImages element
Whereas the package can interpret the current page to find all the images used there, it can't yet interpret CSS files.
To overcome this, the package lets you specify the background images you want processed
in cssImages elements.
Here is what this looks like this:
<cssSpriteGenerator ... >
<cssImages>
<add imageUrl="css/img/logo.png" cssSelector=".backgrounglogo" ... />
<add imageUrl="css/img/biglogo.png" cssSelector=".backgroungbiglogo" ... />
...
</cssImages>
</cssSpriteGenerator>
As you see here, the package also needs the CSS selector where the background image is used.
This is because it generates additional CSS statements that partly override the original CSS, to
ensure that the background image still shows up correctly on the page
after it has been put into a sprite.
After the images have been read from the cssImages elements, they are processed the same way as
images read from the page. This
means that some
background images could be combined with images used in img tags.
It also means they need to be matched to an
image group
before they can be put into a sprite.
For example, if you wanted to place the two "logo" background images in their own group, you could use a group with the
filePathMatch property, like this:
<cssSpriteGenerator ... >
<cssImages>
<add cssSelector=".backgrounglogo" imageUrl="css/img/logo.png" ... />
<add cssSelector=".backgroungbiglogo" imageUrl="css/img/biglogo.png" ... />
...
</cssImages>
<imageGroups>
-->
<add filePathMatch="logo\.png$" ... />
</imageGroups>
</cssSpriteGenerator>
In addition to the
imageUrl and
cssSelector properties,
the combineRestriction property caters for background images that
are repeated horizontally or vertically,
and the
backgroundAlignment property caters for background images used with the
sliding door
technique.
The description of the
imageUrl property
shows in detail how to create a
cssImages element based on a CSS style.
runat=server in head tag
If you use
cssImages elements to process CSS background images,
the package will always generate a separate .css file to override existing
CSS styles to make them work with the new sprites.
So the package can
add a link tag for the .css file to the head section,
make sure that the head tags of your pages have runat="server":
<head runat="server">
Properties Index
cssImages elements have the following properties:
imageUrl (required)
The url of the background image to be processed.
This can be an absolute url or a url relative to the root of the web site - but not a url that is relative to the directory of the CSS file.
Example
<cssSpriteGenerator ... >
<cssImages>
<add imageUrl="css/img/logo.png" ... />
</cssImages>
</cssSpriteGenerator>
As an example, suppose you have a CSS file site.css in directory css with the following CSS:
.backgrounglogo
{
height: 32px; width: 32px;
background: #000000 url(img/logo.png);
}
To have the logo.png image combined into a sprite, you would take these steps:
-
Create a new entry in cssImages:
<cssImages>
...
<add />
</cssImages>
-
Add the CSS selector .backgrounglogo:
<cssImages>
<add cssSelector=".backgrounglogo" />
</cssImages>
If you have multiple selectors using the same background image, you need to create an entry for each selector.
-
Add the url of the background image.
Here the image url - img/logo.png - is relative to the directory of the CSS file, which is css.
However, the package doesn't know where the CSS file is located, so it needs the image url relative to the root of the web site -
css/img/logo.png:
<cssImages>
<add cssSelector=".backgrounglogo" imageUrl="css/img/logo.png" />
</cssImages>
-
If the style uses a repeating background image, or if you use the sliding door technique,
you may need to add
combineRestriction and
backgroundAlignment properties - see their descriptions for more details.
-
Finally, make sure there is an
image group
that matches the background image, otherwise it won't be combined into a sprite.
If there is no such group yet, add one:
<imageGroups>
<add ... />
</imageGroups>
<cssImages>
<add cssSelector=".backgrounglogo" imageUrl="css/img/logo.png" />
</cssImages>
cssSelector (required)
The selector of the style that uses the background image.
Example
<cssSpriteGenerator ... >
<cssImages>
<add cssSelector=".backgrounglogo" ... />
</cssImages>
</cssSpriteGenerator>
See the discussion at the
imageUrl property.
combineRestriction (optional)
Sets restrictions on the way the background image can be combined with other images in a sprite.
Value |
Description |
None (default) | No combine restrictions |
HorizontalOnly | Image will only be combined horizontally, and only with images of same height.
Use with styles that use repeat-y. |
VerticalOnly | Image will only be combined vertically, and only with images of same width.
Use with styles that use repeat-x. |
Example
<cssSpriteGenerator ... >
<cssImages>
<add combineRestriction="VerticalOnly" ... />
</cssImages>
</cssSpriteGenerator>
Whether to use a combine restriction depends on whether you use a repeating background image:
When using | Example | Use combineRestriction |
repeat-x | background: url(bg.png) repeat-x | VerticalOnly |
repeat-y | background: url(bg.png) repeat-y | HorizontalOnly |
Example for vertically repeating background image
To see how this works, have a look at this screen shot:
This uses the following HTML:
<table cellpadding="10"><tr>
<td><div class="hor-gradient-lightblue">B<br />l<br />u<br />e</div></td>
<td><div class="hor-gradient-red">R<br />e<br />d</div></td>
</tr></table>
With this CSS:
.hor-gradient-lightblue
{
width: 10px;
background: #ffffff url(img/gradient-hor-lightblue-w10h1.png) repeat-y;
}
.hor-gradient-red
{
width: 10px;
background: #ffffff url(img/gradient-hor-red-w10h1.png) repeat-y;
}
And these background images, which are both 10px wide - as wide as the div:
|
|
gradient-hor-lightblue-w10h1.png (zoomed in 8 times) |
gradient-hor-red-w10h1.png (zoomed in 8 times) |
Because each background image is tiny, it makes perfect sense to combine them into a sprite, so the browser needs to load only one image instead of two.
However, you wouldn't want a sprite like this with the images stacked on top of each other:
|
sprite with images stacked vertically (zoomed in 8 times) |
Because that would produce this less than stellar result:
Both background images now show up in both backgrounds!
We need to tell the package to only combine these background images horizontally.
That can be done with the combineRestriction property:
<cssImages>
...
<add ... combineRestriction="HorizontalOnly"/>
...
</cssImages>
Combining the background images horizontally gives us this sprite:
|
sprite with images combined horizontally (zoomed in 8 times) |
This allows the package to generate CSS that shifts the sprite over the visible area:
|
correct sprite area
shifted over visible area (zoomed in 4 times) |
Wrapping this up, you would use these entries in cssImages
for your two background images:
<cssImages>
...
<add imageUrl="css/img/gradient-hor-lightblue-w10h1.png"
cssSelector=".hor-gradient-lightblue"
combineRestriction="HorizontalOnly"/>
<add imageUrl="css/img/gradient-hor-red-w10h1.png"
cssSelector=".hor-gradient-red"
combineRestriction="HorizontalOnly"/>
</cssImages>
Horizontally repeating background image
The story for background images that repeat horizontally instead of vertically is the same, except that
you would set
combineRestriction to
VerticalOnly, so the images are guaranteed to be stacked vertically in the sprite.
Sprites and narrow background images
So far, the background images have been precisely as wide as the parent div element.
What happens if we make the background images narrower, say 5px, while the div is still 10px wide?
Without sprites, the browser will show:
However, if we combine the two 5px wide background images into a sprite:
|
sprite with 5px wide images combined horizontally (10px wide, zoomed in 8 times) |
Than this is the result if we use that sprite as the background image:
The red background looks fine, but the blue background seems to have combined with the red background!
The reason why is obvious when you consider
how the CSS sprite technique works -
the sprite is shifted over the div element so the correct image within that sprite becomes visible.
The width and height of the div
then ensure that only that correct image is visible.
However, that breaks down here for the blue background image, because the background image that we want to show is 5px wide, while the div is 10px wide.
As a result, the red background image to its right shows up as well. It happens to work for the red background image, but only because here the sprite has been shifted
5px to the left and the sprite doesn't contain an image to the right of the red background image.
Moral of this story: If a background image is both lower and narrower than the div with which is is used, than it cannot be combined into a sprite.
backgroundAlignment (optional)
Determines how the CSS generated by the package aligns the background image.
Value |
Description |
None (default) | Image will not be aligned |
Top | Image will be top aligned |
Bottom | Image will be bottom aligned |
Left | Image will be left aligned |
Right | Image will be right aligned |
Example
<cssSpriteGenerator ... >
<cssImages>
<add backgroundAlignment="Left" ... />
</cssImages>
</cssSpriteGenerator>
In your CSS, you may be aligning background images, like this:
background: url(img/button-green-left.png) left;
To ensure that the package generates the correct sprite and CSS to cater for alignments,
you need to add not only a
combineRestriction property but also a
backgroundAlignment property to your
cssImages elements in the following cases:
Background Image | Alignment | Example CSS | combineRestriction | backgroundAlignment |
Narrower and as high or higher than parent | Left | background: url(bg.png) left; | VerticalOnly | Left |
As high or higher than parent | Right | background: url(bg.png) right; | VerticalOnly | Right |
Lower and as wide or wider than parent | Top | background: url(bg.png) top; | HorizontalOnly | Top |
As wide or wider than parent | Bottom | background: url(bg.png) bottom; | HorizontalOnly | Bottom |
The parent is for example a div tag that uses the background image. Keep in mind that if the background image is both narrower and lower than the parent element,
it can't be combined with other images into a sprite.
Lets look at a practical example of all this. Have a look at this screen shot:
Both buttons are normally green. When you hover over one, it goes orange.
Here is the HTML for the buttons. Note that
rather than using an image per button, both buttons use the same CSS class flexible-width-button - only the text is different.
This uses the sliding door
technique, which relies on background image alignment.
<table cellpadding="10"><tr><td>
<div class="flexible-width-button"><a href="delivery.aspx">Delivery</a></div>
</td><td>
<div class="flexible-width-button"><a href="buy.aspx">Buy</a></div>
</td></tr></table>
Here is the CSS class
flexible-width-button (some irrelevant bits have been left out).
Note that the height of a button is 25px:
div.flexible-width-button {
background: #ffffff url(img/button-green-left.png) top left no-repeat;
...
}
div.flexible-width-button a {
background: transparent url(img/button-green-right.png) top right no-repeat;
line-height: 25px;
...
}
div.flexible-width-button:hover, div.flexible-width-button:focus {
background: #ffffff url(img/button-orange-left.png) top left no-repeat;
}
div.flexible-width-button:hover a, div.flexible-width-button:focus a {
background: transparent url(img/button-orange-right.png) top right no-repeat;
}
The CSS uses these background images:
|
|
|
|
button-green-left.png |
button-green-right.png |
button-orange-left.png |
button-orange-right.png |
All four images are 25px high, which is the same height as their parent elements.
button-green-left.png and button-orange-left.png are also wider and they are left aligned, so according to the
table above, there is no need to add
combineRestriction and
backgroundAlignment
to their
cssImages elements:
<cssSpriteGenerator ... >
<cssImages>
...
<add imageUrl="css/img/button-green-left.png"
cssSelector="div.flexible-width-button" />
<add imageUrl="css/img/button-orange-left.png"
cssSelector="div.flexible-width-button:hover, div.flexible-width-button:focus" />
</cssImages>
</cssSpriteGenerator>
It's a different story for
button-green-right.png and button-orange-right.png: they are both narrower than their parent elements,
and they are right aligned. According to the
table above, that's two reasons to add not only a
combineRestriction but also a
backgroundAlignment
to their
cssImages elements:
<cssSpriteGenerator ... >
<cssImages>
...
<add imageUrl="css/img/button-green-right.png"
cssSelector="div.flexible-width-button a"
combineRestriction="VerticalOnly" backgroundAlignment="Right" />
<add imageUrl="css/img/button-orange-right.png"
cssSelector="div.flexible-width-button:hover a, div.flexible-width-button:focus a"
combineRestriction="VerticalOnly" backgroundAlignment="Right" />
<add imageUrl="css/img/button-green-left.png"
cssSelector="div.flexible-width-button" />
<add imageUrl="css/img/button-orange-left.png"
cssSelector="div.flexible-width-button:hover, div.flexible-width-button:focus" />
</cssImages>
</cssSpriteGenerator>
Conclusion
As you see from the length of this article, combining images into sprites looks easy at first sight but then it gets
more complicated!
Why not download the package and give it a try on your site.
I would be interested in your comments and if you like what you saw, please give me a vote.
History
Version |
Released |
Description |
1.0 |
3 Aug 2011 |
Initial release. |
1.1 |
15 Aug 2011 |
If an image's file size is greater than the
maxSpriteSize
of a group, it won't be added to that group, regardless of the
group's maxSize.
|
1.2 |
10 Jun 2012 |
Added the
samePixelFormat,
sameImageType
and
maxPixelFormat
properties. Removed the copiedImgAttributes
property. A sprite only containing JPEG images now by default has JPEG image type instead of PNG.
Bug fixes.
|