Click to See Complete Forum and Search --> : I need Help with 16 bit BITMAPS


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

tangzibo
May 4th, 1999, 09:45 PM
If the color masks is 5-5-5 and RGB(0~255)

Pixel to RGB
blue = (pixel & 0x001f) << 3;
red = (pixel & 0x3e0) >> 2;
green = (pixel & 0x7c00) >> 7;

RGB to Pixel
pixel = (blue >> 3) + (red << 2) + (green << 7)

Abadon
May 5th, 1999, 10:55 AM
Yeah i understand how to shift bits for conversion from 1 pixel BYTE to 1 pixel WORD. The thing that im having trouble with is in the BITMAPINFOHEADER, where do i specify the defining of the 5-5-5 color masks. That is if im not using a RGBQUAD to define the color table, i must use the 5-5-5 color masks, according to the documentation on BITMAPINFOHEADER in DevStudio.

tangzibo
May 5th, 1999, 10:11 PM
8bit color is an index color, the data in buf is a position of the color table(RGBQUAD), 16bit color is a direct color, the data in buf is the RGB value.