CameraCapure JPG to PCX file to PrintAbout (MP2000)

This question is answered

hello!
How I can convert a Bitmap to a PCX file to print in PrintAbout?

This process should be done in a 7505 Ikon with "Windows Mobile 6" an compact framework 3.5.

Thanks!

Verified Answer
  • I found a sample that works! convert a bitmap to a 1 or 8 bit per pixel. The problem now is that the converted image is mostly black, beacose if the pixel is white, then the converted pixel is white, but if the pixel have some color (some rgb color) then the resultant pixel is black so the converted image have more black pixel that white.

    I guess somewhere in the code should be able to change this behavior so that not all pixels with any color will become black.

    The sample:

    namespace ImageEditor

    {

       public class BitmapFile

       {

           static int SRCCOPY = 0x00CC0020;

           static uint BI_RGB = 0;

           static uint DIB_RGB_COLORS = 0;

           /// <summary>

           /// Copies a bitmap into a 1bpp/8bpp bitmap of the same dimensions, fast

           /// </summary>

           /// <param name="b">original bitmap</param>

           /// <param name="bpp">1 or 8, target bpp</param>

           /// <returns>a 1bpp copy of the bitmap</returns>

           public static System.Drawing.Bitmap CopyToBpp(System.Drawing.Bitmap b, int bpp)

           {

               if (bpp != 1 && bpp != 8) throw new System.ArgumentException("1 or 8", "bpp");

               // Plan: built into Windows GDI is the ability to convert

               // bitmaps from one format to another. Most of the time, this

               // job is actually done by the graphics hardware accelerator card

               // and so is extremely fast. The rest of the time, the job is done by

               // very fast native code.

               // We will call into this GDI functionality from C#. Our plan:

               // (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed)

               // (2) Create a GDI monochrome hbitmap

               // (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above)

               // (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed)

               int w = b.Width, h = b.Height;

               IntPtr hbm = b.GetHbitmap(); // this is step (1)

               //

               // Step (2): create the monochrome bitmap.

               // "BITMAPINFO" is an interop-struct which we define below.

               // In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs

               BITMAPINFO bmi = new BITMAPINFO();

               bmi.biSize = 40;  // the size of the BITMAPHEADERINFO struct

               bmi.biWidth = w;

               bmi.biHeight = h;

               bmi.biPlanes = 1; // "planes" are confusing. We always use just 1. Read MSDN for more info.

               bmi.biBitCount = (short)bpp; // ie. 1bpp or 8bpp

               bmi.biCompression = BI_RGB; // ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes

               bmi.biSizeImage = (uint)(((w + 7) & 0xFFFFFFF8) * h / 8);

               bmi.biXPelsPerMeter = 1000000; // not really important

               bmi.biYPelsPerMeter = 1000000; // not really important

               // Now for the colour table.

               uint ncols = (uint)1 << bpp; // 2 colours for 1bpp; 256 colours for 8bpp

               bmi.biClrUsed = ncols;

               bmi.biClrImportant = ncols;

               bmi.cols = new uint[256]; // The structure always has fixed size 256, even if we end up using fewer colours

               if (bpp == 1) { bmi.cols[0] = MAKERGB(0, 0, 0); bmi.cols[1] = MAKERGB(255, 255, 255); }

               else { for (int i = 0; i < ncols; i++) bmi.colsIdea = MAKERGB(i, i, i); }

               // For 8bpp we've created an palette with just greyscale colours.

               // You can set up any palette you want here. Here are some possibilities:

               // greyscale: for (int i=0; i<256; i++) bmi.colsIdea=MAKERGB(i,i,i);

               // rainbow: bmi.biClrUsed=216; bmi.biClrImportant=216; int[] colv=new intDevil{0,51,102,153,204,255};

               //          for (int i=0; i<216; i++) bmi.colsIdea=MAKERGB(colv[i/36],colv[(i/6)%6],colv[i%6]);

               // optimal: a difficult topic: en.wikipedia.org/.../Color_quantization

               //

               // Now create the indexed bitmap "hbm0"

               IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.

               IntPtr hbm0 = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);

               //

               // Step (3): use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap

               // GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC".

               IntPtr sdc = GetDC(IntPtr.Zero);       // First we obtain the DC for the screen

               // Next, create a DC for the original hbitmap

               IntPtr hdc = CreateCompatibleDC(sdc);

               SelectObject(hdc, hbm);

               // and create a DC for the monochrome hbitmap

               IntPtr hdc0 = CreateCompatibleDC(sdc);

               SelectObject(hdc0, hbm0);

               // Now we can do the BitBlt:

               BitBlt(hdc0, 0, 0, w, h, hdc, 0, 0, SRCCOPY);

               DeleteDC(hdc);

               DeleteDC(hdc0);

               ReleaseDC(IntPtr.Zero, sdc);

               DeleteObject(hbm);

               GC.Collect();

               // Step (4): convert this monochrome hbitmap back into a Bitmap:

               System.Drawing.Bitmap b0 = System.Drawing.Bitmap.FromHbitmap(hbm0);

               //

               // Finally some cleanup.

               DeleteObject(hbm0);

               //

               return b0;

           }

           [DllImport( "coredll.dll" )]   public static extern bool DeleteObject( IntPtr hObject );  

           [DllImport( "coredll.dll" )]   public static extern int InvalidateRect( IntPtr hwnd, IntPtr rect, int bErase );  

           [DllImport( "coredll.dll" )]   public static extern IntPtr GetDC( IntPtr hwnd );  

           [DllImport( "coredll.dll" )]   public static extern IntPtr CreateCompatibleDC( IntPtr hdc );  

           [DllImport( "coredll.dll" )]   public static extern int ReleaseDC( IntPtr hwnd, IntPtr hdc );  

           [DllImport( "coredll.dll" )]   public static extern int DeleteDC( IntPtr hdc );  

           [DllImport( "coredll.dll" )]   public static extern IntPtr SelectObject( IntPtr hdc, IntPtr hgdiobj );  

           [DllImport( "coredll.dll" )]   public static extern int BitBlt( IntPtr hdcDst, int xDst, int yDst, int w, int h, IntPtr hdcSrc, int xSrc, int ySrc, int rop );  

           [DllImport( "coredll.dll" )]   private static extern IntPtr LocalAlloc( uint flags, uint cb );  

           [DllImport( "coredll.dll" )]   private static extern IntPtr LocalFree( IntPtr hMem );  

           [DllImport( "coredll.dll" )]   private static extern IntPtr CreateDIBSection( IntPtr hdc, IntPtr hdr, uint colors, ref IntPtr pBits, IntPtr hFile, uint offset );

           [DllImport("coredll.dll")]     private static extern IntPtr CreateDIBSection(IntPtr hdc, ref BITMAPINFO bmi, uint Usage, out IntPtr bits, IntPtr hSection, uint dwOffset);

           [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));

           }

       }

    }

All Replies
  • Have a look at this:

    http://www.codeproject.com/KB/graphics/cximage.aspx

  • look nice, but my project is c # and CxImage is in C++ :s. I read that there is a wrapper to use the libraries .. but I can not find it.

    There another solution?, Since it is typical use for PCX images in this type of printers?

  • ..and CxImage is a Desktop application! and i need WM6 based

  • Hello,

    Take a look at the below sample code:

     

    public void printTo(Stream stream) throws IOException
          {
             //            DiagsPanelShow();
             //            tbDiags.Text = "Converting BMP to PCX ..." + CR_LF;
             int bytesPerLine = (width+7)/8;
             int xEnd = width-1;
             int yEnd = height-1;
            
             byte[] header =
             {
                0x0A,           // "PCX File"
                0x05,           // "Version 5"
                0x01,           // RLE Encoding
                0x01,           // 1 bit per pixel
                0x00, 0x00,     // XStart at 0
                0x00, 0x00,     // YStart at 0
                (byte)(xEnd&0xFF), (byte)((xEnd>>8) & 0xFF),      // Xend
                (byte)(yEnd&0xFF), (byte)((yEnd>>8) & 0xFF),      // Yend
                (byte)(xEnd&0xFF), (byte)((xEnd>>8) & 0xFF),      // Xend
                (byte)(yEnd&0xFF), (byte)((yEnd>>8) & 0xFF),      // Yend
                0x0F, 0x0F, 0x0F, 0x0E, 0x0E, 0x0E, 0x0D, 0x0D, 0x0D, 0x0C, 0x0C, 0x0C,   //48-byte EGA palette info
                0x0B, 0x0B, 0x0B, 0x0A, 0x0A, 0x0A, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 
                0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 
                0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 
                0x00,          // Reserved byte, always x00
                0x01,          // 1 bit plane
                (byte)(bytesPerLine&0xFF), (byte)((bytesPerLine>>8) & 0xFF),      // Bytes per scan line: (XEnd - XStart,  1) / 8
                0x01, 0x00,    // Palette type: 1 means color or monochrome
                0x00, 0x00,    // Horizontal screen size (not used)
                0x00, 0x00     // Vertical screen size (not used)
             };    
      
             stream.writeBytes(header);      // Write most of header data.
             stream.writeBytes(new byte[54]); // pad the 128-byte header
      
             byte rowIn[] = new byte[width*3];
             int []bits = {128, 64, 32, 16, 8, 4, 2, 1};
            
             byte[] bytes = new byte[2];
             int last = 0;
             int count = 0;
             for(int y=0; y < height; y++)
             {
                getPixelRow(rowIn,y);
                for (int x=0; x < width; x+=8)
                {
                   int n = x+8;
                   if (n > width) n = width;
                   int b = 0;
                   for (int j=x; j < n; j++)
                      if (rowIn[j+j+j] != 0)
                         b |= bits[j-x];
                   if (last==b && count < 63)
                      count++;
                   else
                   {
                      if (count > 0)
                      {
                         bytes[0] = (byte)(count | 0xC0);
                         bytes[1] = (byte)last;
                         stream.writeBytes(bytes,0,2);
                      }
                      last = b;
                      count = 1;
                   }
                }
                if (count > 0)
                {
                   bytes[0] = (byte)(count | 0xC0);
                   bytes[1] = (byte)last;
                   stream.writeBytes(bytes,0,2);              
                   count = 0;
                   last = 0;
                }
             }
          }

     

  • I saw this example but there is a function "getPixelRow" that is not implemented. Furthermore it is not clear where you get the image data. The input parameter of the "printto (Stream stream)" seems to be used to return the image PCX, but no sign of where you get the bitmap to be processed.

    It is quite incomplete example. : S

  • Another example in VB

     

    Private Function GetControlImageArrayBlackAndWhite(ByVal i_oControl As Control, ByVal i_nMaxRed As Integer, ByVal i_nMaxGreen As Integer, ByVal i_nMaxBlue As Integer) As Byte()
            '1. The images are stored upside down. The first line in the file is the bottom line of the image and the
            '   last line in the file is the first line of the image. This means that if you are computing an image with
            '   resolution of 256 x 192, to be displayed in a system where the (0, 0) pixel is the top left, then the
            '   first pixel to be computed and written to the BMP file is for pixel (191, 0).
            '
            '2. In 24-bit images RGB values are stored in Blue Green Red order.
            '
            '3. Bitmap images if not a multiple of for will contain garbage data and to speed up coding
            '   this function limits the width to a multiple of 4

            Dim oBitmapData() As Byte
            Dim oSigData() As Byte
            Dim nPixsPerRow As Long
            Dim nNumberofRows As Long
            Dim nRow As Integer
            Dim nPixel As Integer
            Dim nIndex As Integer
            Dim bPixelRed As Byte
            Dim bPixelGreen As Byte
            Dim bPixelBlue As Byte
            Dim nFirstPixelColorLocation As Long

            nPixsPerRow = i_oControl.Width
            If ((nPixsPerRow * 3) Mod 4) > 0 Then  'times 3 because of rgb
                MsgBox("Please make your signiture control width is a multiple of 4")
                Return New Byte() {}
            End If

            nNumberofRows = i_oControl.Height
            oBitmapData = ImageEditor.BitmapFile.GetControlBitmapArray(i_oControl, 24)
            ReDim oSigData((nPixsPerRow * nNumberofRows) - 1)

            nIndex = 0
            For nRow = nNumberofRows - 1 To 0 Step -1
                For nPixel = 0 To nPixsPerRow - 1
                    nFirstPixelColorLocation = (nRow * nPixsPerRow * 3) + (nPixel * 3)
                    bPixelBlue = oBitmapData(nFirstPixelColorLocation)
                    bPixelGreen = oBitmapData(nFirstPixelColorLocation + 1)
                    bPixelRed = oBitmapData(nFirstPixelColorLocation + 2)
                    If bPixelBlue <= i_nMaxBlue And bPixelGreen <= i_nMaxGreen And bPixelRed <= i_nMaxRed Then
                        oSigData(nIndex) = 1
                    Else
                        oSigData(nIndex) = 0
                    End If
                    nIndex += 1
                Next
            Next

            Return oSigData

        End Function
        Public Sub PrintControl(ByVal i_oControl As Control)
            PrintControl(i_oControl, 0, 0, 0)
        End Sub

        Public Sub PrintControl(ByVal i_oControl As Control, ByVal i_nMaxRed As Integer, ByVal i_nMaxGreen As Integer, ByVal i_nMaxBlue As Integer)
            Dim bSigniture() As Byte
            Dim bOutput() As Byte

            Dim nWidthBytes As Integer = (i_oControl.Width / 8)
            Dim nHeightLines As Integer = i_oControl.Height
            Dim nNumberOfdividers As Integer = Math.Ceiling(CType(nWidthBytes * nHeightLines, Double) / 128.0)

            Debug.Assert(i_oControl.Width Mod 8 = 0, "Only supports images with width divisible by 8")
            Debug.Assert(i_oControl.Width / 8 <= 104, "Only supports images with width/8 <= 104")  'for MtP300 the value is 72
            Debug.Assert(i_oControl.Height <= 255, "Only supports images with height <= 255")

            bSigniture = GetControlImageArrayBlackAndWhite(i_oControl, i_nMaxRed, i_nMaxGreen, i_nMaxBlue)
            ReDim bOutput(4 + (nWidthBytes * nHeightLines) + nNumberOfdividers - 1)

            bOutput(0) = &H1B
            bOutput(1) = &H76
            bOutput(2) = nHeightLines
            bOutput(3) = nWidthBytes

            Dim nCurrentPixel As Integer = 0
            Dim i As Integer = 4
            Dim nAmoutToProcessBytes As Byte
            Dim nTotalBytes As Integer = bSigniture.Length / 8
            Dim nAmountRemainingBytes As Integer = nTotalBytes

            Do While nAmountRemainingBytes > 0
                If nAmountRemainingBytes > 128 Then
                    nAmoutToProcessBytes = 128
                Else
                    nAmoutToProcessBytes = nAmountRemainingBytes
                End If

                bOutput(i) = nAmoutToProcessBytes - 1
                i += 1

                For h As Integer = 0 To nAmoutToProcessBytes - 1
                    For nCurBit As Integer = 0 To 7
                        bOutput(i) += bSigniture(nCurrentPixel) * (2 ^ (7 - nCurBit))
                        nCurrentPixel += 1
                    Next
                    i += 1
                Next

                nAmountRemainingBytes -= nAmoutToProcessBytes
            Loop

            SetEmulationBackToPrintek()
            moPort.Write(bOutput, 0, bOutput.Length)
            System.Threading.Thread.Sleep(1000) 'give time to print

        End Sub

        Public Sub Print2bitBMP(ByVal i_sFile As String)
            Dim bBMPFile() As Byte
            Dim oFile As System.IO.BinaryReader
            Dim oStream As System.IO.FileStream
            Dim nWidthByte As Integer
            Dim nHeightInRows As Integer
            Dim nBytePerRowBMP As Integer
            Dim bImageToPrint() As Byte
            Dim nBMPOffset As Integer
            Dim nIndexImageToPrint As Integer
            Dim nIndexBMP As Integer
            Dim nBitCount As Integer

            'open the bmp and load it into memory
            oStream = System.IO.File.Open(i_sFile, IO.FileMode.Open)
            oFile = New System.IO.BinaryReader(oStream)
            bBMPFile = oFile.ReadBytes(oFile.BaseStream.Length)
            oFile.Close()
            oStream.Close()

            'Check the file type , bmp files always start with the first 2 bytes being BM
            If bBMPFile(0) <> Asc("B") And bBMPFile(1) <> Asc("M") Then
                MsgBox("File is not a bmp")
                Return
            End If

            'gather some information from the bitmap header and verify it
            nWidthByte = System.BitConverter.ToInt32(bBMPFile, 18) / 8
            nHeightInRows = System.BitConverter.ToInt32(bBMPFile, 22)
            nBMPOffset = System.BitConverter.ToInt32(bBMPFile, 10)
            nBitCount = System.BitConverter.ToInt16(bBMPFile, 28)
            If nBitCount <> 1 Then
                MsgBox("File is wrong bitcount")
                Return
            End If

            'In a bmp file each row should be divisible by 4 bytes
            'if not extra bytes are added
            If (nWidthByte) Mod 4 > 0 Then
                nBytePerRowBMP = (nWidthByte) + (4 - ((nWidthByte) Mod 4))
            Else
                nBytePerRowBMP = nWidthByte
            End If
            'this array is used to store the trimmed array
            ReDim bImageToPrint(nHeightInRows * nWidthByte - 1)

            'loop through each byte, convert and copy
            nIndexImageToPrint = 0
            nIndexBMP = nBMPOffset
            For nRow As Integer = nHeightInRows - 1 To 0 Step -1
                For nCol As Integer = 0 To nWidthByte - 1
                    nIndexImageToPrint = (nRow * nWidthByte) + nCol
                    bImageToPrint(nIndexImageToPrint) = bBMPFile(nIndexBMP) 'Xor &HFF  'xor the byte because bmp stores the pixel as zero for on
                    nIndexBMP += 1
                    'nIndexImageToPrint += 1
                Next
                'skip any extra bytes added to the row
                nIndexBMP += (nBytePerRowBMP - nWidthByte)
            Next

            'print the image
            'Debug.Assert(i_oControl.Width Mod 8 = 0, "Only supports images with width divisible by 8")
            Debug.Assert(nWidthByte <= 104, "Only supports images with width/8 <= 104")  'for MtP300 the value is 72
            Debug.Assert(nHeightInRows <= 255, "Only supports images with height <= 255")

            Dim nNumberOfdividers As Integer = Math.Ceiling(CType(nWidthByte * nHeightInRows, Double) / 128.0)
            Dim bOutput(4 + (nWidthByte * nHeightInRows) + nNumberOfdividers - 1) As Byte
            bOutput(0) = &H1B
            bOutput(1) = &H76
            bOutput(2) = nHeightInRows
            bOutput(3) = nWidthByte

            Dim nCurrentPixel As Integer = 0
            Dim i As Integer = 4
            Dim nAmoutToProcessBytes As Byte
            Dim nTotalBytes As Integer = bImageToPrint.Length
            Dim nAmountRemainingBytes As Integer = nTotalBytes

            Do While nAmountRemainingBytes > 0
                If nAmountRemainingBytes > 128 Then
                    nAmoutToProcessBytes = 128
                Else
                    nAmoutToProcessBytes = nAmountRemainingBytes
                End If

                bOutput(i) = nAmoutToProcessBytes - 1
                i += 1

                For h As Integer = 0 To nAmoutToProcessBytes - 1
                    'For nCurBit As Integer = 0 To 7
                    bOutput(i) = bImageToPrint(nCurrentPixel) '* (2 ^ (7 - nCurBit))
                    nCurrentPixel += 1
                    'Next
                    i += 1
                Next

                nAmountRemainingBytes -= nAmoutToProcessBytes
            Loop

            SetEmulationBackToPrintek()
            moPort.Write(bOutput, 0, bOutput.Length)

            'For nTest As Integer = 0 To 31
            '    i_oPort.Write(bOutput, nTest * 143, 143)
            '    System.Threading.Thread.Sleep(150)
            'Next
        End Sub

     

     

  • after translate this to c#, dont work for me.  

  • Try the attached C# sample

     

    6175.SampleBMP_PCX.zip

  • Hi Scott ...

    I already use this code to take a signature (through InkPad from Intermec) from which I get a bitmap of a "bit per pixel," then this example, converts it to PCX and then sent to the printer. This works.

    The problem is:

    How to get a bitmap of one bit per pixel (in "compact framework") for use with this example?

    I tried several examples and all have a problem. So my original question on this issue was how to convert a JPG taken by the camera and sent to the printer. Thinking that there was any application (even for a console) where you pass a path to a JPG file and the application creates a file with extension PCX, corresponding to the original image.

    I tried several options (convert from one JPG to a bitmap "one bit per pixel" and then convert it to PCX), but always end up with a problem .. black images, OutOfMemory exceptions or PCX completely flawed.

    Examples of conversion are quite complicated to modify them without thinking that adding some bug.

    Help!

  • Sorry, I assumed you were trying to print out a signature capture.

    I am not aware of anything that will convert an actual image captured from a camera and converting that down to a pcx file to print to a mobile printer.  I will check around.

  • I found a sample that works! convert a bitmap to a 1 or 8 bit per pixel. The problem now is that the converted image is mostly black, beacose if the pixel is white, then the converted pixel is white, but if the pixel have some color (some rgb color) then the resultant pixel is black so the converted image have more black pixel that white.

    I guess somewhere in the code should be able to change this behavior so that not all pixels with any color will become black.

    The sample:

    namespace ImageEditor

    {

       public class BitmapFile

       {

           static int SRCCOPY = 0x00CC0020;

           static uint BI_RGB = 0;

           static uint DIB_RGB_COLORS = 0;

           /// <summary>

           /// Copies a bitmap into a 1bpp/8bpp bitmap of the same dimensions, fast

           /// </summary>

           /// <param name="b">original bitmap</param>

           /// <param name="bpp">1 or 8, target bpp</param>

           /// <returns>a 1bpp copy of the bitmap</returns>

           public static System.Drawing.Bitmap CopyToBpp(System.Drawing.Bitmap b, int bpp)

           {

               if (bpp != 1 && bpp != 8) throw new System.ArgumentException("1 or 8", "bpp");

               // Plan: built into Windows GDI is the ability to convert

               // bitmaps from one format to another. Most of the time, this

               // job is actually done by the graphics hardware accelerator card

               // and so is extremely fast. The rest of the time, the job is done by

               // very fast native code.

               // We will call into this GDI functionality from C#. Our plan:

               // (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed)

               // (2) Create a GDI monochrome hbitmap

               // (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above)

               // (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed)

               int w = b.Width, h = b.Height;

               IntPtr hbm = b.GetHbitmap(); // this is step (1)

               //

               // Step (2): create the monochrome bitmap.

               // "BITMAPINFO" is an interop-struct which we define below.

               // In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs

               BITMAPINFO bmi = new BITMAPINFO();

               bmi.biSize = 40;  // the size of the BITMAPHEADERINFO struct

               bmi.biWidth = w;

               bmi.biHeight = h;

               bmi.biPlanes = 1; // "planes" are confusing. We always use just 1. Read MSDN for more info.

               bmi.biBitCount = (short)bpp; // ie. 1bpp or 8bpp

               bmi.biCompression = BI_RGB; // ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes

               bmi.biSizeImage = (uint)(((w + 7) & 0xFFFFFFF8) * h / 8);

               bmi.biXPelsPerMeter = 1000000; // not really important

               bmi.biYPelsPerMeter = 1000000; // not really important

               // Now for the colour table.

               uint ncols = (uint)1 << bpp; // 2 colours for 1bpp; 256 colours for 8bpp

               bmi.biClrUsed = ncols;

               bmi.biClrImportant = ncols;

               bmi.cols = new uint[256]; // The structure always has fixed size 256, even if we end up using fewer colours

               if (bpp == 1) { bmi.cols[0] = MAKERGB(0, 0, 0); bmi.cols[1] = MAKERGB(255, 255, 255); }

               else { for (int i = 0; i < ncols; i++) bmi.colsIdea = MAKERGB(i, i, i); }

               // For 8bpp we've created an palette with just greyscale colours.

               // You can set up any palette you want here. Here are some possibilities:

               // greyscale: for (int i=0; i<256; i++) bmi.colsIdea=MAKERGB(i,i,i);

               // rainbow: bmi.biClrUsed=216; bmi.biClrImportant=216; int[] colv=new intDevil{0,51,102,153,204,255};

               //          for (int i=0; i<216; i++) bmi.colsIdea=MAKERGB(colv[i/36],colv[(i/6)%6],colv[i%6]);

               // optimal: a difficult topic: en.wikipedia.org/.../Color_quantization

               //

               // Now create the indexed bitmap "hbm0"

               IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.

               IntPtr hbm0 = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);

               //

               // Step (3): use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap

               // GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC".

               IntPtr sdc = GetDC(IntPtr.Zero);       // First we obtain the DC for the screen

               // Next, create a DC for the original hbitmap

               IntPtr hdc = CreateCompatibleDC(sdc);

               SelectObject(hdc, hbm);

               // and create a DC for the monochrome hbitmap

               IntPtr hdc0 = CreateCompatibleDC(sdc);

               SelectObject(hdc0, hbm0);

               // Now we can do the BitBlt:

               BitBlt(hdc0, 0, 0, w, h, hdc, 0, 0, SRCCOPY);

               DeleteDC(hdc);

               DeleteDC(hdc0);

               ReleaseDC(IntPtr.Zero, sdc);

               DeleteObject(hbm);

               GC.Collect();

               // Step (4): convert this monochrome hbitmap back into a Bitmap:

               System.Drawing.Bitmap b0 = System.Drawing.Bitmap.FromHbitmap(hbm0);

               //

               // Finally some cleanup.

               DeleteObject(hbm0);

               //

               return b0;

           }

           [DllImport( "coredll.dll" )]   public static extern bool DeleteObject( IntPtr hObject );  

           [DllImport( "coredll.dll" )]   public static extern int InvalidateRect( IntPtr hwnd, IntPtr rect, int bErase );  

           [DllImport( "coredll.dll" )]   public static extern IntPtr GetDC( IntPtr hwnd );  

           [DllImport( "coredll.dll" )]   public static extern IntPtr CreateCompatibleDC( IntPtr hdc );  

           [DllImport( "coredll.dll" )]   public static extern int ReleaseDC( IntPtr hwnd, IntPtr hdc );  

           [DllImport( "coredll.dll" )]   public static extern int DeleteDC( IntPtr hdc );  

           [DllImport( "coredll.dll" )]   public static extern IntPtr SelectObject( IntPtr hdc, IntPtr hgdiobj );  

           [DllImport( "coredll.dll" )]   public static extern int BitBlt( IntPtr hdcDst, int xDst, int yDst, int w, int h, IntPtr hdcSrc, int xSrc, int ySrc, int rop );  

           [DllImport( "coredll.dll" )]   private static extern IntPtr LocalAlloc( uint flags, uint cb );  

           [DllImport( "coredll.dll" )]   private static extern IntPtr LocalFree( IntPtr hMem );  

           [DllImport( "coredll.dll" )]   private static extern IntPtr CreateDIBSection( IntPtr hdc, IntPtr hdr, uint colors, ref IntPtr pBits, IntPtr hFile, uint offset );

           [DllImport("coredll.dll")]     private static extern IntPtr CreateDIBSection(IntPtr hdc, ref BITMAPINFO bmi, uint Usage, out IntPtr bits, IntPtr hSection, uint dwOffset);

           [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));

           }

       }

    }