C++ 从 8 位位图构建像素数据并访问 bmiColor 表信息

Posted

技术标签:

【中文标题】C++ 从 8 位位图构建像素数据并访问 bmiColor 表信息【英文标题】:C++ building pixel data from 8-bit bitmap and getting access to bmiColor table information 【发布时间】:2013-02-11 16:59:06 【问题描述】:

我是一个 n00b,过去几天一直在研究这个问题,但我只是卡住了。我在 OpenSuse Linux 中工作,试图解释 Windows 位图图像以使用 Cairo 图形库进行显示。简单地说,我只需要将每个像素的颜色信息放入一个数组中并将其提供给 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()”指令从原始帖子中的结构周围遗漏了,现在刚刚将它们添加到帖子中。

【问题讨论】:

RGBQUAD的成员是单字节,不是longs。整个 RGBQUAD 结构为 4 个字节。 @cbranch - 谢谢,这解决了其中一个问题,因此现在每次运行程序时颜色都不会改变。但是,显示的颜色仍然关闭。 ATM 我试图在黑色背景上显示一个绿色钻石的小而简单的图像,但绿色钻石却变成了黄色。有什么想法吗? 听起来RGBQUAD结构是倒退的。 @Mark Ransom - 你的意思是重新排列 RGBQUAD 属性的顺序吗?我刚试过,奇怪的是它对显示的图像没有任何影响。它仍然是黄色钻石而不是绿色钻石。 那真的很奇怪。绿色只有 R、G、B 中的一个,而黄色有两个。如果您从结构的未使用字节中获取一个值,则可以对其进行解释,这就是我提出建议的原因。扭转它肯定会做出改变。 【参考方案1】:

RGBQuad 应该是四个字节。

这个宏:

#define RGB(r, g, b) ((long)(((char)(r) | ((char)((short)(g)) << 8)) | (((char)(b)) << 16 )))

有问题。你可能会得到符号扩展。一个完整的绿色像素将具有 g == 0xFF。您将其转换为 (signed) short,因此您可能会得到符号扩展 (0xFFFF),然后移位。现在您已将红色和绿色设置为完整值,因此它看起来是黄色的。

在进行按位操作时,您几乎总是希望使用无符号值。

【讨论】:

这似乎比我自己对反向 RGB 结构的猜测要合理得多。我已经这样做了很长时间,我自动使用无符号值,我什至从未想过要查看那里。 @Adrian - 就是这样,非常感谢! :D 啊,我已经对代码进行了如此多的修改,有一次我确实将 RGB 函数中的强制转换为“无符号”,但当它们似乎没有改变任何东西时,我将它们取出。无论如何,他们在代码开发的这一点上有所作为。谢谢大家!

以上是关于C++ 从 8 位位图构建像素数据并访问 bmiColor 表信息的主要内容,如果未能解决你的问题,请参考以下文章

图片数据随笔

C#位图从像素中读取不正确的颜色

C++:读取位图图像的问题

从位图图像文件中读取像素数据值

Android11_图片处理

从数字数组创建 8 位位图