GDI+ 使用LockBits和指针加快处理速度

 
Lock up your bits

The Bitmap class provides the LockBits and corresponding UnlockBits methods which enable you to fix a portion of the bitmap pixel data array in memory, access it directly and finally replace the bits in the bitmap with the modified data. LockBitsreturns a BitmapData class that describes the layout and position of the data in the locked array.

The BitmapData class contains the following important properties;

  • Scan0 The address in memory of the fixed data array
  • Stride The width, in bytes, of a single row of pixel data. This width is a multiple, or possiblysub-multiple, of the pixel dimensions of the image and may be padded out to include a few more bytes. I'll explain why shortly.
  • PixelFormat The actual pixel format of the data. This is important for finding the right bytes
  • Width The width of the locked image
  • Height The height of the locked image

The relationship of Scan0 and Stride to the array in memory is shown in figure1.

 

Figure 1: The basic layout of a locked bitmap array

The Stride property, as shown in figure 1, holds the width of one row in bytes. The size of a row however may not be an exact multiple of the pixel size because for efficiency, the system ensures that the data is packed into rows that begin on a four byte boundary and are padded out to a multiple of four bytes. This means for example that a 24 bit per pixel image 17 pixels wide would have a stride of 52. The used data in each row would take up 3*17 = 51 bytes and the padding of 1 byte would expand each row to 52 bytes or 13*4 bytes. A 4BppIndexed image of 17 pixels wide would have a stride of 12. Nine of the bytes, or more properly eight and a half,  would contain data and the row would be padded out with a further 3 bytes to a 4 byte boundary.

The data carrying portion of the row, as has been suggested above, is laid out according to the pixel format. A 24 bit per pixel image containing RGB data would have a new pixel every 3 bytes, a 32 bit per pixel RGBA every four bytes. Pixel formats that contain more than one pixel per byte, such as the 4 bit per pixel Indexed and 1 bit per pixel indexed, have to be processed carefully so that the pixel required is not confused with it's neigbour pixels in the same byte.

Finding the right byte.

Because the stride is the width of a row, to index any given row or Y coordinate you can multiply the stride by the Y coordinate to get the beginning of a particular row. Finding the correct pixel within the row is possibly more difficult and depends on knowing the layout of the pixel formats. The following examples show how to access a particular pixel for a given pixel format.

  • Format32BppArgb Given X and Y coordinates,  the address of the first element in the pixel is Scan0+(y * stride)+(x*4). This Points to the blue byte. The following three bytes contain the green, red and alpha bytes.

  • Format24BppRgb Given X and Y coordinates, the address of the first element in the pixel is Scan0+(y*Stride)+(x*3). This points to the blue byte which is followed by the green and the red.

    扫描二维码关注公众号,回复: 12485664 查看本文章
  • Format8BppIndexed Given the X and Y coordinates the address of the byte isScan0+(y*Stride)+x. This byte is the index into the image palette.

  • Format4BppIndexed Given X and Y coordinates the byte containing the pixel data is calculated as Scan0+(y*Stride)+(x/2). The corresponding byte contains two pixels, the upper nibble is the leftmost and the lower nibble is the rightmost of two pixels. The four bits of the upper and lower nibble are used to select the colour from the 16 colour palette.

  • Format1BppIndexed Given the X and Y coordinates, the byte containing the pixel is calculated by Scan0+(y*Stride)+(x/8). The byte contains 8 bits, each bit is one pixel with the leftmost pixel in bit 8 and the rightmost pixel in bit 0. The bits select from the two entry colour palette.

示例程序:

public static bool GrayScale(Bitmap b)
        {
            // GDI+ still lies to us - the return format is BGR, NOT RGB.
            BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int stride = bmData.Stride;
            System.IntPtr Scan0 = bmData.Scan0;

            unsafe
            {
                byte * p = (byte *)(void *)Scan0;

                int nOffset = stride - b.Width*3;

                byte red, green, blue;
    
                for(int y=0;y<b.Height;++y)
                {
                    for(int x=0; x < b.Width; ++x )
                    {
                        blue = p[0];
                        green = p[1];
                        red = p[2];

                        p[0] = p[1] = p[2] = (byte)(.299 * red + .587 * green + .114 * blue);

                        p += 3;
                    }
                    p += nOffset;
                }
            }

            b.UnlockBits(bmData);

            return true;
        }
 
Stride是指图像每一行需要占用的字节数。根据BMP格式的标准,Stride一定要是4的倍数。据个例子,一幅1024*768的24bppRgb的图像,每行有效的像素信息应该是1024*3 = 3072。因为已经是4的倍数,所以Stride就是3072。那么如果这幅图像是35*30,那么一行的有效像素信息是105,但是105不是4的倍数,所以填充空字节,Stride应该是108。这一行计算出来的offset就是3。这里再留个问题,如果是16色图,也就是4位色,一幅50*50的图像,Stride又应该是多少呢

猜你喜欢

转载自blog.csdn.net/panpanloveruth/article/details/7031478