pointer file randomly changes value in middle of reading raw bitmap data

后端 未结 2 1767
灰色年华
灰色年华 2020-12-12 00:29

I am currently working on loading a bitmap file in C. I am kind of new to C, and I ran into a problem: I have a file pointer that reads unsigned chars to a struct of rgb pix

相关标签:
2条回答
  • 2020-12-12 01:07

    Reading in bitmaps is always a difficult thing.

    There are quite some points to consider.

    fread(&im[i][j].res, sizeof(unsigned char), 1, filePtr);
    

    With this line your read the reserved byte of an RGBQUAD from the file. However, this member is not in the file. The image data in the file contains scanlines. See below.

    After reading the Bitmap File Header, you open the file again. However, you didn't close it. This might be succesful or it fails because the file was already open. But you don't chek the return value of fopen. Anyway, there is no need to do this because after having read the BFH the filepointer is positioned at the BITMAPINFOHEADER; you can just read it in. And you need to read it in, otherwise you won't know the dimensions of the bitmap.

    From MSDN documentation:

    The established bitmap file format consists of a BITMAPFILEHEADER structure followed by a BITMAPINFOHEADER [...] structure. An array of RGBQUAD structures (also called a color table) follows the bitmap information header structure. The color table is followed by a second array of indexes into the color table (the actual bitmap data).

    For 24 bits-per-pixel bitmaps, there is no color table.

    so the sequence is now:

        //read the bitmap file header
        fread(&bfh, sizeof(bfh), 1, filePtr);
    
        //read the bitmap info header
        fread(&bih, sizeof(bih), 1, filePtr);
    
        int bpp= bih.biBitCount;
        if (bpp != 24) return 0;  // error: must be 24 bpp/ 3 bytes per pixel
    

    Now we must calculate the amount of memory needed to store the image. An image consists of heighth rows of width pixels. The rows are called scanlines.

    For some reason, a scanline is alligned on a 4 byte boundary. This means that the last bytes of a scanline may not be in use. So the amount of memory to read the bitmap data from the file is the heigth of the image, times the number of scanlines of the image:

    // WIDTHBYTES takes # of bits in a scanline and rounds up to nearest word.
    #define WIDTHBYTES(bits)    (((bits) + 31) / 32 * 4)
    
        int size= bih.biHeight * WIDTHBYTES(bih.biWidth*32));
        unsigned char *im= malloc(size);
    

    Finally you can read the data. If there was a color table, you should have skipped that with a seek but since there is none, you can now read the data:

        fread(im, size, 1, filePtr);
    

    Now, to address the pixels you cannot use a simple two-dimensional notation governed by width and heigth of the image...due to the scanlines. You can use the following:

        int scanlineSize= WIDTHBYTES(bih.biWidth*bih.biBitCount);
        unsigned char *p, *scanline= im;
    
        for (int i=0; i<bih.biHeight; i++)
        {
            p= scanline;
            for (int j=0; j<bih.biWidth; j++)
            {
                g= *p++;
                b= *p++;
                r= *p++;
            }
            scanline += scanlineSize;
        }
    

    So to address a 3-byte pixel (x,y) use: im[y*scanlineSize + x*3]

    .. except that I believe that scanlines are reversed, so the pixel would be at im[(bih.biHeight-y)*scanlinesize + x*3].


    UPDATE: complete function

    #include <winGDI.h>
    // WIDTHBYTES takes # of bits in a scanline and rounds up to nearest word.
    #define WIDTHBYTES(bits)    (((bits) + 31) / 32 * 4)
    
    unsigned char *readBitmap(char *szFilename)
    {
        BITMAPFILEHEADER bfh;
        BITMAPINFOHEADER bih;
        int i, j, size, scanlineSize;
        unsigned char r, g, b, *p, *img, *scanline;
        FILE *filePtr;
    
        if ((filePtr=fopen(szFilename, "rb"))==0) return 0;
    
        //read the bitmap file header
        if (fread(&bfh, sizeof(bfh), 1, filePtr)!=1
        ||  bfh.bfType != 'MB') {fclose(filePtr); return 0;}
    
        //read the bitmap info header
        if (fread(&bih, sizeof(bih), 1, filePtr)!=1
        ||  bih.biSize!=sizeof(bih)) {fclose(filePtr); return 0;}
    
        if (bih.biBitCount != 24) {fclose(filePtr); return 0;}  // error: must be 24 bpp/ 3 bytes per pixel
    
        // allocate memory and read the image
        scanlineSize= WIDTHBYTES(bih.biWidth * bih.biBitCount);
        size= bih.biHeight * scanlineSize;
        if ((img= malloc(size))==0) {fclose(filePtr); return 0;}
        if (fread(img, size, 1, filePtr)!=1) {free (img); fclose(filePtr); return 0;}
        fclose(filePtr);
    
        scanline= img;
        for (i=0; i<bih.biHeight; i++)
        {
            p= scanline;
            for (j=0; j<bih.biWidth; j++)
            {
                g= *p++;
                b= *p++;
                r= *p++;
            }
            scanline += scanlineSize;
        }
        return img;
    }
    
    0 讨论(0)
  • 2020-12-12 01:27

    close filePtr before opening file again

    fclose(filePtr);
    filePtr = fopen("C:\\Users\\mishe\\Desktop\\white.bmp", "rb");
    

    and offset to raw data is bfh.offset

      //point the pointer file to the start of the raw data
        fseek(filePtr, bfh.offset, SEEK_SET);
    
    0 讨论(0)
提交回复
热议问题