From: Steinar H. Gunderson Date: Fri, 2 Jan 2009 10:54:32 +0000 (+0100) Subject: Add unjpeg.c, frmo an earlier project. X-Git-Url: https://git.sesse.net/?p=fjl;a=commitdiff_plain;h=43beb37c8f7723f463af151695d9f176898a4fa6 Add unjpeg.c, frmo an earlier project. --- diff --git a/unjpeg.c b/unjpeg.c new file mode 100644 index 0000000..a1e92b0 --- /dev/null +++ b/unjpeg.c @@ -0,0 +1,448 @@ +#include +#include + +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); + } + } +}