Convert 24bpp to 8bpp - How can I fix this method so that it returns Bitmap with a positive step

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; } 
0
source share
1 answer

According to the documentation for the BITMAPINFOHEADER structure:

biHeight Specifies the height of the bitmap in pixels.

If biHeight is positive, the bitmap is the DIB from the bottom up and its beginning is the bottom left corner.

If biHeight is negative, the bitmap is the top DIB and its beginning is the top left corner.

If biHeight is negative by specifying DIB from top to bottom, biCompression should be either BI_RGB or BI_BITFIELDS. Ultra-low DIBs cannot be compressed.

So, if you change the line in the code you sent:

 bmi.biHeight = -h; 

Then it will create a bitmap from top to bottom with a positive step.

-

There are other possibilities.

 var newBmp = srcBmp.Clone(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), PixelFormat.Format8bppIndexed); 

For me, this creates a bitmap that has a positive step. But then the original bitmap has a positive step. I donโ€™t know what it will do if I name it in a bitmap with a negative step.

All that said, I wonder what problem you are really trying to solve. Why does it matter if the bitmap is from bottom to top or top to bottom?

+2
source

All Articles