]> git.sesse.net Git - fjl/commitdiff
Add unjpeg.c, frmo an earlier project.
authorSteinar H. Gunderson <sesse@debian.org>
Fri, 2 Jan 2009 10:54:32 +0000 (11:54 +0100)
committerSteinar H. Gunderson <sesse@debian.org>
Fri, 2 Jan 2009 10:54:32 +0000 (11:54 +0100)
unjpeg.c [new file with mode: 0644]

diff --git a/unjpeg.c b/unjpeg.c
new file mode 100644 (file)
index 0000000..a1e92b0
--- /dev/null
+++ b/unjpeg.c
@@ -0,0 +1,448 @@
+#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);
+               }
+       }
+}