]> git.sesse.net Git - fjl/blobdiff - dehuff.c
Port some table-generating stuff from unjpeg.
[fjl] / dehuff.c
diff --git a/dehuff.c b/dehuff.c
new file mode 100644 (file)
index 0000000..3e0fd6c
--- /dev/null
+++ b/dehuff.c
@@ -0,0 +1,127 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "dehuff.h"
+
+void reliable_read(raw_input_func_t* input_func, void* userdata, uint8_t* buf, size_t len)
+{
+       while (len > 0) {
+               ssize_t bytes_read = input_func(userdata, buf, len);
+               assert(bytes_read <= len);
+
+               // TODO: We need better error handling here. setjmp()/longjmp()
+               // should hopefully do the trick, but we need to take care for
+               // suspension.
+               if (bytes_read == (ssize_t)-1) {
+                       fprintf(stderr, "Input function returned error\n");
+                       exit(1);
+               }
+               if (bytes_read == 0) {
+                       fprintf(stderr, "Premature EOF\n");
+                       exit(1);
+               }
+
+               buf += bytes_read;
+               len -= bytes_read;
+       }
+}
+
+uint16_t read_length(raw_input_func_t* input_func, void* userdata)
+{
+       uint8_t buf[2];
+       reliable_read(input_func, userdata, buf, 2);
+       return (buf[0] << 8) | buf[1];
+}
+
+void read_huffman_tables(huffman_tables_t* dst, raw_input_func_t* input_func, void* userdata)
+{
+       size_t len = read_length(input_func, userdata);
+       assert(len > 2);
+       len -= 2;
+
+       // TODO: check for NULL return
+       uint8_t* buf = (uint8_t*)malloc(len);
+       uint8_t* inptr = buf;
+       reliable_read(input_func, userdata, buf, len);
+       
+       while (len > 0) {
+               unsigned char tc_th = *inptr++;
+               --len;
+               unsigned table_class = tc_th >> 4;
+               unsigned table_dest = tc_th & 0x0f;
+               
+               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);
+               }
+
+               struct huffman_table* tbl = dst[table_class][table_dest];
+               if (len < 16) {
+                       fprintf(stderr, "Short read for num_codes\n");
+                       exit(1);
+               }
+               for (unsigned i = 0; i < 16; ++i) {
+                       tbl->num_codes[i] = *inptr++;
+                       --len;
+               }
+               for (unsigned i = 0, k = 0; i < 16; ++i) {
+                       if (len < tbl->num_codes[i]) {
+                               fprintf(stderr, "Short read for codes\n");
+                               exit(1);
+                       }
+                       for (unsigned j = 0; j < tbl->num_codes[i]; ++j, ++k) {
+                               tbl->codes[k] = *inptr++;
+                               --len;
+                       }
+               }
+
+               // Generate derived tables
+
+               // generate_size_table (figure C.1)
+               {
+                       unsigned k = 0;
+                       for (unsigned i = 0; i < 16; ++i) {
+                               for (unsigned j = 0; j < tbl->num_codes[i]; ++j, ++k) {
+                                       tbl->huffsize[k] = i + 1;
+                               }
+                       }
+                       tbl->huffsize[k] = 0;
+               }
+
+               // generate_code_table (figure C.2)
+               unsigned si = tbl->huffsize[0];
+               for (unsigned i = 0, j = 0;; ) {
+                       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 (unsigned i = 0, j = 0; i < 16; ++i) {
+                       if (tbl->num_codes[i] == 0) {
+                               tbl->maxcode[i] = -1;
+                               continue;
+                       }
+
+                       tbl->valptr[i] = j;
+                       tbl->mincode[i] = tbl->huffcode[j];
+                       j += tbl->num_codes[i];
+                       tbl->maxcode[i] = tbl->huffcode[j - 1];
+               }
+       }
+
+       free(buf);
+}