--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+
+struct huffman_table {
+ unsigned num_codes[17]; /* BITS */
+ unsigned char codes[256]; /* HUFFVAL */
+
+ /* derived values */
+ unsigned huffsize[256];
+ unsigned huffcode[256];
+ int maxcode[16];
+ int mincode[16];
+ unsigned valptr[16];
+};
+struct huffman_table huffman_tables[2][4];
+
+struct mem_stream {
+ unsigned char *buf;
+ unsigned used, room;
+};
+struct mem_stream streams[128];
+
+void *xmalloc(size_t size)
+{
+ void *buf = malloc(size);
+ if (buf == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+
+ return buf;
+}
+
+static int numch = 0;
+
+unsigned char mygetc()
+{
+ int ch = getchar();
+ ++numch;
+ if (ch == EOF) {
+ if (feof(stdin)) {
+ fprintf(stderr, "Error: Unexpected end of file.\n");
+ exit(1);
+ } else {
+ perror("getchar()");
+ exit(1);
+ }
+ }
+
+ return ((unsigned char)ch);
+}
+
+void putchar_stream(int stream, unsigned char ch)
+{
+ int used = streams[stream].used;
+
+ if (streams[stream].used == streams[stream].room) {
+ streams[stream].room <<= 1;
+ streams[stream].buf = (unsigned char *)realloc(streams[stream].buf, streams[stream].room);
+ }
+
+ streams[stream].buf[streams[stream].used++] = ch;
+}
+
+void flush_stream(int stream)
+{
+ fwrite(streams[stream].buf, streams[stream].used, 1, stdout);
+ streams[stream].used = 0;
+}
+
+unsigned char mygetc_limit(unsigned *len)
+{
+ if (*len == 0) {
+ fprintf(stderr, "Error: Premature end of segment\n");
+ exit(1);
+ }
+ --*len;
+ return mygetc();
+}
+
+unsigned read_length()
+{
+ unsigned char len1 = mygetc();
+ unsigned char len2 = mygetc();
+ unsigned len = (len1 << 8) | len2;
+
+ if (len < 2) {
+ fprintf(stderr, "Error: Got length %u, expected at least 2\n", len);
+ exit(1);
+ }
+
+ return len;
+}
+
+void copy_segment(unsigned char marker_code)
+{
+ unsigned len = read_length();
+ unsigned i;
+
+ /* marker segment header */
+ putchar(0xff);
+ putchar(marker_code);
+ putchar(len >> 8);
+ putchar(len & 0xff);
+
+ for (i = 0; i < len - 2; ++i) {
+ putchar(mygetc());
+ }
+}
+
+void read_huffman_tables()
+{
+ unsigned len = read_length() - 2;
+
+ putchar(0xff);
+ putchar(0xda);
+ putchar(len >> 8);
+ putchar(len & 0xff);
+
+ for ( ;; ) {
+ unsigned char tc_th = mygetc_limit(&len);
+ unsigned table_class = tc_th >> 4;
+ unsigned table_dest = tc_th & 0x0f;
+ unsigned i, j, k, si;
+ struct huffman_table *tbl;
+
+ if (table_class != 0 && table_class != 1) {
+ fprintf(stderr, "Error: Unknown table class %u\n", table_class);
+ exit(1);
+ }
+ if (table_dest >= 4) {
+ fprintf(stderr, "Error: Unknown table destination %u\n", table_dest);
+ exit(1);
+ }
+
+ putchar(tc_th);
+ tbl = &huffman_tables[table_class][table_dest];
+
+ for (i = 0; i < 16; ++i) {
+ tbl->num_codes[i] = mygetc_limit(&len);
+ putchar(tbl->num_codes[i]);
+ fprintf(stderr, "%u ", tbl->num_codes[i]);
+ }
+ fprintf(stderr, "\ndumping tc=%u td=%u\n", table_class, table_dest);
+ for (i = 0, k = 0; i < 16; ++i) {
+ for (j = 0; j < tbl->num_codes[i]; ++j, ++k) {
+ tbl->codes[k] = mygetc_limit(&len);
+ putchar(tbl->codes[k]);
+ fprintf(stderr, "%u: len=%u, ind=%u: code=0x%02x\n", k, i+1, j, tbl->codes[k]);
+ }
+ }
+
+ /* generate derived tables */
+
+ /* generate_size_table (figure C.1) */
+ k = 0;
+ for (i = 0; i < 16; ++i) {
+ for (j = 0; j < tbl->num_codes[i]; ++j, ++k) {
+ tbl->huffsize[k] = i+1;
+ }
+ }
+ tbl->huffsize[k] = 0;
+ fprintf(stderr, "k=%u\n", k);
+
+ /* generate_code_table (figure C.2) */
+ si = tbl->huffsize[0];
+ for (i = 0, j = 0;; ) {
+ fprintf(stderr, "%u = %u [si=%u]\n", i, j, si);
+
+ tbl->huffcode[i++] = j++;
+ if (tbl->huffsize[i] == si) {
+ continue;
+ }
+
+ if (tbl->huffsize[i] == 0) {
+ break;
+ }
+
+ do {
+ j <<= 1;
+ } while (++si != tbl->huffsize[i]);
+ }
+
+ /* decoder_tables (figure F.15) */
+ for (i = 0, j = 0; i < 16; ++i) {
+ if (tbl->num_codes[i] == 0) {
+ tbl->maxcode[i] = -1;
+ continue;
+ }
+
+ tbl->valptr[i] = j;
+ //fprintf(stderr, "minind=%u\n", j);
+ tbl->mincode[i] = tbl->huffcode[j];
+ j += tbl->num_codes[i];
+ //fprintf(stderr, "maxind=%u\n", j-1);
+ tbl->maxcode[i] = tbl->huffcode[j - 1];
+
+ //fprintf(stderr, "%u: min=%u, max=%u\n", i, tbl->mincode[i], tbl->maxcode[i]);
+ }
+
+ if (len == 0) {
+ break;
+ }
+ }
+}
+
+/* procedure NEXTBIT */
+unsigned get_bit(unsigned *bits_available, unsigned char *bit_reservoir)
+{
+ unsigned bit;
+
+ if (*bits_available == 0) {
+ *bit_reservoir = mygetc();
+ //fprintf(stderr, "Read byte (0x%02x)\n", *bit_reservoir);
+ if (*bit_reservoir == 0xff) {
+ unsigned char ch = mygetc();
+ if (ch != 0) {
+ int i;
+ for (i = 0; i < 128; ++i) {
+ flush_stream(i);
+ }
+
+ fprintf(stderr, "Error: Couldn't handle marker 0x%02x\n", ch);
+ exit(1);
+ }
+ //fprintf(stderr, "Read stuff byte (%u)\n", ch);
+ }
+
+ *bits_available = 8;
+ }
+
+ bit = *bit_reservoir >> 7;
+ *bit_reservoir = (*bit_reservoir & 0x7f) << 1;
+ --*bits_available;
+
+ //fprintf(stderr, "%u\n", bit);
+
+ return bit;
+}
+
+/* procedure DECODE (figure F.16) */
+unsigned char get_huffman_byte(unsigned *bits_available, unsigned char *bit_reservoir, struct huffman_table* huff_tbl)
+{
+ unsigned code = get_bit(bits_available, bit_reservoir);
+ int i = 1;
+
+ while (code > huff_tbl->maxcode[i - 1] || huff_tbl->maxcode[i - 1] == -1) {
+ code = (code << 1) | get_bit(bits_available, bit_reservoir);
+ ++i;
+
+ if (i > 16) {
+ fprintf(stderr, "Error in Huffman decoding: Too long code (%x).numch=%u\n", code, numch);
+ exit(1);
+ }
+ }
+
+ /*fprintf(stderr, "h=%d => code=0x%02x\n", huff_tbl->valptr[i - 1] + code - huff_tbl->mincode[i - 1],
+ huff_tbl->codes[huff_tbl->valptr[i - 1] + code - huff_tbl->mincode[i - 1]]);*/
+ return huff_tbl->codes[huff_tbl->valptr[i - 1] + code - huff_tbl->mincode[i - 1]];
+}
+
+/* procedure RECEIVE */
+unsigned get_bits(unsigned num_bits, unsigned *bits_available, unsigned char *bit_reservoir)
+{
+ unsigned val = 0, i;
+
+ for (i = 0; i < num_bits; ++i) {
+ val <<= 1;
+ val |= get_bit(bits_available, bit_reservoir);
+ }
+
+ return val;
+}
+
+/* procedure EXTEND (figure F.12) */
+unsigned extend(int val, int bits)
+{
+ if (val < (1 << (bits - 1))) {
+ return val + (-1 << bits) + 1;
+ } else {
+ return val;
+ }
+}
+
+void read_scan()
+{
+ unsigned len = read_length() - 2;
+ unsigned bits_available;
+ unsigned char bit_reservoir;
+ unsigned num_components = mygetc_limit(&len);
+ unsigned i;
+ unsigned component_num[256];
+ unsigned dc_huffman_table[256], ac_huffman_table[256];
+ unsigned ss, se, ah_al;
+ int last_dc[256];
+ int last_ac[256];
+
+ for (i = 0; i < num_components; ++i) {
+ unsigned char td_ta;
+ component_num[i] = mygetc_limit(&len);
+ td_ta = mygetc_limit(&len);
+ dc_huffman_table[i] = td_ta >> 4;
+ ac_huffman_table[i] = td_ta & 0x0f;
+ last_dc[i] = 0;
+
+ {
+ unsigned j;
+ for (j = 0; j < 64; ++j) {
+ last_ac[j] = 0;
+ }
+ }
+ }
+
+ ss = mygetc_limit(&len);
+ se = mygetc_limit(&len);
+ ah_al = mygetc_limit(&len);
+
+ if (len != 0) {
+ fprintf(stderr, "Error: %u unused bytes at end of SOS segment\n", len);
+ }
+
+ bits_available = 0;
+
+ for ( ;; ) {
+ //fprintf(stderr, "\n");
+
+ int c;
+ for (c = 0; c < num_components; ++c) {
+ int zz[63];
+ /*fprintf(stderr, "coeff %u\n", c);
+ fprintf(stderr, "getting dc (table %u)\n", dc_huffman_table[c]); */
+
+ /* decode DC component */
+ unsigned char dc_category = get_huffman_byte(&bits_available, &bit_reservoir, &huffman_tables[0][dc_huffman_table[c]]);
+ int dc_diff;
+ int i;
+
+ dc_diff = get_bits(dc_category, &bits_available, &bit_reservoir);
+ last_dc[c] += extend(dc_diff, dc_category);
+
+ /*putchar_stream(0, last_dc[c] >> 8);
+ putchar_stream(0, last_dc[c] & 0xff); */
+ putchar_stream(0, dc_diff >> 8);
+ putchar_stream(0, dc_diff & 0xff);
+
+ for (i = 0; i < 63; ++i) {
+ zz[i] = 0;
+ }
+
+ for (i = 0; i < 63; ++i) {
+ unsigned char rs = get_huffman_byte(&bits_available, &bit_reservoir, &huffman_tables[1][ac_huffman_table[c]]);
+ unsigned char r = rs >> 4;
+ unsigned char s = rs & 0xf;
+
+ if (rs == 0x00) {
+ /* end of block */
+ break;
+ }
+ if (rs == 0xf0) {
+ /* 16 zero coefficients */
+ i += 15;
+ continue;
+ }
+
+ i += r;
+ zz[i] = extend(get_bits(s, &bits_available, &bit_reservoir), s);
+ }
+
+ for (i = 0; i < 63; ++i) {
+ int s = i;
+ if (s > 54) {
+ s = 54;
+ }
+
+ int m;
+ if (zz[i] >= 0) {
+ m = zz[i] * 2;
+ } else {
+ m = (1 - zz[i] * 2);
+ }
+ putchar_stream(s * 2 + 2, m >> 8);
+ putchar_stream(s * 2 + 3, m & 0xff);
+ }
+ }
+ }
+}
+
+int main(void)
+{
+ int i;
+ for (i = 0; i < 128; ++i) {
+ streams[i].buf = (unsigned char *)malloc(4096);
+ streams[i].used = 0;
+ streams[i].room = 4096;
+ }
+
+ for ( ;; ) {
+ unsigned char m1 = mygetc();
+ unsigned char m2 = mygetc();
+
+ if (m1 != 0xff) {
+ fprintf(stderr, "Error: Expected marker (0xff), got 0x%02x\n", m1);
+ exit(1);
+ }
+
+ switch (m2) {
+ case 0xe0:
+ case 0xe1:
+ case 0xe2:
+ case 0xe3:
+ case 0xe4:
+ case 0xe5:
+ case 0xe6:
+ case 0xe7:
+ case 0xe8:
+ case 0xe9:
+ case 0xea:
+ case 0xeb:
+ case 0xec:
+ case 0xed:
+ case 0xee:
+ case 0xef:
+ /* APP0 through APPF */
+ case 0xdb:
+ /* DQT */
+ case 0xc0:
+ /* SOF0 (baseline DCT, Huffman encoded) */
+ copy_segment(m2);
+ break;
+ case 0xd8:
+ /* SOI */
+ putchar(m1);
+ putchar(m2);
+ break;
+ case 0xc4:
+ /* DHT (define Huffman tables) */
+ read_huffman_tables();
+ break;
+ case 0xda:
+ /* SOS (start of scan) */
+ read_scan();
+ break;
+ default:
+ fprintf(stderr, "Error: Unknown marker 0x%02x\n", m2);
+ exit(1);
+ }
+ }
+}