Imports System.Drawing
Imports System.Windows
Imports System.Windows.Media
Imports System.Windows.Media.Imaging
Imports System.Runtime.InteropServices
Public Class VisualSnapshot
#Region " Methods "
Private Shared Function BitmapSourceToGDIImage(ByVal bitmapSource As BitmapSource) As System.Drawing.Bitmap
Dim intWidth As Integer = bitmapSource.PixelWidth
Dim intHeight As Integer = bitmapSource.PixelHeight
Dim intStride As Integer = CInt(intWidth * ((bitmapSource.Format.BitsPerPixel + 7) / 8))
'this code ensures that the stride is a multiple of 4
'learned a lot about stride here: http://www.bobpowell.net/lockingbits.htm
Dim intStrideFixer As Integer = intStride Mod 4
If intStrideFixer <> 0 Then
intStride += 4 - intStrideFixer
End If
Dim bits As Byte() = New Byte(intHeight * intStride - 1) {}
bitmapSource.CopyPixels(bits, intStride, 0)
Dim bitmap As System.Drawing.Bitmap = Nothing
'the following optimization was suggested by DrewS http://www.codeproject.com/script/profile/whos_who.asp?id=59807
'Mole used to have to call a C# function becuase unsafe code had to be called.
'With this code, Mole no longer requires a call to unsafe code.
'Oh, VB.NET pointers in ACTION!
'Thanks DrewS :-))
Dim handle As GCHandle = GCHandle.Alloc(bits, GCHandleType.Pinned)
Try
Dim ptr As IntPtr = handle.AddrOfPinnedObject()
bitmap = New System.Drawing.Bitmap(intWidth, intHeight, intStride, System.Drawing.Imaging.PixelFormat.Format32bppPArgb, ptr)
Catch ex As Exception
'this is our insurance policy
' if we get here, function will return Nothing which is OK
Finally
handle.Free()
End Try
Return bitmap
End Function
Private Shared Function CaptureVisual(ByVal target As Visual, ByVal intHeight As Integer, ByVal intWidth As Integer) As BitmapSource
'learned a lot from this article
'http://www.wiredprairie.us/downloads/GhostCursor.cs.txt
Dim objRenderBitmap As New RenderTargetBitmap(intWidth, intHeight, 96, 96, PixelFormats.Pbgra32)
Dim dv As New DrawingVisual()
Using ctx As DrawingContext = dv.RenderOpen()
ctx.DrawRectangle(New VisualBrush(target), Nothing, New Rect(New System.Windows.Point(), VisualTreeHelper.GetDescendantBounds(target).Size))
End Using
objRenderBitmap.Render(dv)
Return objRenderBitmap
End Function
Public Shared Function TakeSnapshot(ByVal target As Object) As System.Drawing.Bitmap
Dim objBitmap As System.Drawing.Bitmap = Nothing
If TypeOf target Is BitmapSource Then
'Convert.BitmapSourceToGDIImage is a function in the C# project Mole.ImageConverter
'this is required to be C# since it makes an Unsafe call
objBitmap = BitmapSourceToGDIImage(DirectCast(target, BitmapSource))
ElseIf TypeOf target Is FrameworkElement Then
Dim fe As FrameworkElement = DirectCast(target, FrameworkElement)
'I know it looks strange, but a visual can have a height of 100 and a width of .4
If 0 < fe.ActualHeight AndAlso 0 < fe.ActualWidth AndAlso TypeOf target Is Visual Then
'so, if the visual has a width of .4, casting to an integer will result in size of 0
'meaning, we won't see anything, so lets fudge so we can at least see one pixel
'Math.Ceiling can't blow up here because we have checked the values above to ensure that they are numbers.
Dim intHeight As Integer = CType(Math.Ceiling(fe.ActualHeight), Integer)
'same as width
Dim intWidth As Integer = CType(Math.Ceiling(fe.ActualWidth), Integer)
'Convert.BitmapSourceToGDIImage is a function in the C# project Mole.ImageConverter
'this is required to be C# since it makes an Unsafe call
objBitmap = BitmapSourceToGDIImage(CaptureVisual(DirectCast(target, Visual), intHeight, intWidth))
End If
End If
If objBitmap Is Nothing Then
'if the above code did not create the image, then return the unavailable image from project resources
'this typically occurs when the target has either an ActualHeight of 0 or an ActualWidth of 0
objBitmap = New System.Drawing.Bitmap(My.Resources.MoleVisualUnavailable)
Return objBitmap
Else
'this is required to prevent memory corruption
Using objBitmap
Return New System.Drawing.Bitmap(objBitmap)
End Using
End If
End Function
#End Region
End Class