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.
- Download source code 1bpp.zip (82k, C#)
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)); }
it's so fast
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);
}
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.
Thanks
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.
How can I change this code to make this with the Compact Framework 2 for a WinCE 5.0 to run.
Thank you
Dirk
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?
myro.
(You are the KING!!!)
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?
How can i make 4bpp image?
Thanks.
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?
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.
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;
}
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.
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...
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.
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.
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!
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
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???
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.
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 ...
It's making image full black for 1bpp. please help
CopyToBpp(bitmap,1) to allblack bytes return
why!
please!
cannot find pinvoke dll 'gdi32.dll' in windows ce 6