Bitmap.Clone

J

John J. Hughes II

The following code throws a out of memory exception, I have another function
that does the same thing using the bitmap lock but its way slow. Anyone
know how to fix this one?

public static Bitmap GetClonedBitmap(int width, int height, string str, Font
font, StringAlignment Alignment)
{
Rectangle r = new Rectangle(1, 1, width-1, height-1);
using (Bitmap tmpBmp = new Bitmap(width, height))
{
using (Graphics g = Graphics.FromImage(tmpBmp))
{
g.FillRectangle(Brushes.White, r);
using (StringFormat sf = new
StringFormat(StringFormat.GenericDefault))
{
sf.Alignment = Alignment;
sf.Trimming = StringTrimming.None;
sf.LineAlignment = StringAlignment.Center;
sf.FormatFlags = StringFormatFlags.NoWrap;
g.DrawString(str, font, Brushes.Black, r, sf);

return tmpBmp.Clone(r, PixelFormat.Format1bppIndexed);
}
}
}
}

Regards,
John
 
B

Bob Powell [MVP]

You can't change the bitmap pixel format like that. That's where the error
is coming from.

If you want to convert an arbitrary bitmap to 1bpp indexed see the GDI+ FAQ.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.
 
J

John J. Hughes II

Thanks for the comments. Yes I am currently doing something similar to the
code on your website but it way to slow. I have to convert 28 128/16
bitmaps and it takes too long on my computer so I know my customers are
going to scream. I'm pretty sure it was faster in .NET 1.1 then 2 but it
what we have now so I was looking for a faster method.

It seemed based on the direction for the Bitmap.Clone function it would
change the pixel format, must have misunderstood.

From BOL:
"Bitmap.Clone (RectangleF, PixelFormat)
Creates a copy of the section of this Bitmap defined with a specified
PixelFormat enumeration."
 
L

Lucian Wischik

Bob Powell said:
If you want to convert an arbitrary bitmap to 1bpp indexed see the GDI+ FAQ.

The GDI+ FAQ says to iterate "for y=0 to height, for x=0 to width"
which is terribly slow. This is a shame, because Windows already
contains built-in functionality to convert from arbitrary pixelformat
into 1bpp pixelformat. And it's fast -- my laptop takes just 50ms to
convert a 1024x768 bitmap into monochrome, or just 5ms to convert a
200x200 bitmap.

Here's the code for it. I've written it in C++ using native win32
calls. Hopefully someone can turn it into C#. (I had trouble turning
it into C#, because I couldn't find how to construct a Bitmap() object
from an existing HBITMAP, and I couldn't find how to BitBlt/DrawImage
onto an existing 1bpp Bitmap object.)

HBITMAP CreateMonochromeCopy(HBITMAP hbm_src)
{
// retrieve the size of the original bitmap
DIBSECTION dibs; GetObject(hbm_src,sizeof(dibs),&dibs);
int width=dibs.dsBm.bmWidth, height=dibs.dsBm.bmHeight;
//
// create a new bitmap of the same dimensions with a 1bpp monchrome
palette
typedef struct {BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[2];}
MONOBITMAPINFO;
MONOBITMAPINFO bmi; ZeroMemory(&bmi,sizeof(bmi));
bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth=width;
bmi.bmiHeader.biHeight=height;
bmi.bmiHeader.biPlanes=1;
bmi.bmiHeader.biBitCount=1;
bmi.bmiHeader.biCompression=BI_RGB;
bmi.bmiHeader.biSizeImage=((width+7)&0xFFFFFFF8)*height/8;
bmi.bmiHeader.biXPelsPerMeter=1000000;
bmi.bmiHeader.biYPelsPerMeter=1000000;
bmi.bmiHeader.biClrUsed=2;
bmi.bmiHeader.biClrImportant=2;
bmi.bmiColors[0].rgbRed=0; bmi.bmiColors[0].rgbGreen=0;
bmi.bmiColors[0].rgbBlue=0; bmi.bmiColors[0].rgbReserved=0;

bmi.bmiColors[1].rgbRed=255;bmi.bmiColors[1].rgbGreen=255;bmi.bmiColors[1].rgbBlue=255;bmi.bmiColors[1].rgbReserved=0;
void *bits; HBITMAP hbm_dst =
CreateDIBSection(0,(BITMAPINFO*)&bmi,DIB_RGB_COLORS,&bits,0,0);
//
// BitBlt the original image into the new one. Windows and the
// graphics accelerator will take care of color thresholds &c.
HDC sdc = GetDC(0);
HDC hdc_src = CreateCompatibleDC(sdc);
HDC hdc_dst = CreateCompatibleDC(sdc);
SelectObject(hdc_src,hbm_src);
SelectObject(hdc_dst,hbm_dst);
BitBlt(hdc_dst,0,0,width,height,hdc_src,0,0,SRCCOPY);
DeleteDC(hdc_src);
DeleteDC(hdc_dst);
ReleaseDC(0,sdc);
//
return hbm_dst;
}



Here's a complete program to watch it in action:

#include <windows.h>

int main()
{ HDC sdc = GetDC(0);
HBITMAP hbm = CreateCompatibleBitmap(sdc,1024,768);
HDC hdc = CreateCompatibleDC(sdc);
SelectObject(hdc,hbm);
RECT rc; rc.left=0; rc.top=0; rc.right=1024; rc.bottom=768;
FillRect(hdc,&rc,(HBRUSH)GetStockObject(WHITE_BRUSH));
SetTextColor(hdc,RGB(255,0,0));
TextOut(hdc,10,10,L"Hello World",11);
DeleteDC(hdc);
//
HBITMAP hbmi[100];
unsigned int time1=GetTickCount();
for (int i=0; i<100; i++) hbmi = CreateMonochromeCopy(hbm);
unsigned int time2=GetTickCount();
unsigned int diff = time2-time1;
//
hdc = CreateCompatibleDC(sdc);
SelectObject(hdc,hbmi[0]);
BitBlt(sdc,0,0,200,200,hdc,0,0,SRCCOPY);
DeleteDC(hdc);
ReleaseDC(0,sdc);
DeleteObject(hbm);
for (int i=0; i<100; i++) DeleteObject(hbmi);
Sleep(2000);
return 0;
}
 
J

John J. Hughes II

Thanks, will look into it.

Lucian Wischik said:
Bob Powell said:
If you want to convert an arbitrary bitmap to 1bpp indexed see the GDI+
FAQ.

The GDI+ FAQ says to iterate "for y=0 to height, for x=0 to width"
which is terribly slow. This is a shame, because Windows already
contains built-in functionality to convert from arbitrary pixelformat
into 1bpp pixelformat. And it's fast -- my laptop takes just 50ms to
convert a 1024x768 bitmap into monochrome, or just 5ms to convert a
200x200 bitmap.

Here's the code for it. I've written it in C++ using native win32
calls. Hopefully someone can turn it into C#. (I had trouble turning
it into C#, because I couldn't find how to construct a Bitmap() object
from an existing HBITMAP, and I couldn't find how to BitBlt/DrawImage
onto an existing 1bpp Bitmap object.)

HBITMAP CreateMonochromeCopy(HBITMAP hbm_src)
{
// retrieve the size of the original bitmap
DIBSECTION dibs; GetObject(hbm_src,sizeof(dibs),&dibs);
int width=dibs.dsBm.bmWidth, height=dibs.dsBm.bmHeight;
//
// create a new bitmap of the same dimensions with a 1bpp monchrome
palette
typedef struct {BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[2];}
MONOBITMAPINFO;
MONOBITMAPINFO bmi; ZeroMemory(&bmi,sizeof(bmi));
bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth=width;
bmi.bmiHeader.biHeight=height;
bmi.bmiHeader.biPlanes=1;
bmi.bmiHeader.biBitCount=1;
bmi.bmiHeader.biCompression=BI_RGB;
bmi.bmiHeader.biSizeImage=((width+7)&0xFFFFFFF8)*height/8;
bmi.bmiHeader.biXPelsPerMeter=1000000;
bmi.bmiHeader.biYPelsPerMeter=1000000;
bmi.bmiHeader.biClrUsed=2;
bmi.bmiHeader.biClrImportant=2;
bmi.bmiColors[0].rgbRed=0; bmi.bmiColors[0].rgbGreen=0;
bmi.bmiColors[0].rgbBlue=0; bmi.bmiColors[0].rgbReserved=0;

bmi.bmiColors[1].rgbRed=255;bmi.bmiColors[1].rgbGreen=255;bmi.bmiColors[1].rgbBlue=255;bmi.bmiColors[1].rgbReserved=0;
void *bits; HBITMAP hbm_dst =
CreateDIBSection(0,(BITMAPINFO*)&bmi,DIB_RGB_COLORS,&bits,0,0);
//
// BitBlt the original image into the new one. Windows and the
// graphics accelerator will take care of color thresholds &c.
HDC sdc = GetDC(0);
HDC hdc_src = CreateCompatibleDC(sdc);
HDC hdc_dst = CreateCompatibleDC(sdc);
SelectObject(hdc_src,hbm_src);
SelectObject(hdc_dst,hbm_dst);
BitBlt(hdc_dst,0,0,width,height,hdc_src,0,0,SRCCOPY);
DeleteDC(hdc_src);
DeleteDC(hdc_dst);
ReleaseDC(0,sdc);
//
return hbm_dst;
}



Here's a complete program to watch it in action:

#include <windows.h>

int main()
{ HDC sdc = GetDC(0);
HBITMAP hbm = CreateCompatibleBitmap(sdc,1024,768);
HDC hdc = CreateCompatibleDC(sdc);
SelectObject(hdc,hbm);
RECT rc; rc.left=0; rc.top=0; rc.right=1024; rc.bottom=768;
FillRect(hdc,&rc,(HBRUSH)GetStockObject(WHITE_BRUSH));
SetTextColor(hdc,RGB(255,0,0));
TextOut(hdc,10,10,L"Hello World",11);
DeleteDC(hdc);
//
HBITMAP hbmi[100];
unsigned int time1=GetTickCount();
for (int i=0; i<100; i++) hbmi = CreateMonochromeCopy(hbm);
unsigned int time2=GetTickCount();
unsigned int diff = time2-time1;
//
hdc = CreateCompatibleDC(sdc);
SelectObject(hdc,hbmi[0]);
BitBlt(sdc,0,0,200,200,hdc,0,0,SRCCOPY);
DeleteDC(hdc);
ReleaseDC(0,sdc);
DeleteObject(hbm);
for (int i=0; i<100; i++) DeleteObject(hbmi);
Sleep(2000);
return 0;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top