locked
Conversion C# to vb.net problem RRS feed

  • Question

  • Hi to all,
    i've found this code of Dan Byström.

    public static void transferOneARGBChannelFromOneBitmapToAnother(
        Bitmap source,
        Bitmap dest,
        ChannelARGB sourceChannel,
        ChannelARGB destChannel )
    {
        if ( source.Size!=dest.Size )
            throw new ArgumentException();
        Rectangle r = new Rectangle( Point.Empty, source.Size );
        BitmapData bdSrc = source.LockBits( r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb );
        BitmapData bdDst = dest.LockBits( r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb );
        unsafe
        {
            byte* bpSrc = (byte*)bdSrc.Scan0.ToPointer();
            byte* bpDst = (byte*)bdDst.Scan0.ToPointer();
            bpSrc += (int)sourceChannel;
            bpDst += (int)destChannel;
            for ( int i = r.Height * r.Width; i > 0; i-- )
            {
                *bpDst = *bpSrc;
                bpSrc += 4;
                bpDst += 4;
            }
        }
        source.UnlockBits( bdSrc );
        dest.UnlockBits( bdDst );
    }
    I've converted it in vb.net with telerik.


        Public Shared Sub transferOneARGBChannelFromOneBitmapToAnother(source As Bitmap, dest As Bitmap, sourceChannel As ChannelARGB, destChannel As ChannelARGB)
            If source.Size <> dest.Size Then
                Throw New ArgumentException()
            End If
            Dim r As New Rectangle(Point.Empty, source.Size)
            Dim bdSrc As BitmapData = source.LockBits(r, ImageLockMode.[ReadOnly], PixelFormat.Format32bppArgb)
            Dim bdDst As BitmapData = dest.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
            Dim bpSrc As Pointer(Of Byte) = CType(bdSrc.Scan0.ToPointer(), Pointer(Of Byte))
            Dim bpDst As Pointer(Of Byte) = CType(bdDst.Scan0.ToPointer(), Pointer(Of Byte))
    
            bpSrc += CInt(sourceChannel)
            bpDst += CInt(destChannel)
            For i As Integer = r.Height * r.Width To 1 Step -1
                bpDst.Target = bpSrc.Target
                bpSrc += 4
                bpDst += 4
            Next
            source.UnlockBits(bdSrc)
            dest.UnlockBits(bdDst)
        End Sub
    

    But the system gives me this error : System.Reflection.Pointer' has no type parameters, so can not have type arguments on this line: 

    Dim bpSrc As Pointer(Of Byte) = CType(bdSrc.Scan0.ToPointer(), Pointer(Of Byte))

    Any ideas about this issue?

    Thank in advance.
    Genko

    Tuesday, December 23, 2014 10:09 PM

Answers

  • This is not an area I know much about, but I found an answer to a somewhat related question on stackoverflow at stackoverflow.com/questions/19698131/problematic-conversion-of-pointers-from-c-sharp-code-to-vb-net. Scroll to the bottom of the page and read the reply by Reed Copsey. He suggests using Marshal.Copy to copy the data to a Byte array, do the manipulation in the byte array and, if necessary, use Marshal.Copy to copy the data back. He points out that the MSDN documentation for Bitmap.LockBits has an example of VB code that does this at msdn.microsoft.com/en-us/library/5ey6h79d.aspx?cs-save-lang=1&cs-lang=vb
    • Marked as answer by Carl Cai Monday, January 5, 2015 10:10 AM
    Wednesday, December 24, 2014 12:20 AM
  • You can do it at just about the same speed using managed code (no unsafe c# shenanigans). 

    I believe you can do it even faster nowadays by using stuff from wpf, but I've not played with it. 

    Option Strict On
    
    Imports System.Drawing.Imaging
    Imports System.Runtime.InteropServices
    
    Public Class Form1
    
        Private WithEvents pb1 As PictureBox
        Private WithEvents pb2 As PictureBox
        Private WithEvents btnGO As Button
        Private cboSourceComponent As New ComboBox With {.DataSource = {"B", "G", "R", "A"}, .Location = New Point(10, 120), .Size = New Size(100, 21)}
        Private cboDestinationComponent As New ComboBox With {.DataSource = {"B", "G", "R", "A"}, .Location = New Point(120, 120), .Size = New Size(100, 21)}
    
        Sub New()
    
            ' This call is required by the designer.
            InitializeComponent()
    
            ' Add any initialization after the InitializeComponent() call.
            pb1 = New PictureBox With {.Location = New Point(10, 10), .Size = New Size(100, 100), .BackColor = Color.Black, .SizeMode = PictureBoxSizeMode.StretchImage}
            pb2 = New PictureBox With {.Location = New Point(120, 10), .Size = New Size(100, 100), .BackColor = Color.Black, .SizeMode = PictureBoxSizeMode.StretchImage}
            btnGO = New Button With {.Location = New Point(230, 10), .Text = "GO"}
            Me.Controls.AddRange({pb1, pb2, cboSourceComponent, cboDestinationComponent, btnGO})
        End Sub
    
        ' in memory the 32 bit pixel data is arranged BGRA where B is the hi byte
        Public Enum ChannelARGB
            Blue = 0
            Green
            Red
            Alpha
        End Enum
    
        Public Shared Sub TransferOneARGBChannelFromOneBitmapToAnother(source As Bitmap,
                destination As Bitmap,
                sourceChannel As ChannelARGB,
                destinationChannel As ChannelARGB)
    
            If (source.Size <> destination.Size) Then Throw New ArgumentException()
            Dim r As New Rectangle(Point.Empty, source.Size)
            ' Lockbits prevents the .Net framework from moving the bitmap in memory, so fast memory access can occur.
            Dim bmdSource As BitmapData = source.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
            Dim bmdDestination As BitmapData = destination.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
            ' Copy the pixels out into array. Its faster than messing about with reading individual bytes
            Dim sourcePixels(4 * destination.Width * destination.Height - 1) As Byte
            Dim destinationPixels(4 * destination.Width * destination.Height - 1) As Byte
            ' Copy from the locked bitmap to an array. 
            Marshal.Copy(bmdSource.Scan0, sourcePixels, 0, sourcePixels.Length)
            Marshal.Copy(bmdDestination.Scan0, destinationPixels, 0, destinationPixels.Length)        
            For i As Integer = 0 To destinationPixels.Length - 1 Step 4
                destinationPixels(i + destinationChannel) = sourcePixels(i + sourceChannel)
            Next
            ' And copy the array back. 
            Marshal.Copy(destinationPixels, 0, bmdDestination.Scan0, destinationPixels.Length)        
            source.UnlockBits(bmdSource)
            destination.UnlockBits(bmdDestination)        
        End Sub
    
    
        Private Sub Pb_Click(sender As Object, e As EventArgs) Handles pb1.Click, pb2.Click
            Using ofd As New OpenFileDialog()
                If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                    Using bm As Bitmap = CType(Bitmap.FromFile(ofd.FileName), Bitmap)
                        Dim bm2 As New Bitmap(bm.Width, bm.Height, PixelFormat.Format32bppArgb)
                        Using g As Graphics = Graphics.FromImage(bm2)
                            g.DrawImageUnscaled(bm, Point.Empty)
                        End Using
                        CType(sender, PictureBox).Image = bm2
                    End Using
                End If
            End Using
        End Sub
    
        Private Sub btnGo_Click(sender As Object, e As EventArgs) Handles btnGO.Click
            Dim source As Bitmap = TryCast(pb1.Image, Bitmap)
            Dim destination As Bitmap = TryCast(pb2.Image, Bitmap)
            Dim sourceChannel As ChannelARGB = GetChannel(TryCast(cboSourceComponent.SelectedValue, String))
            Dim destinationChannel As ChannelARGB = GetChannel(TryCast(cboDestinationComponent.SelectedValue, String))
            If source IsNot Nothing AndAlso destination IsNot Nothing Then
                TransferOneARGBChannelFromOneBitmapToAnother(source, destination, sourceChannel, destinationChannel)
            End If
            pb2.Refresh()
        End Sub
    
        Private Shared Function GetChannel(channelChar As String) As ChannelARGB
            Select Case channelChar
                Case "B"c
                    Return ChannelARGB.Blue
                Case "G"c
                    Return ChannelARGB.Green
                Case "R"c
                    Return ChannelARGB.Red
                Case "A"c
                    Return ChannelARGB.Alpha
                Case Else
                    Throw New Exception("wtf")
            End Select
        End Function
    
    End Class

    Destructions:

    The program works with two images that are the same size. You select a source and destination channel (B, G, R or A) and it copies the pixel data from the source image to the destination image.

    Click pb1 to select source image, click pb2 to select destination image, select the source/dest channels, click go to do the copy.


    • Edited by jo0ls Wednesday, December 24, 2014 1:54 AM I have my reasons. And some raisins. The raisins are tastier.
    • Marked as answer by Carl Cai Monday, January 5, 2015 10:09 AM
    Wednesday, December 24, 2014 1:43 AM
  • Hi _Genko_. You might want to try this for VB.net

    'Scan through the pixels Y by X
    Dim iRow As Integer = 0
    Dim iColumn As Integer = 0
    Dim iPixelIDX As Integer = 0
    Dim arrBytes() As Byte = {0, 0, 0, 0}
    For iRow = 0 To bmp32bppInput.Height - 1
       For iColumn = 0 To bmp32bppInput.Width - 1
          'Set the pixel address
          iPixelIDX = iRow * bmdSource.Stride + iColumn * 4
          arrBytes(0) =    Marshal.ReadByte(bmdSource.Scan0, iPixelIDX)        'Blue
          arrBytes(1) = Marshal.ReadByte(bmdSource.Scan0, iPixelIDX + 1)    'Green
          arrBytes(2) = Marshal.ReadByte(bmdSource.Scan0, iPixelIDX + 2)    'Red
          arrBytes(3) = Marshal.ReadByte(bmdSource.Scan0, iPixelIDX + 3)    'Alpha
          If Color.FromArgb(arrBytes(3), arrBytes(2), arrBytes(1), arrBytes(0)).GetBrightness > sngThreshold Then
          bmdOutput.SetIndexedPixel(x, y, bmdn, True) 
          End If
          Next
    Next
    This is not an exact replacement for your code but rather a general idea - in vb you need to use Marshal method to read the image matrix byte.

    • Marked as answer by Carl Cai Monday, January 5, 2015 10:09 AM
    Wednesday, December 24, 2014 5:19 AM
  • Try tangible solutions, if I see all problems here is it my opinion better suited to VB.

    http://www.tangiblesoftwaresolutions.com/Product_Details/Instant_VB.html

    Beside that is the guy doing it an active member of these forums.


    Success
    Cor

    Thanks for the plug Cor, but 'unsafe' code is not converted by Instant VB.  In the latest builds, we just issue a message saying that unsafe code is not converted - we don't even attempt even a partial conversion now.  It would require the parser to switch between handling 'normal' C# syntax and 'unsafe' C# syntax - this is too troublesome, and given the very rare case of someone using unsafe C# code (in my experience we only see this rarely), it's just not worth it.

    Convert between VB, C#, C++, & Java (http://www.tangiblesoftwaresolutions.com)
    Instant C# - VB to C# Converter
    Instant VB - C# to VB Converter

    • Proposed as answer by Cor Ligthert Wednesday, December 24, 2014 3:23 PM
    • Marked as answer by Carl Cai Monday, January 5, 2015 10:09 AM
    Wednesday, December 24, 2014 3:19 PM
  • Hi,

     I see you have a lot of examples to try already but, i figured i would add one more. I took a guess at how your ChannelARGB enum was set up. I also did not know if you realized that this sub is set up to only work properly with Bitmaps that have a 32bppArgb PixelFormat so, i added some code to throw the exception if the Bitmaps are not 32bppArgb or if the Bitmaps do not have the same PixelFormat, they need to be the same.

        Public Enum ChannelARGB As Integer
            chBlue = 0
            chGreen = 1
            chRed = 2
            chAlpha = 3
        End Enum
    
        Public Shared Sub transferOneARGBChannelFromOneBitmapToAnother(ByVal source As Bitmap, ByVal dest As Bitmap, ByVal sourceChannel As ChannelARGB, ByVal destChannel As ChannelARGB)
            If source.Size <> dest.Size Or source.PixelFormat <> PixelFormat.Format32bppArgb Or source.PixelFormat <> dest.PixelFormat Then
                Throw New ArgumentException()
            End If
            Dim r As New Rectangle(Point.Empty, source.Size)
            Dim bdSrc As BitmapData = source.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
            Dim bdDst As BitmapData = dest.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
            For i As Integer = 0 To (Math.Abs(bdSrc.Stride) * bdSrc.Height) - 5 Step 4
                Marshal.WriteByte(bdDst.Scan0, i + CInt(destChannel), Marshal.ReadByte(bdSrc.Scan0, i + CInt(sourceChannel)))
            Next
            source.UnlockBits(bdSrc)
            dest.UnlockBits(bdDst)
        End Sub
    
     

     However, if you are interested in using 2 32bppArgb Bitmaps or 2 24bppRgb Bitmaps then you could check the PixelFormat of the Bitmaps and set up the loop to Step 4 bytes at a time for 32bppArgb types and Step 3 bytes at a time for 24bppRgb types. There are other PixelFormats but, i just picked 2 of the more common ones. You may need to do more testing to make sure the PixelFormats are compatible and to set the Step value correctly for the PixelFormat.

     This is un-tested but, should work. It is more to give an idea of how you could modify the way it reads/writes the bytes according to the Bitmaps PixelFormat.

        Public Shared Sub transferOneARGBChannelFromOneBitmapToAnother(ByVal source As Bitmap, ByVal dest As Bitmap, ByVal sourceChannel As ChannelARGB, ByVal destChannel As ChannelARGB)
            If source.Size <> dest.Size Or source.PixelFormat <> dest.PixelFormat Then
                Throw New ArgumentException()
            End If
    
            Dim StepVal As Integer = 4 '32bppArgb
            If source.PixelFormat = PixelFormat.Format24bppRgb Then StepVal = 3
    
            If StepVal = 3 And (sourceChannel = ChannelARGB.chAlpha Or destChannel = ChannelARGB.chAlpha) Then
                Throw New ArgumentException() 'there is only 3 bytes per color, can`t read/write a 4th Alpha byte
            End If
    
            Dim r As New Rectangle(Point.Empty, source.Size)
            Dim bdSrc As BitmapData = source.LockBits(r, ImageLockMode.ReadOnly, source.PixelFormat)
            Dim bdDst As BitmapData = dest.LockBits(r, ImageLockMode.ReadWrite, dest.PixelFormat)
    
            For i As Integer = 0 To (Math.Abs(bdSrc.Stride) * bdSrc.Height) - 1 - StepVal Step StepVal
                Marshal.WriteByte(bdDst.Scan0, i + CInt(destChannel), Marshal.ReadByte(bdSrc.Scan0, i + CInt(sourceChannel)))
            Next
            source.UnlockBits(bdSrc)
            dest.UnlockBits(bdDst)
        End Sub
    


    If you say it can`t be done then i`ll try it

    • Marked as answer by Carl Cai Monday, January 5, 2015 10:08 AM
    Wednesday, December 24, 2014 3:36 PM

All replies

  • Try tangible solutions, if I see all problems here is it my opinion better suited to VB.

    http://www.tangiblesoftwaresolutions.com/Product_Details/Instant_VB.html

    Beside that is the guy doing it an active member of these forums.


    Success
    Cor

    Tuesday, December 23, 2014 11:02 PM
  • Try tangible solutions, if I see all problems here is it my opinion better suited to VB.

    http://www.tangiblesoftwaresolutions.com/Product_Details/Instant_VB.html

    Beside that is the guy doing it an active member of these forums.


    Success
    Cor

    Instant VB flags the code in the C# "unsafe" block for manual conversion.
    Tuesday, December 23, 2014 11:05 PM
  • Try tangible solutions, if I see all problems here is it my opinion better suited to VB.

    http://www.tangiblesoftwaresolutions.com/Product_Details/Instant_VB.html

    Beside that is the guy doing it an active member of these forums.


    Success
    Cor

    Instant VB flags the code in the C# "unsafe" block for manual conversion.

    Blackwood, 

    It is Christmas time but otherwise I was sure Dave would come with a code solution. As long as the Genko does not say what he wants to achieve is that the only possibility. 

    (Or should I say I'm around these days to lazy to investigate his code what his goal is.)

    :-)


    Success
    Cor

    Tuesday, December 23, 2014 11:09 PM
  • This is not an area I know much about, but I found an answer to a somewhat related question on stackoverflow at stackoverflow.com/questions/19698131/problematic-conversion-of-pointers-from-c-sharp-code-to-vb-net. Scroll to the bottom of the page and read the reply by Reed Copsey. He suggests using Marshal.Copy to copy the data to a Byte array, do the manipulation in the byte array and, if necessary, use Marshal.Copy to copy the data back. He points out that the MSDN documentation for Bitmap.LockBits has an example of VB code that does this at msdn.microsoft.com/en-us/library/5ey6h79d.aspx?cs-save-lang=1&cs-lang=vb
    • Marked as answer by Carl Cai Monday, January 5, 2015 10:10 AM
    Wednesday, December 24, 2014 12:20 AM
  • You can do it at just about the same speed using managed code (no unsafe c# shenanigans). 

    I believe you can do it even faster nowadays by using stuff from wpf, but I've not played with it. 

    Option Strict On
    
    Imports System.Drawing.Imaging
    Imports System.Runtime.InteropServices
    
    Public Class Form1
    
        Private WithEvents pb1 As PictureBox
        Private WithEvents pb2 As PictureBox
        Private WithEvents btnGO As Button
        Private cboSourceComponent As New ComboBox With {.DataSource = {"B", "G", "R", "A"}, .Location = New Point(10, 120), .Size = New Size(100, 21)}
        Private cboDestinationComponent As New ComboBox With {.DataSource = {"B", "G", "R", "A"}, .Location = New Point(120, 120), .Size = New Size(100, 21)}
    
        Sub New()
    
            ' This call is required by the designer.
            InitializeComponent()
    
            ' Add any initialization after the InitializeComponent() call.
            pb1 = New PictureBox With {.Location = New Point(10, 10), .Size = New Size(100, 100), .BackColor = Color.Black, .SizeMode = PictureBoxSizeMode.StretchImage}
            pb2 = New PictureBox With {.Location = New Point(120, 10), .Size = New Size(100, 100), .BackColor = Color.Black, .SizeMode = PictureBoxSizeMode.StretchImage}
            btnGO = New Button With {.Location = New Point(230, 10), .Text = "GO"}
            Me.Controls.AddRange({pb1, pb2, cboSourceComponent, cboDestinationComponent, btnGO})
        End Sub
    
        ' in memory the 32 bit pixel data is arranged BGRA where B is the hi byte
        Public Enum ChannelARGB
            Blue = 0
            Green
            Red
            Alpha
        End Enum
    
        Public Shared Sub TransferOneARGBChannelFromOneBitmapToAnother(source As Bitmap,
                destination As Bitmap,
                sourceChannel As ChannelARGB,
                destinationChannel As ChannelARGB)
    
            If (source.Size <> destination.Size) Then Throw New ArgumentException()
            Dim r As New Rectangle(Point.Empty, source.Size)
            ' Lockbits prevents the .Net framework from moving the bitmap in memory, so fast memory access can occur.
            Dim bmdSource As BitmapData = source.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
            Dim bmdDestination As BitmapData = destination.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
            ' Copy the pixels out into array. Its faster than messing about with reading individual bytes
            Dim sourcePixels(4 * destination.Width * destination.Height - 1) As Byte
            Dim destinationPixels(4 * destination.Width * destination.Height - 1) As Byte
            ' Copy from the locked bitmap to an array. 
            Marshal.Copy(bmdSource.Scan0, sourcePixels, 0, sourcePixels.Length)
            Marshal.Copy(bmdDestination.Scan0, destinationPixels, 0, destinationPixels.Length)        
            For i As Integer = 0 To destinationPixels.Length - 1 Step 4
                destinationPixels(i + destinationChannel) = sourcePixels(i + sourceChannel)
            Next
            ' And copy the array back. 
            Marshal.Copy(destinationPixels, 0, bmdDestination.Scan0, destinationPixels.Length)        
            source.UnlockBits(bmdSource)
            destination.UnlockBits(bmdDestination)        
        End Sub
    
    
        Private Sub Pb_Click(sender As Object, e As EventArgs) Handles pb1.Click, pb2.Click
            Using ofd As New OpenFileDialog()
                If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                    Using bm As Bitmap = CType(Bitmap.FromFile(ofd.FileName), Bitmap)
                        Dim bm2 As New Bitmap(bm.Width, bm.Height, PixelFormat.Format32bppArgb)
                        Using g As Graphics = Graphics.FromImage(bm2)
                            g.DrawImageUnscaled(bm, Point.Empty)
                        End Using
                        CType(sender, PictureBox).Image = bm2
                    End Using
                End If
            End Using
        End Sub
    
        Private Sub btnGo_Click(sender As Object, e As EventArgs) Handles btnGO.Click
            Dim source As Bitmap = TryCast(pb1.Image, Bitmap)
            Dim destination As Bitmap = TryCast(pb2.Image, Bitmap)
            Dim sourceChannel As ChannelARGB = GetChannel(TryCast(cboSourceComponent.SelectedValue, String))
            Dim destinationChannel As ChannelARGB = GetChannel(TryCast(cboDestinationComponent.SelectedValue, String))
            If source IsNot Nothing AndAlso destination IsNot Nothing Then
                TransferOneARGBChannelFromOneBitmapToAnother(source, destination, sourceChannel, destinationChannel)
            End If
            pb2.Refresh()
        End Sub
    
        Private Shared Function GetChannel(channelChar As String) As ChannelARGB
            Select Case channelChar
                Case "B"c
                    Return ChannelARGB.Blue
                Case "G"c
                    Return ChannelARGB.Green
                Case "R"c
                    Return ChannelARGB.Red
                Case "A"c
                    Return ChannelARGB.Alpha
                Case Else
                    Throw New Exception("wtf")
            End Select
        End Function
    
    End Class

    Destructions:

    The program works with two images that are the same size. You select a source and destination channel (B, G, R or A) and it copies the pixel data from the source image to the destination image.

    Click pb1 to select source image, click pb2 to select destination image, select the source/dest channels, click go to do the copy.


    • Edited by jo0ls Wednesday, December 24, 2014 1:54 AM I have my reasons. And some raisins. The raisins are tastier.
    • Marked as answer by Carl Cai Monday, January 5, 2015 10:09 AM
    Wednesday, December 24, 2014 1:43 AM
  • Hi _Genko_. You might want to try this for VB.net

    'Scan through the pixels Y by X
    Dim iRow As Integer = 0
    Dim iColumn As Integer = 0
    Dim iPixelIDX As Integer = 0
    Dim arrBytes() As Byte = {0, 0, 0, 0}
    For iRow = 0 To bmp32bppInput.Height - 1
       For iColumn = 0 To bmp32bppInput.Width - 1
          'Set the pixel address
          iPixelIDX = iRow * bmdSource.Stride + iColumn * 4
          arrBytes(0) =    Marshal.ReadByte(bmdSource.Scan0, iPixelIDX)        'Blue
          arrBytes(1) = Marshal.ReadByte(bmdSource.Scan0, iPixelIDX + 1)    'Green
          arrBytes(2) = Marshal.ReadByte(bmdSource.Scan0, iPixelIDX + 2)    'Red
          arrBytes(3) = Marshal.ReadByte(bmdSource.Scan0, iPixelIDX + 3)    'Alpha
          If Color.FromArgb(arrBytes(3), arrBytes(2), arrBytes(1), arrBytes(0)).GetBrightness > sngThreshold Then
          bmdOutput.SetIndexedPixel(x, y, bmdn, True) 
          End If
          Next
    Next
    This is not an exact replacement for your code but rather a general idea - in vb you need to use Marshal method to read the image matrix byte.

    • Marked as answer by Carl Cai Monday, January 5, 2015 10:09 AM
    Wednesday, December 24, 2014 5:19 AM
  • Hi to all,
    i've found this code of Dan Byström.

    public static void transferOneARGBChannelFromOneBitmapToAnother(
        Bitmap source,
        Bitmap dest,
        ChannelARGB sourceChannel,
        ChannelARGB destChannel )
    {
        if ( source.Size!=dest.Size )
            throw new ArgumentException();
        Rectangle r = new Rectangle( Point.Empty, source.Size );
        BitmapData bdSrc = source.LockBits( r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb );
        BitmapData bdDst = dest.LockBits( r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb );
        unsafe
        {
            byte* bpSrc = (byte*)bdSrc.Scan0.ToPointer();
            byte* bpDst = (byte*)bdDst.Scan0.ToPointer();
            bpSrc += (int)sourceChannel;
            bpDst += (int)destChannel;
            for ( int i = r.Height * r.Width; i > 0; i-- )
            {
                *bpDst = *bpSrc;
                bpSrc += 4;
                bpDst += 4;
            }
        }
        source.UnlockBits( bdSrc );
        dest.UnlockBits( bdDst );
    }
    I've converted it in vb.net with telerik.


        Public Shared Sub transferOneARGBChannelFromOneBitmapToAnother(source As Bitmap, dest As Bitmap, sourceChannel As ChannelARGB, destChannel As ChannelARGB)
            If source.Size <> dest.Size Then
                Throw New ArgumentException()
            End If
            Dim r As New Rectangle(Point.Empty, source.Size)
            Dim bdSrc As BitmapData = source.LockBits(r, ImageLockMode.[ReadOnly], PixelFormat.Format32bppArgb)
            Dim bdDst As BitmapData = dest.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
            Dim bpSrc As Pointer(Of Byte) = CType(bdSrc.Scan0.ToPointer(), Pointer(Of Byte))
            Dim bpDst As Pointer(Of Byte) = CType(bdDst.Scan0.ToPointer(), Pointer(Of Byte))
    
            bpSrc += CInt(sourceChannel)
            bpDst += CInt(destChannel)
            For i As Integer = r.Height * r.Width To 1 Step -1
                bpDst.Target = bpSrc.Target
                bpSrc += 4
                bpDst += 4
            Next
            source.UnlockBits(bdSrc)
            dest.UnlockBits(bdDst)
        End Sub

    But the system gives me this error : System.Reflection.Pointer' has no type parameters, so can not have type arguments on this line: 

    Dim bpSrc As Pointer(Of Byte) = CType(bdSrc.Scan0.ToPointer(), Pointer(Of Byte))

    Any ideas about this issue?

    Thank in advance.
    Genko

    I don't know if this code will work either. However if code errors like that when using an online coverter like Telerik then I use one of Tangibles free demo converters to see if the conversion is the same. Sometimes it's not and the conversion will work.

    Here's the result using an older version of Tangibles C# to VB converter.

    Imports Microsoft.VisualBasic
    
    Public Shared Sub transferOneARGBChannelFromOneBitmapToAnother(ByVal source As Bitmap, ByVal dest As Bitmap, ByVal sourceChannel As ChannelARGB, ByVal destChannel As ChannelARGB)
    	If source.Size<>dest.Size Then
    		Throw New ArgumentException()
    	End If
    	Dim r As New Rectangle(Point.Empty, source.Size)
    	Dim bdSrc As BitmapData = source.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
    	Dim bdDst As BitmapData = dest.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
    'INSTANT VB TODO TASK: There is no equivalent to an 'unsafe' block in VB:
    '	unsafe
    		Byte* bpSrc = CByte(bdSrc.Scan0.ToPointer())
    		Byte* bpDst = CByte(bdDst.Scan0.ToPointer())
    		bpSrc += CInt(Fix(sourceChannel))
    		bpDst += CInt(Fix(destChannel))
    		For i As Integer = r.Height * r.Width To 1 Step -1
    			*bpDst = *bpSrc
    			bpSrc += 4
    			bpDst += 4
    		Next i
    'INSTANT VB NOTE: End of the original C# 'unsafe' block.
    	source.UnlockBits(bdSrc)
    	dest.UnlockBits(bdDst)
    End Sub


    La vida loca

    • Edited by Mr. Monkeyboy Wednesday, December 24, 2014 5:41 AM
    • Proposed as answer by Cor Ligthert Wednesday, December 24, 2014 3:23 PM
    • Unproposed as answer by Cor Ligthert Wednesday, December 24, 2014 3:24 PM
    Wednesday, December 24, 2014 5:40 AM
  • Many thanks to all.
    I will try all your code suggestions, i take the opportunity to wish Merry Christmas and an Happy new year!

    Thank again.
    Genko
    Wednesday, December 24, 2014 9:26 AM
  • Try tangible solutions, if I see all problems here is it my opinion better suited to VB.

    http://www.tangiblesoftwaresolutions.com/Product_Details/Instant_VB.html

    Beside that is the guy doing it an active member of these forums.


    Success
    Cor

    Thanks for the plug Cor, but 'unsafe' code is not converted by Instant VB.  In the latest builds, we just issue a message saying that unsafe code is not converted - we don't even attempt even a partial conversion now.  It would require the parser to switch between handling 'normal' C# syntax and 'unsafe' C# syntax - this is too troublesome, and given the very rare case of someone using unsafe C# code (in my experience we only see this rarely), it's just not worth it.

    Convert between VB, C#, C++, & Java (http://www.tangiblesoftwaresolutions.com)
    Instant C# - VB to C# Converter
    Instant VB - C# to VB Converter

    • Proposed as answer by Cor Ligthert Wednesday, December 24, 2014 3:23 PM
    • Marked as answer by Carl Cai Monday, January 5, 2015 10:09 AM
    Wednesday, December 24, 2014 3:19 PM
  • Hi,

     I see you have a lot of examples to try already but, i figured i would add one more. I took a guess at how your ChannelARGB enum was set up. I also did not know if you realized that this sub is set up to only work properly with Bitmaps that have a 32bppArgb PixelFormat so, i added some code to throw the exception if the Bitmaps are not 32bppArgb or if the Bitmaps do not have the same PixelFormat, they need to be the same.

        Public Enum ChannelARGB As Integer
            chBlue = 0
            chGreen = 1
            chRed = 2
            chAlpha = 3
        End Enum
    
        Public Shared Sub transferOneARGBChannelFromOneBitmapToAnother(ByVal source As Bitmap, ByVal dest As Bitmap, ByVal sourceChannel As ChannelARGB, ByVal destChannel As ChannelARGB)
            If source.Size <> dest.Size Or source.PixelFormat <> PixelFormat.Format32bppArgb Or source.PixelFormat <> dest.PixelFormat Then
                Throw New ArgumentException()
            End If
            Dim r As New Rectangle(Point.Empty, source.Size)
            Dim bdSrc As BitmapData = source.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
            Dim bdDst As BitmapData = dest.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
            For i As Integer = 0 To (Math.Abs(bdSrc.Stride) * bdSrc.Height) - 5 Step 4
                Marshal.WriteByte(bdDst.Scan0, i + CInt(destChannel), Marshal.ReadByte(bdSrc.Scan0, i + CInt(sourceChannel)))
            Next
            source.UnlockBits(bdSrc)
            dest.UnlockBits(bdDst)
        End Sub
    
     

     However, if you are interested in using 2 32bppArgb Bitmaps or 2 24bppRgb Bitmaps then you could check the PixelFormat of the Bitmaps and set up the loop to Step 4 bytes at a time for 32bppArgb types and Step 3 bytes at a time for 24bppRgb types. There are other PixelFormats but, i just picked 2 of the more common ones. You may need to do more testing to make sure the PixelFormats are compatible and to set the Step value correctly for the PixelFormat.

     This is un-tested but, should work. It is more to give an idea of how you could modify the way it reads/writes the bytes according to the Bitmaps PixelFormat.

        Public Shared Sub transferOneARGBChannelFromOneBitmapToAnother(ByVal source As Bitmap, ByVal dest As Bitmap, ByVal sourceChannel As ChannelARGB, ByVal destChannel As ChannelARGB)
            If source.Size <> dest.Size Or source.PixelFormat <> dest.PixelFormat Then
                Throw New ArgumentException()
            End If
    
            Dim StepVal As Integer = 4 '32bppArgb
            If source.PixelFormat = PixelFormat.Format24bppRgb Then StepVal = 3
    
            If StepVal = 3 And (sourceChannel = ChannelARGB.chAlpha Or destChannel = ChannelARGB.chAlpha) Then
                Throw New ArgumentException() 'there is only 3 bytes per color, can`t read/write a 4th Alpha byte
            End If
    
            Dim r As New Rectangle(Point.Empty, source.Size)
            Dim bdSrc As BitmapData = source.LockBits(r, ImageLockMode.ReadOnly, source.PixelFormat)
            Dim bdDst As BitmapData = dest.LockBits(r, ImageLockMode.ReadWrite, dest.PixelFormat)
    
            For i As Integer = 0 To (Math.Abs(bdSrc.Stride) * bdSrc.Height) - 1 - StepVal Step StepVal
                Marshal.WriteByte(bdDst.Scan0, i + CInt(destChannel), Marshal.ReadByte(bdSrc.Scan0, i + CInt(sourceChannel)))
            Next
            source.UnlockBits(bdSrc)
            dest.UnlockBits(bdDst)
        End Sub
    


    If you say it can`t be done then i`ll try it

    • Marked as answer by Carl Cai Monday, January 5, 2015 10:08 AM
    Wednesday, December 24, 2014 3:36 PM
  • Rather than deal with all the different pixelformats, it's simpler to just paint images into a 32bppargb bitmap - which performs a conversion to a known type. 

    In my example I used marshal.copy to copy the entire bitmap to and from a managed array. This seems wasteful, as the memory is already there and you can access it by ReadByte WriteByte.

    However, the ReadByte, WriteByte and Copy methods are all fairly slow as they are making api calls behind the scenes. So for a 100x100 image that would be 20,000 api calls compared to just the 3 for Marshal.Copy. 

    There's some cool videos on youtube by WhatsACreel showing how to do image manipulation from a C++ application with 64 bit assembly - really quick!

    • Edited by jo0ls Wednesday, December 24, 2014 8:21 PM
    Wednesday, December 24, 2014 8:03 PM
  • "Rather than deal with all the different pixelformats, it's simpler to just paint images into a 32bppargb bitmap - which performs a conversion to a known type."

     That is if you don`t want or need to preserve the original format for any reason. There is also image quality loss that can occur doing that in case quality is of any real importance in the application.

     Because i was curious about the speed, i tested both methods and it seemed to have an average of about 100ms between the two methods when tested on a 1920x1200 image with a transparency channel (32bppArgb). So, unless this function is being called repeatedly from within a loop or is being used on really large images i don`t think the difference in the speed would be very noticeable. I think it boils down to a matter of balancing out the methods for what is most important to you in your application between Speed, Memory Usage, and Image Quality.  8)


    If you say it can`t be done then i`ll try it

    Thursday, December 25, 2014 1:05 AM