Board index » cppbuilder » TBitmap filled from a buffer

TBitmap filled from a buffer


2008-02-06 12:35:53 AM
cppbuilder68
Hi,
I'm trying to create a TBitmap object filled from an internal buffer in
order to be saved as a file. But the problem is that it only works fine
if the buffer (pRGBImage_) is a global variable, does it makes sense?
my code:
pBMPObject = new Graphics::TBitmap ();
if (pBMPObject == NULL) {
BITMAPINFO pbmi;
pbmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi.bmiHeader.biWidth = Width_;
pbmi.bmiHeader.biHeight = Height_;
pbmi.bmiHeader.biPlanes = 1;
pbmi.bmiHeader.biBitCount = 24;
pbmi.bmiHeader.biCompression = BI_RGB;
//pbmi.bmiHeader.biClrUsed = 0;
//pbmi.bmiHeader.biClrImportant = 0;
pBMPObject->Handle = CreateDIBitmap(GetDC(NULL),
(BITMAPINFOHEADER FAR*) &pbmi.bmiHeader, CBM_INIT,
pRGBImage_, &pbmi, DIB_RGB_COLORS);
pBMPObject->SaveToFile(BMPFileNamePath);
DeleteObject (pBMPObject->Handle);
delete pBMPObject;
Any idea?
Thank you. Aitor.
 
 

Re:TBitmap filled from a buffer

"Aitor Matilla" < XXXX@XXXXX.COM >wrote in message
Quote
the problem is that it only works fine if the buffer (pRGBImage_)
is a global variable, does it makes sense?
Please show an example that actually compiles.
Quote
pBMPObject = new Graphics::TBitmap ();
if (pBMPObject == NULL) {
pBMPObject will never be NULL, as the 'new' operator throws an exception if
it cannot instantiate the object instance.
Quote
DeleteObject (pBMPObject->Handle);
Don't do that. The TBitmap owns the HBITMAP handle. When you delete the
TBitmap instance, the HBITMAP is automatically destroyed as well.
Gambit
 

Re:TBitmap filled from a buffer

Hi Aitor
Aitor Matilla says:
Quote
Hi,

I'm trying to create a TBitmap object filled from an internal buffer in
order to be saved as a file. But the problem is that it only works fine
if the buffer (pRGBImage_) is a global variable, does it makes sense?

my code:

pBMPObject = new Graphics::TBitmap ();
if (pBMPObject == NULL) {

BITMAPINFO pbmi;
pbmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi.bmiHeader.biWidth = Width_;
pbmi.bmiHeader.biHeight = Height_;
pbmi.bmiHeader.biPlanes = 1;
pbmi.bmiHeader.biBitCount = 24;
pbmi.bmiHeader.biCompression = BI_RGB;
//pbmi.bmiHeader.biClrUsed = 0;
//pbmi.bmiHeader.biClrImportant = 0;

pBMPObject->Handle = CreateDIBitmap(GetDC(NULL),
(BITMAPINFOHEADER FAR*) &pbmi.bmiHeader, CBM_INIT,
pRGBImage_, &pbmi, DIB_RGB_COLORS);

pBMPObject->SaveToFile(BMPFileNamePath);
DeleteObject (pBMPObject->Handle);
delete pBMPObject;
If I remember correctly TBitmap takes ownership of the handle so
You dont have to delete that, but You do need to delete the DC that
You get from GetDC.
But why dont You just save the bitmap Your self using a TMemoryStream
Here is a sample:
void __fastcall TAjBmp::saveToStream(TStream* Strm)
{
BITMAPFILEHEADER BmFh;
BmFh.bfType = *(WORD*) "BM";
BmFh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) + sizeOfBits() -4;
BmFh.bfReserved1 = 0;
BmFh.bfReserved2 = 0;
BmFh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) - 4;
Strm->Write( &BmFh, sizeof(BITMAPFILEHEADER) );
Strm->Write( &FBmpInfo, sizeof(BITMAPINFO) - 4);
Strm->Write( FDibVoid, sizeOfBits() );
}
The BITMAPINFO FBmpInfo is in an other part of the code, but I can
see that You know how to set that up.
FDibVoid is the actual Bitmap bytes buffer.
The -4 is because of a "bug" (i think) in TBitmap it doesnt read the
bfOffBits value to find the bits.
Kind regards
Asger
 

{smallsort}

Re:TBitmap filled from a buffer

Thanks Asger,
I've been testing for solution.
Use a TStreamMemory is good idea, but applying your method I get a
deformed bmp.
Quote
BmFh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) + sizeOfBits() -4;
What does sizeOfBits() mean? I assume it means the number of data bytes
of my bitmap, bytes to be copied to the TStreamMemory, no?
Thanks in advance. Aitor.
 

Re:TBitmap filled from a buffer

Thanks,
Quote
Please show an example that actually compiles.
pBMPObject = new Graphics::TBitmap ();
BITMAPINFO pbmi;
pbmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi.bmiHeader.biWidth = Width_; //Width_ = BMP Width
pbmi.bmiHeader.biHeight = Height_;//Height_ = BMP Height
pbmi.bmiHeader.biPlanes = 1;
pbmi.bmiHeader.biBitCount = 24;
pbmi.bmiHeader.biCompression = BI_RGB;
//pbmi.bmiHeader.biClrUsed = 0;
//pbmi.bmiHeader.biClrImportant = 0;
pBMPObject->Handle = CreateDIBitmap(GetDC(NULL),
(BITMAPINFOHEADER FAR*) &pbmi.bmiHeader, CBM_INIT,
pRGBImage_, &pbmi, DIB_RGB_COLORS);
pBMPObject->SaveToFile(BMPFileNamePath); //Path + Filename
delete pBMPObject;
//
It only works properly when pRGBImage_ is a global variable. My first
idea was to create pRGBImage_ dinamically depending on the size of the
bitmap but it doesn't work always. Now, I'm trying to do it with
TMemoryStream like Asger said, but I'd like to know why this is happening.
Thanks. Aitor.
 

Re:TBitmap filled from a buffer

"Aitor Matilla" < XXXX@XXXXX.COM >wrote in message
Quote
It only works properly when pRGBImage_ is a global variable.
You did not show how pRGBImage_ is declared, how it is initializaed, or what
your code looks like when it is used locally versus globally.
Quote
My first idea was to create pRGBImage_ dinamically depending on
the size of the bitmap but it doesn't work always.
Please show ALL of the code when it does not work.
Gambit
 

Re:TBitmap filled from a buffer

Hi Aitor
Aitor Matilla says:
Quote
Thanks Asger,

I've been testing for solution.
Use a TStreamMemory is good idea, but applying your method I get a
deformed bmp.
can You upload a small deformed bitmap to the atatchment group ?
and also one that looks right ?
Quote

>BmFh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) + sizeOfBits() -4;


What does sizeOfBits() mean? I assume it means the number of data bytes
of my bitmap, bytes to be copied to the TStreamMemory, no?
Yes, when You have a 24 or 32 bit Bitmap file You have
BITMAPFILEHEADER + BITMAPINFO + ThePixels
I only work with 32 bit top down bitmaps, and that makes
everything a little easier because each row in the bitmap is
the same as the number of pixels * 4 where as in 24 bit you
have to pad each row to a DWORD boundry.
I think this is how it is done:
int __fastcall sizeOfBits(int BmpWidth, int BmpHeight)
{
int LineWidth = ((BmpWidth*3)+3)&~3;
return LineWidth * BmpHeight;
}
I dont know how You have Your source pixels but if You are having them
in vector containing line You will have to pad each line before You
write them to the file.
but maybe You can show how your source pixels are stored.
Kind regards
Asger
 

Re:TBitmap filled from a buffer

Hello,
Finally I've solved the mystery.
The problem was in the BITMAPINFO, I had to fill the biXPel{*word*237}eter and
biYPel{*word*237}eter parameters.
It was quite weird because I could open the image perfectly with
applicacions like PaintShopPro and I couldn't with others like the GIMP.
But now I works fine.
Thank you for your help.
Aitor
En/na Asger Joergensen ha escrit:
Quote
Hi Aitor

Aitor Matilla says:
>Thanks Asger,
>
>I've been testing for solution.
>Use a TStreamMemory is good idea, but applying your method I get a
>deformed bmp.

can You upload a small deformed bitmap to the atatchment group ?
and also one that looks right ?

>>BmFh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) + sizeOfBits() -4;
>
>What does sizeOfBits() mean? I assume it means the number of data bytes
>of my bitmap, bytes to be copied to the TStreamMemory, no?

Yes, when You have a 24 or 32 bit Bitmap file You have
BITMAPFILEHEADER + BITMAPINFO + ThePixels

I only work with 32 bit top down bitmaps, and that makes
everything a little easier because each row in the bitmap is
the same as the number of pixels * 4 where as in 24 bit you
have to pad each row to a DWORD boundry.
I think this is how it is done:

int __fastcall sizeOfBits(int BmpWidth, int BmpHeight)
{
int LineWidth = ((BmpWidth*3)+3)&~3;
return LineWidth * BmpHeight;
}

I dont know how You have Your source pixels but if You are having them
in vector containing line You will have to pad each line before You
write them to the file.

but maybe You can show how your source pixels are stored.

Kind regards
Asger
 

Re:TBitmap filled from a buffer

Hi Aitor
Aitor Matilla says:
Quote
Hello,

Finally I've solved the mystery.

The problem was in the BITMAPINFO, I had to fill the biXPel{*word*237}eter and
biYPel{*word*237}eter parameters.

It was quite weird because I could open the image perfectly with
applicacions like PaintShopPro and I couldn't with others like the GIMP.

But now I works fine.

Happy to hear..
Would You be so kind and post the code, I would like to see
how You got it working.
Thanks in advance
Asger
 

Re:TBitmap filled from a buffer

Hi,
Quote
Would You be so kind and post the code, I would like to see
how You got it working.
First, my buffer (pBuffer) is a RGB array with the format:
row1col1,row1col2,row1col3, ...
row2col1,.....
Code:
{
...
//Create a Compatible buffer
Byte *pCompatibleBuffer (NULL);
if (!SetBufferDataCompatibleWithBitmap (Width_, Height_, pBuffer,
pCompatibleBuffer)) {
if (pBuffer) delete [] pBuffer;
if (pCompatibleBuffer) delete [] pCompatibleBuffer;
return false;
}
if (pBuffer) delete [] pBuffer;
//Create BMP Header
BITMAPFILEHEADER BmFh;
BmFh.bfType = *(WORD*) "BM";
BmFh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) -4
+ BitsSize;
BmFh.bfReserved1 = 0;
BmFh.bfReserved2 = 0;
BmFh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) - 4;
BITMAPINFO pbmi;
pbmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi.bmiHeader.biWidth = Width_;
pbmi.bmiHeader.biHeight = Height_;
pbmi.bmiHeader.biPlanes = 1;
pbmi.bmiHeader.biBitCount = 24;
pbmi.bmiHeader.biCompression = BI_RGB;
pbmi.bmiHeader.biSizeImage=BitsSize;
pbmi.bmiHeader.biXPel{*word*237}eter=2952; // 75 DPI
pbmi.bmiHeader.biYPel{*word*237}eter=2952; // 75 DPI
pbmi.bmiHeader.biClrUsed=0;
pbmi.bmiHeader.biClrImportant=0;
TMemoryStream *Strm = new TMemoryStream ();
if (Strm == NULL) return false;
Strm->Write( &BmFh, sizeof(BITMAPFILEHEADER) );
Strm->Write( &pbmi, sizeof(BITMAPINFO) - 4);
Strm->Write( pCompatibleBuffer, BitsSize);
Strm->SaveToFile (FileName_);
if (Strm) delete Strm;
if (pCompatibleBuffer) delete [] pCompatibleBuffer;
...
}
//---------------------------------------------------------------------------
// SetBufferDataCompatibleWithBitmap
//---------------------------------------------------------------------------
bool SetBufferDataCompatibleWithBitmap (const int &Width_, const int
&Height_, const Byte* pSource, Byte* &pTarget)
{
if (pSource == NULL) return false;
if (pTarget != NULL) return false;
//Size according to DWORD size
int BitsSize = sizeOfBits(Width_, Height_);
//New buffer
pTarget = new Byte [BitsSize];
if (pTarget == NULL) return false;
//Pad the content
int pad = 4 - ((Width_ * nValuesRGB) % 4);
if (pad == 4) { // <==== Bug correction
pad = 0; // <==== Bug correction
}
int rowCount = 1;
int padCount = 0;
int counter = 0;
for (int i=0;i<(Width_*Height_)-1;++i) {
pTarget[counter++] = pSource[i*nValuesRGB];
pTarget[counter++] = pSource[i*nValuesRGB+1];
pTarget[counter++] = pSource[i*nValuesRGB+2];
if (rowCount == Width_) {
padCount += pad;
for (int j = 1; j <= pad; j++) {
pTarget[counter++] = 0;
}
rowCount = 1;
}
else {
rowCount++;
}
}
return true;
}
Aitor
 

Re:TBitmap filled from a buffer

Hi Aitor
Aitor Matilla says:
Quote
Hi,

>Would You be so kind and post the code, I would like to see
>how You got it working.


First, my buffer (pBuffer) is a RGB array with the format:
row1col1,row1col2,row1col3, ...
row2col1,.....

....
pbmi.bmiHeader.biHeight = Height_;
is the height possitive ?
if so doesn't the bitmap turn upside down when You load it
in another program ?
is Your save rutine time critical, I ask because it can
be improved speed wise...
Thanks for sharing
Kind regards
Asger
 

Re:TBitmap filled from a buffer

Hi Asger,
Quote
is the height possitive ?
if so doesn't the bitmap turn upside down when You load it
in another program ?
Yes, previously I've inverted the Y Values in order to avoid this behaviour.
Quote
is Your save rutine time critical, I ask because it can
be improved speed wise...
I haven't done any time test, It's enough quick for me but If it would
be interesting to improve this code.
Aitor.
 

Re:TBitmap filled from a buffer

Hi Aitor
Aitor Matilla says:
Quote

I haven't done any time test, It's enough quick for me but If it would
be interesting to improve this code.
Something like this, two ways of doing it:
bool __fastcall SetBufferDataCompatibleWithBitmap (int Width_,
int Height_, const Byte* pSource, Byte* &pTarget)
{
if (pSource == NULL) return false;
if (pTarget != NULL) return false;
//Size according to DWORD size
int LineWidth = Width_ * 3;
int FullLineWidth = (LineWidth + 3) &~ 3;
int BitsSize = FullLineWidth * Height_;
//New buffer
pTarget = new Byte [BitsSize];
if (pTarget == NULL) return false;
for(int y = 0; y < Height_; ++y)// Hight must be positive
{
Byte* SourceLine = pSource + y * LineWidth;
Byte* TargetLine = pSource + y * FullLineWidth;
memcpy(TargetLine, SourceLine, LineWidth);
}
return true;
}
//----------------------------------------------------------
or write directly
bool __fastcall WritePixelsToStream(TStream* Strm, int Width_,
int Height_, const Byte* pSource)
{
if (pSource == NULL) return false;
//Size according to DWORD size
int LineWidth = Width_ * 3;
int FullLineWidth = (LineWidth + 3) &~ 3;
int BitsSize = FullLineWidth * Height_;
Byte Pad[3];
PadSize = FullLineWidth - LineWidth;
for(int y = 0; y < Height_; ++y)// Hight must be positive !!!
{
Byte* SourceLine = pSource + y * LineWidth;
Strm->Write( SourceLine, LineWidth);
if(PadSize)
Strm->Write( Pad, PadSize );
}
}
//--------------------------------------------------------
Please note that this code is not tested !!!
but I'm sure You get the picture..
P.s.
Remember to set the size of the stream before You start writing
Strm->SetSize(BmFh.bfSize);
otherwise the stream will reallocate during the writing
Another thing is that You would get a lot more functions to
use if You stored Your data like the bitmap does it.
line-of-data + pad + line-of-data + pad + line-of-data pad... etc.
and if You let windows create the buffer for You with:
CreateDIBSection You can use all kind of API functions on Your
data like: BitBlt, FillRect.....
A struct and a couple of functions thats nice to have when
working with 24 bit Bitmaps
struct TPixel
{
Byte Blue;
Byte Green;
Byte Red;
//===============================================
__fastcall TPixel() : Blue(0), Green(0), Red(0){}
//-----------------------------------------------
__fastcall TPixel(Byte R, Byte G, Byte B)
: Blue(B), Green(G), Red(R){}
//-----------------------------------------------
__fastcall TPixel(const TPixel &t)
{
Blue = t.Blue;
Green = t.Green;
Red = t.Red;
}//----------------------------------------------
__fastcall TPixel(TColor t)
{
int i = (int)t;
Red = i; i = i>>8;
Green = i; i = i>>8;
Blue = i;
}//----------------------------------------------
};
TPixel* __fastcall GetScanLine(int Row)
{
// Check if Row is in bitmap
if(BitmapIsBottomUp)
Row = BitmapHeight - Row - 1;
TPixel* Pixels = (TPixel*)YourPixelBufffer;
return Pixels + Row * FullLineWidth;
}
TPixel __fastcall GetPixel(int X, int Y)
{
// Check if X is in Bitmap
return GetScanLine(Y) + X;
}
void __fastcall SetPixel(int X, int Y, TPixel Color)
{
// Check if X is in Bitmap
TPixel *Pixels = GetScanLine(Y);
*(Pixels + X) = Color;
}
Kind regards
Asger