Reputation: 301
In the attempt to learn both vb.net and C# better I am taking a project (TreeViewAdv found on SourceForge) and trying to convert its code to VB. Though I hope to get to the point where I can convert the code manually (as this is a learning project), I am currently using a C# to VB code converter (found at www.DeveloperFusion.com) to get the ball rolling and some basic understanding first. The code I have converted is mostly problem free, but there is one problem (maybe 2). See Code:
C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
namespace Aga.Controls
{
public static class BitmapHelper
{
[StructLayout(LayoutKind.Sequential)]
private struct PixelData
{
public byte B;
public byte G;
public byte R;
public byte A;
}
public static void SetAlphaChanelValue(Bitmap image, byte value)
{
if (image == null)
throw new ArgumentNullException("image");
if (image.PixelFormat != PixelFormat.Format32bppArgb)
throw new ArgumentException("Wrong PixelFormat");
BitmapData bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
PixelData* pPixel = (PixelData*)bitmapData.Scan0;
for (int i = 0; i < bitmapData.Height; i++)
{
for (int j = 0; j < bitmapData.Width; j++)
{
pPixel->A = value;
pPixel++;
}
pPixel += bitmapData.Stride - (bitmapData.Width * 4);
}
}
image.UnlockBits(bitmapData);
}
}
}
VB.Net (Post Conversion)
Imports System.Collections.Generic
Imports System.Text
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Drawing.Imaging
Namespace Aga.Controls
Public NotInheritable Class BitmapHelper
Private Sub New()
End Sub
<StructLayout(LayoutKind.Sequential)> _
Private Structure PixelData
Public B As Byte
Public G As Byte
Public R As Byte
Public A As Byte
End Structure
Public Shared Sub SetAlphaChanelValue(ByVal image As Bitmap, ByVal value As Byte)
If image Is Nothing Then
Throw New ArgumentNullException("image")
End If
If image.PixelFormat <> PixelFormat.Format32bppArgb Then
Throw New ArgumentException("Wrong PixelFormat")
End If
Dim bitmapData As BitmapData = image.LockBits(New Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
Dim pPixel As Pointer(Of PixelData) = DirectCast(bitmapData.Scan0, Pointer(Of PixelData))
For i As Integer = 0 To bitmapData.Height - 1
For j As Integer = 0 To bitmapData.Width - 1
pPixel.A = value
pPixel += 1
Next
pPixel += bitmapData.Stride - (bitmapData.Width * 4)
Next
image.UnlockBits(bitmapData)
End Sub
End Class
End Namespace
The lines of code in questions, I believe, are: C#
PixelData* pPixel = (PixelData*)bitmapData.Scan0;
Which converts to this in VB
Dim pPixel As Pointer(Of PixelData) = DirectCast(bitmapData.Scan0, Pointer(Of PixelData))
Intellisense tells me that there is something wrong with Pointer(Of PixelData)
My Question: What is the best way to get the VB code above to function like the C# code (in results not methodology), and why the solution works (I wish to understand the reasoning)?
Things that should be kept in mind when answering or commenting:
1) I already understand that CRL or managed programming languages do not use pointers
2) I am not trying to find a way to use pointers or unsafe code in vb.net I know this cannot be done.
3) I am not going to ditch VB nor do I wish to simply keep the C# code in a separate assembly and reference it from VB. I understand it has limitations, but this is a LEARNING project so that when I go to a job interview and they asking me, "Can you do X in VB?" I can confidently say yes.
4) Again, what is paramount here is not which road I take but the destination. I understand that there is a shorter road to the destination through C#, but I wish to go through VB.
For bonus points, I expect the statement
pPixel += 1
to not work on the VB side (as pPixel is a structure, see above), but it builds fine on the C# side. Why does this (work/not work) in the respective languages. This is NOT a question about safe/unsafe code blocks, so much as when the code ACTUALLY RUNS and does not throw an error because of inappropriate casting/type use (int 1 --> struct pPixel), WHY?
Upvotes: 3
Views: 2206
Reputation: 942128
No pointer support in VB.NET, you need to fall back to the framework support functions. Marshal.WriteByte fits the ticket:
Dim pPixel = bitmapData.Scan0
For y As Integer = 0 To bitmapData.Height - 1
For x As Integer = 0 To bitmapData.Width - 1
Marshal.WriteByte(pPixel, 4 * x + 3, value)
Next
pPixel += bitmapData.Stride
Next
And of course never dismiss the excellent language interop supported by .NET. A VB.NET program can very easily use a C# class library project.
Upvotes: 5
Reputation: 564771
Unfortunately, VB.Net doesn't support direct manipulation of the data via pointers. This is one of the significant disadvantages of VB when compared to C# - no access to unsafe code when you need it.
The way around it, in this case, is actually to copy from the IntPtr
to a byte array, perform your manipulations, then copy back. This can be done with the Marshal.Copy method.
The Bitmap.LockBits sample code on MSDN for VB shows the entire process, and how to manipulate a bitmap's pixel data from VB.
Upvotes: 1