#include #include #include /* define DEBUG to get debugging output to stderr * 0: no debug output * 1: summary line (default) * 2: 1 + skipped markers * 3: 2 + all markers */ #ifndef DEBUG #define DEBUG 1 #endif int main(int argc, char **argv) { int i, c, keep, scan, marker, bytes, skipped; unsigned short len; /* read JPEG SOI */ c = getchar(); assert(c == 0xff); putchar(c); c = getchar(); assert(c == 0xd8); putchar(c); bytes = 2; skipped = 0; /* do we have to scan for the next marker? (see page 37) */ scan = 0; /* iterate over all blocks, see: http://www.w3.org/Graphics/JPEG/itu-t81.pdf pages 31 and 32 */ while(!feof(stdin)) { /* do we keep this marker or toss it out? */ keep = 0; /* if we were scanning, the marker has already been consumed */ if(!scan) { /* markers can be preceded by any number of FF padding bytes, but don't *have* to */ while((c = getchar()) == 0xff) skipped += 1; skipped -= 1; #if DEBUG > 2 fprintf(stderr, "marker 0xff%02x\n", c); #endif } else { scan = 0; } marker = c; switch(c) { /* EOI does not have a length */ case 0xD9: { fputs("\xFF\xD9", stdout); bytes += 2; #if DEBUG fprintf(stderr, "in=%d bytes, skipped=%d bytes, out=%d bytes, saved %4.2f%%\n", bytes+skipped, skipped, bytes, 100.0*skipped/(bytes+skipped)); #endif return 0; } /* these non-segment markers have no length and should be skipped */ case 0xD0: case 0xD1: case 0xD2: case 0xD3: case 0xD4: case 0xD5: case 0xD6: case 0xD7: { skipped += 2; #if DEBUG > 1 fprintf(stderr, "skipped marker 0xff%02x (2 bytes)\n", marker); #endif break; } /* SOS means we have to scan */ case 0xDA: scan = 1; /* FALLTHROUGH */ /* these segment markers will be let through */ case 0xC0: case 0xC1: case 0xC2: case 0xC3: case 0xC4: case 0xC5: case 0xC6: case 0xC7: case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF: /* we skip restart markers 0xFFD0-7 */ case 0xDB: case 0xDC: /* we skip restart interval 0xFFDD */ case 0xDE: case 0xDF: /* this one is defined by JFIF */ case 0xe0: keep = 1; /* FALLTHROUGH */ /* these markers do not have a length associated with them */ case 0x01: { putchar('\xff'); putchar(c); bytes += 2; /* non-segment markers need not continue */ if (!keep) break; /* segment markers fallthrough */ } default: { c = getchar(); assert(c != -1); if(keep) putchar(c); len = c << 8; c = getchar(); assert(c != -1); if(keep) putchar(c); len += c - 2; /* the marker length includes the 2 bytes for the length itself */ if(keep) { bytes += 2; } else { skipped += 4; } for(i=0; i 1 if(!keep) { fprintf(stderr, "skipped marker 0xff%02x (%d bytes)\n", marker, len + 2); } #endif if(scan) { while(!feof(stdin)) { c = getchar(); assert(c != -1); if(c != 0xff) { putchar(c); bytes += 1; } else { c = getchar(); assert(c != -1); if(c == 0) { /* "stuffed 0xFF00 is a way of escaping 0xFFs that occur naturally in the Huffman or arithmetic-coded data */ putchar('\xff'); putchar('\x00'); bytes += 2; } else if (c >= 0xd0 && c <= 0xd7) { /* 0xFFD0-0xFFD7 are restart markers that can occur within an entropy-coded block - we skip them */ skipped += 2; #if DEBUG > 1 fprintf(stderr, "skipped restart marker 0xff%02x (2 bytes)\n", c); #endif } else break; /* we have found the next marker */ } } } } } } return 1; }