user2883793
user2883793

Reputation: 23

C, trouble reading .bmp

Here's the code:

#include <stdio.h> 
#include <stdlib.h> 

void main(void) 
{ 
  char path_bmp [100]; 
  char path_txt [100]; 
  int w; //width 
  int h; //height 
  int wc; //width counter 
  int hc; //height counter 
  FILE *bfp; //BMP pointer 
  FILE *tfp; //TXT pointer 

  puts("BMP path"); 
  gets(path_bmp); 
  puts("Resulting TXT path"); 
  gets(path_txt); 

  bfp = fopen(path_bmp, "r"); 
  tfp = fopen(path_txt, "w"); 

  fseek(bfp, 18, SEEK_SET); 
  fscanf(bfp, "%i", &w); 
  fseek(bfp, 4, SEEK_CUR); 
  fscanf(bfp, "%i", &h); 
  printf("%i x %i", w, h); 

  char mat [w][h]; 
  fseek(bfp, 54, SEEK_SET); 

  for(hc=0; hc < h; hc++)
      fread(mat, 1, w, bfp); 

  for(hc=0; hc < h; hc++) 
      for(wc=0; wc < w; wc++) 
      { 
          if (mat [wc][hc] == 0) 
              fprintf(tfp, " "); 
          else
              fprintf(tfp, "o"); 
      } 

  fprintf(tfp, "\n"); 
} 

What it's intended to do: It converts a .bmp into ASCII-art, not very complicated, just pretty simple black and white pics. It should read .bmp, get height and width from header, read all pixels byte by byte into matrix(mat array), then write space into text file if pixel is white and write "0" if pixel is any color but white.

What it actually does: It is reading w and h from some wrong place. On printf("%i x %i", w, h); it prints out some large numbers (I use small pics for testing, like 10x6 px) and then crashes, with process returning even larger number. As far as I can see, the program is clearly reading from some garbage instead of where it should read and I can't get why (spent lots of time trying to figure it out).

Also, I have a feel that I'm doing something wrong when reading to array/writing from array, so pointing out those flaws would be greatly appreciated.

UPD: Thank you, guys, you helped me greatly!

Upvotes: 2

Views: 2200

Answers (2)

fvdalcin
fvdalcin

Reputation: 1047

There appear to have some problems with your code, including some basic stuff about C. I commented some errors that I caught at a first glance, but there may be some more. I would also recommend you to take a look at the bitmap format, specially at the section that describes the padding added to the end of each line.

#include <stdio.h> 
#include <stdlib.h> 

void main(void) 
{ 
    char path_bmp [100]; 
    char path_txt [100]; 
    int w; //width 
    int h; //height 
    int wc; //width counter 
    int hc; //height counter 
    FILE *bfp; //BMP pointer 
    FILE *tfp; //TXT pointer 

    puts("BMP path"); 
    gets(path_bmp); 
    puts("Resulting TXT path"); 
    gets(path_txt); 

    bfp = fopen(path_bmp, "r"); 
    tfp = fopen(path_txt, "w"); 

    fseek(bfp, 18, SEEK_SET); 
    fread(&w, sizeof(int), 1, bfp); // Read the width in binary format (signed integer)
    fread(&h, sizeof(int), 1, bfp); // The position indicator of the stream is advanced by the total amount of bytes read (sizeof(int)).

    printf("%i x %i", w, h); 

    pixel mat [w][h]; //This can be a packed structure or a union, so you can read all 3 color components with one fread() call

    fseek(bfp, 54, SEEK_SET); 

    for(hc=0; hc < h; hc++)
    {
        for(wc=0; wc < w; wc++) 
        { 
            /* Note that you have to read three bytes, since every pixel has 3 color components*/
            fread(mat[wc][hc].red, sizeof(char), 1, bfp);
            fread(mat[wc][hc].green, sizeof(char), 1, bfp);
            fread(mat[wc][hc].blue, sizeof(char), 1, bfp);
        }
        // you need to do a fseek() here, in order to advance the padding added to the end of each line defined in .BMP format
        fprintf(tfp, "\n"); 
    }
} 

Upvotes: 2

Kninnug
Kninnug

Reputation: 8053

fscanf with %i reads an integer as a sequence of ASCII digits, but the width & height of the BMP are stored in raw binary. Use fread instead to read them:

fread(&w, 1, 4, bfp); 
fread(&h, 1, 4, bfp);

(fseek isn't necessary before reading the height as the previous fread already advances the stream pointer. Which is also the case for fscanf)

As the width & height are specified to be 4 bytes it's recommended to use int32_t instead of int as the type to make sure it will always fit exactly.

Upvotes: 2

Related Questions