Here's a real "rough" sample I wrote as an example for someone a while back
to change a B/W 32bpp image to 1bpp using a DIB. You will see it's pretty
easy. Looking back at this example, I would change alot of the interop
stuff for GDI to handle intptrs a little cleaner, but in all it should work
for you. I'll assume you only need to go up to the part where I load this
image bits (GetDiBits) and really don't need the rest, but it should give
you something to think about.
Hope this helps,
Barry
Imports System.Drawing
Imports System.Runtime.InteropServices
Module BPPReduction
'
' GDI32 Constant Definitions
'
Public Const BI_RGB = 0&
Public Const DIB_PAL_COLORS = 1
'
' GDI32 Structure Definitions
'
<StructLayout(LayoutKind.Sequential)> Public Structure GDI32BITMAP
Public bmType As Integer
Public bmWidth As Integer
Public bmHeight As Integer
Public bmWidthBytes As Integer
Public bmPlanes As Short
Public bmBitsPixel As Short
Public bmBits As Integer
End Structure
<StructLayout(LayoutKind.Sequential)> Public Structure
GDI32BITMAPINFOHEADER
Public biSize As Integer
Public biWidth As Integer
Public biHeight As Integer
Public biPlanes As Short
Public biBitCount As Short
Public biCompression As Integer
Public biSizeImage As Integer
Public biXPelsPerMeter As Integer
Public biYPelsPerMeter As Integer
Public biClrUsed As Integer
Public biClrImportant As Integer
End Structure
'
' GDI32 DLL Entry Point Declares
'
Public Declare Function CreateCompatibleDC Lib "gdi32" Alias
"CreateCompatibleDC" (ByVal hdc As Integer) As Integer
Public Declare Function SelectObject Lib "gdi32" Alias "SelectObject"
(ByVal hdc As Integer, ByVal hObject As Integer) As Integer
Public Declare Function GetObject Lib "gdi32" Alias "GetObjectA" (ByVal
hObject As Integer, ByVal nCount As Integer, ByVal lpObject As Integer)
As Integer
Public Declare Function CreateDIBSection Lib "gdi32" Alias
"CreateDIBSection" (ByVal hDC As Integer, ByRef pBitmapInfo As
GDI32BITMAPINFOHEADER, ByVal un As Integer, ByRef lplpVoid As Integer,
ByVal handle As Integer, ByVal dw As Integer) As Integer
Public Declare Function GetDIBits Lib "gdi32" (ByVal aHDC As Integer,
ByVal hBitmap As Integer, ByVal nStartScan As Integer, ByVal nNumScans
As Integer, ByVal lpBits As Integer, ByRef lpBI As
GDI32BITMAPINFOHEADER, ByVal wUsage As Integer) As Integer
Public Declare Function DeleteObject Lib "gdi32" Alias "DeleteObject"
(ByVal hObject As Integer) As Integer
Public Declare Function DeleteDC Lib "gdi32" Alias "DeleteDC" (ByVal hdc
As Integer) As Integer
Public Declare Function GdiFlush Lib "gdi32" Alias "GdiFlush" () As
Integer
Sub Main()
Dim dotNetBitmap As Bitmap
Dim XDPI, YDPI As Integer
Dim srcBitmapInfo As GDI32BITMAP
Dim hSrcBitmap As IntPtr = IntPtr.Zero
Dim pSrcBitmapInfo As IntPtr = IntPtr.Zero
Dim hDstMemDC As IntPtr = IntPtr.op_Explicit(CreateCompatibleDC(0))
Dim DestDIBMIH As GDI32BITMAPINFOHEADER
Dim hDestDIBitmap As IntPtr = IntPtr.Zero
Dim hDstOldBitmap As IntPtr = IntPtr.Zero
Dim pDestDIBits As Integer = 0
Dim RCode As Integer
Dim UseCompression As Boolean = True
' Load our Image into our GDI+ bitmap object (1BPP)
dotNetBitmap = Bitmap.FromFile("C:\Sample.tif")
' Creates a Windows HBITMAP from the image. You must de-allocate the
HBITMAP with Windows.DeleteObject(handle).
hSrcBitmap = dotNetBitmap.GetHbitmap() ' hSrcBitmap is now 32bpp
thanks to GDI+
' There most likey should be some protection afforded to the
following
' set of statements to ensure the memory operations don't cause a
GPF in the app
' Allocate enough memory to hold a GDI32 structure and use that as a
buffer area for the call to GetObject
pSrcBitmapInfo =
Marshal.AllocCoTaskMem(Marshal.SizeOf(srcBitmapInfo))
' Call GetObject and use out buffer we just allocated as to store
the returned struct
GetObject(hSrcBitmap.ToInt32, Marshal.SizeOf(srcBitmapInfo),
pSrcBitmapInfo.ToInt32)
' Marshall the data in our buffer back to our GDI32BITMAP structure
srcBitmapInfo = CType(Marshal.PtrToStructure(pSrcBitmapInfo,
GetType(GDI32BITMAP)), GDI32BITMAP)
' Release the memory we allocated for the buffer to the GetObject
Call
If IntPtr.op_Inequality(pSrcBitmapInfo, IntPtr.Zero) Then
Marshal.FreeCoTaskMem(pSrcBitmapInfo)
' Now we have the dimensions of the HBITMAP underlying our dot net
Bitmap Object and can use them to set up our DIB
'
' BTW, if you don't believe me, look at srcBitmapInfo in the
debugger and you'll see out image is now 32bpp
With DestDIBMIH
.biSize = Marshal.SizeOf(DestDIBMIH)
.biWidth = srcBitmapInfo.bmWidth
.biHeight = srcBitmapInfo.bmHeight
.biPlanes = srcBitmapInfo.bmPlanes
.biBitCount = srcBitmapInfo.bmPlanes ' 1 bpp *
srcBitmapInfo.bmPlanes
.biCompression = BI_RGB
End With
' Create our DIBitmap
hDestDIBitmap =
IntPtr.op_Explicit(CreateDIBSection(hDstMemDC.ToInt32, DestDIBMIH, 0,
pDestDIBits, 0, 0))
' Now Select our DIBitmap into its DC
hDstOldBitmap = IntPtr.op_Explicit(SelectObject(hDstMemDC.ToInt32,
hDestDIBitmap.ToInt32))
' Copy the bits from out dotNet Bitmaps objects HBITMAP to out
DIBitmap.
'
' ** IMPORTANT NOTE **
' ** THE SOURCE HBITMAP CANNOT BE SELECTED INTO ANY DC
' ** I don't check in this sample, because I know there's no way it
could be
'
RCode = GetDIBits(hDstMemDC.ToInt32, hSrcBitmap.ToInt32, 0,
srcBitmapInfo.bmHeight, pDestDIBits, DestDIBMIH, DIB_PAL_COLORS)
' Wait for GDI to finish creating and drawing out DIBitmap
GdiFlush()
' Save the resolution of the old image, so we can restore it on the
new one
XDPI = dotNetBitmap.HorizontalResolution
YDPI = dotNetBitmap.VerticalResolution
' All Done with the conversion, create a new Bitmap using out
DIBitmap
dotNetBitmap = Bitmap.FromHbitmap(hDestDIBitmap)
' Just because I'm being a pain, make the dpi of the new bitmap the
same as the old one (300x300 DPI), not the resolution of out MEMDC
(96x96 DPI?).
' This might matter to some image viewers