When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article

ASP ASP.NET ASP FAQs Message Board Feedback
 
Print this Page!
Published: Wednesday, October 13, 2010

Optimize Images Using the ASP.NET Sprite and Image Optimization Framework

By Scott Mitchell


Introduction


The HTML markup of a web page includes the page's textual content, semantic and styling information, and, typically, several references to external resources. External resources are content that is part of web page, but are separate from the web page's markup - things like images, style sheets, script files, Flash videos, and so on. When a browser requests a web page it starts by downloading its HTML. Next, it scans the downloaded HTML for external resources and starts downloading those.

A page with many external resources usually takes longer to completely load than a page with fewer external resources because there is an overhead associated with downloading each external resource. For starters, each external resource requires the browser to make an HTTP request to retrieve the resource. What's more, browsers have a limit as to how many HTTP requests they will make in parallel. For these reasons, a common technique for improving a page's load time is to consolidate external resources in a way to reduce the number of HTTP requests that must be made by the browser to load the page in its entirety.

This article examines the free and open-source ASP.NET Sprite and Image Optimization Framework, which is a project developed by Microsoft for improving a web page's load time by consolidating images into a sprite or by using inline, base-64 encoded images. In a nutshell, this framework makes it easy to implement practices that will improve the load time for a web page that displays several images. Read on to learn more!

- continued -

The Goal: Minimize HTTP Requests


In order to load a web page, the browser must make an HTTP request for the web page's HTML content as well as for any external resources. In their article, Best Practices for Speeding Up Your Web Site, Yahoo!'s Exceptional Performance Team notes: "80% of the end-user response time is spent on the front-end. Most of this time is tied up in downloading all the components in the page: images, stylesheets, scripts, Flash, etc. Reducing the number of components in turn reduces the number of HTTP requests required to render the page. This is the key to faster pages." There are a variety of ways to reduce the total number of HTTP requests needed for a web page. For example, if the page references multiple style sheet files consider combining the multiple files into one. If there are many images on the page consider using sprites or inline images.

Using Sprites to Minimize HTTP Requests


A sprite is a conglomeration of smaller images into one larger image. Imagine a web page that displays nine images like in the screen shot below:

A web page that displays nine small images.

These nine images could be represented as nine separate image files - bell.png, bomb.png, date.png, and so on - that would then each be referenced in the web page via an <img> element. This approach certainly works, but requires nine HTTP requests for the nine images. Alternatively, these images could be combined into a sprite, which is a single image that contains all of the smaller images. For example, the image below is a sprite that comprises the nine images shown in the screen shot above. Note that the sprite is a single image file - Sprite2.png, in this case - that contains all nine smaller images of interest.

A sprite that contains the nine images.

At this point you may be wondering what good a sprite does us. After all, we want to show just one image at a time - the Bell image next to the words "Ding ding ding!", for instance - and not all nine images at once. Well, with a bit of CSS we can instruct the browser to display only a portion of the sprite in the browser, namely the portion that corresponds to the image we want to display. To display the Bell image, for example, we'd use an <img> element with the following markup:

<img src="blank.png" style="width:16px;height:16px;background-image:url(Sprite2.png);background-position:-0px -0px;" />

Here, the <img> element's src attribute is assigned to blank.png, which we can presume (for the time being) is some 1x1 transparent image file. The real magic happens in the <img> element's style attribute, which is where we define CSS rules to have the <img> element render a 16x16 pixel image using the Sprite2.png sprite as the background image. The background-position attribute tells the browser how to shift the sprite so that the appropriate image is displayed. Because the Bell image is the left-most image in the sprite we do not need to adjust the sprite's position. To display the Bomb image, however, we'd set the background-position attribute to -16px -0px, which would shift the sprite 16 pixels to the left so that the Bomb image would appear (rather than the Bell image) within the <img> element's 16x16 pixel window.

There are two primary challenges in using sprites:

  • While combining the various images into a single sprite isn't rocket science, it can be a challenging and time-consuming task for a developer who lacks the image editing tools or experience to create a sprite, and
  • Correctly displaying a particular image in the sprite requires knowing the image's dimensions and offset within the sprite, as well as knowing the appropriate CSS rules to display just that portion of the sprite.
We'll talk more about these challenges in a bit.

Using Inline Images to Minimize HTTP Requests


While sprites can greatly reduce the number of HTTP requests - from nine down to one, in our previous example - they still require that the sprite be downloaded. Another option is to use inline images. Typically an <img> element's src attribute references a URL, like Images/MyDog.jpg. An inline image, however, is one whose actual binary content is specified directly in the src attribute as a base-64 encoded string. For instance, the following <img> element's (abbreviated) src attribute contains the Bell image's content. Consequently, to render this image the browser doesn't need to make another HTTP request, as the image's content is sitting right there in the src attribute:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAB..." />

Inline images reduce the number of HTTP requests for images down to zero, but inflate the size of the page. Ideally, an image's inline content would be spelled out in a separate CSS file, where it could then be cached.

The two primary challenges in using inline images are:

  • Determining the base-64 encoded string that represents the binary content of the image you want to display, and
  • Browser support. Not all browsers support inline images. While Internet Explorer 9 will support inline images, versions 8 and earlier do not. For Firefox, inline image support was added with version 3.5. Therefore, whether you can render an image as inline depends on the browser your visitor is using.

The Solution: Use the ASP.NET Sprite and Image Optimization Framework


The ASP.NET Sprite and Image Optimization Framework is a free and open-source library created by Microsoft. It was designed to simplify using the image optimization techniques we just discussed (using sprites and inline images). In a nutshell, when using the ASP.NET Sprite and Image Optimization Framework you will create a folder in your project named App_Sprites that houses those image files you want to combine into a sprite (or display as inline images). The ASP.NET Sprite and Image Optimization Framework will automatically:
  • Combine those images into one (or more) sprites, and
  • Create two CSS files - one for displaying images from the auto-generated sprite(s) and another for displaying inline images.
The ASP.NET Sprite and Image Optimization Framework also contains an ImageSprite Web control for WebForms applications and an ImageSprite.Image helper method for MVC applications that generate the necessary markup to display a particular image using either standard <img> elements, inline images, or sprites, depending on the browser visiting the page.

To get started with the ASP.NET Sprite and Image Optimization Framework you need to visit the download page and get the latest prebuilt DLLs. There are three prebuilt DLLs:

  • ImageOptimizationFramework.dll - includes the guts of the framework. This assembly must be referenced in order to use any of the framework's features.
  • WebForms\ImageSprite.dll - includes the ImageSprite Web control, which you can use in an ASP.NET WebForms application for displaying a particular optimized image.
  • MVC\ImageSprite.dll - includes a helper method, ImageSprite.Image - used to generate the markup for displaying an optimized image within an ASP.NET MVC view.
The demo available for download at the end of this article is a WebForms application that uses the ASP.NET Sprite and Image Optimization Framework; it contains the ImageOptimizationFramework.dll and WebForms\ImageSprite.dll in its Bin folder. (Note that when I created this demo and wrote this article I used the most recent version, ASP.NET Sprite and Image Optimization Framework Preview 2.)

After downloading the ASP.NET Sprite and Image Optimization Framework and adding it to your website, you'll need to register an HTTP Module in Web.config by adding the following markup:

<configuration>
   <system.web>
      ...

      <httpModules>
         <add type="Microsoft.Samples.Web.ImageOptimizationModule"
              name="Microsoft.Samples.Web.ImageOptimizationModule" />
      </httpModules>
   </system.web>

   <system.webServer>
      <modules runAllManagedModulesForAllRequests="true">
         <add type="Microsoft.Samples.Web.ImageOptimizationModule"
              name="Microsoft.Samples.Web.ImageOptimizationModule" />
      </modules>
   </system.webServer>
</configuration>

This HTTP Module, Microsoft.Samples.Web.ImageOptimizationModule, monitors the App_Sprites folder. If there is any change to this folder - such as if the images in this folder are modified or deleted or new images are added - this module detects the change and rebuilds the sprite(s) and CSS files used by the ASP.NET Sprite and Image Optimization Framework.

Next, add an App_Sprites folder to your website and drop in whatever image files you want to be composed into a sprite. (You can also create subfolders in the App_Sprites folder; each subfolder will have its own sprite(s) created.) By default, the ASP.NET Sprite and Image Optimization Framework will not create a single sprite larger than 450 KB. So if you add more than (roughly) 450 KB worth of images to the App_Sprites folder, the ASP.NET Sprite and Image Optimization Framework will break them up into multiple sprites, each smaller than the 450 KB. (This maximum sprite file size, along with other settings, can be customized by adding a settings.xml file to the App_Sprites folder. Refer to the ASPNET_Image_Optimization.docx Word document that is included in the download for more details on this settings file.)

To display an image in the App_Sprites folder on an ASP.NET page, use the ImageSprite Web control.

An End-To-End Example: Exploring the Demo Application


The demo application available for download at the end of this article has the ASP.NET Sprite and Image Optimization Framework's ImageOptimizationFramework.dll and WebForms\ImageSprite.dll assemblies in its Bin folder, The Web.config file has been modified to register the Microsoft.Samples.Web.ImageOptimizationModule HTTP Module, and I have created an App_Sprites folder and added nine images - bell.png, bomb.png, date.png, and so on. There are two ASP.NET pages in this demo of interest:
  • NonOptimized.aspx - provides an example of a typical web page that displays the nine images in the App_Sprites folder using an <img> element for each image, and
  • Optimized.aspx - displays the nine images using the ImageSprite Web control. Here, the ImageSprite Web control renders <img> elements that uses CSS rules and the automatically-created sprite to reduce the number of HTTP requests.
Let's start with a look at the NonOptimized.aspx. This page is painfully simply - it has a three-column, three-row <table> that displays an image and short bit of text in each cell. The images are displayed using an Image Web control. Here is a snippet of the NonOptimized.aspx page's markup:

<table style="width:95%;text-align:center">
   <tr>
      <td>
         <asp:Image ID="imgBell" runat="server" ImageUrl="~/App_Sprites/bell.png" ... />
         Ding ding ding!
      </td>
      <td>
         <asp:Image ID="imgBomb" runat="server" ImageUrl="~/App_Sprites/bomb.png" ... />
         Explosions!!
      </td>
      <td>
         <asp:Image ID="imgDate" runat="server" ImageUrl="~/App_Sprites/date.png" ... />
         Calendar
      </td>
   </tr>
   ...
</table>

Each Image Web control renders an <img> element whose src attribute references the specified image. As a result, when visiting this page the browser must make a total of ten HTTP requests - one for the web page itself and then nine for the nine images. (Actually, the browser makes 12 HTTP requests as there are two style sheets referenced on this page as well.) You can see these HTTP requests by using an HTTP debugging proxy like Fiddler. Below is a screen shot from Fiddler showing the time line for downloading these images.

Fiddler shows an HTTP request for each of the nine images.

We can reduce the number of HTTP requests by using the ImageSprite Web control. The Optimized.aspx page contains virtually the same markup as the NonOptimized.aspx page, the only difference being that in place of the Image Web control the Optimized.aspx page uses the ImageSprite control. (Note that to use the ImageSprite Web control on an ASP.NET page you must first register the control, either through Web.config or via a @Register directive on the ASP.NET page.) Here's a snippet of the markup in Optimized.aspx; note the use of the <cc1:ImageSprite> control where we previously used an <asp:Image> control.

<table style="width:95%;text-align:center">
   <tr>
      <td>
         <cc1:ImageSprite ID="imgBell" runat="server" ImageUrl="~/App_Sprites/bell.png" ... />
         Ding ding ding!
      </td>
      <td>
         <cc1:ImageSprite ID="imgBomb" runat="server" ImageUrl="~/App_Sprites/bomb.png" ... />
         Explosions!!
      </td>
      <td>
         <cc1:ImageSprite ID="imgDate" runat="server" ImageUrl="~/App_Sprites/date.png" ... />
         Calendar
      </td>
   </tr>
   ...
</table>

The markup rendered by the ImageSprite Web control depends on the browser visiting the page. If the visitor is using Internet Explorer 7 or earlier, the ImageSprite Web control renders a standard <img> element, just as if an <asp:Image> Web control had been used.

If the visitor is using Internet Explorer 8 or a version of Firefox before version 3.5, then the ImageSprite Web control does two things:

  1. It dynamically adds a reference to the lowCompat.css style sheet to the page. The lowCompat.css style sheet is an automatically-generated style sheet that exists in the App_Sprites folder and contains CSS rules to display each of the images in the sprite(s).
  2. It renders an <img> element whose class attribute is assigned the appropriate class for displaying the specified image.
Let's look at a specific example. Consider the ImageSprite Web control that displays the bomb.png image:

<cc1:ImageSprite ID="imgBomb" runat="server" ImageUrl="~/App_Sprites/bomb.png" ... />

This ASP.NET page's rendered markup includes a <link> element in the <head> section that references the lowCompat.css style sheet and an <img> element. Here's a snippet of the page's rendered markup:

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1">
   ...
   <link href="App_Sprites/lowCompat.css" rel="stylesheet" type="text/css" media="all" />
</head>
<body>
   ...
   
   <img id="ContentPlaceHolder1_imgBomb" class="bomb-png"
        src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />

   ...
</body>
</html>

Note that the <img> element's class attribute is assigned to the class name bomb-png. This class is defined in the lowCompat.css style sheet to display a 16x16 pixel portion of the sprite0.png sprite, offset by 16 pixels horizontally. (The file sprite0.png is the name of the sprite that was auto-generated by the ASP.NET Sprite and Image Optimization Framework.)

.bomb-png {
   width:16px;
   height:16px;
   background-image:url(sprite0.png);
   background-position:-16px -0px;
}

The display of the image is handled entirely in CSS. You may wonder, then, why the <img> element rendered by the ImageSprite control contains an src attribute and why it's set to the value data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==. What does this do?

Although the bomb image (in this example) is displayed entirely through CSS, it is important that the <img> element still have an src attribute, as this is a required attribute. The ASP.NET Sprite and Image Optimization Framework could have created a 1x1 transparent image in the App_Sprites folder and referenced it here, but that would prompt the browser to send an HTTP request to the server for this blank image. Instead, the ASP.NET Sprite and Image Optimization Framework uses an inline image here. That gobbledygook is a base-64 encoded string representing a 1x1 transparent PNG image. Granted, a user's browser may not support inline images, but that doesn't matter because the actual image being displayed is defined in the CSS.

The net result is that the ImageSprite Web control reduces the number of HTTP requests. Previously we needed 12 HTTP requests - one for the web page, two for style sheets, and nine for the nine images. Using the sprite, we now have five HTTP requests - one for the web page, three for style sheets (the original two style sheets plus the just-added lowCompat.css), and one for the sprite itself.

Fiddler shows just one HTTP request for the sprite.

As we just saw, the ASP.NET Sprite and Image Optimization Framework uses a sprite when visited by Internet Explorer 8 or a version of Firefox prior to version 3.5. But what happens when visiting with Internet Explorer 9 or Firefox 3.5 or beyond, or Google Chrome, or Apple Safari? In those instances, the ImageSprite Web control uses inline images. When using inline images, the markup rendered by the ImageSprite control is nearly identical to the markup when using a sprite. The only difference is that instead of adding a reference to the style sheet lowCompat.css, when using inline images the highCompat.css style sheet is referenced instead. Recall that the classes defined in the lowCompat.css used the width, height, background-image and background-position CSS settings to display a particular chunk of a sprite. In lowCompat.css, the CSS classes contain the actual content of the image as a base-64 encoded string. For example, here's the (abbreviated) definition of the bomb-png class in highCompat.css:

.bomb-png {
   width:16px;
   height:16px;
   background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hA...) no-repeat 0% 0%;
}

Because the images' contents are defined directly within the CSS file, the browser doesn't need to make any requests back to the server to load the images (or a sprite). Consequently, when using inline images the number of HTTP requests drops to four - one for the web page and three for style sheets. There are no requests for image files.

Fiddler shows that no HTTP requests were made to image files.

Conclusion


Reducing the number of HTTP requests a browser needs to make to completely load a web page is a common technique used to improve a website's performance. The ASP.NET Sprite and Image Optimization Framework makes it easy to improve your page's load times by combining multiple images into sprites and using inline images. As we saw in this article, the ASP.NET Sprite and Image Optimization Framework can automatically create sprites, generate the CSS and markup for displaying an image from a sprite or using inline images, and can determine which of the two approaches to use based on the user's browser.

Happy Programming!

  • By Scott Mitchell


    Attachments:


  • Download the Demo Code Used in this Article

    Further Reading


  • Download the ASP.NET Sprite and Image Optimization Framework
  • Best Practices for Speeding Up Your Web Site
  • CSS Sprites: Image Slicing's Kiss of Death


  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article