Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/155.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 按土壤保存位图时BMP已损坏。截图区_C++_Winapi_Opengl_Soil - Fatal编程技术网

C++ 按土壤保存位图时BMP已损坏。截图区

C++ 按土壤保存位图时BMP已损坏。截图区,c++,winapi,opengl,soil,C++,Winapi,Opengl,Soil,这是我关于将截图保存到土壤的最后一个问题的继续。现在我想知道,如何制作屏幕部分的截图,消除奇怪行为的原因。我的代码: bool saveTexture(string path, glm::vec2 startPos, glm::vec2 endPos) { const char *charPath = path.c_str(); GLuint widthPart = abs(endPos.x - startPos.x); GLuint heightPart = abs(en

这是我关于将截图保存到土壤的最后一个问题的继续。现在我想知道,如何制作屏幕部分的截图,消除奇怪行为的原因。我的代码:

bool saveTexture(string path, glm::vec2 startPos, glm::vec2 endPos)
{
   const char *charPath = path.c_str();

   GLuint widthPart = abs(endPos.x - startPos.x); 
   GLuint heightPart = abs(endPos.y - startPos.y);

   BITMAPINFO bmi;
   auto& hdr = bmi.bmiHeader;
   hdr.biSize = sizeof(bmi.bmiHeader);
   hdr.biWidth = widthPart;
   hdr.biHeight = -1.0 * heightPart;
   hdr.biPlanes = 1;
   hdr.biBitCount = 24;
   hdr.biCompression = BI_RGB;
   hdr.biSizeImage = 0;
   hdr.biXPelsPerMeter = 0;
   hdr.biYPelsPerMeter = 0;
   hdr.biClrUsed = 0;
   hdr.biClrImportant = 0;

   unsigned char* bitmapBits = (unsigned char*)malloc(3 * widthPart * heightPart);

   HDC hdc = GetDC(NULL);
   HDC hBmpDc = CreateCompatibleDC(hdc);
   HBITMAP hBmp = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**)&bitmapBits, nullptr, 0);
   SelectObject(hBmpDc, hBmp);
   BitBlt(hBmpDc, 0, 0, widthPart, heightPart, hdc, startPos.x, startPos.y, SRCCOPY);

   //UPDATE:
   - int bytes = widthPart * heightPart * 3;
   - // invert R and B chanels
   - for (unsigned i = 0; i< bytes - 2; i += 3)
   - {
   -   int tmp = bitmapBits[i + 2];
   -   bitmapBits[i + 2] = bitmapBits[i];
   -   bitmapBits[i] = tmp;
   - }

   + unsigned stride = (widthPart * (hdr.biBitCount / 8) + 3) & ~3;
   + // invert R and B chanels
   + for (unsigned row = 0; row < heightPart; ++row) {
   +     for (unsigned col = 0; col < widthPart; ++col) {
   +         // Calculate the pixel index into the buffer, taking the 
             alignment into account
   +         const size_t index{ row * stride + col * hdr.biBitCount / 8 };
   +         std::swap(bitmapBits[index], bitmapBits[index + 2]);
   +      }
   + }

   int texture = SOIL_save_image(charPath, SOIL_SAVE_TYPE_BMP, widthPart, heightPart, 3, bitmapBits);

   return texture;
}
输出文件功能:

static int outfile(char const *filename, int rgb_dir, int vdir, int x, int 
y, int comp, void *data, int alpha, int pad, char *fmt, ...)
{
   FILE *f = fopen(filename, "wb");
   if (f) {
      va_list v;
      va_start(v, fmt);
      writefv(f, fmt, v);
      va_end(v);
      write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
      fclose(f);
   }
   return f != NULL;
}

损坏的位图图像是Windows位图之间的数据布局与土壤库所期望的不一致的结果1。从
CreateDIBSection
返回的像素缓冲区遵循Windows规则(请参阅):

扫描线是双字对齐的[…]。它们必须填充扫描线宽,以字节为单位,不能被四[…]平均整除

换句话说:每个扫描线的宽度(以字节为单位)是
(biWidth*(bibibitcount/8)+3)和~3
。另一方面,土壤库不希望像素缓冲区是DWORD对齐的

为了解决这个问题,像素数据需要在传递到土壤之前进行转换,方法是剥离(潜在)填充并交换R和B颜色通道。以下代码就地执行此操作2:

无符号步幅=(widthPart*(hdr.bibibitcount/8)+3和~3;
对于(无符号行=0;行<高度部分;++行){
for(无符号列=0;列<宽度部分;++列){
//计算源像素索引,考虑对齐
const size_t index_src{row*stride+col*hdr.bibibitcount/8};
//计算目标像素索引(无对齐)
常量大小索引{(行*宽+列)*(hdr.biBitCount/8)};
//读取彩色通道
常量无符号字符b{bitmapBits[index_src]};
常量无符号字符g{bitmapBits[index_src+1]};
常量无符号字符r{bitmapBits[index_src+2]};
//写入切换R和B的颜色通道,并移除填充
bitmapBits[index_dst]=r;
bitmapBits[index_dst+1]=g;
bitmapBits[index_dst+2]=b;
}
}
使用此代码,
index\u src
是像素缓冲区的索引,其中包括填充以强制执行正确的DWORD对齐
index_dst
是未应用任何填充的索引。将像素从
index_src
移动到
index_dst
可移除(潜在)填充


1指示灯标志是向左或向右移动一个或两个像素的扫描线(或以不同速度移动的单个颜色通道)。这通常是一个安全的指示,表明扫描线对齐不一致。

2此操作具有破坏性,即像素缓冲区在转换后无法再传递给Windows GDI函数,尽管原始数据可以重建,即使涉及更多。

非常感谢您的帮助,但我尝试了不同的变体您的代码(在编辑之前),它没有任何变化。

((每次宽度部分不能被4整除时(无论高度部分是多少)我得到了上面的BMP。我用你的部分更新了这个问题。@hardCode:请不要接受不能解决你问题的答案,也不要编辑你的问题使之不同。相反,发表评论要求澄清。我猜,这个问题仍然与位图图像中的扫描线对齐有关。可能是
sOIL_save_image
不希望缓冲区是DWORD对齐的。我们可以查看
SOIL_save_image
的文档吗?我更新了质量。在SOIL文档中,没有关于写入BMP的任何内容:
static int outfile(char const *filename, int rgb_dir, int vdir, int x, int 
y, int comp, void *data, int alpha, int pad, char *fmt, ...)
{
   FILE *f = fopen(filename, "wb");
   if (f) {
      va_list v;
      va_start(v, fmt);
      writefv(f, fmt, v);
      va_end(v);
      write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
      fclose(f);
   }
   return f != NULL;
}
unsigned stride = (widthPart * (hdr.biBitCount / 8) + 3) & ~3;

for (unsigned row = 0; row < heightPart; ++row) {
    for (unsigned col = 0; col < widthPart; ++col) {
        // Calculate the source pixel index, taking the alignment into account
        const size_t index_src{ row * stride + col * hdr.biBitCount / 8 };
        // Calculate the destination pixel index (no alignment)
        const size_t index_dst{ (row * width + col) * (hdr.biBitCount / 8) };
        // Read color channels
        const unsigned char b{ bitmapBits[index_src] };
        const unsigned char g{ bitmapBits[index_src + 1] };
        const unsigned char r{ bitmapBits[index_src + 2] };
        // Write color channels switching R and B, and remove padding
        bitmapBits[index_dst] = r;
        bitmapBits[index_dst + 1] = g;
        bitmapBits[index_dst + 2] = b;
    }
}