Abadon
May 4th, 1999, 04:55 PM
The way that i make a 256 color bitmap using RGBQUAD. AND without using any MFC functions to create it (ie: StretchDIbits, BltBit, CreateDIBitmap etc)
This is very useful for making a BMP without displaying it in a window, and to directly modify pixels
of a user buffer. (This is a CONSOLE app)
First thing, declare all the BMP stuff in CDib.h, this includes all the supporting functions. This does use the MFC Structs for DIB's
CDib.h
----------
class CDib
{
public:
CDib();
virtual ~CDib();
// Variables
unsigned short bitspersample;
unsigned long bmpwidth,bmpimagesize,bmpscanlinesize,imagewidth,imageheight;
double scale;
int j,loop;
float xresolution, yresolution;
long colors;
DWORD offset;
RGBQUAD* prgbq;
BYTE* buf;
DWORD size;
// Implementation
void DrawTestBmp(BYTE *buffer,int color);
void SetBits(BYTE *buffer);
void SetBitsRGB(BYTE *buffer);
void DrawLine(int x1, int y1, int x2, int y2, unsigned char* buf,int color);
void DrawBoxes(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,unsigned char *buf,int color);
void DrawGrid();
BOOL ReadDib(const char* imageFile);
// structs and inline functions
BITMAPINFOHEADER* BmihPtr() const {return m_pBmih;}
BYTE* GetDibBitsAddr();
LONG GetDibWidth() const {return m_pBmih->biWidth;}
LONG GetDibHeight() const {return m_pBmih->biHeight;}
WORD Planes() const {return m_pBmih->biPlanes;}
WORD BitCount() const {return m_pBmih->biBitCount;}
DWORD SizeImage() const {return m_pBmih->biSizeImage;}
LONG XPelsPerMeter() const {return m_pBmih->biXPelsPerMeter;}
LONG YPelsPerMeter() const {return m_pBmih->biYPelsPerMeter;}
DWORD GetDibDPI();
BYTE* GetDibPtr() const {return m_pData;}
DWORD GetOffBits() const {return m_pBmfh->bfOffBits;}
DWORD Size() const {return m_pBmfh->bfSize;}
void FreeMem();
BOOL MakeDib();
DWORD Save(const char* fileName);
BITMAPFILEHEADER m_Bmfh; // Used by ReadDib. Copied into m_pData.
BITMAPFILEHEADER* m_pBmfh; // Pointer into m_pData where the
// BITMAPFILEHEADER data resides.
BITMAPINFOHEADER* m_pBmih; // Pointer into m_pData where the
// BITMAPINFOHEADER data resides.
BYTE* m_pData; // Contains the complete DIB including
// the BITMAPFILEHEADER data.
}
NEXT, somewhere along the way i initialize all the DIB's struct members
BOOL CDib::MakeDib()
{
xresolution = 200.0;
yresolution = 200.0;
imagewidth = 1000;
imageheight = 1000;
bitspersample = 8;
colors = 256;
scale = 100.0 / 2.54;
// Round bitmap width to an even 32 pixels
// I don't know why, but it works.
bmpwidth = ((imagewidth + 31) / 32) * 32;
bmpscanlinesize = (bmpwidth * bitspersample) / 8;
bmpimagesize = bmpscanlinesize * imageheight;
buf = new BYTE[bmpimagesize];
ZeroMemory(buf, bmpimagesize);
//-------------------------------------------
//Allocate memory to store the complete DIB.
//-------------------------------------------
size = sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER) + bmpimagesize
+ (sizeof(RGBQUAD)*colors);
m_pData = new BYTE[size];
return TRUE;
}
NEXT, I modify my user buffer to change certain pixel values
this code draws solid colored lines all from black (0) to max color (say blue) 255
void CDib::DrawTestBmp(BYTE *buffer,int color)
{
int row,col;
for(row=0;row<imageheight;row++)
{
for(col=0;col<imagewidth;col++)
{
buf[(row*bmpwidth)+col] = color;
}
color++;
}
}
NEXT, After i modify my buffer i set the bits of the DIB's struct and copy the color table and my user buffer into the dib's m_pData buffer\
void CDib::SetBitsRGB(BYTE *buffer)
{
int a=0,b=0,c=0;
prgbq = new RGBQUAD[colors];
// set just blue 0 - 255
for (loop = 0; loop < colors; loop++)
{
prgbq[loop].rgbBlue = a;
prgbq[loop].rgbGreen = 0;
prgbq[loop].rgbRed = 0;
prgbq[loop].rgbReserved = 0;
a++;
}
//--------------------------------------------------------------
// Set the address of the BITMAPFILEHEADER and BITMAPINFOHEADER.
//--------------------------------------------------------------
m_pBmfh = (BITMAPFILEHEADER*) m_pData;
m_pBmih = (BITMAPINFOHEADER*) &m_pData[sizeof(BITMAPFILEHEADER)];
//---------------------------------
// Set the BITMAPFILEHEADER fields.
//---------------------------------
m_pBmfh->bfType = * (WORD *) "BM";
m_pBmfh->bfSize = (DWORD) size;
m_pBmfh->bfReserved1 = 0;
m_pBmfh->bfReserved2 = 0;
m_pBmfh->bfOffBits = (DWORD) (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*colors);
m_pBmih->biSize = sizeof(BITMAPINFOHEADER);
m_pBmih->biWidth = bmpwidth;
m_pBmih->biHeight = imageheight;
m_pBmih->biPlanes = 1;
m_pBmih->biBitCount = bitspersample;
m_pBmih->biCompression = BI_RGB;
m_pBmih->biSizeImage = 0;
m_pBmih->biXPelsPerMeter = (LONG) (xresolution * scale + 0.5);
m_pBmih->biYPelsPerMeter = (LONG) (yresolution * scale + 0.5);
m_pBmih->biClrUsed =0;
m_pBmih->biClrImportant = 0;
//---------------------------------
// Copy the color map into m_pData.
//---------------------------------
CopyMemory(&m_pData[sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)],
prgbq, sizeof(RGBQUAD)*colors);
delete [] prgbq;
offset = m_pBmfh->bfOffBits ;
// copy my user buffer into the dib
CopyMemory(&m_pData[offset], buf, bmpimagesize);
}
NEXT, The last thing to do is save it to a file
DWORD CDib::Save(const char* fileName)
{
DWORD status = NO_ERROR;
//-----------------
// Create the file.
//-----------------
HANDLE hFile = CreateFile(fileName,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
DWORD err = GetLastError();
if (hFile == INVALID_HANDLE_VALUE)
{
if (err != ERROR_ALREADY_EXISTS)
{
// Abort!
status = err;
}
}
else
{
DWORD nBytes;
//----------------------------
// Write the data to the file.
//----------------------------
BOOL bStatus = WriteFile(hFile,
m_pData,
Size(),
&nBytes,
NULL);
//----------------------------------------------------------------------
// If an error occurred OR the total number of bytes written was invalid
//----------------------------------------------------------------------
if ( (!bStatus) || (nBytes != Size()) )
{
status = GetLastError();
}
}
return status;
}
If ANYONE can show me a way to make a 16 bit bitmap using the above method and using BI_BITFIELDS for RGB masks that would be GREAT!!! For some reason i cannot make it work.
I Dont understand the color masking, the doc's say that you should use 5 bits for blue, 5 for red, and 5 for green and the other bit isn't needed.
Quote:
When the biCompression member is BI_BITFIELDS, the system supports only the following 16bpp color masks:
A 5-5-5 16-bit image, where the blue mask is 0x001F, the green mask is 0x03E0, and the red mask is 0x7C00;
and a 5-6-5 16-bit image, where the blue mask is 0x001F, the green mask is 0x07E0, and the red mask is 0xF800
Where do you use the hex mask values at ????
Any help would be VERY much appreciated.
Things that must be changed for 16 bit
- Buffer's are WORD (unsigned short 16 bit) instead of BYTE (unsigned char)
- m_pBmih->biCompression = BI_BITFIELDS instead of BI_RGB
Regards,
Abadon
This is very useful for making a BMP without displaying it in a window, and to directly modify pixels
of a user buffer. (This is a CONSOLE app)
First thing, declare all the BMP stuff in CDib.h, this includes all the supporting functions. This does use the MFC Structs for DIB's
CDib.h
----------
class CDib
{
public:
CDib();
virtual ~CDib();
// Variables
unsigned short bitspersample;
unsigned long bmpwidth,bmpimagesize,bmpscanlinesize,imagewidth,imageheight;
double scale;
int j,loop;
float xresolution, yresolution;
long colors;
DWORD offset;
RGBQUAD* prgbq;
BYTE* buf;
DWORD size;
// Implementation
void DrawTestBmp(BYTE *buffer,int color);
void SetBits(BYTE *buffer);
void SetBitsRGB(BYTE *buffer);
void DrawLine(int x1, int y1, int x2, int y2, unsigned char* buf,int color);
void DrawBoxes(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,unsigned char *buf,int color);
void DrawGrid();
BOOL ReadDib(const char* imageFile);
// structs and inline functions
BITMAPINFOHEADER* BmihPtr() const {return m_pBmih;}
BYTE* GetDibBitsAddr();
LONG GetDibWidth() const {return m_pBmih->biWidth;}
LONG GetDibHeight() const {return m_pBmih->biHeight;}
WORD Planes() const {return m_pBmih->biPlanes;}
WORD BitCount() const {return m_pBmih->biBitCount;}
DWORD SizeImage() const {return m_pBmih->biSizeImage;}
LONG XPelsPerMeter() const {return m_pBmih->biXPelsPerMeter;}
LONG YPelsPerMeter() const {return m_pBmih->biYPelsPerMeter;}
DWORD GetDibDPI();
BYTE* GetDibPtr() const {return m_pData;}
DWORD GetOffBits() const {return m_pBmfh->bfOffBits;}
DWORD Size() const {return m_pBmfh->bfSize;}
void FreeMem();
BOOL MakeDib();
DWORD Save(const char* fileName);
BITMAPFILEHEADER m_Bmfh; // Used by ReadDib. Copied into m_pData.
BITMAPFILEHEADER* m_pBmfh; // Pointer into m_pData where the
// BITMAPFILEHEADER data resides.
BITMAPINFOHEADER* m_pBmih; // Pointer into m_pData where the
// BITMAPINFOHEADER data resides.
BYTE* m_pData; // Contains the complete DIB including
// the BITMAPFILEHEADER data.
}
NEXT, somewhere along the way i initialize all the DIB's struct members
BOOL CDib::MakeDib()
{
xresolution = 200.0;
yresolution = 200.0;
imagewidth = 1000;
imageheight = 1000;
bitspersample = 8;
colors = 256;
scale = 100.0 / 2.54;
// Round bitmap width to an even 32 pixels
// I don't know why, but it works.
bmpwidth = ((imagewidth + 31) / 32) * 32;
bmpscanlinesize = (bmpwidth * bitspersample) / 8;
bmpimagesize = bmpscanlinesize * imageheight;
buf = new BYTE[bmpimagesize];
ZeroMemory(buf, bmpimagesize);
//-------------------------------------------
//Allocate memory to store the complete DIB.
//-------------------------------------------
size = sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER) + bmpimagesize
+ (sizeof(RGBQUAD)*colors);
m_pData = new BYTE[size];
return TRUE;
}
NEXT, I modify my user buffer to change certain pixel values
this code draws solid colored lines all from black (0) to max color (say blue) 255
void CDib::DrawTestBmp(BYTE *buffer,int color)
{
int row,col;
for(row=0;row<imageheight;row++)
{
for(col=0;col<imagewidth;col++)
{
buf[(row*bmpwidth)+col] = color;
}
color++;
}
}
NEXT, After i modify my buffer i set the bits of the DIB's struct and copy the color table and my user buffer into the dib's m_pData buffer\
void CDib::SetBitsRGB(BYTE *buffer)
{
int a=0,b=0,c=0;
prgbq = new RGBQUAD[colors];
// set just blue 0 - 255
for (loop = 0; loop < colors; loop++)
{
prgbq[loop].rgbBlue = a;
prgbq[loop].rgbGreen = 0;
prgbq[loop].rgbRed = 0;
prgbq[loop].rgbReserved = 0;
a++;
}
//--------------------------------------------------------------
// Set the address of the BITMAPFILEHEADER and BITMAPINFOHEADER.
//--------------------------------------------------------------
m_pBmfh = (BITMAPFILEHEADER*) m_pData;
m_pBmih = (BITMAPINFOHEADER*) &m_pData[sizeof(BITMAPFILEHEADER)];
//---------------------------------
// Set the BITMAPFILEHEADER fields.
//---------------------------------
m_pBmfh->bfType = * (WORD *) "BM";
m_pBmfh->bfSize = (DWORD) size;
m_pBmfh->bfReserved1 = 0;
m_pBmfh->bfReserved2 = 0;
m_pBmfh->bfOffBits = (DWORD) (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*colors);
m_pBmih->biSize = sizeof(BITMAPINFOHEADER);
m_pBmih->biWidth = bmpwidth;
m_pBmih->biHeight = imageheight;
m_pBmih->biPlanes = 1;
m_pBmih->biBitCount = bitspersample;
m_pBmih->biCompression = BI_RGB;
m_pBmih->biSizeImage = 0;
m_pBmih->biXPelsPerMeter = (LONG) (xresolution * scale + 0.5);
m_pBmih->biYPelsPerMeter = (LONG) (yresolution * scale + 0.5);
m_pBmih->biClrUsed =0;
m_pBmih->biClrImportant = 0;
//---------------------------------
// Copy the color map into m_pData.
//---------------------------------
CopyMemory(&m_pData[sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)],
prgbq, sizeof(RGBQUAD)*colors);
delete [] prgbq;
offset = m_pBmfh->bfOffBits ;
// copy my user buffer into the dib
CopyMemory(&m_pData[offset], buf, bmpimagesize);
}
NEXT, The last thing to do is save it to a file
DWORD CDib::Save(const char* fileName)
{
DWORD status = NO_ERROR;
//-----------------
// Create the file.
//-----------------
HANDLE hFile = CreateFile(fileName,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
DWORD err = GetLastError();
if (hFile == INVALID_HANDLE_VALUE)
{
if (err != ERROR_ALREADY_EXISTS)
{
// Abort!
status = err;
}
}
else
{
DWORD nBytes;
//----------------------------
// Write the data to the file.
//----------------------------
BOOL bStatus = WriteFile(hFile,
m_pData,
Size(),
&nBytes,
NULL);
//----------------------------------------------------------------------
// If an error occurred OR the total number of bytes written was invalid
//----------------------------------------------------------------------
if ( (!bStatus) || (nBytes != Size()) )
{
status = GetLastError();
}
}
return status;
}
If ANYONE can show me a way to make a 16 bit bitmap using the above method and using BI_BITFIELDS for RGB masks that would be GREAT!!! For some reason i cannot make it work.
I Dont understand the color masking, the doc's say that you should use 5 bits for blue, 5 for red, and 5 for green and the other bit isn't needed.
Quote:
When the biCompression member is BI_BITFIELDS, the system supports only the following 16bpp color masks:
A 5-5-5 16-bit image, where the blue mask is 0x001F, the green mask is 0x03E0, and the red mask is 0x7C00;
and a 5-6-5 16-bit image, where the blue mask is 0x001F, the green mask is 0x07E0, and the red mask is 0xF800
Where do you use the hex mask values at ????
Any help would be VERY much appreciated.
Things that must be changed for 16 bit
- Buffer's are WORD (unsigned short 16 bit) instead of BYTE (unsigned char)
- m_pBmih->biCompression = BI_BITFIELDS instead of BI_RGB
Regards,
Abadon