Programmer » 1bpp in C#

This code shows how to convert a bitmap into monochrome (1bpp) or palettized (8bpp) from C#. The technique is fast because it calls gdi32 (the windows graphics system) directly.

About it

Some functionality that's present in GDI (the windows Graphics Driver Interface) is simply absent from the standard .NET framework. One example is the ability to draw onto PixelFormat.1bpp images -- the Graphics class throws an exception when you try to create a Graphics object out of such an image. (Another example is the ability to create metafiles in memory.) To use this kind of functionality, we have to interop with GDI.

This page shows how to use the Windows GDI from C# to achieve faster conversions to 1bpp/8bpp. How fast? Well, let's compare it to normal C# code which doesn't use the Windows GDI. (taken from Bob Powell's GDI+ faq).

 GDI+ faq:  8.5 seconds for a 4000x5000 image
 This code: 2.2 seconds
 Speedup:   4x speedup!

Code

The rest of this page is publically editable. If you want to add comments, or fix bugs, or ask questions, then click the "Edit" button at the bottom left.


This shows how to copy a Bitmap into a 1bpp copy, using the functions below:

static void Main(string[] args)
{ System.Drawing.Bitmap b = new System.Drawing.Bitmap("c:\\test.jpg");
  System.Drawing.Bitmap b0 = CopyToBpp(b,1);

  // below is just a function I wrote to easily display the result onscreen
  SplashImage(b0,0,0); System.Threading.Thread.Sleep(1000);
}

To convert to an 8bpp (palettized) image with a greyscale palette, do

  System.Drawing.Bitmap b0 = CopyToBpp(b,8);

If you want to convert to an image with a different palette, look at the comments in the source code of CopyToBpp for suggestions. Note that, when you convert to a 1bpp or 8bpp palettized copy, Windows will look at each pixel one by one, and will chose the palette entry that's closest to that pixel. Depending on your source image and choice of palette, you may very well end up with a resulting image that uses only half of the colours available in the palette.

To convert a 1bpp/8bpp image back into a normal 24bpp bitmap,

  System.Drawing.Bitmap b1 = new System.Drawing.Bitmap(b0);

How it works

First, some terminology:

GDI+,.NET GDI equivalent
System.Drawing.Bitmap HBITMAP stores the bitmap data
System.Drawing.Graphics HDC, DisplayContext via this you draw onto bitmaps &c.
Graphics.DrawImage BitBlt copies from one bitmap onto another
note: C# uses IntPtr for HBitmaps and HDCs

And here is the main code.

/// <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>
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.cols[i]=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.cols[i]=MAKERGB(i,i,i);
  // rainbow: bmi.biClrUsed=216; bmi.biClrImportant=216; int[] colv=new int[6]{0,51,102,153,204,255};
  //          for (int i=0; i<216; i++) bmi.cols[i]=MAKERGB(colv[i/36],colv[(i/6)%6],colv[i%6]);
  // optimal: a difficult topic: http://en.wikipedia.org/wiki/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);
  // Step (4): convert this monochrome hbitmap back into a Bitmap:
  System.Drawing.Bitmap b0 = System.Drawing.Bitmap.FromHbitmap(hbm0);
  //
  // Finally some cleanup.
  DeleteDC(hdc);
  DeleteDC(hdc0);
  ReleaseDC(IntPtr.Zero,sdc);
  DeleteObject(hbm);
  DeleteObject(hbm0);
  //
  return b0;
}

To understand the code, here are links to the MSDN documentation for each GDI function and structure that it uses: BitBlt, BITMAPINFO, CreateDIBSection, GetDC, CreateCompatibleDC, SelectObject, SRCCOPY, DeleteDC, ReleaseDC, DeleteObject

Also here's my routine to splash an image onto the screen. I use this just as a quick convenient way to see the contents of a Bitmap. It's easier to call this than to create a proper WinForms project. But note: all this does is splash it onto the screen. Any window on the screen will overpaint it.

/// <summary>
/// Draws a bitmap onto the screen.
/// </summary>
/// <param name="b">the bitmap to draw on the screen</param>
/// <param name="x">x screen coordinate</param>
/// <param name="y">y screen coordinate</param>
static void SplashImage(System.Drawing.Bitmap b, int x, int y)
{ // Drawing onto the screen is supported by GDI, but not by the Bitmap/Graphics class.
  // So we use interop:
  // (1) Copy the Bitmap into a GDI hbitmap
  IntPtr hbm = b.GetHbitmap();
  // (2) obtain the GDI equivalent of a "Graphics" for the screen
  IntPtr sdc = GetDC(IntPtr.Zero);
  // (3) obtain the GDI equivalent of a "Graphics" for the hbitmap
  IntPtr hdc = CreateCompatibleDC(sdc);
  SelectObject(hdc,hbm);
  // (4) Draw from the hbitmap's "Graphics" onto the screen's "Graphics"
  BitBlt(sdc,x,y,b.Width,b.Height,hdc,0,0,SRCCOPY);
  // and do boring GDI cleanup:
  DeleteDC(hdc);
  ReleaseDC(IntPtr.Zero,sdc);
  DeleteObject(hbm);
}

Finally, here are the interop functions we use

  [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));
  }
very nice
it's so fast
thanks
Great stuff, thanks!
 
The only strange thing I encountered was color quantization when converting to grayscale (even with the palette you used). I created a 256x256 bitmap and filled it with horizontal lines starting from (0,0,0) to (255,255,255) and after conversion to 8bpp the resulting image included only something around 20 gray levels.
I checked the color table selected into the DC and everything looks ok, but still quantization occurred.
 
For now I added another method for recalculating the gray image after it's created by the CopyToBpp method (see below). I'm not sure what causes the problem or if I'm doing something wrong.
 
  private static void Calc8BPPGrayscale(Bitmap bmpSrc, Bitmap bmpDst8BPP)
  {
  if (bmpSrc.Size != bmpDst8BPP.Size)
  {
  throw new System.ArgumentException("Destination size mismatch", "bmpDst8BPP.Size");
  }
  if (bmpDst8BPP.PixelFormat != System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
  {
  throw new System.ArgumentException("Format8bppIndexed", "bmpDst8BPP.PixelFormat");
  }
  if (bmpSrc.PixelFormat != System.Drawing.Imaging.PixelFormat.Format24bppRgb &&
  bmpSrc.PixelFormat != System.Drawing.Imaging.PixelFormat.Format32bppArgb &&
  bmpSrc.PixelFormat != System.Drawing.Imaging.PixelFormat.Format32bppRgb)
  {
  throw new System.ArgumentException("Format24bppRgb or Format32bppArgb or Format32bppRgb", "bmpSrc.PixelFormat");
  }
 
  int width = bmpSrc.Size.Width;
  int height = bmpSrc.Size.Height;
  Rectangle rect = new Rectangle(0, 0, width, height);
  BitmapData bmpData = bmpSrc.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmpSrc.PixelFormat);
  BitmapData bmpData8 = bmpDst8BPP.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmpDst8BPP.PixelFormat);
  int iPixelWidth = bmpSrc.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb ? 3 : 4;
 
  unsafe
  {
  byte* pScan = (byte*)bmpData.Scan0.ToPointer();
  byte* pScan8 = (byte*)bmpData8.Scan0.ToPointer();
  for (int y = 0; y < height; y++)
  {
  byte* pPixel = pScan;
  byte* pPixel8 = pScan8;
  for (int x = 0; x < width; x++)
  {
  byte gray = (byte)((*pPixel + *(pPixel + 1) + *(pPixel + 2)) / 3);
  *pPixel8 = gray;
  pPixel += iPixelWidth;
  pPixel8++;
  }
  pScan += bmpData.Stride;
  pScan8 += bmpData8.Stride;
  }
  }
 
  bmpSrc.UnlockBits(bmpData);
  bmpDst8BPP.UnlockBits(bmpData8);
  }
 
nice article but the resulting image always has a resolution of 96 dpi. Is there anyway to change this?
Great code! ;)
 
This way, we can set the original resolution:
 
  // Set Resolution.
  b0.SetResolution(b.HorizontalResolution, b.VerticalResolution);
 
This code comes before CopyToBpp function returns b0 bitmap.
Can you please explain how can i give the hue effect for image using this
 
Thanks
Think there is an error in your MAKERGB function. The r and g components are the wrong way round. The function should read:
 
  static uint MAKERGB(int r,int g,int b)
  { return ((uint)(b&255)) | ((uint)((g&255)<<8)) | ((uint)((r&255)<<16));
  }
 
Otherwise useful code, thanks.
The code works great.
How can I change this code to make this with the Compact Framework 2 for a WinCE 5.0 to run.
 
Thank you
Dirk
For some reason after I convert a bitmap to 1bpp using your function if I do a LockBits on the resulting bitmap it gives me a negative stride!
I replace your function with the slower (GDI+ FAQ) one and the stride is positive, everything is normal... but slower I guess.
Any idea why?
 

Last night a DJ saved my life, Thanks bro.
Brutal Work!!!!!!. Thanks.
Is there a way to create a 16 bit gray scale image from a array of unsigned short?
thank you very much for sharing this.... works great!
myro.
Kralju moj!!
(You are the KING!!!)
Hi.
for convert to grayscale image can be used ColorMatrix.
 
 static Bitmap GetGrayScaleImage(Bitmap source)
  {
  Bitmap grayBitmap = new Bitmap(source.Width, source.Height);
 
  ImageAttributes imgAttributes = new ImageAttributes();
 
  ColorMatrix gray = new ColorMatrix(
  new float[][] {
  new float[] { 0.299f, 0.299f, 0.299f, 0, 0 },
  new float[] { 0.588f, 0.588f, 0.588f, 0, 0},
  new float[] { 0.111f, 0.111f, 0.111f, 0, 0 },
  new float[] { 0, 0, 0, 1, 0 },
  new float[] { 0, 0, 0, 0, 1},
 
  }
  );
 
  imgAttributes.SetColorMatrix(gray);
 
  Graphics g = Graphics.FromImage(grayBitmap);
 
  g.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height),
  0, 0, source.Width, source.Height, GraphicsUnit.Pixel, imgAttributes);
  g.Dispose();
 
  return grayBitmap;
  }
 
But, I don't understand, how can I save 8-bit gray scale jpg or png?
Great Work
I tried opening the converted image using a hex editor and found out that the header of the image file is PNG.
Great code. Excellent job.
Great arcticle!!!!
Hi.
How can i make 4bpp image?
 
Thanks.
Can it be like that?
 
if (bpp == 4)
  {
  bmi.biClrUsed = 16; bmi.biClrImportant = 16;
  int[] colv1 = new int[16] { 8, 24, 38, 56, 72, 88, 104, 120, 136, 152, 168, 184, 210, 216, 232, 248 };
 
  for (int i = 0; i < 16; i++)
  {
  bmi.cols[i] = MAKERGB(colv1[i], colv1[i], colv1[i]);
  }
  }
 
It works. But the same code with bpp == 3 didnt work for me. Can you help me?
Hi,
  i have to draw a rectangle box in the given 8bpp indexed image. so i use the " g.DrawImageUnscaled(img, 0, 0)" method to generate a 32bpp image from the given 8bpp indexed image.. then i use draw method of c# graphics.. then copy the 32bpp image back to 8bpp image using the code given here. all are working well except one thing which bothers me very much is the file size.. it got reduced almost half of its original size. and if i use the rainbow palette the 2MB file is reduced to 380kb. if i use the gray scale(the above code as it is) am getting 1MB file. i need to show the rectangle in color red. with the rainbow palette, am getting red but am worried why the file size is reduced this much. pls guide me. Thanks a lot. FYI: am using TIF format files.
Thank you!
You have saved me. Great work. Thanks
Hi,
this is really interesting.
I was using a similar strategy like Bob Powell's which I lent from http://www.codeproject.com/KB/GDI-plus/BitonalImageConverter.aspx. ConvertToBitonal first copies the BitmapData to a byte[] where processing can be done without Interop. Obviously I was anxious to see how it compares to your code.
 
And here are the results (tada!):
 
GDI conversion time: 3209ms
FAQ conversion time: 8032ms
Bitonal conversion time: 1461ms <---- this is ConvertToBitonal (can you believe it?)
 
I modified the test so it can handle several images (I used 10 Jpegs from my cam for this particular test). I also used Stopwatch rather than TimeSpan/DateTime for measuring.
 
Now, I am a bit confused by the results. From all your explanations about the the graphics hardware accelerator card and so on I was convinced that your code would be way faster. Do I have a crappy graphics card? Can someone test this? Here comes the modified code, first Main, followed by ConvertToBitonal (rest stays the same):-
 
  static void Main(string[] args)
  {
  string[] files = Directory.GetFiles(".", "*.jpg", SearchOption.TopDirectoryOnly);
  //Bitmap b = new Bitmap("test.jpg");
  //SplashImage(b, 0, 0);
  //
  Stopwatch sw = new Stopwatch();
  long tsFaq = 0, tsLu = 0, tsBitonal = 0;
  Bitmap b;
  for (int i = 0; i < files.Length; i++)
  {
  b = new Bitmap(files[i]);
  //
  sw.Start();
  Bitmap b2 = ConvertToBitonal(b);
  sw.Stop();
  tsBitonal += sw.ElapsedMilliseconds;
  sw.Reset();
  //SplashImage(b2, 400, 200);
  b2.Dispose();
  //
  sw.Start();
  Bitmap b0 = CopyToBpp(b, 1);
  sw.Stop();
  tsFaq += sw.ElapsedMilliseconds;
  sw.Reset();
  //SplashImage(b0, 200, 100);
  b0.Dispose();
  //
  sw.Start();
  Bitmap b1 = FaqCopyTo1bpp(b);
  sw.Stop();
  tsLu += sw.ElapsedMilliseconds;
  sw.Reset();
  //SplashImage(b1, 400, 200);
  b1.Dispose();
  b.Dispose();
  }
  Console.WriteLine("GDI conversion time: {0}ms", tsFaq.ToString());
  Console.WriteLine("FAQ conversion time: {0}ms", tsLu.ToString());
  Console.WriteLine("Bitonal conversion time: {0}ms", tsBitonal.ToString());
 
  //
  Console.ReadKey();
  //System.Threading.Thread.Sleep(1000);
  //InvalidateRect(IntPtr.Zero, IntPtr.Zero, 1);
  }
 

  public static Bitmap ConvertToBitonal(Bitmap original)
  {
  Bitmap source = null;
 
  if (original.PixelFormat == PixelFormat.Format1bppIndexed)
  return (Bitmap)original.Clone();
  else if (original.PixelFormat != PixelFormat.Format32bppArgb)
  { // If original bitmap is not already in 32 BPP, ARGB format, then convert
  // unfortunately Clone doesn't do this for us but returns a bitmap with the same pixel format
  //source = original.Clone( new Rectangle( Point.Empty, original.Size ), PixelFormat.Format32bppArgb );
  source = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
  source.SetResolution(original.HorizontalResolution, original.VerticalResolution);
  using (Graphics g = Graphics.FromImage(source))
  {
  //g.CompositingQuality = Drawing2D.CompositingQuality.GammaCorrected;
  //g.InterpolationMode = Drawing2D.InterpolationMode.Low;
  //g.SmoothingMode = Drawing2D.SmoothingMode.None;
  g.DrawImageUnscaled(original, 0, 0);
  }
  }
  else
  {
  source = original;
  }
 
  // Lock source bitmap in memory
  BitmapData sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
 
  // Copy image data to binary array
  int imageSize = sourceData.Stride * sourceData.Height;
  byte[] sourceBuffer = new byte[imageSize];
  Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize);
 
  // Unlock source bitmap
  source.UnlockBits(sourceData);
 
  // Dispose of source if not originally supplied bitmap
  if (source != original)
  {
  source.Dispose();
  }
 
  // Create destination bitmap
  Bitmap destination = new Bitmap(sourceData.Width, sourceData.Height, PixelFormat.Format1bppIndexed);
  destination.SetResolution(original.HorizontalResolution, original.VerticalResolution);
 
  // Lock destination bitmap in memory
  BitmapData destinationData = destination.LockBits(new Rectangle(0, 0, destination.Width, destination.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
 
  // Create destination buffer
  byte[] destinationBuffer = SimpleThresholdBW(
  sourceBuffer,
  sourceData.Width,
  sourceData.Height,
  sourceData.Stride,
  destinationData.Stride);
 
  // Copy binary image data to destination bitmap
  Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, destinationData.Stride * sourceData.Height);
 
  // Unlock destination bitmap
  destination.UnlockBits(destinationData);
 
  // Return
  return destination;
  }
 
  const int threshold = 255 * 3 / 2;
 
  public static byte[] SimpleThresholdBW(byte[] sourceBuffer, int width, int height, int srcStride, int dstStride)
  {
  byte[] destinationBuffer = new byte[dstStride * height];
  int srcIx = 0;
  int dstIx = 0;
  byte bit;
  byte pix8;
 
  int newpixel, i, j;
 
  // Iterate lines
  for (int y = 0; y < height; y++, srcIx += srcStride, dstIx += dstStride)
  {
  bit = 128;
  i = srcIx;
  j = dstIx;
  pix8 = 0;
  // Iterate pixels
  for (int x = 0; x < width; x++, i += 4)
  {
  // Compute pixel brightness (i.e. total of Red, Green, and Blue values)
  newpixel = sourceBuffer[i] + sourceBuffer[i + 1] + sourceBuffer[i + 2];
 
  if (newpixel > threshold)
  pix8 |= bit;
  if (bit == 1)
  {
  destinationBuffer[j++] = pix8;
  bit = 128;
  pix8 = 0; // init next value with 0
  }
  else
  bit >>= 1;
  } // line finished
  if (bit != 128)
  destinationBuffer[j] = pix8;
  } // all lines finished
  return destinationBuffer;
  }
Fascinating! I'm getting the same results, i.e. your Bitonal code is twice as fast as the GDI approach. Very nice.
Hi
I work on Compact Framework 2 for a WinCE 5.0! But There is an error.
 
-- OutOfMemoryException at Microsoft.AGL.Common.MISC.HandleAr()
at this line.
-- CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);
 
How can i solve it?
Thank you.
Great article...
The newly generated image by CopyToBpp() has less color (27 unique color) compare to my original image which 227 no of unique color and got 8ppi.
Any help :(
 
Thanks you...
Hi
I work on Compact Framework 3.5 But There is an error.
 
 OutOfMemoryException at Microsoft.AGL.Common.MISC.HandleAr()
at this line.
 System.Drawing.Bitmap b0 = System.Drawing.Bitmap.FromHbitmap(hbm0);
 
How can i solve it?
Thank you.
Hi,
 
nicely done but juz 1 question. This line here....
 
==> bmi.biSize=40;
 
is dat a fixed size and does it affect memory consumption when used on net.cf?
Thank you.
Hi there. I downloaded the thing and ran the EXE file included but all it did is open what looked like a command prompt window, and then throw three images onto the screen, right after eachother, then everything vanished. I didn't get any options or anything. I'm trying to get an inage converted to 1bpp and have been looking around the web, and I found this... anyways, any contact about this will be appreciated. Please email me at arceus545@yahoo.com
wooooooooow is so fast, tanks
great, thank's alot for this solution
Hello, I have a very simple problem but I dont know, you seem to handle this very easily...
 
I have an indexed bitmap in pixel format 8bpp, I want to shrink it, (same image but with an especific size and height) but I need the result to be an indexed bitmap too at 8bpp.
 
I can not do it the normal way using a Graphics because indexed bitmaps can not be extracted a Graphics from, reading here I think you know how to, do you have a code for it? Thanks!
Hello,
 
Great job .... but where is defined threshould for 1bpp? I test this code and image result is so "white".
I want down threshould.
 
Thanks
 
lldeoj@gmail.com
Hi,
 
There is an issue with the CopyToBpp function when given 8 as the number of bits per pixel.
 
It turns white from the source image into very light grey.
 
Any idea how i could solve this one?
 
Thanks
Ryan

 

Hi,
  I want to display a (512*512)16 bit gray scale raw image in the dialog box. It turn to be red at the time of display..The code i wrote is,,
 
  USHORT *buffer;
 
  buffer = new USHORT[512*512]();
  COLORREF *pImage = new COLORREF[512*512]();
  COLORREF *pImage1 = new COLORREF[512*512]();

 
  //creating compatibleDC
  HDC hdc=::GetDC(m_pic.m_hWnd);
  //HDC memdc=CreateCompatibleDC(hdc);
 

FILE *pFile = NULL;
  pFile = fopen( "D:\\slice_000.raw", "rb" );
  fread( buffer, 512*512*sizeof(USHORT),1 , pFile );
 

 
for (int x = 0; x < 512; x++)
{
for (int y = 0; y < 512; y++)
{

  pImage1[512*x+y] = buffer[512*x+y];

}
}
 

for (int y = 0; y < 512; y++)
{
  for (int x = 0; x < 512; x++)
{

 
SetPixel(hdc,x,y,pImage[512*y + x]);

 
}
}
 

How can i displayed it in grayscale???
Very handy, however there is a memory leak here. The bmi BITMAPINFO is never free'd by the garbage
collector or anything else (its unmanaged memory).
 
It needs to be free'd up manually
 
OR, if your calling repeatedly for the same kind of conversion ..
 
make the bmi static global to the class and just change with width and height each time.
How can I free the bmi??
Why the stride of the resulting Bitmap is negative??
Tank you...
Thank you this is great solution!!
Nice work, but how do the same in .NET compact. You use some methods that don't exist in mobile version of .NET. I would be very grateful for help.
Well ... I do not wanna be blasphemic but ...
where is the advantage of your routine compared to:
 
outBmp = inBmp.Clone(inBmpRect,PixelFormat.Format1bppIndexed) ???
 
You do not apply an thresholding (or other 'optimisation'), so, in my opinion, 2.2 sec for 'simple' 1 bit conversion is not that fast ...
Addition: Bitmap.Clone(...) is only one line of code and needs (in my case) only 0.08 sec for image conversion ...
greyscale: for (int i=0; i<256; i++) bmi.cols[i]=MAKERGB(i,i,i);
 
It's making image full black for 1bpp. please help
as wince5.0
CopyToBpp(bitmap,1) to allblack bytes return
why!
please!
i have a problem with
 
 cannot find pinvoke dll 'gdi32.dll' in windows ce 6
 
add comment  edit commentPlease add comments here