This message was edited by iwilld0it at 2006-2-22 8:46:0
This message was edited by iwilld0it at 2006-2-22 8:45:32
There is a slow and fast way to access pixels in VB.NET. However, even the fast way in VB is surpassed by C# because it can use unsafe code w/ memory pointers.
Fortunately I have some code for you to look over and then I suggest you look up some articles, because graphics maniupulation is a vast topic.
Slow Way
Dim newBmp As New Bitmap(100, 100, Imaging.PixelFormat.Format32bppArgb)
Dim g As Graphics = Graphics.FromImage(newBmp)
g.FillRectangle(Brushes.Blue, New Rectangle(20, 20, 60, 60))
For y As Integer = 0 To newBmp.Height - 1
For x As Integer = 0 To newBmp.Width - 1
Dim c As Color = newBmp.GetPixel(x, y)
newBmp.SetPixel(x, y, _
Color.FromArgb(c.A Mod 100, c.R Mod 100, c.G Mod 100, c.B Mod 100))
Next
Next
pic.Image = DirectCast(newBmp.Clone, Bitmap)
g.Dispose()
newBmp.Dispose()
This example creates a 32-bit Bitmap image w/ an alpha channel and scrolls through each pixel one-by-one. This is very slow even though the GetPixel and SetPixel functions are thin wrappers around the gdiplus GdipBitmapGetPixel() and GdipBitmapSetPixel() API functions. The bottle neck is in these API functions (prob written w/ C++), so there is no way to speed up this routine.
BTW: pic is a PictureBox that I placed on the form.
Fast Way
For this example, I created a wrapper class called BitmapBits.
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
Public Class BitmapBits
Implements IDisposable
Private mBitmap As Bitmap
Private mBitmapData As BitmapData
Private mBounds As Rectangle
Private mData() As Byte
Sub New(ByVal bm As Bitmap)
mBitmap = bm
mBounds = New Rectangle(0, 0, bm.Width, bm.Height)
End Sub
Public Sub Lock()
Dim totalBytes As Integer
mBitmapData = mBitmap.LockBits(mBounds, ImageLockMode.ReadWrite, mBitmap.PixelFormat)
totalBytes = mBitmapData.Stride * mBitmapData.Height
ReDim mData(totalBytes - 1)
Marshal.Copy(mBitmapData.Scan0, mData, 0, totalBytes)
End Sub
Public ReadOnly Property Data() As Byte()
Get
Return mData
End Get
End Property
Public Sub Unlock()
If mBitmapData Is Nothing Then Return
Dim totalBytes As Integer = mBitmapData.Stride * mBitmapData.Height
Marshal.Copy(mData, 0, mBitmapData.Scan0, totalBytes)
mBitmap.UnlockBits(mBitmapData)
mBitmapData = Nothing
mData = Nothing
End Sub
Public Sub Dispose() Implements System.IDisposable.Dispose
If Not mBitmap Is Nothing Then
mBitmap.Dispose()
End If
End Sub
End Class
The .NET Bitmap class has a function called LockBits which returns a BitmapData object. This object is a wrapper for the pixel data. The BitmapData object has a property called Scan0, which is a memory pointer that points to the begining of the pixel data. Unfortunately, Visual Basic does not intrinsically support memory pointers, so we have to perform a few tricks w/ the System.Runtime.Marshal.Copy() function.
Here is code that uses the wrapper class I created. It essentially does the same thing as the first example.
Dim newBmp As New Bitmap(100, 100, Imaging.PixelFormat.Format32bppArgb)
Dim g As Graphics = Graphics.FromImage(newBmp)
g.FillRectangle(Brushes.Blue, New Rectangle(20, 20, 60, 60))
Dim bits As New BitmapBits(newBmp)
Dim pix As Integer = 0
bits.Lock()
For y As Integer = 0 To newBmp.Height - 1
For x As Integer = 0 To newBmp.Width - 1
bits.Data(pix + 0) = CByte((bits.Data(pix + 0) Mod 100))
bits.Data(pix + 1) = CByte((bits.Data(pix + 1) Mod 100))
bits.Data(pix + 2) = CByte((bits.Data(pix + 2) Mod 100))
bits.Data(pix + 3) = CByte((bits.Data(pix + 3) Mod 100))
pix += 4
Next
Next
bits.Unlock()
pic.Image = DirectCast(newBmp.Clone, Bitmap)
g.Dispose()
newBmp.Dispose()
I even tried it this way:
Add this win32 API declaration to the top of your Form class.
Private Declare Sub GetQuads Lib "kernel32" Alias "RtlMoveMemory" ( _
ByRef pDst As RGBQuad, ByVal pSrc As IntPtr, ByVal ByteLen As Long)
Private Declare Sub SetQuads Lib "kernel32" Alias "RtlMoveMemory" ( _
ByVal pDst As IntPtr, ByRef pSrc As RGBQuad, ByVal ByteLen As Long)
<StructLayout(LayoutKind.Sequential)> _
Public Structure RGBQuad
Public R As Byte
Public G As Byte
Public B As Byte
Public A As Byte
End Structure
Dim newBmp As New Bitmap(100, 100, Imaging.PixelFormat.Format32bppArgb)
Dim g As Graphics = Graphics.FromImage(newBmp)
g.FillRectangle(Brushes.Blue, New Rectangle(20, 20, 60, 60))
Dim totalBytes As Integer
Dim bdata As BitmapData = newBmp.LockBits( _
New Rectangle(0, 0, 100, 100), _
ImageLockMode.ReadWrite, newBmp.PixelFormat)
totalBytes = (bdata.Stride * bdata.Height)
Dim data(totalBytes >> 2 - 1) As RGBQuad
Dim pix As Integer = 0
GetQuads(data(0), bdata.Scan0, totalBytes)
For i As Integer = 0 To data.GetUpperBound(0)
data(i).A = CType(data(i).A Mod 100, Byte)
data(i).R = CType(data(i).R Mod 100, Byte)
data(i).G = CType(data(i).G Mod 100, Byte)
data(i).B = CType(data(i).B Mod 100, Byte)
Next
SetQuads(bdata.Scan0, data(0), totalBytes)
newBmp.UnlockBits(bdata)
pic.Image = DirectCast(newBmp.Clone, Bitmap)
g.Dispose()
newBmp.Dispose()
This is just about as fast as the last technique and is simpler because we are representing a pixel as a RGBQuad object. Also, it turns out that the GetQuads (RtlMoveMemory) API function is as fast as the Marshal.Copy function (which essentially does the same thing.)
Finally, for completion, I am going to supply an even faster pixel manipulation written in C#. You may choose to wrap your pixel manipulation in a C# DLL and reference it in your VB project.
Bitmap newBmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(newBmp);
g.FillRectangle(Brushes.Blue, new Rectangle(20, 20, 60, 60));
BitmapData bdata = newBmp.LockBits(
new Rectangle(0, 0, 100, 100),
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb);
int stride = bdata.Stride;
IntPtr scan = bdata.Scan0;
unsafe
{
byte *p = (byte *)scan.ToPointer();
int nWidth = newBmp.Width;
for(int y=0; y < newBmp.Height; ++y)
{
for (int x = 0; x < nWidth; ++x)
{
p[0] %= 100;
p[1] %= 100;
p[2] %= 100;
p[3] %= 100;
p += 4;
}
}
}
newBmp.UnlockBits(bdata);
pic.Image = (Bitmap)newBmp.Clone();
g.Dispose();
newBmp.Dispose();
This uses straight pointer arithematic, which is much faster than navigating a byte array. Also, there is no Marshal.Copy operation needed neither. This is roughly twice as fast as the fastest VB method.
Even though we have pushed the limits here, there are still much faster ways to manipulate pixels. There is still the GDI32 functions that I have not explored w/ .NET yet and graphics libraries like Direct-X. However, the techniques I gave here are sufficiently fast.
You should be able to adapt this code to write pixel manipulation functions at your whim. Please ask any questions about the code, considering the code is not trivial.