我是一个n00b,过去几天一直在关注这个问题,我只是被困住了。我正在使用OpenSuse Linux尝试使用Cairo图形库解释Windows位图图像以进行显示。简单地说,我只需要将每个像素的颜色信息放入一个数组中并将其输入Cairo,例如pixeldata [i] = someColor,用于图像中的所有像素。到目前为止,我已经想出如何解析位图标题并使其适用于显示24位位图。
但是,现在我也在努力让8位位图显示出来,而这只是一个笨拙,不直观的野兽。我能够显示图像,但显示的颜色是错误的......不仅如此,每次运行程序时它们都会改变! :P我想我正在错误地访问和解释bmiColors调色板数组。这是我从构建像素阵列的冗长的互联网研究中拼凑而来的相关代码(注意,在代码中,此时,头信息已经被解析并且在对象m_bmpInfoHeader和m_bmpHeader中可用):
#define RGB(r, g, b) ((long)(((char)(r) | ((char)((short)(g)) << 8)) | (((char)(b)) << 16 )))
#pragma pack (2)
typedef struct tagRGBQUAD {
long rgbBlue;
long rgbGreen;
long rgbRed;
int rgbReserved;
} RGBQUAD;
typedef struct
{
char verifier[2];
unsigned int size;
unsigned short int reserved1, reserved2;
unsigned int offset;
} BITMAPHEADER;
typedef struct
{
unsigned int size; /* Header size in bytes */
signed int width, height; /* Width and height of image */
unsigned short int planes; /* Number of colour planes */
unsigned short int bits; /* Bits per pixel */
unsigned int compression; /* Compression type */
unsigned int imagesize; /* Image size in bytes */
int xresolution,yresolution; /* Pixels per meter */
unsigned int ncolors; /* Number of colors */
unsigned int importantcolors; /* Important colors */
RGBQUAD bmiColors [1];
} BITMAPINFOHEADER;
#pragma pack()
// Function sets up and returns color index for bitmap.
long BitmapDef::GetColorInx (int numbits, char* data, long offset)
{
long inx;
switch (numbits)
{
case 1:
inx = data[offset >> 3];
offset &= 7;
inx >>= offset;
inx &= 0x01;
break;
case 2:
inx = data[offset >> 2];
offset &= 3;
offset <<= 1;
inx >>= offset;
inx &= 0x03;
break;
case 4:
inx = data[offset >> 1];
if (!(offset & 1))
{
inx >>= 4;
}
inx &= 0x0f;
break;
case 24:
{
offset *= 3;
inx = *((long*) &data[offset]);
char r = GetBValue(inx);
char g = GetGValue(inx);
char b = GetRValue(inx);
inx = ((r << 16) + (g << 8) + b);
break;
}
case 8:
default:
inx = data[offset] & 0xff;
break;
}
return inx;
}
void BitmapDef::Build8BitPixelData()
{
m_PixelData = new unsigned int[m_bmpInfoHeader.width * m_bmpInfoHeader.height];
FILE * pFile;
long lSize;
char * buffer;
size_t result;
pFile = fopen ((const char *)m_Filename, "rb" );
if (pFile==NULL)
{
fputs ("File error", stderr);
exit (1);
}
// obtain file size:
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
// allocate memory to contain the whole file:
buffer = (char*) malloc (sizeof(char) * lSize);
if (buffer == NULL) {fputs ("Memory error", stderr); exit (2);}
// copy the file into the buffer:
result = fread (buffer, 1, lSize, pFile);
if (result != lSize) {fputs ("Reading error",stderr); exit (3);}
BITMAPHEADER* bmfh = (BITMAPHEADER*) (&(buffer[0]));
char* bmp = (char*) &buffer[m_bmpHeader.offset];
BITMAPINFOHEADER * bmi = (BITMAPINFOHEADER*) &buffer[sizeof(*bmfh)];
std::cout<<"\nsize: "<<bmi->size<<std::endl;
std::cout<<"width: "<<bmi->width<<std::endl;
std::cout<<"height: "<<bmi->height<<std::endl;
std::cout<<"planes: "<<bmi->planes<<std::endl;
std::cout<<"bits: "<<bmi->bits<<std::endl;
std::cout<<"annnd compression: "<<bmi->planes<<std::endl;
int ix, iy;
int position = 0;
for (iy = 0; iy < bmi->height; ++iy)
{
for (ix = 0; ix < bmi->width; ++ix)
{
int offset = (m_bmpInfoHeader.height - 1 - iy) * m_bmpInfoHeader.width;
offset += ix;
//std::cout<<"offset: "<<offset<<" bmp[offset]: "<<bmp[offset] << " " ;
long inx = GetColorInx (m_bmpInfoHeader.bits, dataBuf, offset);
//std::cout<<inx<<" ";
m_PixelData[position] = RGB(bmi->bmiColors[inx].rgbRed, bmi->bmiColors[inx].rgbGreen, bmi->bmiColors[inx].rgbBlue);
position++;
}
}
fclose (pFile);
free (buffer);
}
有什么想法吗?请注意,我在非Windows环境中工作,所以我必须翻译许多以Windows为中心的函数才能在C ++中工作。感谢任何帮助,谢谢!
更新
感谢cbranch的建议,我将RGBQUAD修改为具有char属性而不是long / int,以使其保持4字节结构。这解决了颜色不断变化的问题。然而,奇怪的是颜色仍然是关闭的。目前我正在尝试在黑色背景上显示绿色钻石的简单图像,但由于某种原因,钻石显示为黄色。有什么想法吗?
另外,我只是注意到我不小心从原始帖子中的结构周围留下了“#pragma pack()”指令,并且刚刚将它们添加到帖子中。
答案 0 :(得分:3)
RGBQuad应该是四个字节。
这个宏:
#define RGB(r, g, b) ((long)(((char)(r) | ((char)((short)(g)) << 8)) | (((char)(b)) << 16 )))
有问题。你可能正在进行签名扩展。全绿色像素将具有g == 0xFF。你把它转换为(签名)短,所以你可能得到符号扩展(0xFFFF),然后转移。现在你已经将红色和绿色设置为完整值,因此它看起来很黄。
进行按位操作时,您几乎总是希望使用无符号值。