Accessing Pixel Data

Graphics Mill provides a wide range of methods to manipulate images: transformations, effects, filters, and so on. However you may want to implement some custom algorithms. In this case you need to get access to bitmap pixels.

There are two approaches to accessing pixels with Graphics Mill: high-level and low-level.

High-Level Access

The Bitmap.GetPixel(Int32, Int32) and Bitmap.SetPixel(Int32, Int32, Color) methods allow for getting and setting the color of the pixel specified by its x and y coordinates.

The advantage of using the high-level access is run-time boundary check, which means that you will never get an access violation problem if you specify the wrong coordinates. Also, there is no need to learn the physical structure of the pixel to access it. The disadvantage of such an approach is its poor performance. If you need to simply change the color of several points, this method is convenient, but we do not recommend using high-level pixel access for intensive pixel processing.

Low-Level Access

How Pixels Are Stored in Memory

If you use low-level pixel access you need to know how pixels are stored in memory. Pixel data in Graphics Mill are stored linearly. This means that the entire bitmap occupies a contiguous memory area. Rows of pixels are stored sequentially. Each row contains the same number of bytes.

The table below demonstrates the order of components in a pixel. The square brackets depict the content of one byte, while bits in a byte are separated by comma. Bytes are separated by a vertical line. A color name (and font color) specifies how the pixel part is interpreted. For example, the [pixel1,pixel2]|[pixel3,pixel4] string defines two bytes, where each byte describes two indexed pixels. And [Blue]|[Green]|[Red] describes one pixel, which consists of three colors (red, green, and blue), and each color component is one byte size.

Pixel Format Name Memory Order
ConstantFormat1bppIndexed [pixel1,pixel2,pixel3,pixel4,pixel5,pixel6,pixel7,pixel8]
ConstantFormat4bppIndexed [pixel1,pixel2]|[pixel3,pixel4]
ConstantFormat8bppIndexed [pixel1]
ConstantFormat8bppGrayscale [Grayscale]
ConstantFormat16bppGrayscale [Grayscale]|[Grayscale]
ConstantFormat16bppAlphaGrayscale [Grayscale]|[Alpha]
ConstantFormat32bppAlphaGrayscale [Grayscale]|[Grayscale]|[Alpha]|[Alpha]
ConstantFormat24bppRgb [Blue]|[Green]|[Red]
ConstantFormat32bppRgb [Blue]|[Green]|[Red]|[Not Used]
ConstantFormat48bppRgb [Blue]|[Blue]|[Green]|[Green]|[Red]|[Red]
ConstantFormat32bppArgb [Blue]|[Green]|[Red]|[Alpha]
ConstantFormat64bppArgb [Blue]|[Blue]|[Green]|[Green]|[Red]|[Red]|[Alpha]|[Alpha]
ConstantFormat32bppCmyk [Black]|[Yellow]|[Magenta]|[Cyan]
ConstantFormat64bppCmyk [Black]|[Black]|[Yellow]|[Yellow]|[Magenta]|[Magenta]|[Cyan]|[Cyan]
ConstantFormat40bppAcmyk [Black]|[Yellow]|[Magenta]|[Cyan]|[Alpha]
ConstantFormat80bppAcmyk [Black]|[Black]|[Yellow]|[Yellow]|[Magenta]|[Magenta]|[Cyan]|[Cyan]|[Alpha]|[Alpha]
ConstantFormat24bppLab [Lightness]|[a]|[b]
ConstantFormat48bppLab [Lightness]|[Lightness]|[a]|[a]|[b]|[b]
ConstantFormat32bppAlab [Lightness]|[a]|[b]|[Alpha]
ConstantFormat64bppAlab [Lightness]|[Lightness]|[a]|[a]|[b]|[b]|[Alpha]|[Alpha]

How to Access Pixels Directly

Low-level pixel access in Graphics Mill is similar to that found in GDI+. Generally, you should perform the following steps to get access to a pixel:

  1. Get the pointer to the data via the Bitmap.Scan0 property, which returns the pointer to the first pixel of the first row (the beginning of the memory occupied by the bitmap).
  2. Get the size of a single row in bytes using the Bitmap.Stride property.
  3. Get a number of bits which are used to represent a single pixel via the PixelFormat.Size property.
  4. Calculate the pixel position using the data retrieved in the previous steps.

To iterate through all the pixels of a bitmap you should start from the Scan0 position and increment the pointer by the PixelFormat.Size bits. To reach the next row, use the Stride property, which specifies how many bytes are taken by one row. The interpretation of the pixel depends on the PixelFormat property; the meaning of this property is described in the table above. So if you need to get a pixel which is in the same position as the current one, but in the following row, just add the value stored in the Stride property.

For example, the position of a pixel in the row i, column j of the bitmap can be calculated as follows:

result = bitmap.Scan0 + bitmap.Stride * i + j * bitmap.PixelFormat.Size / 8

Note

This formula is valid only if the pixel size is divisible by 8 (byte size). Otherwise (for example, if the pixel size is 1 or 4 bits), you need to perform bitwise shift operations to get the pixel.

The following code inverts an image (creates image negative):

C#
using (var bitmap = new Bitmap(@"Images\in.jpg"))
{
    unsafe
    {
        //A pointer to the beginning of the pixel data region
        byte* pointer = (byte*)(bitmap.Scan0.ToPointer());
        //Number of bytes in a row
        int stride = bitmap.Stride;
        //Number of rows
        int height = bitmap.Height;

        for (int i = 0; i < height; i++)
        {
            byte* position = pointer + stride * i;
            for (int j = 0; j < stride; j++)
            {
                //Now we can modify the pixel, for example, invert it
                *position = (byte)(255 - *position);
                position++;
            }
        }
    }
    bitmap.Save(@"Images\Output\out.jpg");
}

See Also

Reference

Manual