I am looking for a quick way to convert Bitmap from 24bpp to 8bpp.
I found a solution on this 1bpp Programmer site in C # . It works, but the resulting bitmart step is negative!
How can I fix the stride of the image?
I already tried this:
Bitmap.Clone: โโstep is still negative
new Bitmap (b0): creates a 32bpp image ... ยฌยฌ
EDIT: Save the file, read it and make a deep copy (Marshal.Copy or memcopy) of the pixel data to disconnect the Bitmap from the file. But this "kinda" ugly ...
Code below:
/// <summary> /// Copy a bitmap into a 1bpp/8bpp bitmap of the same dimensions, fast /// </summary> /// <param name="source">original bitmap</param> /// <param name="bpp">1 or 8, target bpp</param> /// <returns>a 1bpp copy of the bitmap</returns> /// <url>http://www.wischik.com/lu/programmer/1bpp.html</url> private static Bitmap ConvertTo1or8bppNegativeStride(Bitmap source, int bpp = 8) { 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 = source.Width, h = source.Height; IntPtr hbm = source.GetHbitmap(); // this is step (1) // // Step (2): create the monochrome bitmap. // "BITMAPINFO" is an interop-struct which we define below. // In GDI terms, it 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 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: Bitmap b0 = Bitmap.FromHbitmap(hbm0); // // Finally some cleanup. DeleteDC(hdc); DeleteDC(hdc0); ReleaseDC(IntPtr.Zero, sdc); DeleteObject(hbm); DeleteObject(hbm0); //It have negative stride... return b0; }