0

I wrote some quick code that is supposed to invert colors of a BMP image. I test with a 40x40 dimensional BMP image, created by paint. However the function seem to fill it entirelly with white pixels instead.

void
negate
(char *FILE_NAME)
{
    FILE* fp = fopen(FILE_NAME, "r+b");
    uint_64 raw_data, i;

    fseek(fp, 35, SEEK_SET);
    //raw_data = fgetc(fp) + fgetc(fp)*256;
    raw_data = 4800; // calculated

    fseek(fp, 54, SEEK_SET);
    for(i = 54; i != 54 + raw_data; i++) // <=
    {
        int old = fgetc(fp);
        fseek(fp, i, SEEK_SET);
        fputc(255 - old, fp);
    }

    fclose(fp);
}

Where am I mistaken?

Alan Salios
  • 241
  • 2
  • 9
  • Have you tried 1)read the data, 2)close the file and reopen it, 3) save the data. Reading a file and updating it's contents at the same time is tricky, at least for me. – R Sahu Jan 30 '15 at 03:05
  • I haven't tried. I usually don't try things that make no sense to me. ..or things I don't fully understand. – Alan Salios Jan 30 '15 at 03:11
  • possible duplicate of [why fseek or fflush is always required between reading and writing in the read/write "+" modes](http://stackoverflow.com/questions/1713819/why-fseek-or-fflush-is-always-required-between-reading-and-writing-in-the-read-w) – user253751 Jan 30 '15 at 04:18
  • 1
    the data image area of a .bmp file can (and usually is) larger than the pixel*pixel count. First because pixels are rarely ever only 1 byte (most are 3 bytes) (this info can be read from a field in one of the head blocks) Second because each row has to calculate to a 'evenly divisible by 4' length, which can (usually) results in the actual row length being longer than the pixel*pixel length count. Also, offset 54 is not 'set in stone' but is available from one of the fields in the first header block . – user3629249 Feb 01 '15 at 02:34

2 Answers2

3

The C11 draft specification has this to say about the problem

When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end- of-file.

user3386109
  • 34,287
  • 7
  • 49
  • 68
  • Thank you. I can't believe I missed this. Well I actually do believe, hence I am using C89 and the only reference I ever read is from cpp. – Alan Salios Jan 30 '15 at 03:49
2

Adding an fseek into the loop fixes it, although I don't understand why:

for (i = 54; i != 54 + raw_data; i++) // <=
{
    fseek(fp, i, SEEK_SET);
    int old = fgetc(fp);

    fseek(fp, i, SEEK_SET);
    fputc(255 - old, fp);
}

In trying to figure out the problem, I did this, to see if the position in the stream was correct:

for (i = 54; i != 54 + raw_data; i++) // <=
{
    long x = ftell(fp);
    printf("%d,", x);

    int old = fgetc(fp);

    fseek(fp, i, SEEK_SET);
    fputc(255 - old, fp);
}

And indeed it is. It ouputs 54,55,56,57,... so the fseek() should not be necessary! But if you look at the result of the fgetc(), the "old" value always seems to be reading the pixel at 54. I think @RSahu was correct: don't read and write like that to one file. Better to read the data into a buffer, then negate the buffer, then write it to disk. Or write to a different file entirely.

Maybe this has to do with buffering?

Moby Disk
  • 3,761
  • 1
  • 19
  • 38