Bitmap Writing Coming Out Diagonally

2019-08-13 00:17发布

问题:

My code as follows is giving me issues trying to write a bitmap image with an odd size 250x250 pixels. When I write it with an evenly divisibly by 3 area, eg. 160x160 everything is fine. I have spent a few hours now trying to get the 250x250 and other odd area's to write correctly but they just won't, the image is diagonal and half of it with the wrong color bits.

a busy cat http://oi40.tinypic.com/2zxuidw.jpg

    int screen_save_part(string filename,unsigned x,unsigned y,unsigned w,unsigned h) //Assumes native integers are little endian
    {
        unsigned sz = w * h;
        FILE *bmp=fopen(filename.c_str(), "wb");
        if (!bmp) return -1;
        fwrite("BM", 2, 1, bmp);

        std::vector<unsigned char> rgbdata(3*sz);
        glReadPixels(x,window_get_region_height_scaled()-h-y,w,h,GL_BGR, GL_UNSIGNED_BYTE, &rgbdata[0]);
        //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo);

        sz <<= 2;
        fwrite(&sz,4,1,bmp);
        fwrite("\0\0\0\0\x36\0\0\0\x28\0\0",12,1,bmp);
        fwrite(&w,4,1,bmp);
        fwrite(&h,4,1,bmp);
        fwrite("\1\0\x18\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",28,1,bmp);

        if (w & 3) {
            w *= 3;
            size_t pad = 3 - (w % 3);
            sz -= sz >> 2;

            for(unsigned i = 0; i <= sz; i += w) {
                fwrite(&rgbdata[0] + i,sizeof(char),w,bmp);
                fwrite("\0\0\0",sizeof(char),pad,bmp);
            }

        } else { fwrite(&rgbdata[0], w*3, h, bmp); }

        fclose(bmp);
        rgbdata.clear();
        return 0;
    }

Ok, but yah I am about to start pulling my hair out here I have gone through literally everything the closest I got was a for loop after that one without any padding and just added an extra row of null data but that gave me a black line on the very top 1px scanline of the image, but it had no stretching issue.

回答1:

You want the stride between scanlines (in terms of bytes) to be a DWORD multiple. What you are doing right now is based entirely on the number of pixels (at least the if (w & 3) branch suggests this) and not the number of bytes-per-pixel. I would expect to see a test for (w * 3) % 4 if you have a 24-bpp pixel. If this value is > 0, then you need to add that many bytes (per-scanline) to satisfy alignment.

Try replacing your branch:

if (w & 3) {
    ...
}

With something more along the lines of this (24-bpp image):

int scanline_padding = (w * 3) % 4; // This will be a value from 0-3

// DWORD alignment not satisfied, for each scanline add [scanline_padding] bytes
if (scanline_padding > 0) {
  for(unsigned i = 0; i < h; i++) {
    fwrite(&rgbdata[0] + (i * 3 * w),sizeof(char)*3,w,bmp); // Nothing special here
    fwrite("\0\0\0", scanline_padding, 1, bmp);             // Now for the magic
  }
}

// DWORD alignment was satisfied, so we can write the entire thing all at once
else {
  fwrite(&rgbdata[0], w*3, h, bmp);
}

This is untested, but should work, or should at least give you some general direction...