FIX: Unhandled Exception Occurs When You Create a Bitmap with a Negative Stride (317309)



The information in this article applies to:

  • Microsoft GDI+ 1.0
  • Microsoft .NET Framework 1.0
  • Microsoft Windows XP Professional
  • the operating system: Microsoft Windows XP 64-Bit Edition

This article was previously published under Q317309
This article references the following .NET Framework Class Library namespace:
  • System.Drawing

SYMPTOMS

When you call the following GDI+ Bitmap constructor
public Bitmap(int width,int height,int stride,PixelFormat format,IntPtr scan0);
				
a negative stride parameter specifies that the data pointed to by the scan0 parameter is data to a bottom-up bitmap.

When you create a new GDI+ Bitmap object with bottom-up data, an unhandled exception error may occur, or the bitmap bits may be drawn incorrectly.

CAUSE

This behavior occurs if the scan0 parameter points to the start of the buffer that contains the bitmap bits, rather than to the start of the first scan line of the bitmap data.

RESOLUTION

To resolve this issue, move the address pointed to by scan0 from the start of the bitmap buffer to the beginning of the last row of pixels in the buffer, which is the topmost scan line seen when the image is drawn.

STATUS

This bug was corrected in Microsoft .NET Framework 1.1.

MORE INFORMATION

The documentation of this Bitmap constructor defines scan0 as the address of a stream of pixel data. The scan0 parameter should point to the start of the first scan line of the bitmap data. It is only with top-down bitmaps that the start of the first scan line matches the start of the bitmap buffer. After you have created the bitmap with this Bitmap constructor, the data pointed to by the buffer that you allocated must remain valid throughout the lifetime of the Bitmap object.

The following code defines data for two bitmaps. The first buffer defined is a top-down bitmap buffer, and the second is a bottom-up buffer. When drawn, these bitmaps look alike because they are defined with a positive and negative stride, respectively. The first bitmap is created with top-down bitmap data. Therefore, the pointer pointed to by scan0 is defined as follows:
void* p = ptrTopDown
				
The second bitmap is created with bottom-up bitmap data, and therefore the pointer pointed to by scan0 points to the beginning of the last row of pixels in the buffer, as follows:
void* p = &ptrBottomUp[desiredRow * scanlineWidth]
				
This C# code draws the two images side by side to demonstrate how to create two identical bitmaps, one with top-down data and the other with bottom-up data. To compile this code, you must specify the /unsafe compiler option, as follows:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
    // Using pointers, so we must use the "unsafe" keyword.
            unsafe
    {
        // This data defines the top-down bitmap.
        byte[] ptrTopDown =  
         {255,255,255,255,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,
                  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
         255,255,255,255,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,
         255,255,255,255,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  };

         // This data defines the same bitmap, only with bottom-up data.  
         byte[] ptrBottomUp =  
        {255,255,255,255,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,
        255,255,255,255,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,
                  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
        255,255,255,255,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  };

        int xStart = 0;
        int yStart = 0;
        int width = 4;
        int height = 4;
        int bytesPerPixel = 4;
        int scanlineWidth = bytesPerPixel * width;

        // We must obtain a pointer to the first scanline of the top-down data.
        // This happens to be the start of the buffer.
        fixed (void* p = ptrTopDown)
        {
            IntPtr ptr = new IntPtr(p);

            PixelFormat format = PixelFormat.Format32bppRgb;
            Bitmap bitmapImage = new Bitmap(width,height,scanlineWidth,format,ptr);
                    
            Rectangle destRect1 = new Rectangle(0, 0, width, height);
            GraphicsUnit units = GraphicsUnit.Pixel;
            e.Graphics.DrawImage(bitmapImage, destRect1, xStart, yStart, width, height, units);
        }
        //Because this is bottom-up bitmap data, we must obtain a pointer to
        //the beginning of the last row of pixels in the buffer, which is the topmost scanline
        //seen when the image is drawn.
        int desiredRow = 3;  
        // Zero-based desired row. We want the fourth
        // row from the top, because that is the start of the
        // last row of pixels in the buffer.
        fixed (void* p = &ptrBottomUp[desiredRow * scanlineWidth])
        {
            IntPtr ptr = new IntPtr(p);

            PixelFormat format = PixelFormat.Format32bppRgb;
            Bitmap bitmapImage = new Bitmap(width,height,-scanlineWidth,format,ptr);
                    
            Rectangle destRect1 = new Rectangle(10, 0, width, height);
            GraphicsUnit units = GraphicsUnit.Pixel;
            e.Graphics.DrawImage(bitmapImage, destRect1, xStart, yStart, width, height, units);
        }
    }
}
				

Modification Type:MinorLast Reviewed:5/3/2006
Keywords:kbDSWGDI2003Swept kbfix kbgdipimaging kbprb KB317309 kbAudDeveloper