If you want to convert a bitmap into a TIFF, than then you can use the code:

using (System.Drawing.Bitmap sourceBitmap = new System.Drawing.Bitmap(@"C:\Temp\Source.bmp"))
{
    string outputFileName = @"C:\Temp\Destination.tiff";
    if (System.IO.File.Exists(outputFileName))
    {
        System.IO.File.Delete(outputFileName);
    }
    sourceBitmap.Save(outputFileName, System.Drawing.Imaging.ImageFormat.Tiff);
}

If you want to convert a bitmap to a 1bpp monochrome TIFF in C#, I found 4 options:

1. http://www.bobpowell.net/onebit.htm ( no P-Invoke required, but not as fast as other methods )

2. http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/9757dc94-11f3-4b30-a85c-cb9145eba12d (only when you are using WPF)

3. http://www.news2news.com/vfp/?example=493&ver=vcs&PHPSESSID=35494364de9987dfd2a9f3fe18f17565 (less code then option 4, but did not have time to investigate)

4. http://www.wischik.com/lu/programmer/1bpp.html

 

For the last options I cleanup some code:

using (System.Drawing.Bitmap sourceBitmap = new System.Drawing.Bitmap(@"C:\Temp\Source.bmp"))
{
    using (Bitmap destinationBitmap = sourceBitmap.ConvertToMonochromeTiff())
    {
        string outputFileName = @"C:\Temp\Destination.tiff";
        if (System.IO.File.Exists(outputFileName))
        {
            System.IO.File.Delete(outputFileName);
        }
        destinationBitmap.Save(outputFileName);
    }
}

Where the ConvertToMonochromeTiff is an extension method to the System.Drawing.Bitmap type:

 

using System;
using System.Drawing;

namespace Rli.Extensions
{
    /// <summary>
    /// Contains extensions methods to the System.Drawing.Bitmap type.
    /// </summary>
    public static class BitmapExtensions
    {
        #region P-Invoke
        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr hwnd);

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern int DeleteDC(IntPtr hdc);

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern int BitBlt(IntPtr hdcDst, int xDst, int yDst, int w, int h, IntPtr hdcSrc, int xSrc, int ySrc, int rop);
        static int SRCCOPY = 0x00CC0020;

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        static extern IntPtr CreateDIBSection(IntPtr hdc, ref BITMAPINFO bmi, uint Usage, out IntPtr bits, IntPtr hSection, uint dwOffset);
        static uint BI_RGB = 0;
        static uint DIB_RGB_COLORS = 0;
        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            public uint biSize;
            public int biWidth, biHeight;
            public short biPlanes, biBitCount;
            public uint biCompression, biSizeImage;
            public int biXPelsPerMeter, biYPelsPerMeter;
            public uint biClrUsed, biClrImportant;
            [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 256)]
            public uint[] cols;
        }

        static uint MAKERGB(int r, int g, int b)
        {
            return ((uint)(b & 255)) | ((uint)((r & 255) << 8)) | ((uint)((g & 255) << 16));
        }
        #endregion

        /// <summary>
        /// Converts the given bitmap to a monochrome tiff bitmap.
        /// </summary>
        /// <param name="sourceBitmap">The source bitmap.</param>
        /// <returns></returns>
        public static Bitmap ConvertToMonochromeTiff(this Bitmap sourceBitmap)
        {
            Bitmap destination = null;
            int bpp = 1;                                                            // Amount of bits to use in pallet, for monochrome use 1 bit (0 = black, 1 = white).
            uint ncols = (uint)1 << bpp;                                            // Use 2 colours (black and white) for monochrome tiff.
            int width = sourceBitmap.Width;
            int height = sourceBitmap.Height;
            uint size = (uint)(((width + 7) & 0xFFFFFFF8) * height / 8);
            uint[] pallet = new uint[256];                                          // Pallet has a fixed size 256, even when we are using fewer colours.
            pallet[0] = MAKERGB(0, 0, 0);                                           // Create black pixel.
            pallet[1] = MAKERGB(255, 255, 255);                                     // Create white pixel.
            BITMAPINFO bmi = new BITMAPINFO()                                       // Create unmanaged monochrome bitmapinfo.
            {
                biSize = 40,                                                        // The size of the BITMAPHEADERINFO struct.
                biWidth = width,
                biHeight = height,
                biPlanes = 1,
                biBitCount = (short)bpp,                                            // Amount of bits per pixel (1 for monochrome).
                biCompression = BI_RGB,
                biSizeImage = size,
                biXPelsPerMeter = 1000000,
                biYPelsPerMeter = 1000000,
                biClrUsed = ncols,
                biClrImportant = ncols,
                cols = pallet
            };

            IntPtr sourceHbitmap = sourceBitmap.GetHbitmap();                       // Convert bitmap to unmanaged HBitmap.
            IntPtr bits0;                                                           // Pointer to the raw bits that make up the bitmap.
            IntPtr destinationHbitmap = CreateDIBSection
            (
                IntPtr.Zero,
                ref bmi,
                DIB_RGB_COLORS,
                out bits0,
                IntPtr.Zero,
                0
            );                                                                      // Create the indexed bitmap.
            IntPtr screenDC = GetDC(IntPtr.Zero);                                   // Obtain the DC (= GDI equivalent of "Graphics" in GDI+) for the screen.
            IntPtr sourceDC = CreateCompatibleDC(screenDC);                         // Create a DC for the original hbitmap.
            SelectObject(sourceDC, sourceHbitmap);
            IntPtr destinationDC = CreateCompatibleDC(screenDC);                    // Create a DC for the monochrome hbitmap.
            SelectObject(destinationDC, destinationHbitmap);
            BitBlt(destinationDC, 0, 0, width, height, sourceDC, 0, 0, SRCCOPY);    // Use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap.
            destination = System.Drawing.Bitmap.FromHbitmap(destinationHbitmap);    // Convert this monochrome hbitmap back into a Bitmap.

            // Cleanup.
            DeleteDC(sourceDC);
            DeleteDC(destinationDC);
            ReleaseDC(IntPtr.Zero, screenDC);
            DeleteObject(sourceHbitmap);
            DeleteObject(destinationHbitmap);

            return destination;
        }
    }
}

3 Comments

  1. Hi,

    Very good article. i learnt lot of things. I am using .Net Compact Framework 2.0/3.0 and getting error at fallowing line as OutOfMemoryException.
    destination = System.Drawing.Bitmap.FromHbitmap(destinationHbitmap); // Convert this monochrome hbitmap back into a Bitmap.

    Can you please help me to solve this ?

    Thanks,
    Siddeswara GM

    Siddeswara GM
  2. Nice post! I did find that #2 above CAN be used within a C# console application if you include the WPF libraries (PresentationCore.dll and System.Xaml.dll I think). Then code like this can be used to generate pretty decent 1bpp TIF files:

    TiffBitmapEncoder encoder = new TiffBitmapEncoder();
    encoder.Compression = TiffCompressOption.Ccitt4;

    encoder.Frames.Add(BitmapFrame.Create(imgStream));

    Mark

    Mark Quigg

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.