Yeah, 1bpp tiffs are annoying and so far as I've found, the managed way is
great for slicing out tif frames but doesn't cut it completely on depth
conversions.
I got this code from a great newsgroup guy named "BMan" about a year ago. I
made adjustments so that I could set a threshold and let the users despeckle
or allow in more "noise" if needed ... at the moment I can't find that
enhanced code (it was for a previous client). See if the following exact
conversion code helps and over the weekend I'll see if I can track down the
other files.
If you really need CCIT4 compression, as most folks are doing fax image work
with 1bpp tifs, you can change the LZW to CCTI4 and it still works fine.
1) make a class, call it Win32API and paste in the following:
Imports System.Runtime. InteropServices
Public Class win32api
<DllImport("KER NEL32.DLL", EntryPoint:="Rt lMoveMemory", _
SetLastError:=T rue, CharSet:=CharSe t.Auto, _
ExactSpelling:= True, _
CallingConventi on:=CallingConv ention.StdCall) > _
Public Shared Sub CopyArrayTo(<[In](), MarshalAs(Unman agedType.I4)> ByVal
hpvDest As Int32, <[In](), Out()> ByVal hpvSource() As Byte, ByVal cbCopy As
Integer)
' Leave function empty
End Sub
' GDI32 Constant Definitions
Public Const BI_RGB = 0&
Public Const DIB_PAL_COLORS = 1
' GDI32 Structure Definitions'
<StructLayout(L ayoutKind.Seque ntial)> 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(L ayoutKind.Seque ntial)> Public Structure GDI32BITMAPINFO HEADER
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 CreateCompatibl eDC Lib "gdi32" Alias _
"CreateCompatib leDC" (ByVal hdc As Integer) As Integer
Public Declare Function SelectObject Lib "gdi32" Alias "SelectObje ct" _
(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 CreateDIBSectio n Lib "gdi32" Alias _
"CreateDIBSecti on" (ByVal hDC As Integer, ByRef pBitmapInfo As _
GDI32BITMAPINFO HEADER, 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 _
GDI32BITMAPINFO HEADER, ByVal wUsage As Integer) As Integer
Public Declare Function DeleteObject Lib "gdi32" Alias "DeleteObje ct" _
(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
End Class
2) make a class, call it TifConverter and paste in this code:
Imports System.Drawing
Imports System.Drawing. Imaging
Imports System.IO
Imports System.Runtime. InteropServices
Public Class TiffConverter
Public Function MakeCCIT4Exact( ByVal value As Image) As Image
'bman code!
Dim dotNetBitmap As Bitmap
Dim XDPI, YDPI As Single
Dim srcBitmapInfo As win32api.GDI32B ITMAP
Dim hSrcBitmap As IntPtr = IntPtr.Zero
Dim pSrcBitmapInfo As IntPtr = IntPtr.Zero
Dim hDstMemDC As IntPtr = IntPtr.op_Expli cit(win32api.Cr eateCompatibleD C(0))
Dim DestDIBMIH As win32api.GDI32B ITMAPINFOHEADER
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
Try
' Load our Image into our GDI+ bitmap object (1BPP)
dotNetBitmap = CType(value, Bitmap)
XDPI = dotNetBitmap.Ho rizontalResolut ion
YDPI = dotNetBitmap.Ve rticalResolutio n
Dim xrez As Single = 0
Dim rezratio As Single = 1
If XDPI = YDPI Then
xrez = XDPI
rezratio = 1
ElseIf XDPI > YDPI Then
xrez = XDPI
rezratio = XDPI / YDPI
Else
xrez = YDPI
rezratio = XDPI / YDPI
End If
dim b As New Bitmap(dotNetBi tmap, New Size(dotNetBitm ap.Width,
dotNetBitmap.He ight * rezratio))
dotNetBitmap = b
hSrcBitmap = dotNetBitmap.Ge tHbitmap() ' 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.AllocCo TaskMem(Marshal .SizeOf(srcBitm apInfo))
' Call GetObject and use out buffer we just allocated as to store
' the returned struct
win32api.GetObj ect(hSrcBitmap. ToInt32, Marshal.SizeOf( srcBitmapInfo), _
pSrcBitmapInfo. ToInt32)
' Marshall the data in our buffer back to our GDI32BITMAP structure
srcBitmapInfo = CType(Marshal.P trToStructure(p SrcBitmapInfo, _
GetType(win32ap i.GDI32BITMAP)) , win32api.GDI32B ITMAP)
' Release the memory we allocated for the buffer to the GetObject
' Call
If IntPtr.op_Inequ ality(pSrcBitma pInfo, IntPtr.Zero) Then _
Marshal.FreeCoT askMem(pSrcBitm apInfo)
' 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.b mWidth
.biHeight = srcBitmapInfo.b mHeight 'CInt(srcBitmap Info.bmHeight * (XDPI /
YDPI))
.biPlanes = srcBitmapInfo.b mPlanes
.biBitCount = srcBitmapInfo.b mPlanes ' 1 bpp * srcBitmapInfo.b mPlanes()
.biCompression = win32api.BI_RGB
End With
' Create our DIBitmap
hDestDIBitmap = _
IntPtr.op_Expli cit(win32api.Cr eateDIBSection( hDstMemDC.ToInt 32, DestDIBMIH,
0, _
pDestDIBits, 0, 0))
' Now Select our DIBitmap into its DC
hDstOldBitmap = IntPtr.op_Expli cit(win32api.Se lectObject(hDst MemDC.ToInt32,
_
hDestDIBitmap.T oInt32))
' 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, because I know there's no way it
' could(be)
RCode = win32api.GetDIB its(hDstMemDC.T oInt32, hSrcBitmap.ToIn t32, 0, _
srcBitmapInfo.b mHeight, pDestDIBits, DestDIBMIH, win32api.DIB_PA L_COLORS)
' Wait for GDI to finish creating and drawing out DIBitmap
win32api.GdiFlu sh()
' All Done with the conversion, create a new Bitmap using our DIBitmap()
dotNetBitmap = Bitmap.FromHbit map(hDestDIBitm ap)
' Just because I'm being a pain, make the dpi of the new bitmap
' This might matter to some image viewers
dotNetBitmap.Se tResolution(xre z, xrez)
' And finally save it before we cleanup and leave. Should be 1Bpp,
'UseCompression flag determines if compression is to be used
Dim m As New System.IO.Memor yStream
If UseCompression Then
Dim CodecInfo As Imaging.ImageCo decInfo = _
GetEncoderInfo( "image/tiff")
Dim ImgEncoder As New Imaging.Encoder (CodecInfo.Clsi d)
Dim EncParms As New Imaging.Encoder Parameters(1)
EncParms.Param( 0) = New _
Imaging.Encoder Parameter(Imagi ng.Encoder.Comp ression, _
Imaging.Encoder Value.Compressi onLZW)
dotNetBitmap.Sa ve(m, CodecInfo, EncParms)
Return Image.FromStrea m(m)
Else
dotNetBitmap.Sa ve(m, Imaging.ImageFo rmat.Tiff)
Return Image.FromStrea m(m)
End If
Finally
' Clean up GDI resources before leaving
dotNetBitmap.Di spose()
If IntPtr.op_Inequ ality(hSrcBitma p, IntPtr.Zero) Then _
win32api.Delete Object(hSrcBitm ap.ToInt32)
If IntPtr.op_Inequ ality(hDestDIBi tmap, IntPtr.Zero) Then _
win32api.Delete Object(win32api .SelectObject(h DstMemDC.ToInt3 2,
hDstOldBitmap.T oInt32))
If IntPtr.op_Inequ ality(hDstMemDC , IntPtr.Zero) Then _
win32api.Delete DC(hDstMemDC.To Int32)
End Try
End Function
Private Function GetEncoderInfo( ByVal mimeType As String) As
System.Drawing. Imaging.ImageCo decInfo
Dim j As Integer
Dim encoders As Drawing.Imaging .ImageCodecInfo ()
encoders = Drawing.Imaging .ImageCodecInfo .GetImageEncode rs()
For j = 0 To encoders.Length
If encoders(j).Mim eType = mimeType Then
Return encoders(j)
End If
Next
Return Nothing
End Function
End Class
3) make a form, add two buttons and a picturebox. Use one button to load an
image (a jpg or bmp) into the picturebox. Make the other buttom do this:
Dim o As New TiffConverter
Dim i As Image = o.MakeCCIT4Exac t(PictureBox1.I mage)
i.Save("c:\test area\test.tif")
Hope it helps
Robert Smith
Kirkland, WA
www.smithvoice.com
"Maurice Mertens" <hm*****@nospam .nospam> wrote in message
news:Xn******** *************** *****@194.109.1 33.133...
Hello,
I'm having troubles with saving a tiff-file with a certain compression
and colordepth. This is the code I use: