#include /* Global read buffer */ static unsigned char buffer[256]; static char *signed_buffer = (char*)buffer; static int buffer_size; static int index; /* Image dimensions */ static int width, height; /* Input byte */ static int c; /* Read a single character from input */ static void GetChar(FILE *input) { c = EOF; if( index < buffer_size || ((buffer_size = fread(buffer, 1, sizeof(buffer), input)) > 0 && !(index = 0)) ) c = buffer[index++]; } /* Compare global buffer with string, returns nonzero if matched */ static int CompareHeader(int index, char *expected) { return index < buffer_size && *expected != '\0' ? signed_buffer[index] == *expected && CompareHeader(index + 1, expected + 1) : 1; } /* Get dimensions for a single file */ static void GetImageSize(FILE *input) { int x, y; #define INPUT_BYTE GetChar(input); if( c == EOF ) return; buffer_size = index = 0; width = height = 0; INPUT_BYTE if( CompareHeader(0, "GIF87a") || CompareHeader(0, "GIF89a") ) { /* GIF */ width = (buffer[7] << 8) | buffer[6]; height = (buffer[9] << 8) | buffer[8]; } else if( CompareHeader(0, "\xff\xd8") ) { /* JPG */ /* Skip SOI marker */ index = 2; for(;;) { /* Skip markers until we get a SOF marker */ for(c = 0; c != 0xff;) { INPUT_BYTE } /* Got the first byte of segment, read second marker byte */ GetChar(input); if( c >= 0xc0 && c <= 0xcf && c != 0xc4 ) { /* Found a SOF marker. Next 7 bytes: [0..1] = length [2] = precision [3..4] = width [5..6] = height */ for(x = 0; x < 3; x++) { INPUT_BYTE } INPUT_BYTE x = c << 8; INPUT_BYTE x |= c; INPUT_BYTE y = c << 8; INPUT_BYTE height = x; width = y | c; return; } /* Skip over this segment. Next 2 bytes contains payload length, including the 2 bytes for the length itself. */ INPUT_BYTE x = c << 8; INPUT_BYTE x |= c; for(x -= 2; x > 0; x--) { INPUT_BYTE } } } else if( CompareHeader(0, "\x89PNG\r\n\x1a\n") ) { /* PNG */ width = (buffer[16] << 24) | (buffer[17] << 16) | (buffer[18] << 8) | buffer[19]; height = (buffer[20] << 24) | (buffer[21] << 16) | (buffer[22] << 8) | buffer[23]; } else if( buffer[0] == 'P' && buffer[1] >= '1' && buffer[1] <= '7' ) { /* PNM or PAM, remove comments first. This assumes that the comment will fit entirely within the buffer, which is generally the case since GIMP appears to be the only program that bothers to insert these comments. */ for(index = 2; index < buffer_size; index++) { if( buffer[index] == '#' ) { for(; index < buffer_size && buffer[index] != '\n'; index++) buffer[index] = ' '; buffer[index] = ' '; } } /* Make sure sscanf doesn't go past end of buffer */ buffer[sizeof(buffer) - 1] = '\0'; if( buffer[1] == '7' ) { /* PAM */ if( sscanf(signed_buffer, "P7 WIDTH %d HEIGHT %d", &width, &height) != 2 ) width = height = 0; } else { /* PNM */ if( sscanf(signed_buffer, "P%d %d %d", &index, &width, &height) != 3 ) width = height = 0; } } } int main(int argc, char **argv) { int i; FILE *input; if( argc == 1 ) { GetImageSize(stdin); printf("%d %d\n", width, height); } else { for(i = 1; i < argc; i++) { if( argv[i][0] == '-' && argv[i][1] == '\0' ) { GetImageSize(stdin); } else { if( (input = fopen(argv[i], "rb")) != NULL ) { GetImageSize(input); fclose(input); } } printf("%d %d %s\n", width, height, argv[i]); } } return 0; }