Understanding Bitmaps

Toolkits.UnderstandingBitmaps History

Hide minor edits - Show changes to output

June 12, 2007, at 11:36 AM by 24.64.76.194 -
Changed line 94 from:
4). We are now about to do pointer operations. While this is the norm in langauges like C, it is definitely not for C#, which does not allow direct memory manipulation. Thus we have to declare the following code as unsafe. Note that we also to compile the project in UNSAFE mode! To do this, you will have to select the Visual Studio 2005 Build tab in the Project Properties, and check the 'Allow Unsafe Code' checkbox
to:
4). We are now about to do pointer operations. As previously mentioned, we can only do this in C# when we declare the the code containing these opeartons as unsafe. Note that we also to compile the project in UNSAFE mode! To do this, you will have to select the Visual Studio 2005 Build tab in the Project Properties, and check the 'Allow Unsafe Code' checkbox. Also a warning... if you get this code wrong, you can do horrible things, i.e., writing memory you are not supposed to! Be careful when writing unsafe code.
Changed line 105 from:
6). Now we will walk thorugh each row and each column of the bitmap. The data.Height is the height of our bitmap in pixels, while the data.Width is the number of pixels (not bytes!) in our bitmap.
to:
6). Now we will walk through each row and each column of the bitmap. The data.Height is the height of our bitmap in pixels, while the data.Width is the number of pixels (not bytes!) in our bitmap.
June 12, 2007, at 11:31 AM by 24.64.76.194 -
Added lines 29-30:
'''Warning and Compiling'''. This code uses pointer operations. While this is the norm in langauges like C, it is definitely not for C#, which does not normally allow direct memory manipulation unless its critical sections are declared as unsafe. Even so, you have to compile the project in UNSAFE mode. To do this, you will have to select the Visual Studio 2005 Build tab in the Project Properties, and check the 'Allow Unsafe Code' checkbox. More on this shortly.
Added line 52:
Changed line 56 from:
// advance one step to the red value
to:
Changed line 100 from:
5). Before we start going through our bytes, we need to know where the bytes begin. We get theis through the data.Scan0 method, which returnes a pointer to the first byte in our bitmap.
to:
5). Before we start going through our bytes, we need to know where the bytes begin. We get theis through the data.Scan0 method, which returnes a pointer to the first byte in our bitmap. Note that imgPtr is a pointer to each byte, while *imgPtr is the contents of each byte.
Changed line 105 from:
6). Now we will walk thorugh each row and each column of the bitmap. The data.Height is the height of our bitmap, while the data.Width is the Stride.
to:
6). Now we will walk thorugh each row and each column of the bitmap. The data.Height is the height of our bitmap in pixels, while the data.Width is the number of pixels (not bytes!) in our bitmap.
Changed lines 114-115 from:
to:
7). We are using the PixelFormat.Format24bppRgb format. Thus there are 3 bytes for each Pixel, but surprisingly, these are in BGR (Blue, Green, Red) order rather than RGB order! Remember that imgPtr is a pointer to each byte, and *imgPtr is the contents of each byte. For clarity, we create a new byte with the color name and set it to the byte's contents. To produce the washout effect, if a byte's color value is greater than 127 (it could be as high as 255), we limit it to 127. Note that we could have also written this without using the ''byte blue'' declaration, e.g., as ''if (*imgPter > 127) *imgPtr = 127;'' Note too that this code is repeated in the Columns for loop, so it will iterate across every pixel (3 bytes each) in the row.
Added lines 117-127:
byte blue = (byte) (*imgPtr);
if (blue > 127) *imgPtr = 127;
imgPtr++;

byte green= (byte) (*imgPtr);
if (green > 127) *imgPtr = 127;
imgPtr++;

byte red = (byte) (*imgPtr);
if (red > 127) *imgPtr = 127;
imgPtr++;
Changed line 130 from:
to:
8) At this point, we have reached the end of the row, so we have to get to the beginning of the next row. Remember that there may be some empty bytes after our pixels at the end of the row (see Stride above), so we have to account for this. What we do is add the data.Stride (i.e. all of the bytes in a row) which gets us to the same position in the next row, and then subtract the data.Width * 3 (the number of pixels * 3 bytes each). This brings us back to the beginning of the new row.
Changed lines 132-142 from:
=]
to:
}
imgPtr += data.Stride - data.Width * 3;
}
=]

9) We have now completed the last row in our bitmap, so all that is left to do is unlock the bitmap from system memory.
(:source lang=csharp tabwidth=2 :) [=
}
bm.UnlockBits(data);
}

=]
June 11, 2007, at 10:16 PM by 24.64.76.194 -
Changed line 91 from:
4). Before we start going through our bytes, we need to know where the bytes begin. We get theis through the data.Scan0 method, which returnes a pointer to the first byte in our bitmap.
to:
4). We are now about to do pointer operations. While this is the norm in langauges like C, it is definitely not for C#, which does not allow direct memory manipulation. Thus we have to declare the following code as unsafe. Note that we also to compile the project in UNSAFE mode! To do this, you will have to select the Visual Studio 2005 Build tab in the Project Properties, and check the 'Allow Unsafe Code' checkbox
Changed lines 93-94 from:
byte* imgPtr = (byte*)(data.Scan0);
to:
unsafe
{
Changed line 97 from:
4). Now we will walk thorugh each row and each column of the bitmap
to:
5). Before we start going through our bytes, we need to know where the bytes begin. We get theis through the data.Scan0 method, which returnes a pointer to the first byte in our bitmap.
Added line 99:
byte* imgPtr = (byte*)(data.Scan0);
Changed line 102 from:
to:
6). Now we will walk thorugh each row and each column of the bitmap. The data.Height is the height of our bitmap, while the data.Width is the Stride.
Added lines 104-107:
for (int row = 0; row < data.Height; row ++)
{
for (int column = 0; column < data.Width; column ++)
{
Added lines 111-112:
June 11, 2007, at 10:12 PM by 24.64.76.194 -
Changed line 43 from:
for (int i = 0; i < data.Height; i++)
to:
for (int row = 0; row < data.Height; row ++)
Changed line 45 from:
for (int j = 0; j < data.Width; j++)
to:
for (int column = 0; column < data.Width; column ++)
Added line 67:
1). If you want to manipulate bitmaps easily, you should include the above package.
Changed lines 71-72 from:
If you want to manipulate bitmaps easily, you should include the above package.
to:

2). We need a source bitmap. We just pass it into
the method. Note that this method will alter the bitmap, so if you want to keep the original you should pass in a copy (e.g., use the Bitmap.Clone() method).
Added lines 74-109:
private void ManipulateBits (Bitmap bm)
=]

3). The BitmapData specifies the attributes of the Bitmap:
* its size,
* pixel format,
* starting address of the pixel data in memory,
* stride, i.e., length of each scan line or row in memory.
Its arguments as used below are a rectangle with the same width and height as your bitmap, a lock mode (which in this case allowsy you to both read and write each value), and the pixel format (in this case 24bits per pixel in RGB).

(:source lang=csharp tabwidth=2 :) [=
System.Drawing.Imaging.BitmapData data =
bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
=]

4). Before we start going through our bytes, we need to know where the bytes begin. We get theis through the data.Scan0 method, which returnes a pointer to the first byte in our bitmap.
(:source lang=csharp tabwidth=2 :) [=
byte* imgPtr = (byte*)(data.Scan0);
=]

4). Now we will walk thorugh each row and each column of the bitmap
(:source lang=csharp tabwidth=2 :) [=
=]


(:source lang=csharp tabwidth=2 :) [=
=]


(:source lang=csharp tabwidth=2 :) [=
=]


(:source lang=csharp tabwidth=2 :) [=
June 11, 2007, at 09:59 PM by 24.64.76.194 -
Added lines 63-72:
=]

Ok, now lets go through this line by line.

(:source lang=csharp tabwidth=2 :) [=
using System.Drawing.Imaging;
=]
If you want to manipulate bitmaps easily, you should include the above package.

(:source lang=csharp tabwidth=2 :) [=
June 11, 2007, at 09:57 PM by 24.64.76.194 -
Changed line 48 from:
if (blue > 127) **imgPtr = 127;
to:
if (blue > 127) *imgPtr = 127;
Changed lines 50-51 from:
if (green > 127) **imgPtr = 127;
to:
byte green= (byte) (*imgPtr);
if (green > 127)
*imgPtr = 127;
Changed line 55 from:
if (red > 127) **imgPtr = 127;
to:
if (red > 127) *imgPtr = 127;
June 11, 2007, at 09:55 PM by 24.64.76.194 -
Changed line 23 from:
* '''Bits per pixel (bpp)'''. If you have a bitmap that is (say) 24 bits per pixel, this mean that each RGB color is represented by 8 bits or 1 byte each. 8 bits means that each RGB value can be between 0-255. For example if we have three bytes 11111111 00000000 00000000 (i.e, R=255, G=0, B=0), then that pixel is red.
to:
* '''Bits per pixel (bpp)'''. If you have a bitmap that is (say) 24 bits per pixel, this mean that each RGB color is represented by 8 bits or 1 byte each. 8 bits means that each RGB value can be between 0-255. Also, bytes are actually ordered in B G R order. For example if we have three bytes 11111111 00000000 00000000 (i.e, B=255, G=0, R=0), then that pixel is blue.
Changed lines 27-28 from:
So, to see how it works, lets look at some code. Here is the whole thing, and then we will describe what happens line by line.
to:
So, to see how it works, lets look at some code. This example will walk through a bitmap, and examine it pixel by pixel. We can get the actual RGB values, but what we will do is wash out the image, i.e., if a pixel has a value above 127, we will reduce its color to 127. Here is the whole thing, and then we will describe what happens line by line.
Changed line 48 from:
SaturateColor (imgPtr);
to:
if (blue > 127) **imgPtr = 127;
Changed lines 50-51 from:
byte green = (byte) (*imgPtr);
SaturateColor (imgPtr);
to:
if (green > 127) **imgPtr = 127;
Changed line 54 from:
SaturateColor (imgPtr);
to:
if (red > 127) **imgPtr = 127;
Deleted lines 61-68:

unsafe private void SaturateColor(byte* b)
{
if (*b > 127)
*b = 255;
else
*b = 0;
}
June 11, 2007, at 09:51 PM by 24.64.76.194 -
Changed lines 12-13 from:
'''Note:''' This [[http://www.coders4fun.com/2007/05/21/optimized-access-to-bitmaps/en/ | article] is another good intro to bitmaps.
to:
'''Note:''' This [[http://www.coders4fun.com/2007/05/21/optimized-access-to-bitmaps/en/ | article]] is another good intro to bitmaps.
Added lines 29-71:
(:source lang=csharp tabwidth=2 :) [=

...
using System.Drawing.Imaging;
...
private void ManipulateBits (Bitmap bm)
{
System.Drawing.Imaging.BitmapData data =
bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
unsafe
{
byte* imgPtr = (byte*)(data.Scan0);
for (int i = 0; i < data.Height; i++)
{
for (int j = 0; j < data.Width; j++)
{
byte blue = (byte) (*imgPtr);
SaturateColor (imgPtr);
imgPtr++;
byte green = (byte) (*imgPtr);
SaturateColor (imgPtr);
imgPtr++;
// advance one step to the red value
byte red = (byte) (*imgPtr);
SaturateColor (imgPtr);
imgPtr++;
}
imgPtr += data.Stride - data.Width * 3;
}
}
bm.UnlockBits(data);
}

unsafe private void SaturateColor(byte* b)
{
if (*b > 127)
*b = 255;
else
*b = 0;
}
=]
June 11, 2007, at 09:45 PM by 24.64.76.194 -
Added lines 1-28:
[[Toolkits.EasyImage | << Back to the EasyImage Toolkit page]]
\\
\\

!!!How-To - Understanding Bitmaps

!!!What you will learn
This Howt-To describes how bitmaps are constructed as a data structure. With this knowledge, you can then understand how to manipulate them at a pixel level.

The EasyImages package works with Images and Bitmaps. While EasyImages gives you the means to do some simple manipulation of these bitmaps (as does C#), at some point you may want to actually access and manipulate bitmaps yourself at the pixel level. Before you do this, you need to understand how bitmaps are stored as bytes.

'''Note:''' This [[http://www.coders4fun.com/2007/05/21/optimized-access-to-bitmaps/en/ | article] is another good intro to bitmaps.

!!!Background.
The Bitmap class provides two functions: SetPixel() and GetPixel() which lets you access particular pixels in the bitmap. While ok for poking around a few pixels, these methods are slow if you want to do some serious pixel manipulation, e.g., to examine and/or alter all pixels in an image.

Instead, you can access the bitmap data structure directly as a set of bytes that you just walk through. This will let you very quickly examining the bitmap byte by byte (and also pixel by pixel, and row/column by row/column), perhaps altering pixels as you go through them. To do this, you need to know how bitmaps are laid out as a byte structure.

!!!The Bitmap data structure.

A few things that you need to know, along with some terminology.
* '''RGB'''. Each color is comprixed of a mix of three colors: R (red), G (green) and B (blue).
* '''Bits per pixel (bpp)'''. If you have a bitmap that is (say) 24 bits per pixel, this mean that each RGB color is represented by 8 bits or 1 byte each. 8 bits means that each RGB value can be between 0-255. For example if we have three bytes 11111111 00000000 00000000 (i.e, R=255, G=0, B=0), then that pixel is red.
* '''Image Stride'''. Each row in a bitmap always contains multiples of 4 bytes. If your bitmap row size is set smaller than that, then bytes are added automatically to each bitmap row to make it a multiple of 4. This length is called the '''Stride'''. For example, lets say you have a bitmap that is 15 x 15 pixels. 15 pixels / row at 3 bytes each = 45 bytes. This means that each bitmap row is actually stored in memory as (4 * 12) = 48 bytes - the last three bytes are just unused padding to make it a multiple of 4.

!!!Code
So, to see how it works, lets look at some code. Here is the whole thing, and then we will describe what happens line by line.