This Series
Introduction
This is part 3 of a 3 part series about
the ASP.NET CSS Sprite Generator package.
It allows you to
compress and resize the images in your
ASP.NET web site on the fly, and to combine them into sprites.
Simply add the ASP.NET CSS Sprite Generator dll and
configure the package
in your web.config - no need to change the site itself.
Contents
Part 1
Part 2
Part 3
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.
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 |
Int64
|
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).
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.
Type |
Default |
Int64
|
No limit
|
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.
spriteImageType
Sets the image type of the sprite.
Value |
Description |
Png (default) |
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 .jpg file.
Recommended for sprites containing compressed photos, etc. |
Example
<cssSpriteGenerator ... >
<imageGroups>
<add spriteImageType="Jpg" ... />
</imageGroups>
</cssSpriteGenerator>
Because sprites tend to be used to group simple icons, the default image type, Png, is most often want you want. However,
if you are combining thumbnails of photos, you may want to set spriteImageType to Jpg. Another reason to use
Jpg is if you are using the package to compress big .jpg images, using
jpegQuality to compress the images and
giveOwnSprite to give each image their own sprite.
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 .jpg 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:
<!--thumbnails.aspx-->
<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 .jpg 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):
<!--thumbnails.aspx-->
<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 .jpg 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 .jpg 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 .jpg 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 .jpg images into sprites along with small .png and .gif images, while compressing the big .jpg 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 .jpg 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:
-
If the images that went into the sprite have very different colors, you may wind up with a sprite whose color depth is too low - you'll find out because it looks bad.
To fix that, you could try pixel formats with more bits per pixel, to get a greater color depth.
-
You can try pixel formats with fewer bits per pixel to reduce the file size of the sprite.
This is especially useful if you combine .jpg images into .png sprites, because the package assumes that .jpg images use many colors and so gives the sprite
a greater color depth than it really needs.
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:
<!--physical image width is 100px, but shown on page 50px wide-->
<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 .jpg 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>
<!--only include images ending in logo.png in the group-->
<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
This was the last part of this 3 part series.
If you haven't given the ASP.NET CSS Sprite Generator package
a try yet, now would be a good time!
Download it here.
If you enjoyed reading this series, consider buying my book
ASP.NET Site Performance Secrets.
In a structured approach towards improving performance, it shows how to first identify
the most important bottlenecks in your site, and then how to fix them. Very hands on, with a minimum of theory.
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.
|