Chapter 3: Uploading Images
Contents
9.1 GIF Format Overview
Introduced by CompuServe in 1987, Graphics Interchange Format (GIF, pronounced Jiff) is widely used on the Web for sharp-edged images with a limited number of colors, such as navigation buttons, logos, graphs, charts and icons. GIF is based on LZW, a lossless compression algorithm ideally suited for simple graphics (but not photographs.)
A GIF image can contain up to 256 distinct colors from the 24-bit color set. Pixel colors in a GIF image are numbers between 0 and 255 which are indices to a color palette included with the image.
Any pixel of a GIF image can be assigned a "transparent", or see-through, color. This enables GIF images to take arbitrary shapes when appearing on a web page.
GIF is the only widely used graphics format that supports animation. A GIF image can contain multiple frames that are displayed one after another as a movie. To reduce overall image size, some frames are often made smaller than the image itself, and are displayed at an offset to only affect those portions of the image that need redrawing.
9.2 AspJpeg.NET's GIF Output Support
AspJpeg.NET's GIF functionality is encapsulated in the JpegGif object which provides a wide range of operations on GIF images. An instance of this object is obtained via the JpegManager.Gif property. The JpegGif object is completely autonomous, i.e. it is not affected by AspJpeg.NET's other properties and methods.
A simple GIF image is created by calling the JpegGif.AddFrame method followed by some drawing routines, and saved via the Save method. Other saving routines available in the JpegImage object (SaveUnique, SendBinary and Binary) are supported by the JpegGif object as well. AddFrame requires 4 arguments: frame width and height, and horizontal and vertical offsets. For a single-frame image, the offset arguments are usually 0.
Most properties and methods of the JpegGif object operate on the current frame. Once a new frame is added, it becomes the current one. Another frame can be set current by setting the JpegGif.CurrentFrame property to the 1-based index of the desired frame. The total number of frames is returned via JpegGif.FrameCount. A frame can be removed via JpegGif.RemoveFrame.
The JpegGif object supports most of the drawing routines the JpegCanvas object supports, such as PrintText, DrawLine, etc. Setting various drawing properties is somewhat streamlined compared to the Canvas object. For example, an equivalent of JpegImage.Canvas.Pen.Color is simply JpegGif.PenColor. Colors in the JpegGif object are always numbers in the range 0 to 255 which are indices within the current palette. Palette management is described in Section 9.5 below.
The following code sample creates a simple animated 5-frame drawing with some text and a pie chart:
<%@ Import Namespace="Persits.Jpeg"%>
<html>
<head>
<title>AspJpeg.NET User Manual Chapter 9 - GIF Animation</title>
<script runat="server" languge="C#">
void Page_Load( Object Source, EventArgs E)
{
JpegManager objJpeg = new JpegManager();
// Obtain GIF management object
JpegGif objGif = objJpeg.Gif;
// Font path
string strFont = objJpeg.WindowsDirectory + @"\Fonts\Cour.ttf";
// initial market share of hypothetical XYZ company
int nMarketShare = 6;
// create a 5-frame animated gif
for (int i = 1; i <= 5; i++)
{
objGif.AddFrame(300, 200, 0, 0);
objGif.PenColor = 10;
objGif.BrushColor = 10;
objGif.FillRect(0, 0, 300, 200);
objGif.PenColor = 201;
objGif.FontSize = 20;
objGif.PrintText("XYZ, Inc. Market Share", 18, 20, strFont);
objGif.PrintText((2010 + i).ToString(), 120, 50, strFont);
// Draw pie chart
objGif.PenColor = 0;
objGif.BrushColor = 30;
objGif.FillSegment(150, 130, 50, 50, 0, nMarketShare*360/100, 0);
objGif.BrushColor = 20;
objGif.FillSegment(150, 130, 50, 50, nMarketShare*360/100, 360, 0);
objGif.PenColor = 210;
objGif.PrintText(nMarketShare.ToString() + "%", 200, 100, strFont);
// market share almost doubles every year!
nMarketShare = nMarketShare * 2 - 3;
// increase delay on the last frame
if (i == 5)
objGif.Delay = 300; // 3 sec
}
// Save
string strFilename = objGif.SaveUnique(Server.MapPath("chart.gif"));
OutputImage.ImageUrl = strFilename;
}
</script>
</head>
<form runat="server">
<asp:image runat="server" id="OutputImage"/>
</form>
</html>
<%@ Import Namespace="Persits.Jpeg"%>
<html>
<head>
<title>AspJpeg.NET User Manual Chapter 9 - GIF Animation</title>
<script runat="server" languge="vb">
Sub Page_Load(Source As Object, E As EventArgs)
Dim objJpeg As JpegManager = New JpegManager()
' Obtain GIF management object
Dim objGif As JpegGif = objJpeg.Gif
' Font path
Dim strFont As String = objJpeg.WindowsDirectory + "\Fonts\Cour.ttf"
' initial market share of hypothetical XYZ company
Dim nMarketShare As Integer = 6
' create a 5-frame animated gif
For i As Integer = 1 To 5
objGif.AddFrame(300, 200, 0, 0)
objGif.PenColor = 10
objGif.BrushColor = 10
objGif.FillRect(0, 0, 300, 200)
objGif.PenColor = 201
objGif.FontSize = 20
objGif.PrintText("XYZ, Inc. Market Share", 18, 20, strFont)
objGif.PrintText((2010 + i).ToString(), 120, 50, strFont)
' Draw pie chart
objGif.PenColor = 0
objGif.BrushColor = 30
objGif.FillSegment(150, 130, 50, 50, 0, nMarketShare*360/100, 0)
objGif.BrushColor = 20
objGif.FillSegment(150, 130, 50, 50, nMarketShare*360/100, 360, 0)
objGif.PenColor = 210
objGif.PrintText(nMarketShare.ToString() + "%", 200, 100, strFont)
' market share almost doubles every year!
nMarketShare = nMarketShare * 2 - 3
' increase delay on the last frame
If i = 5 Then objGif.Delay = 300 ' 3 sec
Next
' Save
Dim strFilename As String =
objGif.SaveUnique(Server.MapPath("chart.gif"))
OutputImage.ImageUrl = strFilename
End Sub
</script>
</head>
<form runat="server">
<asp:image runat="server" id="OutputImage"/>
</form>
</html>
Click the links below to run this code sample:
9.3 GIF Image Resizing
The JpegGif object is capable of resizing animated GIF images while preserving their animation and transparency. Resizing is performed via the JpegGif.Resize image which accepts three arguments, 2 required and 1 optional: new width, new height and a resizing algorithm. The resizing algorithm is 0 (nearest-neighbor) by default. More information about the resizing algorithms supported by AspJpeg.NET is available here.
The resized image above on the right was generated using the following code:
objGif.Open(strPath);
objGif.Resize(objGif.Width / 2, objGif.Height / 2);
objGif.SaveUnique(Server.MapPath("WalkingCat_small.gif"));
objGif.Open(strPath)
objGif.Resize(objGif.Width / 2, objGif.Height / 2)
objGif.SaveUnique(Server.MapPath("WalkingCat_small.gif"))
Click the links below to run this code sample:
Note that the file size of a resized animated image may be larger that the original image even though the pixel size is smaller (as in this example.) Also note that the 3rd argument to the Resize method (resizing algorithm) can be set to 1 to create higher-quality thumbnails, but some animated images may produce undesired "artifacts" in that case.
9.4 Using External Images as Frames
The Gif object is capable of converting RGB images such as JPEGs into 256-colors GIFs with only a minor loss in image quality. An arbitrary existing image can be added to a GIF as a new frame via the Gif.AddImage method. This method expects a populated instance of the JpegImage object as the first argument, and (X, Y)-offsets of this new frame within the GIF image being created. You must perform all desired operations over the JpegImage object (resizing, cropping, drawing, etc.) before passing it to the AddImage method. The image being added must be in the RGB color space.
The following snippet resizes a JPEG image and converts it to a single-frame GIF:
JpegGif objGif = objJpeg.Gif;
JpegImage objImage = objJpeg.OpenImage(@"c:\path\image.jpg");
objImage.PreserveAspectRatio = true;
objImage.Width = 200;
objGif.AddImage(objImage, 0, 0);
// Save
objGif.SaveUnique(@"c:\path\out.gif");
Because of the loss of quality and larger file size, converting a JPEG photograph to GIF is not beneficial, unless other GIF features are used, such as animation.
The code sample of Section 6.1 generates an image containing the thumbnails of several photographs shown side by side. Let's rewrite this application to generate an animated GIF showing the thumbnails in rotation instead:
<%@ Import Namespace="Persits.Jpeg"%>
<html>
<head>
<title>AspJpeg.NET User Manual Chapter 9 - Images as Frames</title>
<script runat="server" languge="C#">
void Page_Load( Object Source, EventArgs E)
{
JpegManager objJpeg = new JpegManager();
// Obtain GIF management object
JpegGif objGif = objJpeg.Gif;
// Read images from Images directory of the installation
string [] arrFileNames = new string[3];
arrFileNames[0] = "apple.jpg";
arrFileNames[1] = "clock.jpg";
arrFileNames[2] = "photo.jpg";
string strPath = Server.MapPath("../images");
// Stipulate output image size
objGif.Width = 100;
objGif.Height = 100;
for( int i = 0; i < 3; i++ )
{
JpegImage objImage =
objJpeg.OpenImage( strPath + "\\" + arrFileNames[i] );
// Resize to inscribe in 100x100 square
objImage.PreserveAspectRatio = true;
if(objImage.OriginalWidth > 100 || objImage.OriginalHeight > 100)
{
if( objImage.OriginalWidth > objImage.OriginalHeight )
objImage.Width = 100;
else
objImage.Height = 100;
}
objGif.AddImage( objImage,
(100 - objImage.Width) / 2, (100 - objImage.Height) / 2 );
objGif.DisposalMethod = 2;
}
// Save
string strFilename = objGif.SaveUnique(Server.MapPath("picture.gif"));
OutputImage.ImageUrl = strFilename;
}
</script>
</head>
<form runat="server">
<asp:image runat="server" id="OutputImage"/>
</form>
</html>
<%@ Import Namespace="Persits.Jpeg"%>
<html>
<head>
<title>AspJpeg.NET User Manual Chapter 9 - Images as Frames</title>
<script runat="server" languge="vb">
Sub Page_Load(Source As Object, E As EventArgs)
Dim objJpeg As JpegManager = New JpegManager()
' Obtain GIF management object
Dim objGif As JpegGif = objJpeg.Gif
' Read images from Images directory of the installation
Dim arrFileNames(3) As String
arrFileNames(0) = "apple.jpg"
arrFileNames(1) = "clock.jpg"
arrFileNames(2) = "photo.jpg"
Dim strPath As String = Server.MapPath("../images")
' Stipulate output image size
objGif.Width = 100
objGif.Height = 100
For i As Integer = 0 To 2
Dim objImage As JpegImage =
objJpeg.OpenImage( strPath + "\\" + arrFileNames(i) )
' Resize to inscribe in 100x100 square
objImage.PreserveAspectRatio = true
if objImage.OriginalWidth>100 Or objImage.OriginalHeight>100 Then
If objImage.OriginalWidth > objImage.OriginalHeight Then
objImage.Width = 100
Else
objImage.Height = 100
End If
End If
objGif.AddImage( objImage,
(100 - objImage.Width) / 2, (100 - objImage.Height) / 2 )
objGif.DisposalMethod = 2
Next
' Save
Dim strFilename =
objGif.SaveUnique(Server.MapPath("picture.gif"))
OutputImage.ImageUrl = strFilename
End Sub
</script>
</head>
<form runat="server">
<asp:image runat="server" id="OutputImage"/>
</form>
</html>
Click the links below to run this code sample:
Note that the overall GIF image size is dictated by the size of the first frame added, unless we explicitly specify it via JpegGif.Width and JpegGif.Height properties. In this application, we want the image to always be 100 x 100 regardless of the size and orientation of the thumbnails being added. Therefore, we have to explicitly specify the image size.
Note also that we set the DisposalMethod for each frame to 2, which means the canvas should be restored to the background color before the next frame is drawn. This property is 1 by default which means the previous frame needs to be left in place and the next frame is to be drawn on top of it. In our application, the default behavior is undesirable since all thumbnails are different sizes.
Converting true-color to 256-color images is a fairly complex process usually referred to as quantization. The speed and quality of the conversion is controlled by the property JpegGif.Quantization. Valid values for this property are 1 to 30, 1 being highest quality and slowest speed. The default value of 20 provides a reasonably good trade-off between quality and speed.
9.5 Palette Management
9.5.1 GIF Palette Overview
GIF is an indexed color format. Each pixel color is specified via an index pointing to an RGB entry in a palette. Each GIF image contains at least one palette. Usually, there is a single global palette which applies to each frame of the image. In some cases, a frame has its own local palette which takes precedence over the global palette within that frame but does not apply to any other frames. If each frame has its own local palette, a global palette is usually not present at all.
GIF format requires that a palette contain 2, 4, 8, 16, 32, 64, 128, or 256 colors. Each color entry in a palette contains exactly three bytes: the R, G, and B values.
9.5.2 Accessing and Modifying Palettes
The JpegGif object offers a number of properties and methods to access and modify the global and local palettes of an image.
To specify an entire palette (global or local) in a single step, the SetPalette method should be used. This method expects a Boolean flag indicating whether the palette is global (true) or local (false), and a byte array of numbers specifying the RGB values of the entire palette. The array must contain a valid number of colors (2, 4, 8, etc.) multiplied by 3. If the first argument is false (indicating a local palette) the image must already contain at least one frame, and the palette pertaining to the current frame will be affected.
The following code snippet sets the global palette to contain 4 colors: black (0, 0, 0), white (255, 255, 255), green (0, 255, 0) and yellow (255, 255, 0):
byte[] Colors = new byte[] { 0, 0, 0,
255, 255, 255,
0, 255, 0,
255, 255, 255 };
objGif.SetPalette(true, Colors);
...
Dim Colors() as Byte = { 0, 0, 0,
255, 255, 255,
0, 255, 0,
255, 255, 0}
objGif.SetPalette(True, Colors)
...
To set or obtain the size of a palette, use the methods SetPaletteSize and GetPaletteSize, respectively. These methods expect the same global/local flag as the SetPalette method as the first argument. SetPaletteSize also expects the palette size as the 2nd argument. GetPaletteSize returns the palette size. The size of a palette is the number of colors, not the total number of color components. Valid values are 2, 4, 8, ..., 256. Setting this property to 0 effectively removes the palette entirely. For example:
objGif.SetPaletteSize(false, 256);
int N = objGif.GetPaletteSize( false );
...
objGif.SetPaletteSize(False, 256)
Dim N As Integer = objGif.GetPaletteSize( False )
...
To set or get an individual color component of a palette, use the methods SetPaletteItem and GetPaletteItem. Both expect the the global/local flag as the first argument. The 2nd argument is the address (0-based) of the desired color component within the palette. The three RGB components of the 1st color have the addresses 0, 1 and 2, the 2nd color -- 3, 4, and 5, etc. The address parameter must be in the range [0, GetPaletteSize() * 3 - 1]. The 3rd argument of SetPaletteItem is the value for the specified color component to be set to. GetPaletteItem returns the value for the specified color component.
The JpegGif object enables you to specify one of several built-in palettes via the SetStockPalette method. This method expects two arguments: the global/local flag, and the palette number. Currently, 3 stock palettes are available:
Palette #1: Web-safe colors (default palette, 216 actual colors, 40 reserved slots);
Palette #2: Standard HTML colors (16 colors);
Palette #3: Grayscale colors (256 colors).
Palette #1 is the default global palette in all new images created with the JpegGif object. This palette contains the standard Web-safe colors as described in the HTML specifications. There are 216 standard Web-safe colors, and the indices 216 to 255 are not used and set to black. These unused slots can be set to any arbitrary colors, if necessary.
The standard Web-safe color palette looks as follows:
0 | 1 | 2 | 3 | 4 | 5 | |
0 | 000000 | 000033 | 000066 | 000099 | 0000CC | 0000FF |
6 | 003300 | 003333 | 003366 | 003399 | 0033CC | 0033FF |
12 | 006600 | 006633 | 006666 | 006699 | 0066CC | 0066FF |
18 | 009900 | 009933 | 009966 | 009999 | 0099CC | 0099FF |
24 | 00CC00 | 00CC33 | 00CC66 | 00CC99 | 00CCCC | 00CCFF |
30 | 00FF00 | 00FF33 | 00FF66 | 00FF99 | 00FFCC | 00FFFF |
36 | 330000 | 330033 | 330066 | 330099 | 3300CC | 3300FF |
42 | 333300 | 333333 | 333366 | 333399 | 3333CC | 3333FF |
48 | 336600 | 336633 | 336666 | 336699 | 3366CC | 3366FF |
54 | 339900 | 339933 | 339966 | 339999 | 3399CC | 3399FF |
60 | 33CC00 | 33CC33 | 33CC66 | 33CC99 | 33CCCC | 33CCFF |
66 | 33FF00 | 33FF33 | 33FF66 | 33FF99 | 33FFCC | 33FFFF |
72 | 660000 | 660033 | 660066 | 660099 | 6600CC | 6600FF |
78 | 663300 | 663333 | 663366 | 663399 | 6633CC | 6633FF |
84 | 666600 | 666633 | 666666 | 666699 | 6666CC | 6666FF |
90 | 669900 | 669933 | 669966 | 669999 | 6699CC | 6699FF |
96 | 66CC00 | 66CC33 | 66CC66 | 66CC99 | 66CCCC | 66CCFF |
102 | 66FF00 | 66FF33 | 66FF66 | 66FF99 | 66FFCC | 66FFFF |
108 | 990000 | 990033 | 990066 | 990099 | 9900CC | 9900FF |
114 | 993300 | 993333 | 993366 | 993399 | 9933CC | 9933FF |
120 | 996600 | 996633 | 996666 | 996699 | 9966CC | 9966FF |
126 | 999900 | 999933 | 999966 | 999999 | 9999CC | 9999FF |
132 | 99CC00 | 99CC33 | 99CC66 | 99CC99 | 99CCCC | 99CCFF |
138 | 99FF00 | 99FF33 | 99FF66 | 99FF99 | 99FFCC | 99FFFF |
144 | CC0000 | CC0033 | CC0066 | CC0099 | CC00CC | CC00FF |
150 | CC3300 | CC3333 | CC3366 | CC3399 | CC33CC | CC33FF |
156 | CC6600 | CC6633 | CC6666 | CC6699 | CC66CC | CC66FF |
162 | CC9900 | CC9933 | CC9966 | CC9999 | CC99CC | CC99FF |
168 | CCCC00 | CCCC33 | CCCC66 | CCCC99 | CCCCCC | CCCCFF |
174 | CCFF00 | CCFF33 | CCFF66 | CCFF99 | CCFFCC | CCFFFF |
180 | FF0000 | FF0033 | FF0066 | FF0099 | FF00CC | FF00FF |
186 | FF3300 | FF3333 | FF3366 | FF3399 | FF33CC | FF33FF |
192 | FF6600 | FF6633 | FF6666 | FF6699 | FF66CC | FF66FF |
198 | FF9900 | FF9933 | FF9966 | FF9999 | FF99CC | FF99FF |
204 | FFCC00 | FFCC33 | FFCC66 | FFCC99 | FFCCCC | FFCCFF |
210 | FFFF00 | FFFF33 | FFFF66 | FFFF99 | FFFFCC | FFFFFF |
To obtain the index for a given color, add the numbers in the leftmost column and top row corresponding to this color.
Palette #2 contains the standard 16 HTML colors (alphabetically ordered by name), as follows:
Index | Color | Name | Index | Color | Name |
---|---|---|---|---|---|
0 | 00FFFF | aqua | 8 | 000080 | navy |
1 | 000000 | black | 9 | 808000 | olive |
2 | 0000FF | blue | 10 | 800080 | purple |
3 | FF00FF | fuchsia | 11 | FF0000 | red |
4 | 008000 | green | 12 | C0C0C0 | silver |
5 | 808080 | grey | 13 | 008080 | teal |
6 | 00FF00 | lime | 14 | FFFFFF | white |
7 | 800000 | maroon | 15 | FFFF00 | yellow |
Palette #3 contains 256 grayscale colors from RGB(0, 0, 0) to RGB(255, 255, 255) and is not shown here.
9.6 Transparency
Any single color index in a frame can be assigned to be transparent, or see-through. This is done via the property JpegGif.TranspColor. The following code snippet creates a new GIF image and assigns color index 216 (the first unused index in the default palette) to be transparent, and then fills the image with a transparent background:
objGif.TranspColor = 216;
objGif.PenColor = 216;
objGif.BrushColor = 216;
objGif.FillRect( 0, 0, 100, 100 );
...
objGif.TranspColor = 216
objGif.PenColor = 216
objGif.BrushColor = 216
objGif.FillRect( 0, 0, 100, 100 )
...
To remove transparency from the current frame, the property JpegGif.TranspColorSet needs to be set to false.
9.7 Miscellaneous Features
9.7.1 Other Frame Management Methods
In addition to the JpegGif.AddFrame and JpegGif.RemoveFrame methods covered above, there are also JpegGif.Clear and JpegGif.MoveFrame methods.
The Clear method, which takes no arguments, simply removes all frames from the image. An image with no frames cannot be saved or drawn on.
MoveFrame takes two arguments: the original index and desired index. It moves the frame specified by the first argument to a new location specified by the 2nd argument. If the current frame is affected by the move, the CurrentFrame property will change to reflect the new position of the current frame. The current frame remains the same, albeit at a new location.
9.7.2 Other Animation Management Properties
The JpegGif.Loops property controls how many times the animation sequence runs before stopping. By default, this property is 0 which designates an infinite number of loops.
The JpegGif.Delay property affects the time delay of the current frame measured in 1/100 sec. By default, this property is set to 100 (1 sec) for every frame being added.
9.7.3 Saving Individual Frames
The property JpegGif.FrameToSave, if set to a non-zero value, instructs the JpegGif object to save only the frame specified by this property, and no other frames, when a Save method is called. This enables you to view every individual frame of an animated GIF. This feature was added mostly for debugging purposes.
The method JpegGif.FindClosestColor enables you to search a palette for the index of a color closest to a given RGB trio. The method expects the global/local flag and three RGB values. It returns an index in the specified palette which corresponds to the color closest to the specified RGB values.
The property JpegGif.DisposalMethod controls how frames in an animated GIF replace each other. The valid values are:
- Leave the current image in place and draw the next image on top of it. This is the default method.
- The canvas should be restored to the background color before the next image is rendered.
- The canvas should be restored to its previous state before the current image is drawn.